fastmcp
by: punkpeye
A TypeScript framework for building MCP servers.
📌Overview
Purpose: FastMCP is a TypeScript framework for building MCP servers that effectively manage client sessions.
Overview: FastMCP simplifies the development of MCP servers by providing an intuitive interface for defining tools, managing sessions, and handling client interactions via various transport methods, including SSE. It is designed to facilitate seamless communication between clients and servers.
Key Features:
-
Simple Tool and Resource Definition: Easily create and manage executable functions and resources that clients can access.
-
Authentication: Implement custom client authentication mechanisms to safeguard server interactions.
-
Sessions Management: Supports dedicated sessions for each client, enabling personalized communication and state management.
-
SSE and Real-time Communication: Built-in support for Server-Sent Events (SSE) to facilitate real-time updates between server and clients.
-
Progress Notifications and Error Handling: Features like progress reporting and user-friendly error handling enhance user experience and debugging.
FastMCP
A TypeScript framework for building MCP servers capable of handling client sessions.
For a Python implementation, see FastMCP.
Features
- Simple Tool, Resource, Prompt definition
- Authentication
- Sessions
- Image content
- Audio content
- Logging
- Error handling
- SSE
- CORS (enabled by default)
- Progress notifications
- Typed server events
- Prompt argument auto-completion
- Sampling
- Automated SSE pings
- Roots
- CLI for testing and debugging
Installation
npm install fastmcp
Quickstart
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",
});
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
SSE
Server-Sent Events (SSE) provide a mechanism for servers to send real-time updates to clients over HTTPS. In MCP, SSE is used to enable remote MCP communication over the network.
Run the server with SSE support:
server.start({
transportType: "sse",
sse: {
endpoint: "/sse",
port: 8080,
},
});
This starts the server listening for SSE connections at http://localhost:8080/sse
.
You can connect to the server using SSEClientTransport
:
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);
Core Concepts
Tools
Tools in MCP allow servers to expose executable functions that clients and LLMs can use.
FastMCP supports the Standard Schema specification for defining tool parameters. Any schema validation library implementing this spec (like Zod, ArkType, or Valibot) can be used.
Zod Example:
import { z } from "zod";
server.addTool({
name: "fetch-zod",
description: "Fetch the content of a url (using Zod)",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return await fetchWebpageContent(args.url);
},
});
ArkType Example:
import { type } from "arktype";
server.addTool({
name: "fetch-arktype",
description: "Fetch the content of a url (using ArkType)",
parameters: type({
url: "string",
}),
execute: async (args) => {
return await fetchWebpageContent(args.url);
},
});
Valibot Example:
import * as v from "valibot";
server.addTool({
name: "fetch-valibot",
description: "Fetch the content of a url (using Valibot)",
parameters: v.object({
url: v.string(),
}),
execute: async (args) => {
return await fetchWebpageContent(args.url);
},
});
Returning a string
execute
methods can return simple strings:
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return "Hello, world!";
},
});
Equivalent to:
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return {
content: [
{
type: "text",
text: "Hello, world!",
},
],
};
},
});
Returning a list
Return multiple messages as a list in an object with a content
property:
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return {
content: [
{ type: "text", text: "First message" },
{ type: "text", text: "Second message" },
],
};
},
});
Returning an image
Use the imageContent
helper to return images:
import { imageContent } from "fastmcp";
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return imageContent({
url: "https://example.com/image.png",
});
},
});
Options for imageContent
include: url
, path
, or buffer
. Only one should be specified.
Equivalent to:
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return {
content: [
{
type: "image",
data: "base64-encoded-image-data",
mimeType: "image/png",
},
],
};
},
});
Returning an audio
Use audioContent
similarly for audio files:
import { audioContent } from "fastmcp";
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return audioContent({
url: "https://example.com/audio.mp3",
});
},
});
Options include url
, path
, or buffer
.
Equivalent to:
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return {
content: [
{
type: "audio",
data: "base64-encoded-audio-data",
mimeType: "audio/mpeg",
},
],
};
},
});
Logging
Tools can log messages to the client using a log
object in the execution context:
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args, { log }) => {
log.info("Downloading file...", { url: args.url });
// ...
log.info("Downloaded file");
return "done";
},
});
Available log methods:
debug(message: string, data?)
error(message: string, data?)
info(message: string, data?)
warn(message: string, data?)
Errors
Throw errors meant for user display as UserError
instances:
import { UserError } from "fastmcp";
server.addTool({
name: "download",
parameters: z.object({ url: z.string() }),
execute: async (args) => {
if (args.url.startsWith("https://example.com")) {
throw new UserError("This URL is not allowed");
}
return "done";
},
});
Progress
Report progress via the reportProgress
function in context:
server.addTool({
name: "download",
parameters: z.object({ url: z.string() }),
execute: async (args, { reportProgress }) => {
reportProgress({ progress: 0, total: 100 });
// ...
reportProgress({ progress: 100, total: 100 });
return "done";
},
});
Resources
Resources represent data the MCP server exposes to clients, such as files, images, or logs. Each has a unique URI and contains text or binary data.
Example:
server.addResource({
uri: "file:///logs/app.log",
name: "Application Logs",
mimeType: "text/plain",
async load() {
return {
text: await readLogFile(),
};
},
});
load()
can return multiple resources (e.g., multiple files):
async load() {
return [
{ text: "First file content" },
{ text: "Second file content" },
];
}
Binary data can be returned as a base64-encoded blob:
async load() {
return {
blob: 'base64-encoded-data'
};
}
Resource templates
Define resource templates with URI patterns and arguments:
server.addResourceTemplate({
uriTemplate: "file:///logs/{name}.log",
name: "Application Logs",
mimeType: "text/plain",
arguments: [
{
name: "name",
description: "Name of the log",
required: true,
},
],
async load({ name }) {
return {
text: `Example log content for ${name}`,
};
},
});
Argument auto-completion
Provide complete
functions for arguments to enable auto-completion:
server.addResourceTemplate({
uriTemplate: "file:///logs/{name}.log",
name: "Application Logs",
mimeType: "text/plain",
arguments: [
{
name: "name",
description: "Name of the log",
required: true,
complete: async (value) => {
if (value === "Example") {
return { values: ["Example Log"] };
}
return { values: [] };
},
},
],
async load({ name }) {
return {
text: `Example log content for ${name}`,
};
},
});
Prompts
Prompts define reusable prompt templates and workflows for clients and LLMs.
Example:
server.addPrompt({
name: "git-commit",
description: "Generate a Git commit message",
arguments: [
{
name: "changes",
description: "Git diff or description of changes",
required: true,
},
],
load: async (args) => {
return `Generate a concise but descriptive commit message for these changes:\n\n${args.changes}`;
},
});
Prompt argument auto-completion
Prompts support argument auto-completion with complete
functions:
server.addPrompt({
name: "countryPoem",
description: "Writes a poem about a country",
load: async ({ name }) => {
return `Hello, ${name}!`;
},
arguments: [
{
name: "name",
description: "Name of the country",
required: true,
complete: async (value) => {
if (value === "Germ") {
return { values: ["Germany"] };
}
return { values: [] };
},
},
],
});
Auto-completion using enum
Provide an enum
array to enable automatic completions:
server.addPrompt({
name: "countryPoem",
description: "Writes a poem about a country",
load: async ({ name }) => {
return `Hello, ${name}!`;
},
arguments: [
{
name: "name",
description: "Name of the country",
required: true,
enum: ["Germany", "France", "Italy"],
},
],
});
Authentication
FastMCP supports client authentication with a custom function:
import { AuthError } from "fastmcp";
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
authenticate: ({request}) => {
const apiKey = request.headers["x-api-key"];
if (apiKey !== '123') {
throw new Response(null, {
status: 401,
statusText: "Unauthorized",
});
}
// This returned object is accessible in `context.session`.
return {
id: 1,
};
},
});
Access session data in tools:
server.addTool({
name: "sayHello",
execute: async (args, { session }) => {
return `Hello, ${session.id}!`;
},
});
Sessions
The session
object represents active client sessions (FastMCPSession
) with one server instance allocated per client connection.
server.sessions;
Typed server events
Listen to server events with on
:
server.on("connect", (event) => {
console.log("Client connected:", event.session);
});
server.on("disconnect", (event) => {
console.log("Client disconnected:", event.session);
});
FastMCPSession
FastMCPSession
represents a client session and provides methods to interact with the client.
requestSampling
Create a sampling request and receive the response:
await session.requestSampling({
messages: [
{
role: "user",
content: {
type: "text",
text: "What files are in the current directory?",
},
},
],
systemPrompt: "You are a helpful file system assistant.",
includeContext: "thisServer",
maxTokens: 100,
});
Properties
clientCapabilities
: The client capabilities.loggingLevel
: Logging level set by client.roots
: Roots set by the client.server
: MCP server instance associated with the session.
Typed session events
Listen to session events:
session.on("rootsChanged", (event) => {
console.log("Roots changed:", event.roots);
});
session.on("error", (event) => {
console.error("Error:", event.error);
});
Running Your Server
Test with mcp-cli
Test and debug your server easily with:
npx fastmcp dev server.js
npx fastmcp dev server.ts
This uses mcp-cli
in the terminal.
Inspect with MCP Inspector
Inspect your server via the web UI:
npx fastmcp inspect server.ts
FAQ
How to use with Claude Desktop?
Add the following configuration:
{
"mcpServers": {
"my-mcp-server": {
"command": "npx",
"args": [
"tsx",
"/PATH/TO/YOUR_PROJECT/src/index.ts"
],
"env": {
"YOUR_ENV_VAR": "value"
}
}
}
}
Showcase
If you've developed a server using FastMCP, please submit a PR to showcase it!
- apinetwork/piapi-mcp-server - generate media using Midjourney/Flux/Kling/LumaLabs/Udio/Chrip/Trellis
- domdomegg/computer-use-mcp - controls your computer
- LiterallyBlah/Dradis-MCP – manages projects and vulnerabilities in Dradis
- Meeting-Baas/meeting-mcp - create meeting bots, search transcripts, and manage recordings
- drumnation/unsplash-smart-mcp-server – enables AI agents to search and deliver stock photos from Unsplash
- ssmanji89/halopsa-workflows-mcp - HaloPSA Workflows integration with AI assistants
- aiamblichus/mcp-chat-adapter – provides a clean interface for LLM chat completions
- cswkim/discogs-mcp-server - connects to the Discogs API for music collection management
Acknowledgements
- Inspired by the Python implementation by Jonathan Lowin
- Codebase parts adopted from LiteMCP
- Codebase parts adopted from Model Context protocol SSE exploration article