Skip to content

MCP, A2A, OAuth & Skills API

All endpoints on this page are served by the Core Proxy (:8100) — the Rust runtime plane.

Base URL http://localhost:8100
Auth Gateway JWT (X-Gateway-Authorization: Bearer <jwt>) or API key (X-API-Key / Authorization: Bearer sk_brutor_api_...), plus X-Tenant-ID where the tenant isn’t in the credential
Public (no auth) A2A discovery well-knowns, /health
Errors {"error": "<message>"} with HTTP status

Guides: MCP Clients, A2A Agents, OAuth, Agent Skills.

Base: /v1/proxy/mcp/{server_id} — the governed front door to every registered MCP server. Speaks JSON-RPC 2.0 over POST plus an optional SSE listener (streamable HTTP transport).

Method Path Purpose
POST /v1/proxy/mcp/{server_id} JSON-RPC 2.0 request (initialize, tools/list, tools/call, resources/read, …)
GET /v1/proxy/mcp/{server_id} SSE listener — send Accept: text/event-stream (streamable HTTP)
ANY /v1/proxy/mcp/{server_id}/{path} Sub-path passthrough to the upstream server
GET /v1/proxy/mcp/{server_id}/.well-known/oauth-protected-resource OAuth discovery (gateway-rewritten)
GET /v1/proxy/mcp/{server_id}/.well-known/openid-configuration OIDC discovery (gateway-rewritten)
GET /v1/proxy/mcp/health MCP subsystem health

The gateway is bilingual across the MCP spec’s two eras (the spec calls this “dual-era”):

  • Handshake dialect (≤ 2025-11-25): initializeMcp-Session-Idnotifications/initialized. Fully supported, unchanged — every existing client and server keeps working.
  • Stateless dialect (2026-07-28): no handshake or sessions; every request carries params._meta (io.modelcontextprotocol/protocolVersion, .../clientCapabilities) and the MCP-Protocol-Version / Mcp-Method / Mcp-Name HTTP headers; capability discovery via the server/discover method. Serving this dialect from the gateway’s synthesized servers (VMCP, Agent Skills, Web Search) is gated behind MCP_STATELESS_ENABLED (default off).

Two behaviors are always on, regardless of the flag:

  • Header/body consistency enforcement — a request whose Mcp-Method or Mcp-Name header contradicts the JSON-RPC body is rejected with HTTP 400 + JSON-RPC -32020 (Header Mismatch). Absent headers (legacy clients) pass; lying headers never do. This closes the spoofing gap where header-routing infrastructure sees a different action than the body performs.
  • Dialect discovery & recording — at capability discovery the gateway probes upstreams (server/discover first when the flag is on, initialize fallback) and records each server’s negotiated protocol_version and dialect (handshake | stateless) on the server record; the proxy speaks each upstream’s own dialect.

Error-code note: the 2026-07-28 spec retires -32002 (resource not found) in favor of -32602; the gateway accepts both from upstreams during the transition.

Terminal window
curl http://localhost:8100/v1/proxy/mcp/hubspot-server \
-H "Authorization: Bearer sk_brutor_api_..." \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_contacts",
"arguments": { "limit": 10 }
}
}'

Responses are standard JSON-RPC 2.0: {"jsonrpc": "2.0", "id": 1, "result": {...}} or {"jsonrpc": "2.0", "id": 1, "error": {...}}.

Base: /v1/proxy/vmcp

Method Path Purpose
POST /v1/proxy/vmcp/{virtual_server_id} JSON-RPC to a virtual server aggregating multiple MCP servers
GET /v1/proxy/vmcp/health Health

A virtual server presents one endpoint exposing the tools of many upstream servers. Tool names are namespaced by server slug; every tools/call is delegated to the originating upstream, so per-server OAuth works transparently — no extra client work.

Base: /v1/proxy/oauth/{mcp_server_id}/{idp_server_id} — brokered OAuth for MCP servers behind identity providers.

Method Path Purpose
GET /.../.well-known/oauth-protected-resource Discovery metadata with gateway-rewritten URLs
GET /.../.well-known/openid-configuration OIDC discovery, gateway-rewritten
POST /.../token Authorization-code → token exchange
POST /.../revoke Revoke
GET /.../tokens List the caller’s stored tokens
POST /.../register RFC 7591 Dynamic Client Registration

