Routing a document service behind one MCP URL
Use-case design, not a verified MCP integration. This page targets ngx_mruby
3.x on next. Its HTTP routing and streamed relay are tested; an MCP-specific
recipe and interoperability suite have not been implemented. MCP protocol
requirements below target revision 2026-07-28. They are requirements for the
service you build, not capabilities supplied automatically by ngx_mruby.
Who this helps
Section titled “Who this helps”A service offers document search, text retrieval and expensive source processing.
Clients should keep one MCP endpoint while the operator separates those workloads.
A gateway can select a configured backend using service-specific rules and an
allowlist. Ruby is useful when several conditions or service rules determine the
choice; a fixed header-to-backend mapping may only need nginx map.
The proposed path is AI client → one /mcp endpoint → nginx with Ruby rules →
search, text or source-processing backend → original response. Separate workers
can keep source processing from consuming the search workers, but this arrangement
is not itself a latency or capacity guarantee. Measure the deployed workload.
Keep the protocols separate: the LLM provider path carries Messages or Responses; the MCP path carries JSON-RPC. One gateway can host both locations without translating either protocol. This is not a stdio-to-HTTP converter, an MCP server, a tool-discovery aggregator or an OAuth implementation.
Responsibilities before deploying
Section titled “Responsibilities before deploying”Use the Streamable HTTP specification and versioning rules as the contract. Declare which versions your endpoint accepts; older clients need an explicit compatibility strategy. The 2026-07-28 revision removed protocol sessions and the GET stream endpoint; an SSE disconnect now signals cancellation.
Routing metadata (Mcp-Method, Mcp-Name, MCP-Protocol-Version) must agree with
the JSON-RPC body after the specified decoding. The destination MCP service must
validate that agreement before executing a tool. Headers alone do not establish
permission. Keep a service responsible for discovery and the complete tool list;
refuse unknown tools and unsupported versions without selecting an arbitrary
backend. Preserve JSON and SSE responses and required error headers.
Validate Origin and apply the MCP authorization contract.
A lookup of a Bearer string in a Ruby hash is not OAuth. The resource server must
validate the issuer, intended audience and granted scope; never repurpose a token
for another service. Avoid logging bodies or secret identifiers. Do not enable
unconditional retries for tools/call: repeating a modifying tool can repeat its
side effects.
What can be reused today
Section titled “What can be reused today”- HTTP routing: request data selects a configured upstream.
- Agent proxy recipes: nginx connection limits, credential replacement and byte-preserving streaming, tested with mock LLM APIs.
- Request phases: where Ruby runs within nginx.
These tests establish those mechanisms, not MCP compatibility. Connection limits are concurrent-work controls, not monthly quotas or token billing. A long-lived subscription should not silently consume the same allowance as a short tool call. Shared usage accounting, event-level processing and first-byte deadlines remain planned v3 work; see the implementation-status table in the agent proxy guide.
Acceptance test for the first executable recipe
Section titled “Acceptance test for the first executable recipe”A follow-up recipe should use two localhost mock MCP backends and one endpoint, with no provider account or real credentials. Its test must cover:
- Discovery and a complete tool list, known and unknown tool names, supported and unknown protocol versions, and header/body disagreements.
- Search versus source-processing routing, with the original request body and response bytes preserved. Editing the Ruby rule must change the destination.
- Origin rejection, authorization failures and their response headers, JSON and SSE, cancellation on disconnect, and a modifying call sent only once.
- Any supported older client version in a separate case, rather than applying the new cancellation/session rules to it.
Until that recipe runs, use this page to design the boundary and the HTTP recipe to learn routing. Do not advertise an MCP-conformance result from the LLM tests.