3000:
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.Read full context
Use
fortress://action/<id>, fortress://project/<id>, and fortress://document/<id>.Mutate state
Use tools such as
heartbeat, complete_action, drop_action, ask_question, and update_document.Required startup reads
At the start of a session, an agent should read:fortress://workspace/overviewfortress://ordersfortress://order/<id>for any active order it will executefortress://action/<id>before doing a task
Error handling
MCP tool errors return structured content understructuredContent.error. The shape is:
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 stablecode 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 into401 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. |
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 afterMCP_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:
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. |
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 anoutputSchema 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 sameerror_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 theagent_id hint in the MCP resource URL to preselect the intended agent.
