MeridiansMeridians

MCP runtime — transports, trust, and attribution [Flow]

From the Meridians Wiki · Public · Maintained · joint

How local and remote clients reach one generated tool catalog, and where authority becomes an attributed record write. Capability navigation is documented separately in MCP action chains.

flowchart LR
    LocalClient(["Local AI client<br/>Claude Code · Codex · IDE"])
    RemoteClient(["Remote AI client<br/>Claude · ChatGPT · IDE"])

    LocalClient -->|"stdio · machine boundary"| Stdio["mcp/server.ts<br/>role pinned at startup"]
    RemoteClient -->|"Streamable HTTP<br/>expiring Bearer + session id"| Http["Gateway /api/mcp<br/>verify token + entitlement<br/>bind session to Director"]

    Stdio --> Catalog["shared mcp/catalog.ts<br/>manifest → first-class tools"]
    Http --> Catalog

    Stdio -->|"loopback /api/local/*"| Daemon
    Http -->|"outbound SSE relay"| Instance["Director's instance<br/>admit signed-in Director"]
    Instance -->|"resolve canonical Director<br/>redact People read"| Daemon["daemon query · control · mutate · op"]

    Daemon --> Reads["Tier 0<br/>pure queries"]
    Daemon --> Control["Control plane<br/>Exchange · Account · Platform admin"]
    Control --> Scope{"declared scope<br/>director / admin / owner"}
    Scope -->|"resolved from the cached<br/>platform verdict"| Central[("central plane<br/>Supabase · Stripe")]
    Daemon --> Ops["Tier 2<br/>effectful operations"]
    Daemon --> Writer["Tier 1 terminus<br/>applyLocalMutation"]
    Ops -->|"typed actions"| Writer
    Writer --> Gate["gate → reduce → persist<br/>→ attribute → echo"]
    Gate --> Record[("canonical on-disk record")]
    Gate --> Activity[("activity journal<br/>Director id · name · role")]
    Ops --> Usage[("provider-usage receipts<br/>same Director actor")]

Trust invariants

  • Remote requests re-check token expiry and entitlement; an MCP session cannot be reused by another Director.
  • Key rotation never strands live tokens. An instance verifies a bearer against a SET of gateway public keys (current,previous, selected by kid, via verifyMcpTokenAny), so rotating the gateway signing key stays invisible to holders; MCP_JWT_PUBLIC_KEY accepts the comma-separated set and a redeploy re-bakes it. An admin cross-instance lane (owner > admin > director) exists for fleet diagnosis, with every access written to an admin-access trail on the instance. Fleet-side runbook: FLEET_OPS.md 🔒.
  • A control action declares its scope, checked locally before the call leaves the process — so a Director calling an admin tool learns why, not just that it failed. The remote route stays the authority and re-checks; the local gate is fail-fast, never the decision. See platform model.
  • Authed gateway calls take one redirect manually: fetch strips Authorization across origins, so a gateway URL that canonicalises (apex → www) would otherwise arrive with no credential and 401. The Bearer is re-attached only for a same-site hop over an equally-secure scheme (lib/core/access/gateway-redirect.ts).
  • The relay ignores body.actor. Authority comes from the verified connection; identity comes from the instance People registry.
  • Provider receipts use per-operation async context, so concurrent work on different domains cannot swap Director or scope attribution.
  • Remote permission assignment receives only member id, name, and role. Credentials and contact fields stay local.
  • HTTP focus is session memory. A process restart requires a new initialize call; it cannot lose canonical domain state.
  • Long operations resume identically on both transports: one shared OperationRuntime + operation lane, with only the event stream differing (stdio drains the daemon's SSE; remote runs the op in-process). A long op returns { status: "running", operationId } before the client deadline, and meridians_op_status/meridians_op_cancel poll or stop it either way — a cancel aborts the underlying op, re-checked at every emit/commit boundary. Operation ids are process-local: a restart invalidates them, which is a prompt to reconcile the record, never to blind-retry (see mcp/README.md).
  • Per-actor throttle: /api/mcp meters every authenticated actor (mcpSessionActorKey) through a process-local token bucket and refuses an exhausted one with a JSON-RPC error at HTTP 429. It is a spend backstop on one box, not a platform-wide allowance (SECURITY.md 🔒).