MCP Gateway is the tool federation layer that turns CyberOS's 24 modules into a single, coherent MCP server. Each module publishes a per-module server (named cyberos.memory, cyberos.skill, cyberos.crm, ...) that exposes its verbs as tools (memory.update_memory, skill.execute_skill, crm.update_account, ...); the gateway aggregates these into a federated surface that Claude / Codex / Cursor / Cline / any 2025-11-25-spec client sees as one server. OAuth 2.1 + PKCE (RFC 7636) gates every tool call; the audience claim pins the call to a specific module; the RBAC predicate from AUTH enforces who can do what; the persona-version stamp captures which agent authored the call. Tool annotations (destructive / readOnly / idempotent / openWorld) drive human-confirm gating. The Tasks primitive handles long-running work; Elicitation reverses control mid-execution to ask the user a question.

The bigger picture - three strategic roles

MCP Gateway is "the external-agent door." That sentence does a lot of work. Without this module, every external agent (Claude Code on a Member's laptop, Cursor on an engineer's IDE, Codex in CI) would need 24 separate OAuth flows + 24 separate discovery loops + 24 separate audit emissions. The naive design - "one MCP server per module, agents figure it out" - is intractable past 3 modules. MCP Gateway collapses 24 to 1 at the edge while keeping per-module ownership intact behind the federation.

Role 1 - external-client federation

24 modules -> one MCP server. Claude Code, Cursor, Codex, Cline, and any 2025-11-25-spec client hit a single gateway whose OAuth discovery is served at https://mcp.cyberos.com/.well-known/oauth-protected-resource (RFC 9728, which names the authorization server). The gateway aggregates per-module tool catalogues via SEP-986 naming (cyberos.memory.update_memory, cyberos.proj.create_issue, etc.) so external agents see one server. Per-module servers stay separate behind the edge; federation only happens at the surface. This means Members can use the AI tools they already trust without learning a new orchestrator.

Role 2 - capability broker

Every external invocation gated. Audience-bound OAuth 2.1 + PKCE tokens carry tenant_id + agent_persona + scope_grants per AUTH §2.7. Tool annotations (destructive / readOnly / idempotent / openWorld) drive gating: destructive ops require either an explicit confirmation token in the request or an Elicitation flow that asks the user. The persona stamp on every audit row makes "external agent X did Y" replayable down to which Claude/Cursor session authored it.

Role 3 - tool-discovery surface

The registry external agents introspect. tools/list returns ~80 tools at P0 (~300+ at P3); prompts/list exposes module-specific prompt templates; resources/list exposes memory paths the agent can read. Long-running work (KB ingestion, big migrations, bulk imports) is tracked via the Tasks primitive - the agent can start a task, poll status, get a streaming progress update, and resume on reconnect. Elicitation reverses control mid-execution to ask "should I proceed with the destructive step?".

MCP Gateway in the runtime - federation map

flowchart TB
  subgraph clients["External MCP clients (2025-11-25)"]
    CC["Claude Code"]
    CURSOR["Cursor"]
    CODEX["Codex / GPT-5 agent"]
    CLINE["Cline"]
    OTHER["any compliant agent"]
  end
  MCPGW["MCP Gateway<br/>OAuth 2.1 / Streamable HTTP / Tasks / Elicitation"]
  subgraph perm["Per-module MCP servers (internal)"]
    BRSV["cyberos.memory"]
    SKSV["cyberos.skill"]
    PRSV["cyberos.proj"]
    CRMSV["cyberos.crm"]
    KBSV["cyberos.kb"]
    N["... 19 more"]
  end
  subgraph dep["Platform deps"]
    AUTH["AUTH<br/>scope_grants check"]
    AIGW["AI Gateway<br/>persona stamp resolution"]
    memory["memory<br/>audit chain"]
    OBS["OBS<br/>traces"]
  end
  CC --> MCPGW
  CURSOR --> MCPGW
  CODEX --> MCPGW
  CLINE --> MCPGW
  OTHER --> MCPGW
  MCPGW -. "JWT audience verify" .-> AUTH
  MCPGW -. "fetch persona-version" .-> AIGW
  MCPGW --> BRSV
  MCPGW --> SKSV
  MCPGW --> PRSV
  MCPGW --> CRMSV
  MCPGW --> KBSV
  MCPGW --> N
  MCPGW --> memory
  MCPGW --> OBS
  classDef hub fill:#cffafe,stroke:#0e7490,stroke-width:3px,color:#164e63
  classDef client fill:#e0e7ff,stroke:#3730a3
  classDef internal fill:#f5ede6,stroke:#45210e
  classDef dep fill:#fef6e0,stroke:#9c750a
  class MCPGW hub
  class CC,CURSOR,CODEX,CLINE,OTHER client
  class BRSV,SKSV,PRSV,CRMSV,KBSV,N internal
  class AUTH,AIGW,memory,OBS dep

External clients never reach per-module servers directly. The gateway is the only external surface; this is the protocol-level guarantee that auth + audit + persona-stamp are uniformly applied.

Auto vs human-in-loop operations matrix

OperationHow it happensWhy this split
Tool discovery (tools/list)Auto - filtered by the client's scope_grantsAgent only sees tools it could invoke; reduces the surface area shown.
Read-only tool call (annotation: readOnly)Auto when scope_grants permitRead is low-risk; latency-sensitive; the agent flow shouldn't break.
Idempotent write (annotation: idempotent)Auto with idempotency-keyRetry-safe; the idempotency-key prevents double-write on transport retry.
Non-idempotent write (default)Auto with scope check + audit emitStandard write path; the broker checks scope + writes pre/post audit rows.
Destructive op (annotation: destructive)Human-confirm always - explicit token or ElicitationEU AI Act Art. 14 + safety policy; cannot be auto-invoked regardless of confidence.
Long-running work (Tasks primitive)Auto-start, polled by agent, resumable on reconnectBig ingests, migrations, bulk imports - must survive network blips.
Mid-execution question (Elicitation)Server-initiated; agent forwards to userReverses control: the server asks "should I proceed?" mid-call.
Tool catalogue update (list_changed notification)Auto; pushed to subscribed clientsNew tools appear without a client re-poll; old tools removed live.
OAuth token refreshAuto via the standard refresh-token flowSession continuity for long-lived agent sessions.

Why MCP Gateway exists

Per-module MCP servers create N integration points where every AI agent has to negotiate auth, discover tools, and handle errors. As N grows to 24, the agent's job becomes intractable. The gateway pattern collapses N into 1: agents see a single MCP server with one OAuth flow, one tool catalogue, one discovery endpoint. The 24 modules continue to publish their per-module tools; the gateway federates them. Naming (cyberos.memory.update_memory) preserves the module origin so audit + revocation remain per-module.

The bet: pay the cost of one good federation once. Without MCP Gateway, every agent client (Claude Desktop, Cursor, Cline, custom) has to maintain its own list of N module URLs, N OAuth registrations, N rate-limit handlings. With MCP Gateway, that list collapses to one URL - and replacing a module is a federation-registry update, not a client release.

