{
  "openapi": "3.1.0",
  "info": {
    "title": "PomoDeck MCP Server",
    "version": "1.5.7",
    "summary": "Local Model Context Protocol server exposed by the PomoDeck desktop app.",
    "description": "PomoDeck is a focus timer and day planner for Windows. The desktop app serves a Model Context Protocol endpoint on the user's own machine so an AI assistant can read their timer state, tasks, schedule and focus history, and, with a Pro licence, create or change tasks.\n\nThis is not a hosted API. The `servers` entry below is loopback on purpose: there is no pomodeck.com API host, no account and no server of ours in the request path. Nothing responds unless the PomoDeck app is running on the same machine as the client, and the endpoint stays closed until the user pairs a client in Settings, Integrations.\n\nOne path, `POST /mcp`, carries the whole surface as JSON-RPC 2.0. The 18 tools are not enumerated here because `tools/call` is a single path and every tool's JSON Schema is returned by `tools/list` at runtime; a static copy would only go stale. Human documentation: https://pomodeck.com/developers\n\nAn assistant cannot start, pause or skip the timer. There is deliberately no such tool, which is what makes the recorded sessions evidence of real work.",
    "contact": { "name": "AJ Studios", "url": "https://pomodeck.com/support" },
    "license": { "name": "Proprietary", "url": "https://pomodeck.com/eula" },
    "termsOfService": "https://pomodeck.com/eula"
  },
  "externalDocs": {
    "description": "PomoDeck developer docs: scopes, per-client config, error codes",
    "url": "https://pomodeck.com/developers"
  },
  "servers": [
    {
      "url": "http://127.0.0.1:{port}",
      "description": "The user's own machine. Loopback only. The port is configurable in the app; 1314 is the default.",
      "variables": { "port": { "default": "1314" } }
    }
  ],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    {
      "name": "mcp",
      "description": "Model Context Protocol, Streamable HTTP transport, protocol revision 2025-06-18."
    }
  ],
  "paths": {
    "/mcp": {
      "post": {
        "operationId": "mcpRpc",
        "tags": ["mcp"],
        "summary": "Send one MCP JSON-RPC message",
        "description": "Handles a single JSON-RPC 2.0 message and answers with a single `application/json` body. No SSE stream is opened, because every tool is request and response.\n\nSupported methods: `initialize`, `ping`, `tools/list`, `tools/call`. A message with no `id` whose method starts with `notifications/` is accepted and answered with 202 and no body.\n\nRequests whose `Origin` or `Host` header is not loopback are rejected with 403. This is the MCP-mandated DNS-rebinding defence, and it is why a web page cannot reach this endpoint even though it is HTTP.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/JsonRpcRequest" },
              "examples": {
                "initialize": {
                  "summary": "Handshake",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "initialize",
                    "params": {
                      "protocolVersion": "2025-06-18",
                      "capabilities": {},
                      "clientInfo": { "name": "example-client", "version": "1.0.0" }
                    }
                  }
                },
                "toolsList": {
                  "summary": "Fetch the 18 tool schemas",
                  "value": { "jsonrpc": "2.0", "id": 2, "method": "tools/list" }
                },
                "readTasks": {
                  "summary": "Read today's open tasks (read scope, free on every plan)",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 3,
                    "method": "tools/call",
                    "params": { "name": "get_tasks", "arguments": {} }
                  }
                },
                "createTask": {
                  "summary": "Create a task (write scope, needs Pro)",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 4,
                    "method": "tools/call",
                    "params": {
                      "name": "create_task",
                      "arguments": {
                        "title": "Draft the launch email",
                        "estimatedPomos": 2,
                        "quadrant": 2,
                        "scheduledFor": "2026-08-20",
                        "subtasks": ["Outline the three points", "Write it", "Read it back cold"]
                      }
                    }
                  }
                },
                "notification": {
                  "summary": "Notification, answered with 202 and no body",
                  "value": { "jsonrpc": "2.0", "method": "notifications/initialized" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A JSON-RPC response. Note that a tool failure also arrives here: it is reported inside `result` with `isError: true` so the model sees it, rather than as a JSON-RPC error.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/JsonRpcResponse" },
                "examples": {
                  "toolResult": {
                    "summary": "Successful tools/call",
                    "value": {
                      "jsonrpc": "2.0",
                      "id": 3,
                      "result": {
                        "content": [{ "type": "text", "text": "{\"tasks\":[]}" }]
                      }
                    }
                  },
                  "toolError": {
                    "summary": "Tool refused: the token lacks the scope",
                    "value": {
                      "jsonrpc": "2.0",
                      "id": 4,
                      "result": {
                        "content": [{ "type": "text", "text": "forbidden: scope 'write' required" }],
                        "isError": true
                      }
                    }
                  },
                  "methodNotFound": {
                    "summary": "Unknown JSON-RPC method",
                    "value": {
                      "jsonrpc": "2.0",
                      "id": 9,
                      "error": { "code": -32601, "message": "method not found: resources/list" }
                    }
                  }
                }
              }
            }
          },
          "202": { "description": "Notification accepted. No body." },
          "401": {
            "description": "Missing, unknown or revoked bearer token. JSON-RPC error code -32001.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/JsonRpcResponse" },
                "example": {
                  "jsonrpc": "2.0",
                  "id": null,
                  "error": { "code": -32001, "message": "missing bearer token" }
                }
              }
            }
          },
          "403": {
            "description": "The `Origin` or `Host` header is not loopback.",
            "content": { "text/plain": { "example": "bad origin" } }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "A token issued by the user in Settings, Integrations. Shown once; only its SHA-256 is stored. Format `pdk_` followed by 64 hex characters. Each token carries a subset of three scopes: `read` (implied by every token, free on every plan), `write` and `plan` (both require Pro). Writes are limited to 60 per minute per token; reads are not limited. Revoking a token takes effect on the next request."
      }
    },
    "schemas": {
      "JsonRpcRequest": {
        "type": "object",
        "required": ["jsonrpc", "method"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": {
            "description": "Omit only for `notifications/*` messages, which get 202 and no body.",
            "oneOf": [{ "type": "string" }, { "type": "integer" }]
          },
          "method": {
            "type": "string",
            "enum": ["initialize", "ping", "tools/list", "tools/call", "notifications/initialized"]
          },
          "params": { "type": "object", "additionalProperties": true }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": ["jsonrpc"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": { "oneOf": [{ "type": "string" }, { "type": "integer" }, { "type": "null" }] },
          "result": { "type": "object", "additionalProperties": true },
          "error": { "$ref": "#/components/schemas/JsonRpcError" }
        }
      },
      "JsonRpcError": {
        "type": "object",
        "required": ["code", "message"],
        "properties": {
          "code": {
            "type": "integer",
            "description": "-32700 parse error, -32601 method not found, -32603 internal error, -32001 authentication failure (the only one that also sets a non-200 status)."
          },
          "message": { "type": "string" }
        }
      },
      "ToolResult": {
        "type": "object",
        "description": "The `result` of a `tools/call`. The tool's JSON payload is stringified into the single text block.",
        "properties": {
          "content": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": { "type": "string", "const": "text" },
                "text": { "type": "string" }
              }
            }
          },
          "isError": {
            "type": "boolean",
            "description": "True when the tool refused or failed. The text begins with one of: forbidden, rate_limited, bad_request, not_found, invalid_plan, unknown_tool, internal."
          }
        }
      }
    }
  }
}