Base: /a2a/{tenant_id}/{card_name} — Agent-to-Agent protocol v1.0 endpoints for agents published through the gateway.

Method Path Purpose
POST /a2a/{tenant_id}/{card_name}/message:send Synchronous message send
POST /a2a/{tenant_id}/{card_name}/message:stream Streaming message (SSE)
GET /a2a/{tenant_id}/{card_name}/tasks List tasks
GET /a2a/{tenant_id}/{card_name}/tasks/{task_id} Get task
POST /a2a/{tenant_id}/{card_name}/tasks/{task_id}:cancel Cancel task
GET /a2a/{tenant_id}/{card_name}/tasks/{task_id}:subscribe Subscribe to task updates (SSE)
POST /a2a/{tenant_id}/{card_name}/tasks/{task_id}/pushNotificationConfigs Create push-notification config
GET /a2a/{tenant_id}/{card_name}/tasks/{task_id}/pushNotificationConfigs List push configs
GET /a2a/{tenant_id}/{card_name}/tasks/{task_id}/pushNotificationConfigs/{config_id} Get push config
DELETE /a2a/{tenant_id}/{card_name}/tasks/{task_id}/pushNotificationConfigs/{config_id} Delete push config
GET /a2a/{tenant_id}/{card_name}/extendedAgentCard Extended (authenticated) agent card
Method Path Purpose
GET /.well-known/agent-card/{tenant_id}/{card_name} Public agent card (served as application/json)
GET /.well-known/a2a-jwks/{tenant_id} Tenant Ed25519 JWK Set (RFC 7517) for verifying card signatures

Agent cards are signed with the tenant’s Ed25519 key; verifiers resolve the signature kid via the JWKS endpoint.

Terminal window
curl http://localhost:8100/.well-known/agent-card/default/revenue-sentinel
Method Path Purpose
POST /v1/proxy/a2a/outbound Governed outbound call to an external agent
POST /v1/proxy/a2a/inbound Governed inbound call from an external agent

Delegation context is carried on an HMAC-signed x-brutor-delegation-* header chain. Prefer the v1.0 surface for new integrations.

Skills are served by the per-tenant system MCP server — there is no separate skills protocol. Guide: Agent Skills.

Endpoint POST /v1/proxy/mcp/system-agent-skill-server-{tenant} (e.g. system-agent-skill-server-default)
Protocol MCP JSON-RPC 2.0 (initialize, tools/list, tools/call)

initialize returns the protocol preamble plus the caller’s access-scoped skill catalog in instructions. tools/list advertises exactly five generic verbs (skills themselves are not tools):

Tool Layer Arguments (required in bold)
skills__list L1 discover
skills__load L2 load skill_name
skills__read_resource L3 act skill_name, path
skills__run_script L3 act skill_name, script, args, approval_token
skills__render_template L3 act skill_name, template, vars
  • skills__list{"skills": [{"name", "description"}, ...]} (access-scoped).
  • skills__load → two content blocks: protocol preamble + unmodified SKILL.md, then {"skill": {name, version, description}, "manifest": {resources, scripts, templates}}. Executes nothing; audited as skill_load.
  • skills__run_script → the script’s output only (sandbox execution), or a {"execution_id", "status": "pending_approval", ...} envelope when approval-gated — retry with approval_token. Audited as skill_run.
  • skills__read_resource / skills__render_template → governed file content / rendered text; audited as skill_read / skill_render.
  • Failures return isError: true in the tool result (MCP transport is always HTTP 200); the governance verdict lands on the audit row.

A legacy skill__<name> tools/call is tolerated as an alias for skills__load; it is unadvertised and never executes.

  • MCP servers and skills are resource-group gated — bind them to the caller’s resource group in the Control Plane (Admin API). Skill access control is resource-group-only.
  • Group per request resolves via X-Resource-Group header → primary group → first membership.
  • Guardrails apply on mcp_input/mcp_output, skill_input/skill_output, and a2a_inbound/a2a_outbound surfaces when enabled — see Guardrails.