Skip to content
Browse the documentation
Reference

MCP server and connectors directory

The MCP server assistants read verdicts and evidence from, the OAuth flow, the tool list with its annotations, and the Claude connectors directory listing.

Last changed Markdown versionllms.txt

The agent may declare its intent. It may read the verdict. It must never control the independent facts, the governing contract, or the final evidence.

Two pieces, built on that line:

  • The evidence server, inside the API at ${API_URL}/mcp: an assistant (Claude, ChatGPT, Cursor, an agent framework) reads actions, verdicts, reasons and evidence pointers, contracts, coverage and usage; validates, dry-runs and drafts contracts without saving; and, with the actions:record scope, declares an action before it happens.
  • The recorder, recorder/python and recorder/js: a stdio proxy in front of any MCP server an agent uses, which records a mapped tool call as an action before forwarding it. See the READMEs there.

Nothing on either surface writes a fact, saves or activates a contract, overrides a verdict, deletes anything, or reads a credential. No scope grants any of these.

Transport and auth

Streamable HTTP, stateless, JSON responses (json_response=true), mounted by app/api/main.py at the root so the OAuth metadata documents sit at the issuer. Bearer tokens of three kinds are accepted (app/mcp/auth.py):

Token Who Scopes
spk_… API key A service, an agent runtime, a local MCP client The key's scopes (Settings → API keys; keys from before scopes carry all)
cla_… OAuth access token A remote client a member approved on the consent page The grant, capped to the member's current role on every call
Session JWT The web app itself The member's role

Unauthenticated requests get 401 with WWW-Authenticate: Bearer resource_metadata=…/.well-known/oauth-protected-resource/mcp, which is how an OAuth-capable client discovers the authorization server.

Scopes (app/mcp/scopes.py)

actions:read, actions:record, facts:write, contracts:read, contracts:validate, contracts:draft, reconcile:read, connectors:read, usage:read. Presets: recorder (actions:record, actions:read), evidence (facts:write, actions:read, connectors:read, bound to named sources), reader (every read scope plus validate and draft), full (all). Owners, admins and analysts may hold every scope; viewers only the read scopes. A model-facing principal (an OAuth token, any bearer on this server) never holds facts:write: the model may declare its intent and read the verdict; the evidence arrives by another credential. Over REST an API key acts as an analyst when it may write and as a viewer otherwise, and each write endpoint checks its scope.

Tools

Tool Scope What it returns
get_action(action_ref) actions:read The action with both lenses, status, reason codes, checks, evidence pointers
list_actions(contract_id?, workflow_id?, verdict?, period?, limit) actions:read Actions with their latest verdict, newest first
pending_actions(limit) actions:read Provisional actions, what they wait on, when they settle
workflow_summary(period?) actions:read Per-contract counts for a month
record_action(contract_id, subject_id, …) actions:record The provisional verification and the allowance; channel: mcp
verify_hash(hash) actions:read Whether a statement / verdict / dispute hash exists and its chain holds
connector_health() connectors:read Connections with status, trust, provides, last sync, watermark; never credentials
usage() usage:read Plan, limits, features, usage, allowance
list_contracts(), get_contract(contract_id, version?) contracts:read Summaries; YAML, compiled rules, plain language, versions
validate_contract(yaml) contracts:validate Errors, compiled rules, plain language, weakening warnings
dry_run_verify(contract_id, period, yaml?, version?) contracts:validate Counts per lens, verified amount, reasons; nothing persisted
draft_contract(action_type? | contract_id?, …) contracts:draft Proposed YAML with statistics and warnings; nothing saved
list_reconciliations(vendor?, period?, limit), get_reconciliation(id) reconcile:read Reconciliation views (coverage, claims, run, statement totals)

Resources: commitlayer://reason-codes (every code with label and explanation), commitlayer://actions/{ref}. Prompts: investigate_failed_action(action_ref), review_contract_change(contract_id, yaml).

Every tool carries a title and the standard annotations (0.9.12). Fourteen are readOnlyHint: true, so a client such as Claude runs them without asking the person before each call; record_action is readOnlyHint: false, destructiveHint: false, idempotentHint: false (recording without an action_id twice is two actions). openWorldHint is false throughout. The hint must say what the scope enforces: tests/test_mcp.py fails when a tool's readOnlyHint disagrees with its _call scope, and the exported product-catalog/mcp-tools.yaml derives read_only and destructive from the annotations.

Errors are returned as content, never as a stack trace: {"error": {"code", "message", …}} with codes insufficient_scope (plus required_scope), plan_required (plus upgrade_to), not_found, invalid_request, invalid_contract (plus errors), or an engine code. Every call is audited as mcp.tool_called with object_id = tool name, the actor (apikey:<prefix>, <email> via <client_id>, or the email) and metadata {…args, ok, code}.

