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

# MCP conventions

> How the MCP server behaves: one transport, the same key, the same answers as the API, request ids, rate-limit headers and refusals.

The MCP server and the HTTP API are **one surface with two doors**. The same
actions, the same arguments, the same answers and the same rules — so
everything in [API conventions](/api/conventions) holds here too. This page
covers what is specific to MCP.

## The connection

| | |
| - | - |
| Server URL | `https://app.goosybear.ai/api/mcp/mcp` |
| Transport | Streamable HTTP, one message per request. Server-sent events are not used. |
| Authorization | `Bearer <key>` — the same key the API takes. There is no OAuth sign-in. |
| Capabilities | Tools only. The server offers no prompts and no resources. |

Setting up a particular client: [Connect any other client](/connect/any-client).

## Tools are the API's actions

Each tool is one API action with the same name — `credits.balance` over MCP is
`POST /api/v1/tools/credits.balance` over HTTP — and takes the same arguments,
including `workspace`. A tool's answer is the same JSON the API returns, with
`ok` saying how it went.

## What each tool tells your client

Every tool in `tools/list` carries **annotations** your client can use to decide
how carefully to call it:

| Annotation | Set to `true` when |
| - | - |
| `readOnlyHint` | The tool only reads. |
| `destructiveHint` | The tool does something that cannot be undone. Anything else says `false`. |
| `idempotentHint` | Calling again with the same arguments has no further effect: a read, or a write that takes an `idempotency_key`. |

Every tool result carries the answer twice: as `structuredContent`, a JSON
object, and as a text block holding the same JSON. Read whichever your client
supports; they never differ.

## Retrying and paging

* **Retrying a write:** pass the same `idempotency_key` argument again. MCP has
  no per-call header, so the argument is how an assistant retries safely.
* **Paging a list:** pass the result's `next_cursor` back as `cursor` until no
  `next_cursor` comes back.
* **Rotating the key:** `keys.rotate` works over MCP too. It takes two calls, and
  the new key comes back once in `key`. See
  [Rotating a key](/api/conventions#rotating-a-key).

## When a tool call fails

There are two ways a call can fail, and they look different on purpose:

* **Turned away at the door** — no key, access switched off, too many calls.
  The HTTP request itself is refused (`401`, `403`, `429` …) with a body of
  `{ "ok": false, "code": "…", "message": "…" }`. Most clients show that body
  as the connection error.
* **The tool answered no** — the call got through and the tool declined. The
  tool result carries `ok: false`, a `code` and a `message`.

`isError` is set on a tool result when the call was stopped before the tool ran
— for example, when it could not tell which workspace to use. A tool that ran
and declined sets `ok: false` without `isError`. **Read `ok` in the result for
the outcome**; `isError` is a hint for clients that only look at the flag.

## Request ids and rate-limit headers

Every HTTP response from the MCP server carries an `x-request-id` header — the
one your client sent, or one we generated — and, whenever the call counted
against your key, the `RateLimit-Limit`, `RateLimit-Remaining` and
`RateLimit-Reset` headers. Each message an assistant sends counts once against
the key's per-minute allowance. See [Rate limits](/api/rate-limits).
