Documentation

Developer documentation

Everything to go from an OpenAPI spec to a live, governed MCP server — and to call it from an agent.

Quickstart

The fastest way to see a governed MCP server working is to connect Claude to one that's already live — no signup, no API key. This uses Cat Facts, one of 5 public example servers running on the platform, and takes under 5 minutes.

  1. In Claude, open Settings → Connectors → Add custom connector.
  2. Paste in the MCP endpoint URL (name it whatever you like, e.g. "Cat Facts"):
    https://mcpplatform.dev/mcp/srv_c7dcf1cd45
  3. Save the connector. Claude calls initialize and tools/list behind the scenes and discovers one tool, get_cat_fact.
  4. Ask Claude something that makes it reach for the tool:
    Use the Cat Facts connector to tell me a random cat fact.

Claude issues a tools/call and gets real content back over the same JSON-RPC connection:

POST /mcp/srv_c7dcf1cd45 { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "get_cat_fact", "arguments": {} } } → 200 { "result": { "content": [{ "type": "text", "text": "{\"fact\": \"Cats only sweat through their paws...\", \"length\": 65}" }], "isError": false } }

Claude reads that JSON and answers you in plain English with the fact — that's the whole loop: connect, discover tools, call, respond.

Zero setup: srv_c7dcf1cd45 is one of 5 public, no-auth example servers live on the platform right now — safe to connect to directly, nothing to configure or sign up for.

Prefer to see the raw request/response before opening Claude? Try the live sandbox — it fires the same tools/list/tools/call requests straight from your browser, no signup required.

Ready to publish your own governed MCP server from an OpenAPI spec instead of a demo one? Sign up to create a tenant, then pick up at Import a contract below.

Core concepts

Tenant

An isolated customer workspace. All servers, contracts, tools, and audit belong to exactly one tenant, enforced at the database layer.

Contract

An OpenAPI spec or REST base URL you register. Each operation is mapped to a tool.

MCP server

A published, versioned bundle of tools, resources, and prompts that an agent installs by URL.

Policy decision point

The runtime component that decides allow, allow_with_confirmation, allow_with_approval, or denied for every call.

Import a contract

In the console go to Build → Contracts → Import. Provide a spec URL or paste JSON/YAML, choose an environment, and we validate it. The validation report flags missing schemas and risky defaults before publish.

Configure policy

For each tool set its risk class and required scopes. The mapping to decisions:

  • Read-only within scope → allow
  • Financial / side-effectingallow_with_confirmation
  • Destructiveallow_with_approval (held for a human)
  • Missing scope or over rate limitdenied

Connect credentials

Under Secure → Credentials, connect an OAuth provider or store a secret. The runtime brokers short-lived downstream tokens per call — the agent never sees the raw key.

API reference

GET /v1/bootstrap

Returns the full tenant-scoped dataset the console renders — tenants, servers, tools, contracts, audit, and reference docs. Tenant-scoped via RLS.

GET /mcp/:serverId/tools/list

Lists a server's tools (name, description, method, path, risk, scopes). Discovery only — no side effects, not billed.

POST /mcp/:serverId/tools/call

Invokes a tool. Body: { toolName, args, ctx? }. Returns the policy decision, the downstream response, and the audit row written.

POST /mcp/mcp_msg/tools/call { "toolName": "delete_message", "args": { "sid": "SM1" } } → 200 { "result": { "decision": "allow_with_approval", ... } }

Runtime & decisions

Each call runs a fixed pipeline: resolve tenant (RLS scope) → validate scope → rate limit → policy decision → broker credentials → invoke downstream → append audit. A denial short-circuits before any downstream call is made.

Security & RLS

The application connects to Postgres as a non-privileged role subject to Row-Level Security. Every tenant-owned table carries a tenant_id and a policy:

CREATE POLICY tenant_isolation ON tool USING (tenant_id = current_setting('app.tenant_id', true)) WITH CHECK (tenant_id = current_setting('app.tenant_id', true));

Because RLS is enforced by the database, a query that forgets its WHERE clause still cannot read or write another tenant's rows. Migrations run as a separate BYPASSRLS owner role.

Verify it yourself: request /v1/bootstrap with a different X-Tenant-Id and you'll see zero rows from other tenants.

Self-host on GCP

The platform runs on Cloud SQL for PostgreSQL (multitenant store), Cloud Run (control + data plane), a global HTTPS load balancer with Cloud Armor, and Cloud KMS for the vault. See the full runbook in doc/gcp-deployment-runbook.md.