Transportation
Follow an MCP request through JSON-RPC, Elysia's lifecycle, and response marshaling.
The adapter exposes MCP over HTTP at POST /mcp by default. It accepts JSON-RPC requests, resolves a registered primitive, and keeps route-backed calls inside Elysia by constructing an internal Request and passing it to app.handle().
Supported JSON-RPC methods
initializepingtools/listandtools/callresources/list,resources/templates/list, andresources/readprompts/listandprompts/get
Notifications receive an empty 202 response. Batches are processed as JSON-RPC batches, and protocol errors are returned as JSON-RPC error objects.
Route-backed request lifecycle
This architecture preserves parsers, transforms, TypeBox validation, derived context, local hooks, guards, auth plugins, error handlers, and response mapping. The adapter does not invoke a route handler directly.
Request identity and credentials
The JSON-RPC request itself becomes the invocation context. For route-backed calls, configured inbound headers are copied onto the internal request. The secure defaults pass through authorization, cookie, and x-api-key, while tool arguments cannot set headers unless you explicitly allow them.
mcp({
headers: {
passThroughFromMcpRequest: ['authorization'],
allowFromToolInput: ['x-request-id']
}
})The internal route still sees an ordinary Request; existing Elysia authentication and authorization hooks run in their usual lifecycle stages.
OpenTelemetry
Install Elysia's OpenTelemetry plugin and register it before the routes you want to observe:
import { opentelemetry } from '@elysiajs/opentelemetry'
import { mcp } from '@mwillbanks/elysia-mcp-adapter'
import { Elysia } from 'elysia'
const app = new Elysia()
.use(opentelemetry())
.use(mcp())
.get(
'/reports/:id',
({ params }) => ({ id: params.id }),
{
detail: { operationId: 'reports.get' }
}
)No adapter-specific telemetry setup is required. Because route-backed tools call app.handle(), Elysia's OpenTelemetry plugin observes the internal request and groups its lifecycle spans just like an HTTP request. Name important hooks so trace waterfalls are readable, and use Elysia's record, getCurrentSpan, or setAttributes APIs when a route needs custom spans or attributes.
Export and sampling behavior remains owned by your OpenTelemetry configuration and collector.
Streaming and sessions
GET Server-Sent Events and DELETE session termination are disabled by default. Enable them only when your client requires those transport methods:
mcp({
transport: {
enableGetSse: true,
enableDeleteSession: true
}
})See Security before exposing browser origins or forwarding additional headers.