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

# MCP reference

> How the Fortress MCP server is shaped.

Each Fortress agent connects to the MCP server with its own scoped URL. Most local clients use a bearer token; OAuth-capable remote clients can use Fortress's OAuth flow.

```text theme={"theme":"github-dark"}
https://api.fortressproductivity.com/mcp?agent_id=<agent-id>
Authorization: Bearer ft_...
```

Local development uses the API origin running on port `3000`:

```text theme={"theme":"github-dark"}
http://localhost:3000/mcp?agent_id=<agent-id>
Authorization: Bearer ft_...
```

## Server identity

| Field       | Value                                                        |
| ----------- | ------------------------------------------------------------ |
| Name        | `fortress`                                                   |
| Title       | `Fortress`                                                   |
| Description | Mission control for a human and their bring-your-own agents. |
| Transport   | HTTP MCP endpoint                                            |
| Auth        | Bearer token or OAuth access token scoped to one agent       |

## Resources before tools

Agents should use resources for reads when they know the URI. Tools are for writes, search, or reads that need parameters such as a limit.

<Columns cols={2}>
  <Card title="Read full context" icon="book-open">
    Use `fortress://action/<id>`, `fortress://project/<id>`, and `fortress://document/<id>`.
  </Card>

  <Card title="Mutate state" icon="wrench">
    Use tools such as `heartbeat`, `complete_action`, `drop_action`, `ask_question`, and `update_document`.
  </Card>
</Columns>

## Required startup reads

At the start of a session, an agent should read:

1. `fortress://workspace/overview`
2. `fortress://orders`
3. `fortress://order/<id>` for any active order it will execute
4. `fortress://action/<id>` before doing a task

## Error handling

MCP tool errors return structured content under `structuredContent.error`. The shape is:

```json theme={"theme":"github-dark"}
{
  "structuredContent": {
    "error": {
      "code": "already_terminal",
      "message": "Action is already complete."
    }
  }
}
```

Agents should branch on the stable `code` field, not natural-language messages. Examples include:

| Code                       | Meaning                                                                                  |
| -------------------------- | ---------------------------------------------------------------------------------------- |
| `already_terminal`         | Another actor already completed or dropped the action. Refetch and stop retrying.        |
| `not_agent_actionable`     | The action is no longer ready work for this agent.                                       |
| `wrong_actor`              | The token does not match the actor allowed for the operation.                            |
| `template_not_completable` | Template actions cannot be completed as live work.                                       |
| `TOO_MANY_IDS`             | A batch read such as `get_actions` exceeded the 100-id cap. Split the request and retry. |

### Uniform error boundary

The same structured envelope is enforced across every MCP surface — tools, prompts, resources, and elicitation responses. Unexpected server failures and elicitation rejections are caught at the MCP boundary and returned as a structured error with a stable `code` instead of an opaque transport-level 500. Agents can rely on parsing `structuredContent.error.code` for any failure path, including ones that previously surfaced as raw HTTP errors.

### Transport status codes

When a request cannot reach the structured envelope at all, Fortress picks an HTTP status that reflects the real failure category instead of collapsing everything into `401` or `500`. Agents and orchestrators can use the status to decide whether to refresh credentials, retry with backoff, or surface the failure to the operator.

| Status | Meaning                                                                                                                                                                                                                           |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401`  | The bearer token or OAuth access token is missing, expired, or scoped to a different agent. Re-authenticate before retrying.                                                                                                      |
| `404`  | The `mcp-session-id` is no longer live. The error envelope carries `data.details.reason` so the agent can branch on the typed reason instead of retrying the same request — see [Session lifecycle](#session-lifecycle).          |
| `503`  | A transient backend dependency failed — for example, database or KMS unavailability during session init, or an upstream option-fetch timeout while resolving an elicitation. Retry with backoff.                                  |
| `5xx`  | An internal MCP client received a non-JSON upstream response. The original status and a body preview are preserved in the error message instead of being rewritten to `500`, so logs reflect what the upstream actually returned. |

### Session lifecycle

Long-lived agents (Claude Code in a persistent shell, scheduled-job runners) regularly outlive their MCP session. Fortress sweeps idle sessions after `MCP_SESSION_MAX_IDLE_MS` (default 30 minutes), evicts the oldest sessions when concurrent sessions exceed `MCP_MAX_SESSIONS` (default 40), and honors client-initiated `DELETE` closes.

Any subsequent request bound to a closed session returns a JSON-RPC `-32001` 404 with structured `data.details`:

```json theme={"theme":"github-dark"}
{
  "jsonrpc": "2.0",
  "id": 42,
  "error": {
    "code": -32001,
    "message": "Unknown MCP session",
    "data": {
      "details": {
        "reason": "idle_timeout",
        "hint": "reinitialize the MCP session"
      }
    }
  }
}
```

`reason` is one of:

| Reason          | Cause                                                                                        |
| --------------- | -------------------------------------------------------------------------------------------- |
| `idle_timeout`  | The session was swept after exceeding `MCP_SESSION_MAX_IDLE_MS` of inactivity.               |
| `session_cap`   | The session was evicted as the oldest entry once concurrent sessions hit `MCP_MAX_SESSIONS`. |
| `client_closed` | The client itself closed the session (e.g. an MCP `DELETE` against the session id).          |
| `unknown`       | The session id was never recognized — typically a misconfigured or replayed agent header.    |

Agents should treat any of these as a signal to send a fresh `initialize` and replay the request against the new session id, rather than retrying the same request on the closed id.

Mutation tools always commit before projecting their response. If a mutation succeeds but the follow-up read drifts (for example, a stale read replica), the tool still returns success and the drift is reported separately so agents do not retry an already-applied write.

### Response shape guarantee

Every tool that publishes an `outputSchema` is contractually bound to it. Composite tools — the mutation-then-projection tools that return a freshly read view alongside the write — validate their projected payload against the declared schema before responding. A projection that would otherwise drift off-schema (extra, missing, or wrong-typed fields from a stale read) is rejected at the boundary and surfaced through the standard structured error envelope rather than being passed through to the agent. Agents can rely on `structuredContent` matching the documented `outputSchema` on success, so generated clients and typed parsers do not need defensive fallbacks.

### Failure analytics

Every MCP tool failure is also tagged in Fortress analytics with the same `error_code` that appears in the structured response. Operators reviewing telemetry can group failures by `error_code` to see which codes (for example, `already_terminal` versus `wrong_actor`) drive the bulk of agent retries, without parsing free-text messages.

## Subscriptions

The MCP server supports resource subscriptions. Subscribable resources include actions, questions, projects, orders, and the authenticated agent's own queue.

Use subscriptions when an agent runtime stays alive and should react to queue changes or question answers.

## OAuth clients

Fortress also exposes OAuth 2.1 flows for MCP clients that discover and register dynamically. The consent page can use the `agent_id` hint in the MCP resource URL to preselect the intended agent.
