Fastify
Purpose and Scope
Use the Fastify serving path when you want an MCP endpoint inside a Fastify application, but still want the SDK serving model to stay the same as the generic Streamable HTTP model. The Fastify recipe does not ask you to manage protocol sessions or wire a server transport by hand. Instead, you build a server factory, adapt the web-standard handler once for Node request and response objects, and mount that adapted function on a route. The result is a normal Fastify app whose MCP endpoint can live beside ordinary application routes.
Sources: docs/serving/fastify.md, docs/serving/http.md
Relevant Source Files
- docs/serving/fastify.md - Primary how-to for installing Fastify support, creating the Fastify app helper, mounting the MCP route, forwarding parsed bodies and auth, and verifying the endpoint.
- docs/serving/authorization.md - Explains bearer-token verification, protected resource metadata, scopes, and how authorization information reaches MCP request context.
- docs/serving/express.md - Provides the closest Node-framework comparison, especially the shared pattern of adapting the same MCP handler through the Node adapter.
- docs/serving/hono.md - Shows the web-standard variant, which clarifies how parsed bodies and auth info are passed without the Node adapter.
- docs/serving/http.md - Defines the underlying Streamable HTTP handler, the per-request factory model, handler lifecycle, request context, and scaling implications.
- docs/serving/legacy-clients.md - Documents how the same handler family can accept or reject legacy protocol-era requests depending on the configured posture.
Core Primitives
The Fastify integration has three main primitives. A server factory is a function that creates and returns a fresh server instance for one HTTP request. The MCP handler is the web-standard request handler produced from that factory. The Node adapter converts that web-standard handler into a function compatible with Fastify raw request and response objects. Keeping these roles separate matters because Fastify owns routing and body parsing, while the SDK owns MCP protocol handling, tool registration, capability responses, and response formatting. That separation also makes the same server factory portable across other serving recipes.
Sources: docs/serving/fastify.md, docs/serving/http.md, docs/serving/express.md, docs/serving/hono.md
Install the runtime pieces used by the Fastify recipe:
npm install @modelcontextprotocol/server @modelcontextprotocol/fastify @modelcontextprotocol/node fastifyMounting Flow
Start by creating a handler with a factory that registers tools, resources, and prompts on a new server instance. In the documented example, the server is named notes and exposes an add note tool whose input schema requires text. Then create the Fastify application with the SDK helper, wrap the handler with the Node adapter, and route all methods for the MCP endpoint to the adapted function. The Fastify callback passes the raw Node request, the raw Node response, and the already parsed body so the adapter does not consume the request stream again.
Sources: docs/serving/fastify.md, docs/serving/http.md
import { createMcpFastifyApp } from '@modelcontextprotocol/fastify';
import { toNodeHandler } from '@modelcontextprotocol/node';
import { createMcpHandler, McpServer } from '@modelcontextprotocol/server';
import * as z from 'zod/v4';
const handler = createMcpHandler(() => {
const server = new McpServer({ name: 'notes', version: '1.0.0' });
server.registerTool('add-note', { description: 'Append a note', inputSchema: z.object({ text: z.string() }) }, async ({ text }) => ({
content: [{ type: 'text', text: `Saved: ${text}` }]
}));
return server;
});
const app = createMcpFastifyApp();
const node = toNodeHandler(handler);
app.all('/mcp', (request, reply) => node(request.raw, reply.raw, request.body));Request Handling, Auth, and Body Parsing
Fastify parses JSON bodies before your route handler runs, so the SDK recipe intentionally passes the parsed body as the adapter third argument. That detail prevents a common integration bug: once a framework has consumed the request stream, a downstream adapter cannot safely re-read it. Authentication follows the raw Node request path in this integration. If application middleware or the route handler verifies a bearer token, attach the resulting authorization object to the raw request before calling the adapted handler. MCP handlers can then read the forwarded value from the HTTP request context.
Sources: docs/serving/fastify.md, docs/serving/authorization.md
publicApp.all('/mcp', async (request, reply) => {
const auth = await verifyToken(request.headers.authorization);
return node(Object.assign(request.raw, { auth }), reply.raw, request.body);
});The Fastify page treats token verification as application code, while the authorization guide explains the resource server responsibilities in more detail. A protected MCP server verifies access tokens issued elsewhere; it does not issue tokens itself. Missing, malformed, expired, or insufficient tokens should be rejected before the MCP route is allowed to run. When authorization succeeds, server code can tailor tool behavior, resource access, or prompt results to the authenticated caller by reading the request context provided to the server factory or handlers.
Sources: docs/serving/fastify.md, docs/serving/authorization.md, docs/serving/http.md
Host and Origin Protection
The SDK Fastify helper is not just a convenience constructor. It creates a Fastify application with DNS rebinding protection already applied. That protection checks Host and Origin before the MCP handler runs, which is important for local servers reachable on loopback addresses. With the default local binding, requests that claim a non-localhost host or origin are rejected. If you bind to all interfaces, the default local-only assumption no longer describes the deployment, so explicitly list the public hostnames and origins that are allowed to reach the endpoint.
Sources: docs/serving/fastify.md, docs/serving/express.md, docs/serving/hono.md
const publicApp = createMcpFastifyApp({ host: '0.0.0.0', allowedHosts: ['api.example.com'] });The allow lists are hostname based and port agnostic in the documented recipe. Requests without an Origin header still pass, which keeps non-browser MCP clients working while protecting against browser-originated attacks. This design matches the other web framework helpers: Express and Hono provide similar default defenses, while the raw HTTP handler is lower level and expects you to place Host, Origin, and authorization checks in front of it. For production deployments, decide these boundaries before exposing the endpoint beyond localhost.
Sources: docs/serving/fastify.md, docs/serving/express.md, docs/serving/hono.md, docs/serving/http.md
Run and Verify
After the route is mounted, start the Fastify app with a listen call and send a JSON-RPC request to the endpoint. The documented verification request lists tools and includes both JSON and event-stream in the Accept header. A successful response is delivered as a single server-sent event message containing the tools list, including the registered add note tool and its generated JSON Schema input description. This is a useful smoke test because it exercises routing, body parsing, MCP request handling, schema conversion, and response framing in one request.
Sources: docs/serving/fastify.md
await app.listen({ port: 3000 });curl -s -X POST http://127.0.0.1:3000/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Legacy and Runtime Considerations
The Fastify route uses the same handler family described by the Streamable HTTP and legacy-client pages, so legacy protocol posture is chosen when creating the handler rather than in the Fastify route itself. By default, the HTTP serving entry can handle older requests in a stateless way, while a strict configuration can reject legacy protocol revisions. If you still operate a sessionful legacy deployment, route legacy traffic ahead of the strict modern handler using the documented classification helper, and keep the Fastify route focused on the modern MCP endpoint.
Sources: docs/serving/fastify.md, docs/serving/http.md, docs/serving/legacy-clients.md
Related Pages and Next Steps
Use the Streamable HTTP page next if you need to understand factory lifetime, request context, handler shutdown, notifications, and scaling. Use the Authorization page before publishing a protected endpoint, because Fastify only forwards authorization information that your application has verified. Compare Express and Hono when choosing framework adapters: Express and Fastify use the Node adapter with raw request objects, while Hono can call the web-standard handler directly. Finally, read Legacy Clients if your host must support older clients during a migration window.
Sources: docs/serving/http.md, docs/serving/authorization.md, docs/serving/express.md, docs/serving/hono.md, docs/serving/legacy-clients.md