Skip to content

ADR 0005: MCP server with the official C# SDK, stateless streamable HTTP and shared authorization

Status: Accepted (release 0.1.0)

Bookend offers an MCP surface that mirrors the REST API (loans, findings, boarding preview, package submission, evidence, queue statistics) for integrations and assistants, authenticated with the same tokens and audited call by call. The options were the official ModelContextProtocol C# SDK, a hand-rolled JSON-RPC endpoint, or a separate MCP sidecar process.

  • Use the official SDK (ModelContextProtocol.AspNetCore 2.2.0, pinned) hosted inside the API process: AddMcpServer().WithHttpTransport(Stateless = true).WithTools<BookendMcpTools>() and app.MapMcp("/mcp").RequireAuthorization(Policies.ReadOnly).
  • Stateless streamable HTTP: no server-side session table, nothing to replicate, and a bank’s reverse proxy needs no sticky routing. Each request carries its own bearer token, so the ASP.NET authentication and authorization pipeline applies unchanged.
  • Tools are thin: they resolve the same application services the REST endpoints use and never touch persistence directly. A per-call wrapper (McpToolAudit) checks the tool’s policy with IAuthorizationService, serializes the result, converts BookendExceptions into tool errors, and appends an auth_events row (mcp_call) with the arguments’ SHA-256 and the result size.
  • No separate sidecar: a second process would need its own token validation, dependency graph and audit path, for no isolation benefit inside the bank’s network.
  • One container, one authorization model, one audit trail. The contract tests drive /mcp through the official client SDK with an API-key token, so wire compatibility is verified, not assumed.
  • Stateless mode means no server-initiated notifications or resumable streams; none of the tools need them.
  • Tool arguments are not stored (they may contain PDF bytes); only their hash is, which is enough to correlate a call with what the caller logged.