What it does - 5W1H2C5M

This table is the working summary.

AxisQuestionAnswer
5W - WhatWhat is MCP Gateway?A Rust-axum service that implements the MCP 2025-11-25 spec on the edge, federates tool calls to per-module backends, enforces OAuth 2.1 PRM, applies tool annotations, manages Tasks for long-running work, proxies Elicitation prompts, and emits one audit row per call.
5W - WhoWho calls it?External agents (Claude Desktop / Cursor / Cline / Codex) and internal agents (CUO when invoking Skill, scheduled tasks). Owner: CTO seat (interim CEO).
5W - WhenWhen is it hit?(a) at agent session start (discovery + auth); (b) on every tool call. P0 expected RPS: ~20/s peak; P3+: ~200/s peak.
5W - WhereWhere does it run?Fargate task in SG-1 (P0); multi-region active-active at P3+. TLS terminated at the ALB; HTTP(S) forwarding to per-module backends.
5W - WhyWhy a gateway?Because N agent clients x N modules = N^2 OAuth registrations otherwise. Federation collapses to N+1.
1H - HowHow does it work?Agent does OAuth 2.1 PKCE -> gateway issues an audience-bound token -> agent calls tools/list -> gateway aggregates from federated servers -> agent calls a tool -> gateway validates audience + scope + annotation -> forwards the call over HTTPS to the module's registered MCP endpoint -> streams the response back -> audit row written.
2C - CostCost?Negligible - Fargate task ~$30/month at P0. The gateway adds ~3 ms per tool call; the audit write is the main cost.
2C - ConstraintsConstraints?(a) MCP 2025-11-25 spec compliance. (b) PKCE-only (no implicit grant). (c) Destructive tools MUST require human-confirm. (d) Persona-version MUST be stamped in audit (task pending). (e) Tool names MUST follow SEP-986 verbNoun.dotted.
5M - MaterialsStack?Rust, axum + jsonrpsee (the MCP JSON-RPC 2.0 surface), reqwest (forwards tools/call to each module's registered MCP endpoint), sqlx + PostgreSQL, jsonwebtoken + argon2 + chacha20poly1305 (OAuth 2.1 tokens + payload sealing), OpenTelemetry, serde_json.
5M - MethodsMethod choices?Streamable HTTP transport (chunked transfer). SSE for server-to-client events. Tasks primitive over a polling endpoint. Elicitation as an inline request/response interrupt. Tool registry as a DB-backed catalogue + in-memory cache.
5M - MachinesDeployment?Fargate (2 CPU, 4 GB); behind an ALB with WAF. Per-module backends are also Fargate, addressed by Cloud Map service-discovery.
5M - ManpowerWho maintains?0.3 FTE CTO + 0.2 FTE CSO at P0. Each module owner extends the tool catalogue for their module.
5M - MeasurementHow measured?N(task pending) (read tool p95 <= 500 ms) + N(task pending) (write tool p95 <= 1 s). Spec compliance via the MCP conformance test suite. Audit completeness at 100%.

External-client federation - 24 modules behind one surface

An external agent never knows it's talking to 24 modules. From the agent's perspective, https://mcp.cyberos.com is one MCP server with ~80 tools at P0 (~300+ at P3). The federation is at the edge - per-module servers (cyberos.memory, cyberos.skill, etc.) stay separate inside the cluster, but the gateway is the only thing external clients reach. This section locks the federation contract.

SEP-986 naming convention (per-module ownership preserved)

Tool names follow the cyberos.{module}.{verb}_{noun} SEP-986 convention. The module prefix preserves ownership for audit + revocation; the verb_noun form aligns with MCP convention.

PatternExampleWhat it doesOwning module
cyberos.{module}.update_*cyberos.memory.update_memoryCreate or replace a resource (idempotent)memory
cyberos.{module}.get_*cyberos.memory.get_memoryRead a resourcememory
cyberos.{module}.list_*cyberos.proj.list_issuesList resources (paginated)PROJ
cyberos.{module}.create_*cyberos.proj.create_issueCreate a new entityPROJ
cyberos.{module}.update_*cyberos.crm.update_accountMutate an existing entityCRM
cyberos.{module}.execute_*cyberos.skill.execute_skillTrigger a skill / actionSkill
cyberos.{module}.delete_*cyberos.proj.delete_issueTombstone an entity (destructive)PROJ
cyberos.{module}.generate_*cyberos.memory.generate_dsarExport bulk data (destructive sensitivity)memory

Per-module server registration flow

Each module's MCP server registers itself with the gateway at startup. Registration carries its tool catalogue (with annotations), prompt templates, and resource paths. The gateway maintains a versioned catalogue and pushes list_changed notifications to subscribed clients when modules deploy.

sequenceDiagram
  autonumber
  participant M as cyberos.memory (per-module server)
  participant G as MCP Gateway
  participant DB as Catalogue DB
  participant CL as Subscribed clients (Claude, Cursor)
  participant BR as memory audit
  M->>G: register_module(name, tools[], prompts[], resources[], version)
  G->>DB: upsert module record + tool catalogue
  G->>BR: emit mcp.module_registered audit row
  G->>CL: notification tools/list_changed
  Note over G,CL: subscribed agents re-fetch tools/list
  G-->>M: registered (catalogue_version, registration_token)
  M->>G: heartbeat every 10s (keep-alive)
  Note over G: missed 3 heartbeats -> module marked unhealthy -> tools removed from federation

External client compatibility matrix

ClientMCP spec versionOAuth 2.1Streamable HTTPTasksElicitation
Claude Code2025-11-25yesyesyesyes
Claude Desktop2025-11-25yesyesyesyes
Cursor2025-11-25yesyesyesyes
Codex / GPT-5 agent2025-11-25yesyespartialyes
Cline2025-11-25yesyesyespartial
Older 2024-11-05 clients2024-11-05partialSSE-onlynono

The gateway negotiates the highest mutual version at session init. Older clients fall back to SSE transport + bearer-token auth (no PKCE). The older-client tool surface excludes the Tasks and Elicitation features.

Capability broker - every external invocation gated

The threat model is precise: an external agent (running in Claude Code on a Member's laptop) carries an OAuth token; the agent is fundamentally untrusted (the user might have approved a malicious tool, the agent process might be compromised, the prompt might be injected). The capability broker is the protocol-level guarantee that even a fully-compromised agent cannot escape the bounds of the user's authorised scope.

Tool annotations - what drives gating

AnnotationMeaningGating behaviourExample tools
readOnly: trueNo state mutation. Idempotent always.Auto-invoke if scope permits; no confirm neededcyberos.memory.get_memory, cyberos.proj.list_issues, cyberos.kb.search
idempotent: trueWrite that can be safely retried with the same args + idempotency-keyAuto-invoke if scope permits; idempotency-key requiredcyberos.proj.update_issue, cyberos.crm.update_account
destructive: trueIrreversible: purge / delete / send-money / public-postHuman confirmation required - explicit confirmation_token in args OR an Elicitation flowcyberos.memory.delete_memory{mode: purge}, cyberos.proj.delete_engagement, cyberos.email.send
openWorld: trueReaches external systems (third-party APIs, web fetch)Auto-invoke with an additional rate limit + content filtercyberos.web.fetch_url, cyberos.zoominfo.update_contact
longRunning: trueReturns a task_id; agent polls for completionAuto-start as Task; agent polls; timeout default 1 hcyberos.kb.create_corpus, cyberos.memory.generate_dsar
elicits: trueServer may ask the agent for additional input mid-callAgent must support Elicitation; falls back to a user promptcyberos.cuo.execute_routing, cyberos.crm.create_deal_interactive

Audience-bound OAuth - the broker's auth contract

{
  "iss": "https://auth.cyberos.io/<tenant>",
  "sub": "user:stephen@cyberskill.world",
  "aud": "mcp.cyberos.com",          // audience-bound: this token is ONLY valid at MCP Gateway
  "iat": 1763112131,
  "exp": 1763115731,
  "tenant_id": "org:cyberskill",
  "agent_persona": "claude-code@user-stephen",  // which external agent is calling
  "scope_grants": [
    {"resource": "memory", "actions": ["read", "write"], "sync_class_max": "shareable"},
    {"resource": "proj",  "actions": ["read", "write"], "engagements": ["acme-q3"]},
    {"resource": "kb",    "actions": ["read"]}
  ],
  "client_id": "claude-code-vsce-extension",   // which MCP client requested the token
  "code_challenge": "sha256:...",              // PKCE binding
  "jti": "01HZK..."
}

The aud claim is the protocol-level guarantee: a token issued for mcp.cyberos.com cannot be replayed against api.cyberos.com. PKCE binding prevents code interception during the auth flow. The scope_grants are checked per tool call against the tool's required resource + action.

Destructive-op confirmation flow (Elicitation example)

// Agent calls a destructive tool
-> {"method": "tools/call", "params": {"name": "cyberos.memory.delete_memory", "arguments": {"path": ".../draft.md", "mode": "purge"}}}

// Gateway intercepts: annotation destructive=true, no confirmation_token in args
<- {"method": "elicitation/create", "params": {
    "message": "Are you sure you want to PURGE this memory? This is irreversible.",
    "schema": {"type": "object", "properties": {"confirm": {"type": "boolean"}, "reason": {"type": "string"}}, "required": ["confirm", "reason"]}
  }}

// Agent forwards to user; user responds in the client UI
-> {"method": "elicitation/response", "params": {"confirm": true, "reason": "Outdated draft superseded by v2"}}

// Gateway forwards the original call with confirmation context
// The audit row records BOTH the elicitation prompt AND the user's response
{"op": "put", "path": "meta/mcp-invocations/...", "extra": {"tool": "cyberos.memory.delete_memory", "elicitation": {"prompt": "...", "user_confirmed": true, "reason": "Outdated draft superseded by v2"}}}

Tool-discovery surface - the registry agents introspect

An agent's effectiveness is bounded by what it can discover. MCP Gateway is the registry external agents use to introspect what CyberOS can do: tools/list (verbs), prompts/list (templated workflows), resources/list (readable data paths), and capabilities (which MCP features are supported). This section locks the discovery surface contracts.

Discovery endpoints

EndpointWhat it returnsFilter / scopingCadence
/.well-known/oauth-protected-resourceProtected Resource Metadata (RFC 9728) - the resource audience + the authorization servers whose tokens this resource acceptspublicstatic (cached 1 h)
/.well-known/oauth-authorization-serverAuthorization-server metadata (RFC 8414) - issuer, authorize/token endpoints, S256 PKCE method, supported scopespublicstatic (cached 1 h)
capabilitiesSupported MCP features (Tasks, Elicitation, sampling, etc.)noneper-session init
tools/listTools available to the calling token's scope_grantsfiltered by JWT scope_grantsper-call + list_changed push
prompts/listPrompt templates for common workflows ("Find the right CUO persona", "Draft a cycle review")filtered by scopeper-call
resources/listMemory paths / KB documents the calling token can readfiltered by sync_class + scopeper-call
resources/templates/listURI templates for resource lookup (e.g. memory://memories/decisions/{slug})filtered by scopeper-call

Tasks primitive - long-running work

For operations that take > 30 s (KB corpus ingestion, memory export, bulk migrations), MCP Gateway returns a Task immediately and lets the agent poll for status. The agent can disconnect and reconnect; the Task persists in Postgres.

Task fieldTypePurpose
task_idUUIDStable handle; agent polls tasks/get?id=...
statuspending / running / waiting_for_input / completed / failed / cancelledDrives the agent's polling cadence + UI
progress0.0 - 1.0 OR nullIf determinate, the agent can render a progress bar
messagestring (streamed)Latest human-readable status; SSE-streamed if the agent subscribes
resultJSON, null until completedFinal response; matches the tool's result schema
error{code, message, retriable}If failed; the agent decides retry behaviour
created_at / updated_at / expires_attimestampsTasks expire after 24 h by default; per-tool override
memory_chainhashReferences the audit row that recorded task start

Prompt templates - pre-canned workflows

Prompts are MCP-defined templated workflows the agent can use. They reduce hallucination by giving the agent a known-good way to invoke a multi-step CyberOS operation.

Prompt namePurposeBound tools
cyberos.weekly_briefGenerate the user's weekly brief from memory + PROJ + CALmemory.list_recent, proj.list_my_issues, cal.list_events
cyberos.decision_to_issuesConvert a decision memory into parent + child issuesmemory.get_memory, cuo.execute_skill (cuo.cpo.decision-to-issues@1), proj.create_issue
cyberos.draft_cycle_reviewGenerate a cycle review from issues + comments + historyproj.get_cycle, proj.list_cycle_issues, ai.execute_chat (with CUO/COO persona)
cyberos.deal_to_engagementConvert a won CRM deal to a PROJ Engagement with rate cardcrm.get_deal, proj.create_engagement, memory.update_memory (audit decision)
cyberos.find_memory_citationsGiven an Issue, suggest memories to citeproj.get_issue, memory.search_memory, proj.create_memory_link

Each prompt is a versioned, audit-anchored workflow; the agent receives the prompt and the bound tool names; the gateway audits prompt invocations separately from individual tool calls.

Architecture

Three layers: an edge that speaks the MCP spec to agents, a federation router that forwards tool calls to per-module HTTP backends, and an audit + observability bridge. The 24 modules each run a per-module server (e.g. cyberos.memory) that registers itself with the gateway at startup.

graph TB
  subgraph CLIENTS ["Agents (MCP 2025-11-25 clients)"]
    CL_D["Claude Desktop"]
    CL_C["Claude Code CLI"]
    CUR["Cursor / Cline / Codex"]
    CUO["CUO (internal)"]
  end
  subgraph GATEWAY ["MCP Gateway (Rust axum + jsonrpsee)"]
    WK["well_known.rs<br/>/.well-known/oauth-authorization-server<br/>/.well-known/oauth-protected-resource (PRM)"]
    AUTH_E["oauth_edge.rs<br/>OAuth 2.1 PKCE handshake<br/>(delegates to AUTH service)"]
    DISC["discovery.rs<br/>tools/list aggregation"]
    ROUT["federation.rs<br/>tool name -> backend server"]
    ANN["annotations.rs<br/>destructive / readOnly / idempotent"]
    GATE["gate.rs<br/>RBAC + human-confirm + audience check"]
    TASKS["tasks.rs<br/>long-running primitive"]
    ELI["elicitation.rs<br/>mid-execution prompt proxy"]
    AUD["audit.rs<br/>per-call memory write"]
  end
  subgraph BACKENDS ["Per-module MCP servers (HTTP JSON-RPC)"]
    BR["cyberos.memory<br/>update_memory / get_memory / search_memory / ..."]
    SK["cyberos.skill<br/>execute_skill / list_skills / ..."]
    AU["cyberos.auth<br/>get_identity / get_permission / ..."]
    AI["cyberos.ai<br/>execute_chat / generate_embedding / get_usage"]
    CRM["cyberos.crm<br/>(planned, 19 more modules)"]
  end
  subgraph SINKS ["Sinks"]
    AUTHSVC["AUTH service<br/>token issuance + RBAC.Check"]
    memory["memory<br/>mcp.invocation rows"]
    OBS["OBS<br/>traces + metrics"]
  end
  CL_D --> WK
  CL_C --> WK
  CUR --> WK
  CUO --> WK
  WK --> AUTH_E
  AUTH_E --> AUTHSVC
  WK --> DISC
  DISC --> ROUT
  CL_D -->|tools/call| ROUT
  CUR -->|tools/call| ROUT
  ROUT --> ANN
  ANN --> GATE
  GATE --> AUTHSVC
  GATE --> BR
  GATE --> SK
  GATE --> AU
  GATE --> AI
  GATE --> CRM
  ROUT --> TASKS
  GATE --> ELI
  GATE --> AUD
  AUD --> memory
  GATEWAY --> OBS
  classDef planned fill:#e0f2fe,stroke:#0369a1
  classDef backend fill:#cffafe,stroke:#0891b2
  classDef sink fill:#f5ede6,stroke:#45210e
  class WK,AUTH_E,DISC,ROUT,ANN,GATE,TASKS,ELI,AUD,CL_D,CL_C,CUR,CUO planned
  class BR,SK,AU,AI,CRM backend
  class AUTHSVC,memory,OBS sink

Internal components

ComponentPath (planned)Responsibility
well_known.rsservices/mcp-gateway/src/well_known.rsServes the /.well-known/oauth-authorization-server metadata (RFC 8414) + /.well-known/oauth-protected-resource (PRM, RFC 9728).
oauth_edge.rsservices/mcp-gateway/src/oauth_edge.rsHandles the OAuth 2.1 + PKCE handshake with the agent client. The gateway is its own authorization server; the AUTH-service session JWT identifies the calling subject at /authorize.
discovery.rsservices/mcp-gateway/src/discovery.rsAggregates tools/list across federated backends. Caches the catalogue with a 60 s TTL; invalidated on backend register.
federation.rsservices/mcp-gateway/src/federation.rsRoutes a tool call (cyberos.memory.update_memory) to the right backend server via JSON-RPC over HTTP (reqwest). Caches resolved endpoints in-memory.
annotations.rsservices/mcp-gateway/src/annotations.rsParses tool annotations from the backend manifest. Enforces destructive -> human-confirm; readOnly -> fast-path RBAC; idempotent -> safe-retry; openWorld -> strict scope check.
gate.rsservices/mcp-gateway/src/gate.rsComposite gate - verifies the audience claim, calls AUTH RBAC.Check, validates the idempotency-key, applies the rate-limit token bucket.
tasks.rsservices/mcp-gateway/src/tasks.rsTasks primitive (task pending) - long-running tool invocations get a task_id; clients poll for status / result; results streamed via SSE.
elicitation.rsservices/mcp-gateway/src/elicitation.rsElicitation (task pending) - mid-execution, a backend can request user input; the gateway proxies that back to the agent client with a content-safety filter.
tool_registry.rsservices/mcp-gateway/src/tool_registry.rsPostgres-backed registry. Each backend registers its tools with annotations; collisions rejected at register time (task pending).
rate_limit.rsservices/mcp-gateway/src/rate_limit.rsPer-tool + per-tenant token-bucket rate limit; circuit-breaker on backend errors.
idempotency.rsservices/mcp-gateway/src/idempotency.rsReplay safety via the Idempotency-Key header (task pending).
audit.rsservices/mcp-gateway/src/audit.rsEmits one mcp.invocation row per call: agent, tool, args_hash, RBAC decision, latency, outcome, persona-version.
streamable_http.rsservices/mcp-gateway/src/streamable_http.rsRFC 9112 chunked-transfer HTTP transport per MCP 2025-11-25.
conformance.rsservices/mcp-gateway/tests/conformance.rsMCP conformance test suite runner. Gating CI test (task pending).

Data model

The gateway is mostly stateless. Postgres holds the tool registry, Redis caches tool catalogues and Tasks status, memory absorbs the audit rows.

erDiagram
  SERVER ||--o{ TOOL_DEFINITION: "publishes"
  TOOL_DEFINITION ||--o{ ANNOTATION: "has"
  TOOL_DEFINITION ||--o{ TOOL_INVOCATION: "fulfils"
  TOOL_DEFINITION ||--o{ ELICITATION: "may trigger"
  TOOL_INVOCATION ||--o{ TASK: "may spawn"
  TASK ||--o{ TASK_STATUS_UPDATE: "emits"
  AGENT_CLIENT ||--o{ TOOL_INVOCATION: "calls"
  AGENT_CLIENT ||--o| OAUTH_REGISTRATION: "registered as"
  SERVER {
    string id PK "cyberos.memory"
    string display_name
    string endpoint "https://memory.internal/mcp"
    string version
    string status "active | draining | offline"
    timestamp registered_at
  }
  TOOL_DEFINITION {
    string name PK "cyberos.memory.update_memory"
    string server_id FK
    string description
    obj input_schema "JSON Schema"
    obj output_schema
    string scope_required "memory.update"
  }
  ANNOTATION {
    string tool_name FK
    string key "destructive | readOnly | idempotent | openWorld | titleHuman | requiresConfirm"
    string value
  }
  AGENT_CLIENT {
    uuid id PK
    string display_name "Claude Desktop, Cursor, ..."
    string redirect_uri
    string client_type "public (PKCE) | confidential"
  }
  OAUTH_REGISTRATION {
    uuid client_id FK
    string scopes
    timestamp registered_at
  }
  TOOL_INVOCATION {
    uuid id PK
    string tool_name FK
    uuid agent_id FK
    uuid subject_id "on_behalf_of"
    string persona_version
    string args_hash "SHA-256(canonical)"
    string idempotency_key
    string outcome "ok | denied | error | task_created"
    int latency_ms
    string memory_chain
    timestamp ts
  }
  TASK {
    uuid id PK
    uuid invocation_id FK
    string status "queued | running | completed | failed | cancelled"
    obj progress "fraction / stage / message"
    obj result
    timestamp created_at
    timestamp completed_at
  }
  TASK_STATUS_UPDATE {
    uuid id PK
    uuid task_id FK
    string status
    obj progress
    timestamp ts
  }
  ELICITATION {
    uuid id PK
    uuid invocation_id FK
    string prompt
    obj response_schema
    obj user_response
    timestamp asked_at
    timestamp answered_at
  }

Tool naming convention (SEP-986)

All tool names follow cyberos.{module}.{verb}_{noun}. The verb comes from a closed set of 15 approved SEP-986 verbs (DEC-2361) - get, list, create, update, delete, send, fetch, sync, validate, generate, execute, search, replay, accept, reject; adding one needs a SEP RFC, and a module that registers a tool whose verb is outside the set is refused at registration.

Example tool nameVerb classAnnotations
cyberos.memory.update_memorywritedestructive=false, idempotent=true, scope=memory.update
cyberos.memory.get_memoryreadreadOnly=true, idempotent=true, scope=memory.read
cyberos.memory.delete_memorydeletedestructive=true, requiresConfirm=true, scope=memory.delete
cyberos.memory.search_memoryqueryreadOnly=true, scope=memory.read
cyberos.skill.execute_skillexecutedestructive=true, requiresConfirm=conditional, scope=skill.execute
cyberos.skill.list_skillsreadreadOnly=true, scope=skill.read
cyberos.auth.get_permissionqueryreadOnly=true, scope=auth.read
cyberos.auth.delete_sessiondestructivedestructive=true, requiresConfirm=true, scope=auth.session_revoke
cyberos.ai.execute_chatexecutedestructive=false, openWorld=true, scope=ai.invoke
cyberos.crm.create_accountwritedestructive=false, idempotent=false, scope=crm.write

API surface

The gateway speaks MCP 2025-11-25 to agents and forwards tools/call to backends as JSON-RPC over HTTP. A small admin REST surface lets operators inspect the registry and replay invocations.

MCP surface (canonical)

MethodMCP primitivePurpose
GET/.well-known/oauth-authorization-serverRFC 8414 authorization-server metadata - issuer, authorize/token endpoints, S256 PKCE, scopes.
GET/.well-known/oauth-protected-resourcePRM (RFC 9728) - auth-server URL, scopes, audience.
POST/mcpSingle streamable-HTTP endpoint for all MCP JSON-RPC calls.
JSON-RPCinitializeClient capability negotiation.
JSON-RPCtools/listDiscovery - returns the federated tool catalogue.
JSON-RPCtools/callInvoke a tool with arguments.
JSON-RPCresources/listList MCP resources (files, URIs).
JSON-RPCresources/readRead a resource.
JSON-RPCprompts/listList prompt templates.
JSON-RPCprompts/getMaterialise a prompt template.
JSON-RPCcompletion/completeTool-arg autocomplete.
JSON-RPCtasks/getPoll long-running task status.
JSON-RPCtasks/cancelCancel a running task.
JSON-RPCelicitation/respondReply to a server-initiated elicitation.
JSON-RPCsampling/createMessageServer-initiated LLM sampling (rate-limited, task pending).
JSON-RPClogging/setLevelServer log-level config.
JSON-RPCroots/listList filesystem roots.
JSON-RPCnotifications/initializedCapability acknowledgement.
JSON-RPCnotifications/progressStreaming progress events.

Backend HTTP surface (per-module servers)

There is no separate backend wire protocol: a per-module server is a plain MCP HTTP endpoint. It self-registers its catalogue over the control plane, then the gateway forwards tools/call to the registered endpoint as JSON-RPC 2.0 over HTTP (reqwest).

Registration - POST /v1/mcp/register:

{
  "module": "memory",
  "endpoint": "https://memory.internal/mcp",
  "tools": [
    {
      "name": "cyberos.memory.update_memory",
      "description": "Create or replace a memory file",
      "inputSchema": { "type": "object" },
      "annotations": { "title": "Update memory", "idempotentHint": true },
      "requiresScope": ["mcp:tools"]
    }
  ]
}

The endpoint must be an http:// or https:// URL; any other scheme is rejected at registration. A module keeps its registration live with POST /v1/mcp/heartbeat ({"module": "memory"}) and withdraws with POST /v1/mcp/deregister. The dev slice gates the control plane behind MCP_DEV_REGISTRATION=1; production requires authenticated registration (TASK-MCP-004).

Forwarding - the gateway POSTs a JSON-RPC 2.0 tools/call to the module's registered endpoint:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "cyberos.memory.update_memory", "arguments": {} }
}

The module returns a JSON-RPC result ({ "result": { "content": [ ... ], "isError": false } }). A transport failure, non-2xx status, non-JSON body, or JSON-RPC error envelope maps to the closed error code -32004 module_unreachable.

Admin REST surface (operator-only)

MethodPathPurpose
GET/admin/serversList registered backends + their status.
GET/admin/toolsFull tool catalogue with annotations.
POST/admin/tools/{name}/disableSoft-disable a tool (server returns "method not allowed").
GET/admin/invocationsRecent invocations (filterable by tool, agent, subject).
POST/admin/invocations/{id}/replayReplay an invocation in dry-run mode (read-only tools only).
GET/admin/tasksList active tasks.
POST/admin/tasks/{id}/cancelForce-cancel a stuck task.

Key flows

Flow 1 - well-known discovery + tool listing

sequenceDiagram
  autonumber
  participant A as Agent (Claude Desktop)
  participant G as MCP Gateway
  participant AS as AUTH service
  participant BR as cyberos.memory (backend)
  participant SK as cyberos.skill
  participant Cache as Catalogue cache
  A->>G: GET /.well-known/oauth-protected-resource
  G-->>A: {resource, authorization_servers, bearer_methods}
  A->>G: GET /.well-known/oauth-authorization-server
  G-->>A: {issuer, authorize/token endpoints, S256, scopes}
  A->>AS: OAuth 2.1 PKCE auth code flow
  AS-->>A: {access_token aud=mcp.cyberos.com}
  A->>G: POST /mcp {jsonrpc "2.0", method "initialize"}
  G-->>A: {capabilities, server_info}
  A->>G: POST /mcp {method "tools/list"}
  G->>Cache: get catalogue
  alt cache hit
    Cache-->>G: full tool list
  else cache miss
    G->>BR: ListTools
    G->>SK: ListTools
    BR-->>G: 12 tools
    SK-->>G: 8 tools
    G->>Cache: SET catalogue TTL=60s
  end
  G-->>A: {tools cyberos.memory.update_memory, cyberos.skill.execute_skill, ...}

The agent sees one server; under the hood, 24 modules contribute their tools. Caching keeps catalogue assembly < 5 ms p95.

Flow 2 - tool invocation with OAuth + RBAC

sequenceDiagram
  autonumber
  participant A as Agent
  participant G as MCP Gateway
  participant GA as gate.rs
  participant AS as AUTH RBAC.Check
  participant BR as cyberos.memory
  participant AUD as audit.rs
  participant B as memory
  A->>G: POST /mcp tools/call cyberos.memory.update_memory {path, body, idempotency_key}
  G->>GA: verify audience claim (mcp.cyberos.com)
  G->>GA: load annotations (destructive=false, scope_required=memory.update)
  GA->>AS: RBAC.Check(subject_jwt, action="memory.update", resource="memories/...")
  AS-->>GA: {allow true, reason "role.founder + scope.memory.update"}
  GA->>BR: POST tools/call {name, arguments}
  BR-->>GA: {result {content, isError}}
  GA-->>A: {content [text], isError false}
  GA->>AUD: write mcp.invocation
  AUD->>B: append row {tool, agent, subject, persona_version, outcome "ok"}

Per-call latency: ~3 ms gateway overhead + backend processing time. The audit write is fire-and-forget; a backlog > 60 s triggers an alert.

Flow 3 - destructive tool with human-confirm gating

sequenceDiagram
  autonumber
  participant A as Agent
  participant G as MCP Gateway
  participant ANN as annotations.rs
  participant ELI as elicitation.rs
  participant U as User UI (CHAT or IDE)
  participant SK as cyberos.skill
  A->>G: tools/call cyberos.skill.execute_skill {skill_id "vietnam-vat-invoice"}
  G->>ANN: load annotations
  ANN-->>G: destructive=true, requiresConfirm=true
  G->>ELI: needs user confirmation
  ELI->>U: "Skill 'vietnam-vat-invoice' will create an invoice in memory. Proceed?"
  alt user approves
    U-->>ELI: {approved true}
    ELI-->>G: proceed
    G->>SK: InvokeTool(...)
    SK-->>G: result
    G-->>A: result
  else user denies
    U-->>ELI: {approved false}
    ELI-->>G: denied
    G-->>A: {error "user_denied", isError true}
  end

(task pending): destructive tool calls without confirmation are rejected. The elicitation UI is rendered by the agent client (Claude Desktop, Cursor) - the gateway is content-agnostic.

Flow 4 - long-running task via the Tasks primitive

sequenceDiagram
  autonumber
  participant A as Agent
  participant G as MCP Gateway
  participant T as tasks.rs
  participant BG as cyberos.kb (backend)
  A->>G: tools/call cyberos.kb.sync_corpus
  G->>BG: InvokeTool(...)
  BG-->>G: {task_id "tsk_01HZJ...XK", expected_ms 120000}
  G->>T: register task tsk_01HZJ...XK
  G-->>A: {task_id, status "running"}
  Note over A: agent receives the task handle and returns control to the user
  loop poll every 2 s
    A->>G: tasks/get {id "tsk_..."}
    G->>BG: TaskStatus(task_id)
    BG-->>G: {status "running", progress 0.34, message "embedding 3,400 / 10k docs"}
    G-->>A: {status "running", progress 0.34, message "..."}
  end
  BG-->>G: {status "completed", result {indexed 10000, elapsed_s 118}}
  A->>G: tasks/get
  G-->>A: {status "completed", result {...}}

(task pending): Tasks primitive for long-running tool invocations. The agent can release the call and resume the conversation; polling fetches updates.

Flow 5 - mid-execution elicitation

sequenceDiagram
  autonumber
  participant A as Agent
  participant G as MCP Gateway
  participant SK as cyberos.skill (vietnam-bank-transfer)
  participant ELI as elicitation.rs
  participant U as User UI
  A->>G: tools/call cyberos.skill.execute_skill {skill "vietnam-bank-transfer", amount 25000000}
  G->>SK: InvokeTool(...)
  SK->>G: elicitation/request {prompt "Confirm transfer of VND 25,000,000 to ACME - please type CONFIRM"}
  G->>ELI: proxy elicitation (with content-safety filter)
  ELI->>U: render prompt in client UI
  U->>ELI: types "CONFIRM"
  ELI-->>G: response {text "CONFIRM"}
  G-->>SK: continue invocation
  SK->>SK: execute transfer
  SK-->>G: result {napas_tx_id "..."}
  G-->>A: result

(task pending): elicitation proxied with a content-safety filter - prevents a compromised backend from injecting prompts that exfiltrate via the user.

Tool-call lifecycle

A single tool invocation traverses up to nine states. Most calls run synchronously and reach Completed in < 1 s; long-running ones go via the Tasks primitive.

stateDiagram-v2
  [*] --> Received: agent POSTs /mcp tools/call
  Received --> AudienceOK: aud claim matches gateway
  AudienceOK --> AnnotationLoaded: tool registry hit
  AnnotationLoaded --> RBACChecked: AUTH RBAC.Check
  AnnotationLoaded --> Rejected: tool disabled / unknown
  RBACChecked --> Confirming: destructive + requiresConfirm
  RBACChecked --> Forwarding: readOnly OR confirmed
  Confirming --> Forwarding: user approves
  Confirming --> Denied: user denies
  Forwarding --> Streaming: backend producing chunks
  Forwarding --> TaskCreated: long-running, task_id issued
  TaskCreated --> Polled: agent polls tasks/get
  Polled --> Polled: status running
  Polled --> Completed: status completed
  Polled --> Failed: status failed
  Streaming --> Completed: done frame
  Streaming --> Failed: backend error
  Streaming --> Cancelled: agent / user cancels
  Completed --> Audited: mcp.invocation row written
  Failed --> Audited
  Cancelled --> Audited
  Rejected --> Audited
  Denied --> Audited
  Audited --> [*]

Functional requirements

The CyberOS task catalogue is being rebuilt one feature at a time via the open task-author Agent Skill.

Previous task enumerations were archived 2026-05-14 and are no longer reflected on this page. Specific tasks land here as they are re-authored.

Non-functional requirements

Performance + security NFRs that flow through MCP Gateway. Cross-referenced at nfr-catalog.html#mcp.

NFR IDConcernTargetMeasurement
N(task pending)MCP read tool p95<= 500 msk6 load test
N(task pending)MCP write tool p95<= 1 sk6 load test
N(task pending)Gateway overhead per call<= 5 ms p95internal bench
N(task pending)tools/list discovery<= 100 ms p95 (cache hit)internal bench
N(task pending)Gateway availability (28-day)>= 99.95%SLO monitor
N(task pending)MCP spec conformance100% of the conformance suiteCI gate
N(task pending)Audit completeness100% (no dropped invocations)chaos test + memory walk
N(task pending)Destructive without confirm= 0 eventsCI regression + runtime check
N(task pending)Tool name violations= 0 (rejected at register)registry validator
N(task pending)Idempotency-key collision rate= 0 false positivesproperty-based test
N(task pending)Task status update latency<= 2 s after backend updatebench/tasks
N(task pending)Per-tool rate-limit enforcement100% (no overflow)load test
N(task pending)OAuth 2.1 conformanceOAuth 2.1 + RFC 9728 PRMconformance suite

Dependencies

graph LR
  subgraph upstream ["MCP Gateway depends on"]
    AUTH["AUTH<br/>token + RBAC.Check"]
    memory["memory<br/>mcp.invocation rows"]
    OBS["OBS<br/>traces + metrics"]
    PG["PostgreSQL<br/>tool registry"]
    REDIS["Redis<br/>catalogue + tasks cache"]
  end
  MCP["MCP Gateway"]
  subgraph backends ["Per-module servers"]
    BR["cyberos.memory"]
    SK["cyberos.skill"]
    AU["cyberos.auth"]
    AI["cyberos.ai"]
    OTH["+20 more"]
  end
  subgraph clients ["MCP clients"]
    CL["Claude Desktop"]
    CC["Claude Code"]
    CUR["Cursor / Cline / Codex"]
    INT["Internal agents"]
  end
  AUTH --> MCP
  memory --> MCP
  OBS --> MCP
  PG --> MCP
  REDIS --> MCP
  MCP --> BR
  MCP --> SK
  MCP --> AU
  MCP --> AI
  MCP --> OTH
  CL --> MCP
  CC --> MCP
  CUR --> MCP
  INT --> MCP
  classDef shipped fill:#f5ede6,stroke:#45210e
  classDef planned fill:#fef6e0,stroke:#9c750a
  class memory,SK,REDIS,PG shipped
  class MCP,AUTH,AI,AU,BR,OTH,OBS planned
  class CL,CC,CUR,INT shipped

Compliance scope

Regulation / standardArticle / clauseMCP Gateway feature
EU AI ActArt. 12 - LoggingOne mcp.invocation row per tool call.
EU AI ActArt. 14 - Human oversightDestructive tools require human-confirm via elicitation.
EU AI ActArt. 26 - Deployer obligationsPersona-version stamping (task pending).
Vietnam PDPLArt. 14 - DSARPer-subject mcp.invocation export.
GDPRArt. 25 - Privacy by designAudience-bound tokens; confused-deputy mitigation.
GDPRArt. 32 - Security of processingOAuth 2.1 PKCE only; HTTPS to backends.
OWASP Gen AI Top-10LLM02: Insecure output handlingElicitation content-safety filter.
OWASP Gen AI Top-10LLM07: Insecure plugin designTool annotations + RBAC gate; closed catalogue.
OWASP Gen AI Top-10LLM08: Excessive agencyAudience-bound token + persona scope; can't escalate.
RFC 9728OAuth 2.0 Protected Resource MetadataPRM at /.well-known/oauth-protected-resource.
RFC 7636PKCEPKCE-only - implicit grant disabled.
SOC 2 Type IICC6.7 - Restriction of system accessTool registry as a closed catalogue; new tool registration requires CTO approval.

Risk entries

IDRiskLikelihoodImpactOwnerMitigation
R-MCP-001Spec drift - agent client expects newer features than the gateway providesMediumMediumCTOQuarterly spec sync; CI gates on the conformance suite; capability negotiation surfaces gaps.
R-MCP-002Tool-name squatting - a module registers a tool name belonging to anotherLowHighCTOServer_id is part of the registry key; cross-module names rejected at register (task pending).
R-MCP-003Confused-deputy attack - a token from one audience used against another moduleMediumHighCSOAudience claim mandatory - the gateway verifies each access token's audience is exactly this resource server; a token minted for another audience is refused and the replay attempt audited.
R-MCP-004Destructive tool annotation bypass via a newly-added toolMediumHighCSORegistration requires CTO + CSO approval; the default annotation is destructive=true.
R-MCP-005Tasks primitive leak - task_id reused across tenantsLowHighCTOtask_id is UUIDv7 + tenant_id in the lookup; cross-tenant query property-tested.
R-MCP-006Elicitation injection - a backend prompts the user into a harmful actionMediumHighCSOContent-safety filter; allow-list of elicitation prompts per tool; CHAT log of all elicitations.
R-MCP-007Backend slow -> caller times out -> orphaned audit rowMediumLowCTOPer-tool timeout enforced; audit row written on cancellation; a reconciliation job catches orphans.
R-MCP-008OAuth registration scaling - every agent client must registerLowLowCTODynamic Client Registration (DCR) per RFC 7591; the tenant admin approves before activation.
R-MCP-009Tool catalogue cache staleness after a backend deployMediumLowCTO60 s TTL + register-time invalidation; deploys trigger a cache reset.
R-MCP-010Sampling-with-Tools amplification (LLM-driven loop)MediumMediumCSO(task pending): per-session sampling rate limit; circuit-breaker on recursion depth.
R-MCP-011External agent token theft (Member's laptop compromised -> Claude Code token grants full CyberOS access)MediumCriticalCSOShort-lived tokens (1 h default); refresh-token rotation; device-binding via DPoP (P2+); destructive-op confirmation gating even with a valid token; emergency revoke endpoint pages the CSO instantly.
R-MCP-012Prompt injection in a tool description (agent reads the description, follows attacker instruction)MediumHighCSOTool descriptions are protocol-trusted by the gateway, untrusted by agents; CyberOS modules go through CSO review before publishing tool descriptions; agent-side system prompts treat tool descriptions as data, not instructions.
R-MCP-013Elicitation fatigue - too many destructive confirms -> the user starts rubber-stampingHighMediumCPOBundle destructive ops where safe (batch delete with one confirm); show the explicit consequence in the elicitation prompt; rate-limit elicitations per session; never auto-decay to "approve all".
R-MCP-014Federation lag - a module deploys a new tool, the agent calls the old name, gets a cryptic errorMediumLowCTO60 s cache TTL + list_changed push to subscribed clients; deprecation aliases (old name -> new name) supported for 1 minor release; per-tool version field in the tool catalogue.
R-MCP-015Task storm - an agent starts 1000s of long-running tasks, exhausts capacityLowHighCTOPer-tenant per-agent task limit (default 50 concurrent); rate limit on tasks/create; auto-cancel tasks older than 24 h; alarm on task backlog > 100.
R-MCP-016Resource leak via subscribed list_changed notifications (agent disconnects, gateway keeps emitting)MediumLowCTOSubscription tied to session lifetime; ping/pong heartbeat 30 s; auto-unsubscribe on missed heartbeats.
R-MCP-017Per-module heartbeat false-positive - gateway marks a healthy module as downLowMediumCTO3 consecutive heartbeat misses required (not 1); the module health endpoint is independent of the MCP server itself; manual override via cyberos-mcp servers reactivate.
R-MCP-018OAuth Dynamic Client Registration (DCR) abuse - an attacker registers many clientsMediumMediumCSOTenant-admin approval gate before activation; rate limit on the DCR endpoint (10/hour per tenant); flag suspicious registrations (unusual redirect URIs).
R-MCP-019An older 2024-11-05 client connects, falls back to SSE + bearer token, less secureMediumMediumCSOOlder clients explicitly opted in per tenant (deny by default); the audit row flags "older protocol session"; sunset the older protocol by P3 exit (push all tenants to 2025-11-25).
R-MCP-020SEP-986 naming collision (two modules define delete_thing)LowMediumCTOThe cyberos.<module> prefix is the namespace; cross-module same-noun is allowed; the registration validator rejects exact duplicates within the same prefix.

KPIs

KPIFormulaSourceTarget
Conformance pass ratepassed / totalMCP conformance suite100%
Read-tool p95 latencyhistogramOBS<= 500 ms
Write-tool p95 latencyhistogramOBS<= 1 s
Gateway overhead p95histogramOBS<= 5 ms
Audit completenessrows_in_memory / invocationschaos test100%
Destructive-without-confirmcount / 28 dmcp.invocation= 0
Tool-name violationscountregistry validator= 0
Active taskscount of status=runningtasks DBtracked; alert on backlog > 100
Backend healthhealthy / registered/admin/servers>= 95% always
Persona-stamp coverageaudit rows with persona stamp / totalmemory audit replay= 1.0 (hard floor)
Elicitation acceptance rateuser-confirmed elicitations / total elicitationsmcp.invocation rowstracked; a sudden drop = UI confusion alarm
Tasks completion ratecompleted / createdtasks DB>= 95% (excluding user-cancelled)
Cross-tenant token-replay attemptscount / 28 dOAuth + audit cross-checktracked; spike = active attack
Older-protocol session rate2024-11-05 sessions / totalsession init logsdeclining trend; = 0 by P3 exit
List_changed push latency p95histogram, module deploy -> subscriber notifiedOBS<= 30 s
Destructive-op confirm fatiguemedian elicitations per Member per daymcp.invocation rows<= 5; alarm on > 15 (rubber-stamping signal)
External-client tools coveragedistinct external clients that successfully invoked >= 1 tool / total registeredsession logs>= 0.90 (clients should not register and never call)
SEP-986 compliancetools matching the naming pattern / totalregistry validator= 1.0 (CI gate at registration)

RACI matrix

ActivityCEOCTOCSOModule ownersCDO
Gateway design + implementationARCII
Per-module backend implementationICCA/RI
Tool annotations reviewICA/RRI
Spec conformanceIA/RCII
OAuth + audience designICA/RII
Audit pipeline maintenanceIRCIA
Tool catalog curationACCRC

Planned CLI surface

Operator CLI cyberos-mcp + ad-hoc mcp-inspector for development.

1. List registered backends

$ cyberos-mcp servers list

SERVER_ID STATUS VERSION TOOLS LAST_SEEN
cyberos.memory active 2.0.0 12 2026-05-14T07:21Z
cyberos.skill active 1.4.0 8 2026-05-14T07:21Z
cyberos.auth active 0.9.0 7 2026-05-14T07:20Z
cyberos.ai active 0.8.0 5 2026-05-14T07:21Z
cyberos.crm draining 0.3.0 11 2026-05-14T07:15Z

2. Inspect a tool

$ cyberos-mcp tool inspect cyberos.memory.delete_memory

name: cyberos.memory.delete_memory
server: cyberos.memory
description: "Delete a memory file (tombstone by default; purge requires DSAR reason)"
scope_required: memory.delete
annotations:
 destructive: true
 readOnly: false
 idempotent: false
 requiresConfirm: true
 openWorld: false
input_schema: {path: string, mode: "tombstone"|"purge", reason: string?}

3. Replay an invocation (dry-run, read-only)

$ cyberos-mcp invocations replay --id inv_01HZJ...XK --dry-run

[replay] invocation inv_01HZJ...XK
 tool: cyberos.memory.search_memory
 agent: Claude Desktop (claude-desktop-v1.0.45)
 args: {query: "Singapore HoldCo"}
 original_outcome: ok (12 hits)
 replay_outcome: ok (12 hits) - identical
[replay] read-only tool only - audit row NOT written

4. Cancel a stuck task

$ cyberos-mcp tasks cancel --id tsk_01HZJ...XK --reason "client disconnected"

[cancel] tsk_01HZJ...XK status: running -> cancelled
[backend] cyberos.kb signalled to abort
[audit] mcp.invocation updated, outcome=cancelled

5. Conformance test

$ cyberos-mcp conformance run

[conformance] MCP 2025-11-25 spec suite, 142 tests
 initialize pass (12 / 12)
 tools/list pass (18 / 18)
 tools/call pass (24 / 24)
 resources/* pass (16 / 16)
 prompts/* pass (14 / 14)
 completion/complete pass (8 / 8)
 tasks/* pass (12 / 12)
 elicitation/* pass (10 / 10)
 sampling/* pass (6 / 6)
 oauth (pkce) pass (14 / 14)
 prm pass (8 / 8)
[result] 142 / 142 PASSED, 0 failed, 0 skipped

6. Register a new backend (CTO + CSO approval gate)

$ cyberos-mcp servers register \
 --id cyberos.crm \
 --endpoint https://crm.internal/mcp \
 --version 0.3.0 \
 --manifest crm-tools.json \
 --approval-jira CYB-1234

[validate] manifest: 11 tools, SEP-986 ok, annotations ok
[approve] CTO + CSO approved (per CYB-1234)
[register] cyberos.crm registered
[catalog] cache invalidated
[audit] memory seq=14856

Phase status & estimates

CapabilityStatus
Streamable HTTP transport (RFC 9112)planned, P0
OAuth 2.1 + PKCE edge handshakeplanned, P0
Well-known discovery + PRMplanned, P0
Tool registry (SEP-986 enforced)planned, P0
tools/list federationplanned, P0
tools/call gating (annotations + RBAC)planned, P0
Tasks primitiveplanned, P0
Elicitation back-channelplanned, P0
Idempotency-Key replay safetyplanned, P0
Per-tool rate limit + circuit breakerplanned, P0
mcp.invocation audit rowplanned, P0
Conformance suite (142 tests)planned, P0
Sampling-with-Tools rate limitingplanned, P1
Dynamic Client Registration (RFC 7591)planned, P1
Multi-region active-activeplanned, P3+

References

Previous: AI Gateway | Next module: Observability

Changelog

History lives in the changelog; this page describes only the current state.


Generated from services/mcp-gateway/docs/index.md — edit the markdown source, not this file (TASK-DOCS-002).