> ## Documentation Index
> Fetch the complete documentation index at: https://docs.doers.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Protocol

> What the server implements, and the choices it makes.

The server implements the MCP specification **2025-11-25** over Streamable HTTP. It is stateless and
answers every request with plain JSON.

## Versions

| Accepted                                 | Preferred    |
| ---------------------------------------- | ------------ |
| `2025-11-25`, `2025-06-18`, `2025-03-26` | `2025-11-25` |

* No `MCP-Protocol-Version` header: the server assumes `2025-03-26`, as the specification says.
* A known version in the header: accepted.
* An unknown version in the header: `400`, with the accepted versions in `error.data`.
* In `initialize`, a known requested version is echoed back; an unknown one gets `2025-11-25`, and
  the client decides whether to continue.

## Methods

Every method needs a credential, `initialize` and `ping` included.

| Method                                                         | Behaviour                                                                                                          |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `initialize`                                                   | announces `tools`, `resources` and `prompts`, the server info (`doers`, version `2.0.0`) and the instructions text |
| `notifications/*`                                              | `202`, empty body                                                                                                  |
| `ping`                                                         | `{}`                                                                                                               |
| `tools/list`, `tools/call`                                     | the [tool reference](/mcp/reference/tasks)                                                                         |
| `resources/list`, `resources/templates/list`, `resources/read` | the [resources](/mcp/reference/resources-and-prompts)                                                              |
| `prompts/list`, `prompts/get`                                  | the [prompts](/mcp/reference/resources-and-prompts)                                                                |
| anything else                                                  | `-32601`                                                                                                           |

`completion/complete`, `logging/setLevel` and resource subscriptions are not implemented, and the
matching capabilities are not announced.

## Transport

| Request                   | Response                                      |
| ------------------------- | --------------------------------------------- |
| `POST /mcp`               | `application/json`, never `text/event-stream` |
| `GET /mcp`, `DELETE /mcp` | `405` with `Allow: POST, OPTIONS`             |
| `OPTIONS /mcp`            | `204` with CORS headers                       |

The server never issues an `Mcp-Session-Id`. `/mcp` and `/mcp/` both work.

**Batches.** JSON-RPC batches left the specification in `2025-06-18`, but a client that sends no
version header is assumed to speak `2025-03-26`, where they existed. The server accepts an array
without announcing it: it answers with an array, answers `202` to an array of notifications only,
and refuses an empty array with `400`.

## Origin

A request with an `Origin` header must come from a known web client: `doers.sh`, `claude.ai`,
`claude.com`, `chatgpt.com`, `chat.openai.com`, `platform.openai.com`, or the loopback address.
Anything else gets `403`. Native clients send no `Origin` and are never affected. This is defence
against DNS rebinding; the credential remains the real protection.

## Tool results

```json theme={null}
{
  "content": [
    {
      "type": "text",
      "text": "2 open task(s), 1 overdue, 1 due today.\nPriorities:\n- [0ebb429f-…] Send the Acme renewal quote · 2 days overdue, Priority P3"
    }
  ],
  "structuredContent": { "day": "2026-09-22", "counts": { "open": 2, "…": "…" }, "…": "…" }
}
```

* `content[0].text` is a compact rendering with the identifiers in it, so a client that ignores
  `structuredContent` still has everything it needs to act.
* `structuredContent` is the operation's full output. Every tool declares its `outputSchema`.
* The text is not a copy of the JSON, as the specification recommends: sending thirty tasks twice
  would double the tokens on the most frequent calls.

## Annotations

| Annotation        | How it is set                                                                     |
| ----------------- | --------------------------------------------------------------------------------- |
| `readOnlyHint`    | `true` for tools that change nothing                                              |
| `destructiveHint` | `true` for tools that remove content or access (33 tools)                         |
| `idempotentHint`  | `true` unless the matching REST route is a `POST`, with a few declared exceptions |
| `openWorldHint`   | `false` on every tool                                                             |

`openWorldHint` is `false` everywhere, including the calendar tools, which call Google Calendar, and
the coach tools that spend a model turn on your Anthropic key. Each tool's page in the reference
says which outside service it calls.

## Errors

| Situation                                                     | Answer                             |
| ------------------------------------------------------------- | ---------------------------------- |
| No credential, or a refused one                               | HTTP `401` with `WWW-Authenticate` |
| Origin not allowed                                            | HTTP `403`                         |
| Unsupported protocol version                                  | HTTP `400`                         |
| Body is not JSON                                              | HTTP `400`, `-32700`               |
| Not JSON-RPC 2.0                                              | `-32600`                           |
| Unknown method                                                | `-32601`                           |
| Unknown tool, resource or prompt; missing prompt argument     | `-32602`                           |
| **Invalid arguments**                                         | a result with `isError: true`      |
| **The operation refused** (not found, role too low, conflict) | a result with `isError: true`      |

Invalid arguments come back as a tool result rather than `-32602` on purpose: a model reads
"`when`: Invalid ISO date" and retries correctly, where a protocol error makes it give up. The
specification classes these as tool execution errors.
