by flaviodelgrosso
Provides a Fastify plugin that adds streamable HTTP transport for Model Context Protocol servers, handling session management, authentication, and graceful shutdown out of the box.
Enables Fastify applications to act as Model Context Protocol (MCP) servers, exposing /mcp endpoints that support POST, GET, and DELETE for session‑based communication.
npm install fastify-mcp-server @modelcontextprotocol/sdk
import Fastify from 'fastify';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import FastifyMcpServer, { getMcpDecorator } from 'fastify-mcp-server';
const app = Fastify({ logger: true });
const mcp = new McpServer({ name: 'my-mcp-server', version: '1.0.0' });
mcp.tool('hello-world', () => ({
content: [{ type: 'text', text: 'Hello from MCP!' }]
}));
await app.register(FastifyMcpServer, {
server: mcp.server,
endpoint: '/mcp', // optional, defaults to /mcp
// optional auth configuration → see README for bearer token example
});
const mcpDecorator = getMcpDecorator(app);
await app.listen({ host: '127.0.0.1', port: 3000 });
/.well-knownQ: Do I have to use Bearer token authentication?
A: No. Authentication is optional; omit the authorization option to keep the endpoints open.
Q: How can I expose OAuth metadata for clients?
A: Provide authorization.oauth2.authorizationServerOAuthMetadata and/or protectedResourceOAuthMetadata when registering the plugin; the plugin will automatically serve the .well-known endpoints.
Q: What happens if the Fastify server shuts down abruptly?
A: Use the provided decorator’s shutdown() method or integrate with close-with-grace to gracefully close all MCP sessions before the underlying Fastify instance stops.
Q: Can I customize the endpoint path?
A: Yes, set the endpoint option (e.g., /custom-mcp) when registering the plugin.
Q: Is there a way to get active session counts?
A: Call mcpDecorator.getStats() which returns an object containing activeSessions and other metrics.
A robust Fastify plugin that provides seamless integration with the Model Context Protocol (MCP) through streamable HTTP transport. This plugin enables your Fastify applications to act as MCP servers, allowing AI assistants and other clients to interact with your services using the standardized MCP protocol.
The Model Context Protocol (MCP) is an open standard that enables AI assistants to securely connect to external data sources and tools. This plugin provides a streamable HTTP transport implementation for MCP servers built with Fastify, offering:
@modelcontextprotocol/sdknpm install fastify-mcp-server @modelcontextprotocol/sdk
To quickly see the plugin in action, you can run the following example:
npm run dev
npm run inspector
This will start a Fastify server with the MCP plugin enabled, allowing you to interact with it via the MCP inspector or any MCP-compatible client.
import Fastify from 'fastify';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import FastifyMcpServer, { getMcpDecorator } from 'fastify-mcp-server';
const app = Fastify({ logger: true });
// Create MCP server instance
const mcp = new McpServer({
name: 'my-mcp-server',
version: '1.0.0',
});
// Define MCP tools
mcp.tool('hello-world', () => ({
content: [{ type: 'text', text: 'Hello from MCP!' }]
}));
// Register the plugin
await app.register(FastifyMcpServer, {
server: mcp.server,
endpoint: '/mcp', // optional, defaults to '/mcp'
});
// Get MCP decorator for advanced features
const mcpServer = getMcpDecorator(app);
// Start the server
await app.listen({ host: '127.0.0.1', port: 3000 });
type FastifyMcpServerOptions = {
server: Server; // MCP Server instance from @modelcontextprotocol/sdk
endpoint?: string; // Custom endpoint path (default: '/mcp')
authorization?: { // Authorization configuration
bearerMiddlewareOptions?: {
verifier: OAuthTokenVerifier; // Custom verifier for Bearer tokens
requiredScopes?: string[]; // Optional scopes required for access
resourceMetadataUrl?: string; // Optional URL for resource metadata
};
oauth2?: { // OAuth2 metadata configuration
authorizationServerOAuthMetadata: OAuthMetadata; // OAuth metadata for authorization server
protectedResourceOAuthMetadata: OAuthProtectedResourceMetadata; // OAuth metadata for protected resource
};
};
}
The plugin decorates your Fastify instance with an MCP server that provides several useful methods:
const mcpServer = getMCPDecorator(app);
// Get session statistics
const stats = mcpServer.getStats();
console.log(`Active sessions: ${stats.activeSessions}`);
// Access session manager for event handling
const sessionManager = mcpServer.getSessionManager();
// Graceful shutdown
await mcpServer.shutdown();
Monitor session lifecycle with event listeners:
const sessionManager = mcpServer.getSessionManager();
// Session created
sessionManager.on('sessionCreated', (sessionId: string) => {
console.log(`New MCP session: ${sessionId}`);
});
// Session destroyed
sessionManager.on('sessionDestroyed', (sessionId: string) => {
console.log(`MCP session ended: ${sessionId}`);
});
// Transport errors
sessionManager.on('transportError', (sessionId: string, error: Error) => {
console.error(`Error in session ${sessionId}:`, error);
});
The plugin exposes three HTTP endpoints for MCP communication:
/mcpcontent-type: application/jsonmcp-session-id: <session-id> (optional, for existing sessions)/mcpmcp-session-id: <session-id> (required)/mcpmcp-session-id: <session-id> (required)Sessions are managed through a dedicated SessionManager class that:
sessionManager.on('transportError', (sessionId, error) => {
console.error(`Transport error: ${error.message}`);
});
// Periodic health check
setInterval(() => {
const stats = mcpServer.getStats();
console.log(`Health Check - Active Sessions: ${stats.activeSessions}`);
// Alert if too many sessions
if (stats.activeSessions > 100) {
console.warn('High session count detected');
}
}, 30000);
import closeWithGrace from 'close-with-grace';
closeWithGrace({ delay: 500 }, async ({ signal, err }) => {
if (err) {
app.log.error({ err }, 'server closing with error');
} else {
app.log.info(`${signal} received, server closing`);
}
// Shutdown MCP sessions before closing Fastify
await mcpServer.shutdown();
await app.close();
});
You can secure your MCP endpoints using Bearer token authentication. The plugin provides a bearerMiddlewareOptions option, which enables validation of Bearer tokens in the Authorization header for all MCP requests.
Pass the authorization.bearerMiddlewareOptions option when registering the plugin. It accepts BearerAuthMiddlewareOptions from the SDK:
import type { BearerAuthMiddlewareOptions } from '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js';
await app.register(FastifyMcpServer, {
server: mcp.server,
authorization: {
bearerMiddlewareOptions: {
verifier: myVerifier, // implements verifyAccessToken(token)
requiredScopes: ['mcp:read', 'mcp:write'], // optional
resourceMetadataUrl: 'https://example.com/.well-known/oauth-resource', // optional,
}
}
});
verifyAccessToken(token) method that returns the decoded token info or throws on failure. It must implements the OAuthTokenVerifier interface from the SDK.WWW-Authenticate header for 401 responses.The plugin uses a Fastify preHandler hook applied in the context of the MCP registered routes (see addBearerPreHandlerHook) to:
Authorization header (Authorization: Bearer TOKEN).req.raw.auth).WWW-Authenticate headers on failure.You can access the validated authentication information in your MCP tools via the authInfo parameter:
mcp.tool('example-auth-tool', 'Demo to display the validated access token in authInfo object', ({ authInfo }) => {
return {
content: [
{
type: 'text',
// Just a bad example, do not expose sensitive information in your LLM responses! :-)
text: `Authenticated with token: ${authInfo.token}, scopes: ${authInfo.scopes.join(', ')}, expires at: ${new Date(authInfo.expiresAt).toISOString()}`
}
]
};
});
If authentication fails, the response will include a WWW-Authenticate header:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token", error_description="Token has expired"
Content-Type: application/json
{"error":"invalid_token","error_description":"Token has expired"}
{
"inputs": [
{
"type": "promptString",
"id": "bearer_token",
"description": "Enter your MCP Bearer Token",
"password": true
}
],
"servers": {
"my-mcp-server": {
"url": "http://localhost:9080/mcp",
"headers": {
"Authorization": "Bearer ${input:bearer_token}"
}
}
}
}
The plugin can automatically register standard OAuth 2.0 metadata endpoints under the .well-known path, which are useful for interoperability with OAuth clients and resource servers. You can test metadata discovery with the MCP inspector in the Authentication tab.
To enable these endpoints, provide the authorization.oauth2.authorizationServerOAuthMetadata and/or authorization.oauth2.protectedResourceOAuthMetadata options when registering the plugin:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import FastifyMcpServer from 'fastify-mcp-server';
const mcp = new McpServer({
name: 'my-mcp-server',
version: '1.0.0',
});
const authorizationServerMetadata = {
issuer: 'https://your-domain.com',
authorization_endpoint: 'https://your-domain.com/oauth/authorize',
token_endpoint: 'https://your-domain.com/oauth/token',
// ...other OAuth metadata fields
};
const protectedResourceMetadata = {
resource: 'https://your-domain.com/.well-known/oauth-protected-resource',
// ...other resource metadata fields
};
await app.register(FastifyMcpServer, {
server: mcp.server,
authorization: {
oauth2: {
authorizationServerOAuthMetadata: authorizationServerMetadata, // Registers /.well-known/oauth-authorization-server
protectedResourceOAuthMetadata: protectedResourceMetadata, // Registers /.well-known/oauth-protected-resource
}
}
});
GET /.well-known/oauth-authorization-server — Returns the OAuth authorization server metadata.GET /.well-known/oauth-protected-resource — Returns the OAuth protected resource metadata.These endpoints are registered only if the corresponding metadata options are provided.
# Clone the repository
git clone https://github.com/flaviodelgrosso/fastify-mcp-server.git
cd fastify-mcp-server
# Install dependencies
npm install
# Run development server with hot reload
npm run dev
npm run dev - Run development server with hot reloadnpm run build - Build TypeScript to JavaScriptnpm test - Run test suite with 100% coveragenpm run test:lcov - Generate LCOV coverage reportnpm run release - Create a new releaseThe project maintains 100% test coverage. Run tests with:
npm test
Contributions are welcome! Please read our contributing guidelines and ensure:
ISC © Flavio Del Grosso
Please log in to share your review and rating for this MCP.
Explore related MCPs that share similar capabilities and solve comparable challenges
by zed-industries
A high‑performance, multiplayer code editor designed for speed and collaboration.
by modelcontextprotocol
Model Context Protocol Servers
by modelcontextprotocol
A Model Context Protocol server for Git repository interaction and automation.
by modelcontextprotocol
A Model Context Protocol server that provides time and timezone conversion capabilities.
by cline
An autonomous coding assistant that can create and edit files, execute terminal commands, and interact with a browser directly from your IDE, operating step‑by‑step with explicit user permission.
by continuedev
Enables faster shipping of code by integrating continuous AI agents across IDEs, terminals, and CI pipelines, offering chat, edit, autocomplete, and customizable agent workflows.
by upstash
Provides up-to-date, version‑specific library documentation and code examples directly inside LLM prompts, eliminating outdated information and hallucinated APIs.
by github
Connects AI tools directly to GitHub, enabling natural‑language interactions for repository browsing, issue and pull‑request management, CI/CD monitoring, code‑security analysis, and team collaboration.
by daytonaio
Provides a secure, elastic infrastructure that creates isolated sandboxes for running AI‑generated code with sub‑90 ms startup, unlimited persistence, and OCI/Docker compatibility.