Skip to main content
Platform MCP lets a human super admin drive the control plane from Cursor (or another MCP client). It is a dedicated HTTP server at POST /mcp, not a wrapper around GraphQL, and v1 is not a machine PAT. If you only remember one sentence: catalog metadata lives in shared, handlers live in the backend registry, portal governance is never an MCP tool.

Why this exists

Operators already have session governance in the portal. MCP is the same actions, same scopes, same finalizers — reachable from an agent session so a super admin can list and revoke credentials without clicking through the UI. The cage is RBAC: tools/list hides what you cannot call, and tools/call checks again.

Locked decisions

Sequence

OAuth authorize borrows portal cookies so the human can say yes. The minted credential is still audience=mcp. Do not put the portal access token in mcp.json.

Layout

Unknown input fields are rejected (z.object({}).strict() / additionalProperties: false). AllScopes members must be string literals — TypeScript will not let you initialize an enum from an imported constant.

Observability

Every tools/call (success and deny) logs a stable message, bumps mcp.tools.call with tags tool / outcome / server=platform, and writes mcp_tool_audits (actor, credential id, tool, outcome, correlation id, argument digest). Never raw tokens or raw args.

Adding a tool

Follow the checklist in apps/backend/src/mcp/AGENTS.md. Short version: shared catalog → defineMcpTool handler that calls a domain utility → register in buildDefaultPlatformMcpRegistry → contract tests for success, deny (canAccessScopes stub), and invalid input. Related auth model: Authentication §9.1. Session UI: /admin/super/sessions. Portal cockpit: /admin/super/mcp.