OAuth 2.1 (app/mcp/auth.py, app/api/routers/oauth.py)

The API is the authorization server. Endpoints (served by the MCP SDK): /.well-known/oauth-authorization-server, /.well-known/oauth-protected-resource/mcp, POST /register (dynamic client registration, open), GET /authorize (PKCE S256 required), POST /token (authorization_code, refresh_token), POST /revoke.

Flow: the client registers → /authorize redirects the browser to ${WEB_URL}/oauth/consent?req=<signed request, 10 min> → the signed-in member sees the client, the workspace and the requested scopes (GET /v1/oauth/consent), unticks what they do not want, approves or denies (POST /v1/oauth/consent {req, approve, scopes?}) → the answer carries redirect_to (the client's redirect URI with code and state, or error=access_denied) → the client exchanges the code with its verifier at /token.

Codes are single use and live 10 minutes; access tokens 1 hour; refresh tokens 30 days. A refresh rotates the pair and the old pair dies. Tokens are stored hashed (oauth_tokens), bound to the member and the workspace they chose; a member who leaves the workspace loses every token, and a token never outranks the member's current role. Client secrets are stored envelope-encrypted because the token endpoint compares them in full. Audit: oauth.approved {scopes, client_name}, oauth.denied.

The Claude connectors directory

The server meets the directory's published requirements (checked against the live API on 2026-11-02): remote Streamable HTTP over HTTPS, a 401 with resource_metadata on unauthenticated calls, protected-resource metadata naming /mcp as the resource, authorization-server metadata with PKCE S256 and open dynamic client registration (a client that registers as a public client gets no secret and is accepted), authorization-code and refresh grants, per-tool errors as content, titles and annotations on every tool. Submission is at claude.ai/directory/manage (Submit new → MCP connector) from an account on a paid Claude plan. What the form asks for, and the answers:

  • Connection: https://api.commitlayer.ai/mcp, a single URL.
  • Listing. Name: CommitLayer. One-liner (≤200): Read what your AI agents actually did: PASS, FAIL or UNKNOWN verdicts with the evidence, from the systems the action touched. Description (≤2000): the first paragraph of docs/mcp.md in plain words: CommitLayer verifies agent actions against independent systems of record (Stripe, Shopify, CRMs, your own events) and returns a verdict with the step that did not land; through this connector Claude reads recorded actions and their verdicts, reasons and evidence pointers, lists what is still pending and what each is waiting on, reads contracts, validates and dry-runs contract changes without saving, drafts a contract from recorded actions, checks connector health and the workspace's plan and usage, and, when the token carries actions:record, records an action before it is performed. It never writes a fact, changes a verdict, activates a contract or deletes anything. Categories: Developer tools, Productivity. Documentation URL: https://commitlayer.ai/mcp. Privacy policy URL: https://commitlayer.ai/legal/privacy. Support: support@commitlayer.ai. Icon: stateprf-ui/public/logo/icon-512.png. Slug: commitlayer (permanent once published).
  • Use cases, three prompts that work on the demo workspace: "Which of my agent's refunds failed this month, and why?" (list_actions with verdict FAIL, then get_action); "What is still waiting on a system of record, and when does it settle?" (pending_actions); "Draft an action contract from the refunds recorded in the last 30 days and tell me what limits it would set." (draft_contract). Prerequisites: a CommitLayer workspace (free tier). The connector reads; it writes only through record_action, on a token that carries that scope.
  • Authentication: required; OAuth with dynamic client registration (oauth_dcr); the person signs in to CommitLayer and approves the client for one workspace with the scopes they choose.
  • Data handling: the API is our own; no health data, no sponsored content.
  • Test & launch: a reviewer account with sample data. Create a member of the synthetic demo workspace (181 verified refund-workflow actions, connectors, contracts) with a password sign-in, enter its email and password in the portal's test-account card, and write: Add the URL as a custom connector, sign in with the credentials above when CommitLayer's consent page opens, approve the default scopes, then run list_actions with verdict: FAIL, get_action on one of the results, and pending_actions. Confirm every tool was run through MCP Inspector or as a custom connector first.
  • Compliance: the seven acknowledgments; the server collects nothing from the conversation, stores no conversation content, and calls no API it does not own.

A Community listing follows the automatic policy scan; a Verified one, where reviewers run every tool, is Anthropic's call later. Escalations: mcp-review@anthropic.com.

Testing

tests/test_mcp.py starts the API under uvicorn and drives it with the MCP Python client: tool catalog, every read tool, resources, audit rows, a read-only key refused record_action over MCP and POST /v1/actions over REST, a recorder key recording, a revoked key refused, metadata documents, and the full OAuth flow (registration, authorize, consent describe / deny / approve with trimmed scopes, wrong verifier, single-use code, refresh rotation, revocation).