Skip to content

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.

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.

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.

  • 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:

  1. Discovery and a complete tool list, known and unknown tool names, supported and unknown protocol versions, and header/body disagreements.
  2. Search versus source-processing routing, with the original request body and response bytes preserved. Editing the Ruby rule must change the destination.
  3. Origin rejection, authorization failures and their response headers, JSON and SSE, cancellation on disconnect, and a modifying call sent only once.
  4. 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.