# How OpenSEO Implements JSON-RPC Transport for Its MCP Server

> Discover how OpenSEO implements JSON-RPC transport for its MCP server using WebStandardStreamableHTTPServerTransport and specialized handlers. Learn about its CORS and validation policies.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: internals
- Published: 2026-09-02

---

**OpenSEO implements JSON-RPC transport for its MCP server by leveraging the `WebStandardStreamableHTTPServerTransport` class from the Model Context Protocol SDK with `enableJsonResponse` enabled, routing legacy requests through specialized handlers in [`src/server/mcp/transport.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/transport.ts) while maintaining strict CORS and validation policies.**

The OpenSEO repository provides a robust Model Context Protocol (MCP) implementation that supports both modern Server-Sent Events (SSE) streaming and traditional JSON-RPC request/response patterns. Understanding how OpenSEO implements JSON-RPC transport reveals a dual-mode architecture designed to accommodate both legacy clients and modern streaming consumers within a unified request handler.

## Core JSON-RPC Transport Architecture

### Transport Layer Configuration

At the heart of the implementation lies the `WebStandardStreamableHTTPServerTransport` class imported from the `@modelcontextprotocol/server` package. When configured with `enableJsonResponse: true`, this transport buffers the entire MCP response into a single JSON payload instead of streaming SSE events. This configuration is essential for supporting traditional JSON-RPC clients that expect a complete response in one HTTP round-trip.

The transport instantiation occurs within the `handleLegacyJsonRequest` function, where it is paired with an OpenSEO-specific server instance:

```typescript
// src/server/mcp/transport.ts
const transport = new WebStandardStreamableHTTPServerTransport({
  sessionIdGenerator: undefined,
  enableJsonResponse: true, // Enables JSON-RPC mode
});

```

### CORS Handling for Cross-Origin Requests

To ensure consistent behavior across both transport modes, OpenSEO defines a constant `MCP_CORS_HEADERS` that mirrors the Model Context Protocol SDK's default CORS options. The helper function `withMcpCors` injects these headers into every response, guaranteeing that legacy JSON-RPC calls receive the same cross-origin treatment as modern SSE connections. This prevents CORS-related failures when browser-based clients or cross-origin scripts invoke the MCP endpoint.

## Legacy Request Processing Pipeline

### Request Validation and Origin Checking

Before processing any legacy JSON-RPC call, OpenSEO enforces strict security through the `validateLegacyRequest` function. This utility applies the same host and origin validation checks used by the Agents SDK for modern requests, utilizing `hostHeaderValidationResponse` and `originValidationResponse` to inspect incoming headers. Requests that fail these checks are immediately rejected with appropriate error responses, to which CORS headers are appended to ensure the client can read the rejection reason.

### The handleLegacyJsonRequest Method

The `handleLegacyJsonRequest` function serves as the primary entry point for JSON-RPC processing when a POST request lacks the modern `_meta` envelope. The implementation follows a strict three-step protocol:

1. **Method Guarding** – Rejects any non-POST requests with a JSON-RPC error object (`code: -32000`), ensuring only valid JSON-RPC method invocations proceed.

2. **Server Instantiation** – Creates an OpenSEO-specific MCP server via `createOpenSeoMcpServer(props)`, which configures the available tools and capabilities for the requesting context.

3. **Transport Coordination** – Constructs the `WebStandardStreamableHTTPServerTransport` with JSON response mode enabled, then processes the incoming request and wraps the output with CORS headers via `withMcpCors`.

```typescript
// Example: Manual JSON-RPC call to the OpenSEO MCP endpoint
const resp = await fetch("https://open-seo.test/mcp", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Accept: "application/json",
  },
  body: JSON.stringify({
    jsonrpc: "2.0",
    id: 1,
    method: "tools/list",
  }),
});

const data = await resp.json(); // JSON-RPC response object
console.log(data);

