A TypeScript framework for building MCP servers capable of handling client sessions.
[!NOTE]
For a Python implementation, see FastMCP.
FastMCP is built on top of the official SDK.
The official SDK provides foundational blocks for building MCPs, but leaves many implementation details to you:
FastMCP eliminates this complexity by providing an opinionated framework that:
When to choose FastMCP: You want to build MCP servers quickly without dealing with low-level implementation details.
When to use the official SDK: You need maximum control or have specific architectural requirements. In this case, we encourage referencing FastMCP's implementation to avoid common pitfalls.
npm install fastmcp
[!NOTE]
There are many real-world examples of using FastMCP in the wild. See the Showcase for examples.
import { FastMCP } from "fastmcp";
import { z } from "zod"; // Or any validation library that supports Standard Schema
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
});
server.addTool({
name: "add",
description: "Add two numbers",
parameters: z.object({
a: z.number(),
b: z.number(),
}),
execute: async (args) => {
return String(args.a + args.b);
},
});
server.start({
transportType: "stdio",
});
That's it! You have a working MCP server.
You can test the server in terminal with:
git clone https://github.com/punkpeye/fastmcp.git
cd fastmcp
pnpm install
pnpm build
# Test the addition server example using CLI:
npx fastmcp dev src/examples/addition.ts
# Test the addition server example using MCP Inspector:
npx fastmcp inspect src/examples/addition.ts
If you are looking for a boilerplate repository to build your own MCP server, check out fastmcp-boilerplate.
FastMCP supports multiple transport options for remote communication, allowing an MCP hosted on a remote machine to be accessed over the network.
HTTP streaming provides a more efficient alternative to SSE in environments that support it, with potentially better performance for larger payloads.
You can run the server with HTTP streaming support:
server.start({
transportType: "httpStream",
httpStream: {
port: 8080,
},
});
This will start the server and listen for HTTP streaming connections on http://localhost:8080/mcp.
Note: You can also customize the endpoint path using the
httpStream.endpointoption (default is/mcp).Note: To serve HTTP streaming and built-in OAuth routes under an issuer path, set
httpStream.basePath(for example,/issuer1). This exposes authorization server metadata at/.well-known/oauth-authorization-server/issuer1per RFC 8414.Note: This also starts an SSE server on
http://localhost:8080/sse.
You can connect to these servers using the appropriate client transport.
For HTTP streaming connections:
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const client = new Client(
{
name: "example-client",
version: "1.0.0",
},
{
capabilities: {},
},
);
const transport = new StreamableHTTPClientTransport(
new URL(`http://localhost:8080/mcp`),
);
await client.connect(transport);
For SSE connections:
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
const client = new Client(
{
name: "example-client",
version: "1.0.0",
},
{
capabilities: {},
},
);
const transport = new SSEClientTransport(new URL(`http://localhost:8080/sse`));
await client.connect(transport);
FastMCP supports HTTPS for secure connections by providing SSL certificate options:
server.start({
transportType: "httpStream",
httpStream: {
port: 8443,
sslCert: "./path/to/cert.pem",
sslKey: "./path/to/key.pem",
sslCa: "./path/to/ca.pem", // Optional: for client certificate authentication
},
});
This will start the server with HTTPS on https://localhost:8443/mcp.
SSL Options:
sslCert - Path to SSL certificate filesslKey - Path to SSL private key filesslCa - (Optional) Path to CA certificate for mutual TLS authenticationFor testing, you can generate self-signed certificates:
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"
For production, obtain certificates from a trusted CA like Let's Encrypt.
See the https-server example for a complete demonstration.
By default, FastMCP enables CORS with a standard set of allowed headers. You can customize the CORS behavior by passing a cors option:
server.start({
transportType: "httpStream",
httpStream: {
port: 8080,
cors: {
origin: "http://localhost:3000",
allowedHeaders: [
"Content-Type",
"Authorization",
"Accept",
"Mcp-Session-Id",
"Mcp-Protocol-Version",
"Last-Event-Id",
"X-Custom-Header",
],
credentials: true,
},
},
});
The cors option accepts:
true (default) - enable CORS with default settingsfalse - disable CORS entirelyorigin - a string, array of strings, or a function (origin: string) => booleanallowedHeaders - a string or array of stringsmethods - array of allowed HTTP methodsexposedHeaders - array of headers to exposecredentials - boolean to allow credentialsmaxAge - preflight cache duration in secondsThe CorsOptions type is exported from fastmcp for convenience.
FastMCP allows you to add custom HTTP routes alongside MCP endpoints, enabling you to build comprehensive HTTP services that include REST APIs, webhooks, admin interfaces, and more - all within the same server process.
// Add REST API endpoints
server.addRoute("GET", "/api/users", async (req, res) => {
res.json({ users: [] });
});
// Handle path parameters
server.addRoute("GET", "/api/users/:id", async (req, res) => {
res.json({
userId: req.params.id,
query: req.query, // Access query parameters
});
});
// Handle POST requests with body parsing
server.addRoute("POST", "/api/users", async (req, res) => {
const body = await req.json();
res.status(201).json({ created: body });
});
// Serve HTML content
server.addRoute("GET", "/admin", async (req, res) => {
res.send("<html><body><h1>Admin Panel</h1></body></html>");
});
// Handle webhooks
server.addRoute("POST", "/webhook/github", async (req, res) => {
const payload = await req.json();
const event = req.headers["x-github-event"];
// Process webhook...
res.json({ received: true });
});
Custom routes support:
:param) and wildcards (*)authenticate function as MCPRoutes are matched in the order they are registered, allowing you to define specific routes before catch-all patterns.
By default, custom routes require authentication (if configured). You can make routes public by adding the { public: true } option:
// Public route - no authentication required
server.addRoute(
"GET",
"/.well-known/openid-configuration",
async (req, res) => {
res.json({
issuer: "https://example.com",
authorization_endpoint: "https://example.com/auth",
token_endpoint: "https://example.com/token",
});
},
{ public: true },
);
// Private route - requires authentication
server.addRoute("GET", "/api/users", async (req, res) => {
// req.auth contains authenticated user data
res.json({ users: [] });
});
// Public static files
server.addRoute(
"GET",
"/public/*",
async (req, res) => {
// Serve static files without authentication
res.send(`File: ${req.url}`);
},
{ public: true },
);
Public routes are perfect for:
.well-known/*)See the custom-routes example for a complete demonstration.
FastMCP supports edge runtimes like Cloudflare Workers, enabling deployment of MCP servers to the edge with minimal latency worldwide.
| Use Case | Class | Import |
|---|---|---|
| Node.js, Express, Bun | FastMCP |
import { FastMCP } from "fastmcp" |
| Cloudflare Workers, Deno Deploy | EdgeFastMCP |
import { EdgeFastMCP } from "fastmcp/edge" |
| Feature | FastMCP | EdgeFastMCP |
|---|---|---|
| Runtime | Node.js | Edge (V8 isolates) |
| Start method | server.start({ port }) |
export default server |
| Transport | stdio, httpStream, SSE | HTTP Streamable only |
| Sessions | Stateful or stateless | Stateless only |
| File system | Yes | No |
| OAuth/Authentication | Built-in authenticate option |
Use Hono middleware (built-in planned) |
| Custom routes | server.getApp() |
server.getApp() |
Note: Built-in authentication for EdgeFastMCP is planned for a future release. Both FastMCP and EdgeFastMCP use Hono internally, so there's no technical barrier—EdgeFastMCP was simply written before OAuth was added to FastMCP. PRs are welcome to add an
authenticateoption that accepts webRequestinstead of Node.jshttp.IncomingMessage.In the meantime, use Hono middleware:
ts const app = server.getApp(); app.use("/api/*", async (c, next) => { if (c.req.header("authorization") !== "Bearer secret") { return c.json({ error: "Unauthorized" }, 401); } await next(); });
To deploy FastMCP to Cloudflare Workers, use the EdgeFastMCP class from the /edge subpath:
```ts import { EdgeFastMCP } from "fastmcp/
$ claude mcp add fastmcp \
-- python -m otcore.mcp_server <graph>