The external MCP server
Cinatra’s MCP server is mounted at /api/mcp on the same Next.js app that serves the UI. There is no separate process. The same hostname, the same authentication surface, the same Postgres database.
This page covers what the server exposes, how the transport works, and how the primitive surface is organised.
Endpoint
Section titled “Endpoint”POST /api/mcp — JSON-RPC envelope, MCP transport. Streaming responses use the standard MCP transport mechanics; non-streaming responses return the JSON-RPC result inline.
The route is wired in src/app/api/mcp/route.ts and delegates to the mcpServerMount in src/lib/mcp-server.ts. The in-process deterministic clients described in Internal architecture share the same handler families and the same Zod schemas as the external surface, so external and internal callers see the same primitive catalog and the same validation rules.
Authentication
Section titled “Authentication”External clients authenticate with a Bearer JWT issued by Cinatra’s Better Auth OAuth-provider plugin. See Authentication for the OAuth flow, where to register a client, and the JWT format.
Primitive catalog
Section titled “Primitive catalog”The server exposes primitives across the platform’s domain packages:
- Agents — list, get, run, poll, resume, list runs, list messages.
- Lists & objects — typed CRUD plus list-membership operations.
- Skills — catalog, installed, personal, match-evaluation.
- Extensions — install, archive, restore, search.
- Connectors — each connector contributes its own operations (Gmail send, Apollo people search, WordPress create-draft, and so on).
- Dashboards — list, get, create, update, publish, archive.
- Metrics — cost and usage queries, time series, by-agent / by-provider rollups.
- Permissions — invite, member updates, platform-role changes.
- Chat — thread list, get, send, pause/resume the assistant.
The full live list comes back over the MCP tools/list call. Each primitive declares its input schema (Zod-validated) and a description; the LLM or the human reads those and picks the right one.
Primitive names follow the convention <domain>_<resource>_<action> — for example agent_run, accounts_list, wordpress_post_create_draft. See Primitives for the naming rules and the actor-context envelope every primitive receives.
Same primitive contract, different surfaces
Section titled “Same primitive contract, different surfaces”The MCP server is the one capability surface Cinatra exposes externally. The chat assistant inside the app uses it. The /agents/status UI uses it. The built-in agent runtime uses it. An external Claude Code instance uses it. A ChatGPT connector uses it. All these paths converge on the same primitive contract — same primitive names, same Zod input schemas, same handler functions.
This is intentional. There is no “internal API” that does more than the external one, and no “external API” with fewer capabilities than the internal one. When the chat assistant can call agent_run, you can too.
The path-specific bits — which actor envelope a call carries, where its audit row lands — depend on the entry point. Most authoritative-actor primitives (agents, dashboards, the agent-builder surface) propagate the OAuth/session actor end-to-end. A few connector and entity primitives currently register with a fixed actor ({ actorType: "model", source: "agent" }) intended for in-platform LLM tool use; running those over the external MCP surface still works for capabilities they don’t gate per-actor, but the per-actor authorization model is not uniform across every primitive yet. See Primitives for which categories propagate the actor and which don’t.
What the server does not do
Section titled “What the server does not do”A few capabilities live outside the MCP surface by design:
- Long-running streaming UIs. The agent run page streams AG-UI events over a separate SSE channel; the MCP primitive
agent_run_getis the polling/get pair, not a streaming event consumer. See Agent runs over MCP for the resume contract. - Authentication itself. Better Auth’s OAuth endpoints live at
/api/auth/*— the MCP server only consumes the JWTs they issue. - The realtime notifications feed. It lives at
/notificationswith PostgresLISTEN/NOTIFYpush, not over MCP.
Where to go next
Section titled “Where to go next”- The auth surface: Authentication
- The primitive contract: Primitives
- The internal architecture that backs these primitives: Internal architecture