MCP Gateway Fix Sprint — 4 Protocol-Breaking Bugs Eliminated
2026-03-08
LOCKSTEP TRACEABILITY MATRIX --- api_endpoints: [ "POST /v1/mcp/tools/call", "GET /v1/mcp/governance/status", "GET /v1/mcp/governance/approvals", "POST /v1/mcp/governance/approve", "POST /v1/mcp/governance/deny", "PUT /v1/mcp/governance/policy", "POST /v1/mcp/discover", "POST /auth/mcp/tools/call", "GET /auth/mcp/governance/status", "GET /auth/mcp/governance/approvals", "POST /auth/mcp/governance/approve", "POST /auth/mcp/governance/deny", "PUT /auth/mcp/governance/policy", "POST /auth/mcp/discover", ] sdk_methods_updated: [ "client.mcp.discover()", "client.mcp.callTool()", "client.mcp.governanceStatus()", "client.mcp.approvals()", "client.mcp.approve()", "client.mcp.deny()", "client.mcp.updatePolicy()", ] mcp_tools_updated: ["McpAuthContext agentId propagation"] ---
What We Built
A sprint to fix four protocol-breaking bugs in the MCP gateway that made it non-functional despite claiming "Zero-Trust Control Plane" status.
Shared MCP Service Layer (src/mcp/mcp-service.ts): Extracted all MCP operations — server CRUD, tool execution, audit, governance queries, discovery — into a single service module. Both the API-key authenticated surface (/v1/mcp/) and the JWT authenticated surface (/auth/mcp/) now delegate to this shared service, eliminating six independent code drift vectors: ID formats, defaults, response shapes, enabled filtering, governance enforcement, and agent identity propagation.
MCP Streamable HTTP Client (src/mcp/mcp-client.ts): Replaced a broken custom POST /call-tool endpoint (which no MCP server implements) with a proper MCP protocol client — JSON-RPC 2.0 over HTTP with Mcp-Session-Id session tracking, initialize handshake, and tools/call and tools/list methods with SSE stream response parsing. SSRF protection enforced via validateEndpointUrl() and egress domain allowlist via checkEgressDomain().
Governance on the HTTP Hot Path: Wired McpGovernor.checkPolicy() into the shared service's executeToolCall() method. Denied calls return 403, queued-for-approval calls return 202. Added five HTTP governance endpoints (status, approvals list, approve, deny, policy update) available on both auth surfaces — ten new endpoints total.
Identity Propagation: agentId now flows through both MCP route surfaces (API-key via _agentIdentity context, JWT via the same mechanism) and into the BR-as-MCP-server McpAuthContext. Audit entries record which agent made each tool call, closing the accountability gap.
Why It Matters
An MCP gateway that cannot speak MCP protocol is not an MCP gateway. The previous implementation had a custom POST /call-tool HTTP endpoint that no MCP server in the ecosystem implements, governance enforcement only on the WebSocket transport (which most integrations do not use), two separate route files that drifted on ID formats and response shapes, and no agent identity in audit trails. Customers integrating external MCP servers would hit immediate failures, and those who somehow got calls through would bypass all governance policy.
These fixes make BrainstormRouter the only AI gateway where MCP tool calls go through proper protocol negotiation, policy enforcement, and identity-attributed audit — on every transport, for every auth method.
How It Works
The shared service pattern eliminates drift by construction:
// Both /v1/mcp/* and /auth/mcp/* routes delegate to the same service
const service = createMcpService(deps);
// API-key route
app.post("/v1/mcp/tools/call", async (c) => {
const agentId = c.get("_agentIdentity")?.agentId;
return service.executeToolCall(c.req, { agentId, tenantId });
});
// JWT route — identical call, different auth extraction
app.post("/auth/mcp/tools/call", async (c) => {
const agentId = c.get("_agentIdentity")?.agentId;
return service.executeToolCall(c.req, { agentId, tenantId });
});
The MCP client implements proper protocol negotiation:
// JSON-RPC 2.0 initialize handshake
const initResult = await client.request("initialize", {
protocolVersion: "2025-03-26",
capabilities: { tools: {} },
clientInfo: { name: "brainstormrouter", version },
});
// Tool call with session tracking
const result = await client.request(
"tools/call",
{
name: toolName,
arguments: toolArgs,
},
{ headers: { "Mcp-Session-Id": sessionId } },
);
Governance intercepts every tool call before execution:
async executeToolCall(req, context) {
const decision = await this.governor.checkPolicy({
tool: toolName, server: serverId,
agentId: context.agentId, tenantId: context.tenantId
});
if (decision.action === 'deny') return { status: 403, reason: decision.reason };
if (decision.action === 'queue') return { status: 202, approvalId: decision.id };
// Proceed with actual MCP protocol call
return this.client.callTool(server, toolName, args);
}
The Numbers
| Metric | Before | After |
|---|---|---|
| Files changed | — | 14 |
| Lines added | — | 1,360 |
| Lines removed | — | 461 |
| New core files | 0 | 2 (mcp-service.ts, mcp-client.ts) |
| Governance endpoints (HTTP) | 0 | 10 (5 per auth surface) |
| Transport types | 4 (stdio, websocket, sse, streamable-http) | 2 (sse, streamable-http) |
| Auth types | 3 (none, api-key, oauth) | 2 (none, api-key) |
| Code drift vectors | 6 | 0 |
| MCP protocol compliance | Broken (custom endpoint) | Compliant (JSON-RPC 2.0) |
| Governance on HTTP path | No | Yes |
| Agent identity in audit | No | Yes |
Competitive Edge
No other AI gateway treats MCP as a governed, auditable control plane. Portkey and OpenRouter do not support MCP at all. Letta has MCP tool integration but without governance, policy enforcement, or identity attribution. BrainstormRouter is the only platform where an enterprise can connect external MCP servers and get automatic policy enforcement (approve/deny/queue), per-agent audit trails, and proper MCP protocol compliance — not a custom HTTP hack that breaks on first contact with a real MCP server.
Lockstep Checklist
> _You MUST check these boxes [x] and verify the corresponding files are updated BEFORE committing this log._
- [x] API Routes:
src/api/routes/updated — shared service wired into both mcp.ts and auth.ts route files. - [x] TS SDK:
packages/sdk-tsupdated — discover + governance methods, transport type narrowed to"streamable-http" | "sse". - [x] Python SDK:
packages/sdk-pyupdated —call_tool()endpoint fix, discover + governance methods added. - [x] MCP Schemas:
agentIdpropagated intoMcpAuthContextfor BR-as-MCP-server identity. - [x] Dashboard:
site/dashboard/MCP page updated with discover buttons, governance status section, normalized response shapes. - [x] Master Record:
docs/architecture/master-capability-record.mdreflects MCP gateway capabilities.