```

## Request Routing and Entry Points

### Unified Request Routing Logic

The `createRequestHandler` function acts as the central router for all MCP traffic. It first short-circuits OPTIONS preflight requests with static CORS headers, then verifies that the request path matches the `MCP_ROUTE` constant. The critical routing decision occurs via `isLegacyRequest`, which detects the absence of the modern `_meta` envelope. Legacy requests are funneled through `validateLegacyRequest` and subsequently `handleLegacyJsonRequest`, while modern requests utilize the SSE-based `createMcpHandler`.

```typescript
// Example: Internal invocation of the legacy transport
async function legacyCall(request: Request, props: McpProps) {
  // handleLegacyJsonRequest creates server + transport with JSON mode enabled
  return await handleLegacyJsonRequest(request, props);
}

```

### Authenticated (Hosted) Flow

For authenticated deployments, `handleAuthenticatedOpenSeoMcpRequest` extracts the OAuth-validated MCP context from the incoming request. It verifies the required `MCP_SCOPE`, checks user membership through the authentication repository, and constructs per-request `McpProps` via `createWorkersOAuthMcpProps`. These properties are then passed to `createRequestHandler`, which automatically selects the JSON-RPC transport for legacy requests while applying the hosted security context.

### Self-Hosted Flow

Self-hosted instances utilize `handleSelfHostedOpenSeoMcpRequest`, which resolves identity using either the Cloudflare Access resolver or a local no-authentication resolver. After constructing the appropriate `McpProps`, the function forwards the request to the same unified `createRequestHandler`, ensuring that self-hosted deployments retain full JSON-RPC compatibility without requiring OAuth infrastructure.

## Summary

- OpenSEO uses `WebStandardStreamableHTTPServerTransport` with `enableJsonResponse: true` to buffer MCP responses into single JSON payloads for legacy clients.
- The `handleLegacyJsonRequest` function in [`src/server/mcp/transport.ts`](https://github.com/every-app/open-seo/blob/main/src/server/mcp/transport.ts) guards HTTP methods, instantiates the MCP server, and manages JSON-specific transport configuration.
- CORS headers are standardized across both transport modes via `MCP_CORS_HEADERS` and the `withMcpCors` helper to ensure cross-origin compatibility.
- `validateLegacyRequest` enforces host and origin validation using SDK utilities `hostHeaderValidationResponse` and `originValidationResponse` before processing legacy calls.
- The `createRequestHandler` function automatically routes requests to the appropriate transport based on the presence of modern `_meta` envelopes in the request body.
- Both authenticated (`handleAuthenticatedOpenSeoMcpRequest`) and self-hosted (`handleSelfHostedOpenSeoMcpRequest`) entry points support legacy JSON-RPC through the unified routing pipeline.

## Frequently Asked Questions

### What is the difference between OpenSEO's SSE and JSON-RPC transport modes?

The SSE (Server-Sent Events) mode streams responses as discrete events for modern MCP clients that support progressive updates, while the JSON-RPC mode buffers the entire response into a single JSON payload suitable for traditional HTTP clients. OpenSEO determines which mode to use based on whether the incoming request contains the modern `_meta` envelope or requires legacy handling.

### How does OpenSEO handle CORS for JSON-RPC requests?

OpenSEO uses the `MCP_CORS_HEADERS` constant to define standard cross-origin headers matching the Model Context Protocol SDK defaults. The `withMcpCors` helper function wraps every JSON-RPC response—both successful results and validation rejections—with these headers, ensuring browser-based clients and cross-origin scripts can interact with the MCP endpoint without CORS errors.

### What happens if a non-POST request is sent to the JSON-RPC endpoint?

The `handleLegacyJsonRequest` function explicitly guards against non-POST methods by returning a JSON-RPC error object with code `-32000`. This adheres to the JSON-RPC 2.0 specification expectations while preventing invalid HTTP methods from reaching the MCP server logic.

### How does the server distinguish between modern and legacy MCP requests?

The `createRequestHandler` function relies on `isLegacyRequest` to detect the absence of the modern `_meta` envelope in POST request bodies. Requests lacking this envelope are routed to `validateLegacyRequest` and subsequently `handleLegacyJsonRequest` for JSON-RPC processing, while requests containing the envelope are handled by the modern SSE-capable `createMcpHandler`.