FastMCP
Um framework TypeScript para construir servidores MCP com manipulação de sessão de cliente.
Documentação
FastMCP
Um framework TypeScript para construir servidores MCP capazes de lidar com sessões de clientes.
[!IMPORTANT]
O FastMCP implementa as revisões legadas do MCP baseadas em handshake (
2025-11-25e anteriores). Ele não suporta a especificação atual,2026-07-28, que tornou o protocolo sem estado — sem handshakeinitializee semMcp-Session-Id. Para um framework voltado à especificação atual, use ViteMCP.
Recursos
- Definição simples de Tools, Resources e Prompts
- Conversão de OpenAPI para MCP
- Autenticação
- Passagem de cabeçalhos por contexto
- Rastreamento de Session ID e Request ID
- Sessões
- Conteúdo de imagem
- Conteúdo de áudio
- Embutido
- Registro de logs
- Tratamento de erros
- HTTP Streaming (com compatibilidade com SSE)
- Suporte a HTTPS para conexões seguras
- Rotas HTTP personalizadas para APIs REST, webhooks e interfaces administrativas
- Suporte a Edge Runtime para Cloudflare Workers, Deno Deploy e outros
- Modo sem estado para implantações serverless
- CORS (habilitado por padrão)
- Notificações de progresso
- Saída em streaming
- Eventos de servidor tipados
- Autocompletar argumentos de prompts
- Amostragem
- Elicitação
- Comportamento de ping configurável
- Endpoint de verificação de saúde
- Roots
- Transporte em memória para testes unitários sem vincular uma porta
- CLI para testes e depuração
Quando usar FastMCP em vez do SDK oficial?
O FastMCP é construído sobre o SDK oficial.
O SDK oficial fornece blocos fundamentais para construir MCPs, mas deixa muitos detalhes de implementação para você:
- Iniciar e configurar todos os componentes do servidor
- Tratamento de conexões
- Tratamento de ferramentas
- Tratamento de respostas
- Tratamento de recursos
- Adicionar prompts, recursos, modelos de recursos
- Incorporar recursos, blocos de conteúdo de imagem e áudio
O FastMCP elimina essa complexidade fornecendo um framework opinativo que:
- Lida com todo o código repetitivo automaticamente
- Fornece APIs simples e intuitivas para tarefas comuns
- Inclui práticas recomendadas e tratamento de erros integrados
- Permite que você foque na funcionalidade principal do seu MCP
Quando escolher FastMCP: Você quer construir servidores MCP rapidamente sem lidar com detalhes de implementação de baixo nível.
Quando usar o SDK oficial: Você precisa de controle máximo ou tem requisitos arquiteturais específicos. Nesse caso, recomendamos consultar a implementação do FastMCP para evitar armadilhas comuns.
Instalação
npm install fastmcp
Início rápido
[!NOTE]
Existem muitos exemplos reais de uso do FastMCP na prática. Veja o Showcase para exemplos.
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",
});
É isso! Você tem um servidor MCP funcional.
Você pode testar o servidor no terminal com:
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
Se você está procurando um repositório modelo para construir seu próprio servidor MCP, confira fastmcp-boilerplate.
Opções de servidor remoto
O FastMCP suporta múltiplas opções de transporte para comunicação remota, permitindo que um MCP hospedado em uma máquina remota seja acessado pela rede.
HTTP Streaming
O HTTP streaming fornece uma alternativa mais eficiente ao SSE em ambientes que o suportam, com potencialmente melhor desempenho para cargas maiores.
Você pode executar o servidor com suporte a HTTP streaming:
server.start({
transportType: "httpStream",
httpStream: {
port: 8080,
},
});
Isso iniciará o servidor e escutará conexões de HTTP streaming em http://localhost:8080/mcp.
Nota: Você também pode personalizar o caminho do endpoint usando a opção
httpStream.endpoint(o padrão é/mcp).
Nota: Para servir rotas de HTTP streaming e OAuth integradas sob um caminho de emissor, defina
httpStream.basePath(por exemplo,/issuer1). Isso expõe os metadados do servidor de autorização em/.well-known/oauth-authorization-server/issuer1conforme RFC 8414.
Nota: Isso também inicia um servidor SSE em
http://localhost:8080/sse.
Você pode se conectar a esses servidores usando o transporte de cliente apropriado.
Para conexões de HTTP streaming:
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);
Para conexões SSE:
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);
Suporte a HTTPS
O FastMCP suporta HTTPS para conexões seguras fornecendo opções de certificado SSL:
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
},
});
Isso iniciará o servidor com HTTPS em https://localhost:8443/mcp.
Opções de SSL:
sslCert- Caminho para o arquivo de certificado SSLsslKey- Caminho para o arquivo de chave privada SSLsslCa- (Opcional) Caminho para o certificado CA para autenticação TLS mútua
Para testes, você pode gerar certificados autoassinados:
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"
Para produção, obtenha certificados de uma CA confiável como Let's Encrypt.
Veja o exemplo de servidor https para uma demonstração completa.
Configuração de CORS
Por padrão, o FastMCP habilita CORS com um conjunto padrão de cabeçalhos permitidos. Você pode personalizar o comportamento de CORS passando uma opção cors:
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,
},
},
});
A opção cors aceita:
true(padrão) - habilita CORS com configurações padrãofalse- desabilita CORS completamente- Um objeto com estes campos:
origin- uma string, array de strings ou uma função(origin: string) => booleanallowedHeaders- uma string ou array de stringsmethods- array de métodos HTTP permitidosexposedHeaders- array de cabeçalhos a exporcredentials- booleano para permitir credenciaismaxAge- duração do cache de preflight em segundos
O tipo CorsOptions é exportado de fastmcp por conveniência.
Rotas HTTP personalizadas
O FastMCP permite adicionar rotas HTTP personalizadas junto aos endpoints MCP, permitindo que você construa serviços HTTP abrangentes que incluem APIs REST, webhooks, interfaces administrativas e muito mais — tudo no mesmo processo do servidor.
const app = server.getApp();
// Add REST API endpoints with Hono's native API
app.get("/api/users", async (c) => {
return c.json({ users: [] });
});
// Handle path parameters
app.get("/api/users/:id", async (c) => {
return c.json({
userId: c.req.param("id"),
query: c.req.query(), // Access query parameters
});
});
// Handle POST requests with body parsing
app.post("/api/users", async (c) => {
const body = await c.req.json();
return c.json({ created: body }, 201);
});
// Serve HTML content
app.get("/admin", async (c) => {
return c.html("<html><body><h1>Admin Panel</h1></body></html>");
});
// Handle webhooks
app.post("/webhook/github", async (c) => {
const payload = await c.req.json();
const event = c.req.header("x-github-event");
// Process webhook...
return c.json({ received: true });
});
Rotas personalizadas usam o aplicativo Hono subjacente retornado por server.getApp() e suportam:
- Métodos HTTP do Hono:
get,post,put,delete,patch,optionse outros - Parâmetros de caminho (
:param) e curingas (*) - Análise de query string
- Auxiliares de JSON, texto, formulário e outros corpos de
c.req - Códigos de status e cabeçalhos personalizados
- Middleware e grupos de rotas através do Hono
As rotas são correspondidas na ordem em que são registradas, permitindo que você defina rotas específicas antes de padrões abrangentes.
Rotas públicas e protegidas
Rotas Hono personalizadas são públicas, a menos que você adicione seu próprio middleware de rota ou verificações de autenticação. Para rotas personalizadas protegidas, coloque sua lógica de autenticação em um auxiliar reutilizável e chame-o tanto da opção authenticate do FastMCP quanto dos seus manipuladores de rota Hono:
import type { IncomingMessage } from "node:http";
import type { Context } from "hono";
import { FastMCP } from "fastmcp";
async function authenticateRequest(request: IncomingMessage) {
const apiKey = request.headers["x-api-key"];
return apiKey === "123" ? { userId: "123" } : undefined;
}
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
authenticate: authenticateRequest,
});
const app = server.getApp();
async function requireAuth(c: Context) {
const auth = await authenticateRequest(c.env.incoming);
if (!auth) {
return c.json({ error: "Authentication required" }, 401);
}
return auth;
}
// Public route - no authentication required
app.get("/.well-known/openid-configuration", async (c) => {
return c.json({
issuer: "https://example.com",
authorization_endpoint: "https://example.com/auth",
token_endpoint: "https://example.com/token",
});
});
// Private route - requires authentication
app.get("/api/users", async (c) => {
const auth = await requireAuth(c);
if (auth instanceof Response) {
return auth;
}
return c.json({ users: [] });
});
// Public static files
app.get("/public/*", async (c) => {
return c.text(`File: ${c.req.path}`);
});
Rotas públicas são perfeitas para:
- Endpoints de descoberta OAuth (
.well-known/*) - Verificações de saúde e páginas de status
- Ativos estáticos e documentação
- Endpoints de webhook de serviços externos
- APIs públicas que não exigem autenticação de usuário
Veja o exemplo de rotas personalizadas para uma demonstração completa.
Suporte a Edge Runtime
O FastMCP suporta edge runtimes como Cloudflare Workers, permitindo a implantação de servidores MCP na borda com latência mínima em todo o mundo.
Escolhendo entre FastMCP e EdgeFastMCP
| Caso de uso | Classe | Importação |
|---|---|---|
| Node.js, Express, Bun | FastMCP | import { FastMCP } from "fastmcp" |
| Cloudflare Workers, Deno Deploy | EdgeFastMCP | import { EdgeFastMCP } from "fastmcp/edge" |
| Recurso | FastMCP | EdgeFastMCP |
|---|---|---|
| Runtime | Node.js | Edge (isolados V8) |
| Método de início | server.start({ port }) | export default server |
| Transporte | stdio, httpStream, SSE | Somente HTTP Streamable |
| Sessões | Com ou sem estado | Somente sem estado |
| Sistema de arquivos | Sim | Não |
| OAuth/Autenticação | Opção authenticate integrada | Use middleware Hono (integrado planejado) |
| Rotas personalizadas | server.getApp() | server.getApp() |
Nota: A autenticação integrada para EdgeFastMCP está planejada para uma versão futura. Tanto o FastMCP quanto o EdgeFastMCP usam Hono internamente, então não há barreira técnica — o EdgeFastMCP foi simplesmente escrito antes da adição de OAuth ao FastMCP. PRs são bem-vindos para adicionar uma opção
authenticateque aceiteRequestweb em vez dehttp.IncomingMessagedo Node.js.Enquanto isso, use middleware Hono:
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(); });
Cloudflare Workers
Para implantar FastMCP em Cloudflare Workers, use a classe EdgeFastMCP do subcaminho /edge:
import { EdgeFastMCP } from "fastmcp/edge";
import { z } from "zod";
const server = new EdgeFastMCP({
name: "My Edge Server",
version: "1.0.0",
description: "MCP server running on Cloudflare Workers",
});
// Add tools, resources, prompts as usual
server.addTool({
name: "greet",
description: "Greet someone",
parameters: z.object({
name: z.string(),
}),
execute: async ({ name }) => {
return `Hello, ${name}! Served from the edge.`;
},
});
// Export the server as the default (required for Cloudflare Workers)
export default server;
Diferenças de Edge Runtime
Ao executar em edge runtimes:
- Sem estado por padrão: Cada requisição é tratada de forma independente
- Sem acesso ao sistema de arquivos: Use APIs fetch para dados externos
- Isolados V8: Inicializações a frio rápidas e uso eficiente de recursos
- Implantação global: Distribuição automática para locais de borda
Rotas personalizadas na borda
Você pode acessar o aplicativo Hono subjacente para adicionar rotas HTTP personalizadas:
const app = server.getApp();
// Add a landing page
app.get("/", (c) => c.html("<h1>Welcome to my MCP server</h1>"));
// Add REST API endpoints
app.get("/api/status", (c) => c.json({ status: "ok" }));
Implantação
Configure seu wrangler.toml:
name = "my-mcp-server"
main = "src/index.ts"
compatibility_date = "2024-01-01"
Implante com:
wrangler deploy
Veja o exemplo edge-cloudflare-worker para uma demonstração completa.
Modo sem estado
O FastMCP suporta operação sem estado para HTTP streaming, onde cada requisição é tratada de forma independente sem manter sessões persistentes. Isso é ideal para ambientes serverless, implantações com balanceamento de carga ou quando o estado da sessão não é necessário.
No modo sem estado:
- Nenhuma sessão é rastreada no servidor
- Cada requisição cria uma sessão temporária que é descartada após a resposta
- Menor uso de memória e melhor escalabilidade
- Perfeito para ambientes de implantação sem estado
Você pode habilitar o modo sem estado adicionando a opção stateless: true:
server.start({
transportType: "httpStream",
httpStream: {
port: 8080,
stateless: true,
},
});
Nota: O modo sem estado está disponível apenas com transporte HTTP streaming. Recursos que dependem de sessões persistentes (como estado específico de sessão) não estarão disponíveis no modo sem estado.
Você também pode habilitar o modo sem estado usando argumentos de CLI ou variáveis de ambiente:
# Via CLI argument
npx fastmcp dev src/server.ts --transport http-stream --port 8080 --stateless true
# Via environment variable
FASTMCP_STATELESS=true npx fastmcp dev src/server.ts
O endpoint de verificação de saúde /ready indicará quando o servidor estiver rodando em modo sem estado:
{
"mode": "stateless",
"ready": 1,
"status": "ready",
"total": 1
}
Conceitos principais
Tools
Tools no MCP permitem que servidores exponham funções executáveis que podem ser invocadas por clientes e usadas por LLMs para realizar ações.
O FastMCP usa a especificação Standard Schema para definir parâmetros de ferramentas. Isso permite que você use sua biblioteca de validação de schema preferida (como Zod, ArkType ou Valibot), desde que ela implemente a especificação.
Exemplo com Zod:
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);
},
});
Exemplo com ArkType:
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);
},
});
Exemplo com Valibot:
O Valibot requer a dependência de peer @valibot/to-json-schema.
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);
},
});
Exemplo com JSON Schema puro:
Se você já tem um JSON Schema — de um documento OpenAPI, um arquivo de configuração ou
outro servidor — o jsonSchemaAdapter o envolve para que possa ser usado diretamente, sem
nenhuma biblioteca de schema no meio.
Ele requer a dependência de peer ajv, que faz a validação, além de
ajv-formats se o seu schema usar palavras-chave format como email ou uri.
Ambas são importadas na primeira vez que uma ferramenta é chamada, então servidores que não usam
isso não pagam nada por isso.
npm install ajv ajv-formats
import { jsonSchemaAdapter } from "fastmcp";
server.addTool({
name: "fetch-json-schema",
description: "Fetch the content of a url (using plain JSON Schema)",
parameters: jsonSchemaAdapter({
type: "object",
properties: {
url: { type: "string", format: "uri" },
},
required: ["url"],
}),
execute: async (args) => {
const { url } = args as { url: string };
return await fetchWebpageContent(url);
},
});
Funciona também para outputSchema. Observe que o FastMCP anuncia schemas de entrada com
additionalProperties: false, independentemente do que o seu schema dizia — o mesmo tratamento
que os schemas Zod e Valibot recebem. A exceção é um objeto composto apenas de
propriedades adicionais: um dicionário (sem properties e com um
schema additionalProperties, como z.record()) mantém o schema de seus valores, e um
argumento de forma livre ({ type: "object" }, ou additionalProperties: true)
permanece aberto. Schemas de saída mantêm qualquer regra de propriedades adicionais que o seu schema
declarou.
Ao contrário das bibliotecas de schema acima, um JSON Schema puro não carrega tipos
TypeScript, então execute recebe argumentos unknown. Faça o cast ou o estreitamento você mesmo.
Ferramentas Sem Parâmetros
Ao criar ferramentas que não exigem parâmetros, você tem duas opções:
-
Omita a propriedade de parâmetros completamente:
server.addTool({ name: "sayHello", description: "Say hello", // No parameters property execute: async () => { return "Hello, world!"; }, }); -
Defina explicitamente parâmetros vazios:
import { z } from "zod"; server.addTool({ name: "sayHello", description: "Say hello", parameters: z.object({}), // Empty object execute: async () => { return "Hello, world!"; }, });
[!NOTE]
Ambas as abordagens são totalmente compatíveis com todos os clientes MCP, incluindo o Cursor. O FastMCP gera automaticamente o schema apropriado em ambos os casos.
Saída Estruturada de Ferramentas
As ferramentas podem declarar um outputSchema e retornar dados estruturados. O FastMCP expõe esse valor como structuredContent do MCP, enquanto também retorna um fallback de texto JSON para clientes que renderizam apenas conteúdo de texto.
server.addTool({
name: "get-weather",
description: "Get weather for a city",
parameters: z.object({
city: z.string(),
}),
outputSchema: z.object({
temperature: z.number(),
humidity: z.number(),
}),
execute: async ({ city }) => {
const weather = await getWeather(city);
return {
temperature: weather.temperature,
humidity: weather.humidity,
};
},
});
Você também pode retornar conteúdo de texto explícito e conteúdo estruturado juntos:
server.addTool({
name: "get-weather",
description: "Get weather for a city",
parameters: z.object({
city: z.string(),
}),
outputSchema: z.object({
temperature: z.number(),
humidity: z.number(),
}),
execute: async ({ city }) => {
const weather = await getWeather(city);
return {
content: [
{
type: "text",
text: `${city}: ${weather.temperature}F`,
},
],
structuredContent: {
temperature: weather.temperature,
humidity: weather.humidity,
},
};
},
});
Quando outputSchema é fornecido, o FastMCP valida structuredContent antes de enviar o resultado da ferramenta. Saída estruturada inválida é retornada ao cliente como um erro de ferramenta, em vez de violar silenciosamente o schema anunciado.
Autorização de Ferramentas
Você pode controlar quais ferramentas estão disponíveis para usuários autenticados adicionando uma função canAccess opcional à definição de uma ferramenta. Essa função recebe o contexto de autenticação e deve retornar true se o usuário tiver permissão para acessar a ferramenta.
server.addTool({
name: "admin-tool",
description: "An admin-only tool",
canAccess: (auth) => auth?.role === "admin",
execute: async () => "Welcome, admin!",
});
Um servidor sem uma função authenticate não tem autenticação para verificar, então cada
sessão vê todas as ferramentas. Com uma, uma sessão que ainda termina sem autenticação —
no stdio, onde o servidor continua quando authenticate lança uma exceção ou retorna
nada — não vê nenhuma das ferramentas que têm canAccess.
Retornando uma string
execute pode retornar uma string:
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return "Hello, world!";
},
});
O último é equivalente a:
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return {
content: [
{
type: "text",
text: "Hello, world!",
},
],
};
},
});
Retornando uma lista
Se você quiser retornar uma lista de mensagens, pode retornar um objeto com uma propriedade content:
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" },
],
};
},
});
Retornando uma imagem
Use o imageContent para criar um objeto de conteúdo para uma imagem:
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",
});
// or...
// return imageContent({
// path: "/path/to/image.png",
// });
// or...
// return imageContent({
// buffer: Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", "base64"),
// });
// or...
// return {
// content: [
// await imageContent(...)
// ],
// };
},
});
A função imageContent aceita as seguintes opções:
url: A URL da imagem.timeoutMs: Timeout opcional para download de URL em milissegundos (padrão: 30 segundos).path: O caminho para o arquivo de imagem.buffer: Os dados da imagem como um buffer.
Apenas um de url, path ou buffer deve ser especificado.
O exemplo acima é equivalente a:
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return {
content: [
{
type: "image",
data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
mimeType: "image/png",
},
],
};
},
});
Comportamento de Ping Configurável
O FastMCP inclui um mecanismo de ping configurável para manter a saúde da conexão. O comportamento do ping pode ser personalizado por meio das opções do servidor:
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
ping: {
// Explicitly enable or disable pings (defaults vary by transport)
enabled: true,
// Configure ping interval in milliseconds (default: 5000ms)
intervalMs: 10000,
// Set log level for ping-related messages (default: 'debug')
logLevel: "debug",
},
});
Por padrão, o comportamento do ping é otimizado para cada tipo de transporte:
- Habilitado para conexões SSE e HTTP streaming (que se beneficiam de keep-alive)
- Desabilitado para conexões
stdio(onde pings são tipicamente desnecessários)
Essa abordagem configurável ajuda a reduzir a verbosidade dos logs e otimizar o desempenho para diferentes cenários de uso.
Mantendo Chamadas de Ferramentas Longas Vivas (streamKeepalive)
Os pings viajam no stream standalone de servidor para cliente, então eles nunca mantêm a
conexão de uma chamada de ferramenta individual aberta — e no modo stateless esse stream não
existe de forma alguma. Uma ferramenta que executa por minutos sem produzir saída deixa
sua conexão de resposta silenciosa, e um timeout de conexão ociosa na frente do
servidor (o padrão do AWS ALB é 60 segundos) a fecha antes que o resultado seja escrito.
Isso se aplica a ambos os modos de sessão. Uma implantação com estado mantém seu stream standalone aquecido com pings enquanto a conexão que carrega a chamada de ferramenta fica ociosa e é fechada mesmo assim; sem estado não tem stream standalone para começar.
streamKeepalive escreve periodicamente no stream de resposta da própria chamada de ferramenta em andamento,
que é a conexão que de outra forma seria fechada. É opt-in:
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
streamKeepalive: {
// Opt in; disabled by default
enabled: true,
// Keep comfortably below the shortest idle timeout on the path
intervalMs: 20000,
},
});
Cada keepalive é um notifications/message (nível configurável via logLevel,
padrão debug) marcado com o logger fastmcp-keepalive, relacionado à chamada de ferramenta
que está sendo atendida. Os keepalives começam quando uma ferramenta começa a executar e param quando
a solicitação para de esperar por ela — em sucesso, erro, timeout ou cancelamento
do cliente — então conexões ociosas permanecem silenciosas. Como as mensagens estão relacionadas
à solicitação, isso funciona no modo com estado e stateless igualmente, e não precisa de
suporte do cliente além da notificação de log padrão.
Limites que vale a pena conhecer:
- Não tem efeito com
httpStream.enableJsonResponse, que armazena em buffer uma única resposta JSON em vez de fazer streaming, então não há stream aberto para escrever. - Cobre apenas chamadas de ferramentas. Outras solicitações de longa duração (
resources/read,prompts/get) ainda não escrevem nada até terminarem. - No
stdionão há proxy para manter vivo, então habilitá-lo lá apenas adiciona tráfego de notificações.
Cancelando Chamadas de Ferramentas Longas (context.signal)
Cada execute recebe um AbortSignal que dispara quando seu resultado não pode mais
alcançar ninguém. Encaminhe-o para o que faz o trabalho real para que o trabalho pare
com a chamada em vez de sobreviver a ela:
server.addTool({
name: "fetch_report",
parameters: z.object({ url: z.string() }),
timeoutMs: 30000,
execute: async (args, { signal }) => {
const response = await fetch(args.url, { signal });
return await response.text();
},
});
Ele aborta em qualquer um dos três eventos:
- O cliente cancelou a chamada — ele enviou
notifications/cancelled, que é o que o SDK MCP emite quando um chamador aborta sua própria solicitação. - A sessão terminou — o transporte fechou, ou a sessão foi fechada explicitamente. Nenhuma notificação de cancelamento está envolvida aqui.
timeoutMsdecorrido —signal.reasoné o mesmoUserErrorque o chamador recebe, então uma ferramenta pode distinguir um timeout de um cancelamento.
O sinal nunca é abortado após uma chamada ser concluída normalmente, então anexar limpeza a ele é seguro.
Limites que vale a pena conhecer:
- Nada é morto por você. O FastMCP para de esperar pela ferramenta, mas a
promise
executeretornada continua rodando até ser resolvida. Uma ferramenta que ignora o sinal ainda executa até o fim — apenas o faz sem ter para onde reportar. - Um cliente HTTP que desaparece no meio da solicitação não é detectado. O SDK MCP apenas
aborta o sinal da própria solicitação para um
notifications/cancelledexplícito, eStreamableHTTPServerTransportnão trata um stream de resposta abandonado como um fechamento de sessão. Um chamador que desliga sem encerrar sua sessão (DELETE) deixa a ferramenta rodando até terminar ou dar timeout. DefinatimeoutMsem qualquer coisa cara em vez de confiar na detecção de desconexão. loadnão recebe um. Recursos, templates de recursos e prompts recebem um contexto menor semsignal.
Endpoint de Verificação de Saúde
Quando você executa o FastMCP com o transporte httpStream, você pode opcionalmente expor um
endpoint HTTP simples que retorna uma resposta de texto simples útil para verificações de
liveness de balanceadores de carga ou orquestração de contêineres.
Habilite (ou personalize) o endpoint por meio da chave health nas opções do servidor:
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
health: {
// Enable / disable (default: true)
enabled: true,
// Body returned by the endpoint (default: 'ok')
message: "healthy",
// Path that should respond (default: '/health')
path: "/healthz",
// HTTP status code to return (default: 200)
status: 200,
},
});
await server.start({
transportType: "httpStream",
httpStream: { port: 8080 },
});
Agora uma solicitação para http://localhost:8080/healthz retornará:
HTTP/1.1 200 OK
content-type: text/plain
healthy
O endpoint é ignorado quando o servidor é iniciado com o transporte stdio.
Gerenciamento de Roots
O FastMCP suporta Roots — um recurso que permite que clientes forneçam um conjunto de locais raiz semelhantes a sistema de arquivos que podem ser listados e atualizados dinamicamente. O recurso Roots pode ser configurado ou desabilitado nas opções do servidor:
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
roots: {
// Set to false to explicitly disable roots support
enabled: false,
// By default, roots support is enabled (true)
},
});
Isso fornece os seguintes benefícios:
- Melhor compatibilidade com diferentes clientes que podem não suportar Roots
- Logs de erro reduzidos ao conectar a clientes que não implementam o recurso de roots
- Controle mais explícito sobre os recursos do servidor MCP
- Degradação graciosa quando a funcionalidade de roots não está disponível
Você pode ouvir mudanças de roots no seu servidor:
server.on("connect", (event) => {
const session = event.session;
// Access the current roots
console.log("Initial roots:", session.roots);
// Listen for changes to the roots
session.on("rootsChanged", (event) => {
console.log("Roots changed:", event.roots);
});
});
Quando um cliente não suporta roots ou quando a funcionalidade de roots está explicitamente desabilitada, essas operações lidarão graciosamente com a situação sem lançar erros.
Retornando um áudio
Use o audioContent para criar um objeto de conteúdo para um áudio:
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",
});
// or...
// return audioContent({
// path: "/path/to/audio.mp3",
// });
// or...
// return audioContent({
// buffer: Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", "base64"),
// });
// or...
// return {
// content: [
// await audioContent(...)
// ],
// };
},
});
A função audioContent aceita as seguintes opções:
url: A URL do áudio.timeoutMs: Timeout opcional para download de URL em milissegundos (padrão: 30 segundos).path: O caminho para o arquivo de áudio.buffer: Os dados do áudio como um buffer.
Apenas um de url, path ou buffer deve ser especificado.
O exemplo acima é equivalente a:
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return {
content: [
{
type: "audio",
data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
mimeType: "audio/mpeg",
},
],
};
},
});
Retornando tipo de combinação
Você pode combinar vários tipos dessa forma e enviá-los de volta para a IA
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args) => {
return {
content: [
{
type: "text",
text: "Hello, world!",
},
{
type: "image",
data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
mimeType: "image/png",
},
{
type: "audio",
data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
mimeType: "audio/mpeg",
},
],
};
},
// or...
// execute: async (args) => {
// const imgContent = await imageContent({
// url: "https://example.com/image.png",
// });
// const audContent = await audioContent({
// url: "https://example.com/audio.mp3",
// });
// return {
// content: [
// {
// type: "text",
// text: "Hello, world!",
// },
// imgContent,
// audContent,
// ],
// };
// },
});
Logger Personalizado
O FastMCP permite que você forneça uma implementação de logger personalizada para controlar como o servidor registra mensagens. Isso é útil para integrar com infraestrutura de logging existente ou personalizar a formatação de logs.
import { FastMCP, Logger } from "fastmcp";
class CustomLogger implements Logger {
debug(...args: unknown[]): void {
console.log("[DEBUG]", new Date().toISOString(), ...args);
}
error(...args: unknown[]): void {
console.error("[ERROR]", new Date().toISOString(), ...args);
}
info(...args: unknown[]): void {
console.info("[INFO]", new Date().toISOString(), ...args);
}
log(...args: unknown[]): void {
console.log("[LOG]", new Date().toISOString(), ...args);
}
warn(...args: unknown[]): void {
console.warn("[WARN]", new Date().toISOString(), ...args);
}
}
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
logger: new CustomLogger(),
});
Veja src/examples/custom-logger.ts para exemplos com Winston, Pino e logging baseado em arquivo.
Logging
As ferramentas podem registrar mensagens para o cliente usando o objeto log no objeto de contexto:
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args, { log }) => {
log.info("Downloading file...", {
url,
});
// ...
log.info("Downloaded file");
return "done";
},
});
O objeto log tem os seguintes métodos:
debug(message: string, data?: SerializableValue)error(message: string, data?: SerializableValue)info(message: string, data?: SerializableValue)warn(message: string, data?: SerializableValue)
Um cliente pode aumentar o nível mínimo que deseja com a solicitação logging/setLevel,
e o FastMCP então descarta qualquer coisa menos severa em vez de enviá-la — então
após logging/setLevel com error, log.debug e log.info não alcançam ninguém.
Até que um cliente peça, todos os níveis são enviados. O nível em vigor pode ser lido como
session.loggingLevel.
Erros
Os erros que devem ser mostrados ao usuário devem ser lançados como instâncias de UserError:
import { UserError } from "fastmcp";
server.addTool({
name: "download",
description: "Download a file",
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";
},
});
Progresso
As ferramentas podem relatar progresso chamando reportProgress no objeto de contexto:
server.addTool({
name: "download",
description: "Download a file",
parameters: z.object({
url: z.string(),
}),
execute: async (args, { reportProgress }) => {
await reportProgress({
progress: 0,
total: 100,
});
// ...
await reportProgress({
progress: 100,
total: 100,
});
return "done";
},
});
reportProgress aceita um message legível por humanos opcional junto com os campos numéricos, que os clientes podem exibir ao lado do indicador de progresso:
await reportProgress({
progress: 40,
total: 100,
message: "Downloading chunk 4 of 10…",
});
Notificações de progresso são emitidas apenas quando o cliente opta por fornecer um progressToken na chamada da ferramenta; caso contrário, reportProgress é um no-op. Como notifications/progress faz parte da especificação MCP (o campo message desde a revisão 2025-03-26), esta é a maneira portátil de enviar atualizações incrementais durante uma chamada de ferramenta de longa duração — veja Streaming Output abaixo para a diferença.
Saída em Streaming
O FastMCP pode transmitir resultados parciais de ferramentas enquanto elas ainda estão em execução, permitindo UIs responsivas e feedback em tempo real. Isso é particularmente útil para:
- Operações de longa duração que geram conteúdo incrementalmente
- Geração progressiva de texto, imagens ou outras mídias
- Operações onde os usuários se beneficiam de ver resultados parciais imediatos
[!IMPORTANT]
streamContenté uma extensão do FastMCP, não faz parte da especificação MCP. Ele emite uma notificaçãonotifications/tool/streamContent, que a especificação MCP não define — a partir da revisão2025-11-25não existe um mecanismo padrão para streaming de saída de ferramentas (SEP-2998 é a proposta em andamento para adicionar um).Clientes descartam notificações para as quais não possuem um handler registrado, silenciosamente e sem erro. Um cliente só vê conteúdo em streaming se registrar um handler para o método (ou definir um
fallbackNotificationHandler), e nenhum cliente conhecido renderiza isso como saída de ferramenta — o MCP Inspector, por exemplo, registra no painel de notificações via um handler de fallback, mas o resultado da ferramenta em si ainda mostra apenas o queexecuteretornou. Streaming é, portanto, útil principalmente quando você também controla o cliente — veja Consumindo conteúdo em streaming abaixo. Se você precisar de atualizações incrementais que funcionem em qualquer cliente, usereportProgresscom ummessage.
Para fazer streaming a partir de uma ferramenta, use o método streamContent:
server.addTool({
name: "generateText",
description: "Generate text incrementally",
parameters: z.object({
prompt: z.string(),
}),
annotations: {
streamingHint: true, // Advisory only; see below
readOnlyHint: true,
},
execute: async (args, { streamContent }) => {
// Send initial content immediately
await streamContent({ type: "text", text: "Starting generation...\n" });
// Simulate incremental content generation
const words = "The quick brown fox jumps over the lazy dog.".split(" ");
for (const word of words) {
await streamContent({ type: "text", text: word + " " });
await new Promise((resolve) => setTimeout(resolve, 300)); // Simulate delay
}
// Always return a final result. Returning nothing sends an empty tool
// result, so clients that ignore the streamed notifications see no output
// at all.
return "The quick brown fox jumps over the lazy dog.";
},
});
[!WARNING] Retornar
undefineddeexecuteproduz um resultado de ferramenta comcontentvazio. Se você transmitir tudo e não retornar nada, a chamada da ferramenta resolve para um resultado vazio sem nenhuma indicação de que algo foi perdido — inclusive em clientes que registram a notificação. Retorne também o resultado completo e trate o conteúdo em streaming puramente como um aprimoramento de renderização progressiva.
A anotação streamingHint é metadados consultivos. Ela é encaminhada literalmente aos clientes em tools/list, mas não habilita nem controla streamContent, e o próprio FastMCP nunca a lê. Nenhum cliente conhecido age sobre ela hoje, embora SEP-2998 proponha padronizar o mesmo nome de anotação.
Consumindo conteúdo em streaming
Um cliente vê essas notificações apenas se registrar um handler para o método (ou definir um fallbackNotificationHandler):
import { z } from "zod";
const StreamContentNotificationSchema = z.object({
method: z.literal("notifications/tool/streamContent"),
params: z.object({
content: z.array(z.any()),
toolName: z.string(),
}),
});
client.setNotificationHandler(
StreamContentNotificationSchema,
(notification) => {
const { content, toolName } = notification.params;
// Render the partial content however you like.
},
);
Observe que as notificações carregam apenas toolName, não um token de requisição ou progresso, então chamadas concorrentes para a mesma ferramenta em uma sessão não podem ser distinguidas.
Streaming funciona com todos os tipos de conteúdo (texto, imagem, áudio) e pode ser combinado com relatórios de progresso:
server.addTool({
name: "processData",
description: "Process data with streaming updates",
parameters: z.object({
datasetSize: z.number(),
}),
annotations: {
streamingHint: true,
},
execute: async (args, { streamContent, reportProgress }) => {
const total = args.datasetSize;
for (let i = 0; i < total; i++) {
// Standard progress notification: reaches every spec-compliant client
await reportProgress({
progress: i,
total,
message: `Processed ${i} of ${total} items`,
});
// Richer partial content: only reaches clients that opt in
if (i % 10 === 0) {
await streamContent({
type: "text",
text: `Processed ${i} of ${total} items...\n`,
});
}
await new Promise((resolve) => setTimeout(resolve, 50));
}
return "Processing complete!";
},
});
Elicitação
Ferramentas podem solicitar informações adicionais do usuário durante a execução via elicitação, usando o método elicit no objeto de contexto. O cliente deve anunciar o modo de capacidade elicitation correspondente — elicitation: { form: {} } para requisições de formulário (o padrão) e/ou elicitation: { url: {} } para requisições de URL.
server.addTool({
name: "delete-file",
description: "Delete a file",
parameters: z.object({
path: z.string(),
}),
execute: async (args, { elicit }) => {
const response = await elicit({
message: `Are you sure you want to delete ${args.path}?`,
requestedSchema: {
type: "object",
properties: {
confirmed: {
type: "boolean",
},
},
required: ["confirmed"],
},
});
if (response.action !== "accept" || !response.content?.confirmed) {
return "Deletion cancelled.";
}
// ...
return `Deleted ${args.path}`;
},
});
A resposta action é "accept", "decline" ou "cancel"; ao aceitar, content contém as respostas do usuário correspondentes a requestedSchema. A elicitação também está disponível fora de ferramentas via session.requestElicitation.
Anotações de Ferramenta
A partir da Especificação MCP (2025-03-26), ferramentas podem incluir anotações que fornecem contexto e controle mais ricos ao adicionar metadados sobre o comportamento de uma ferramenta:
server.addTool({
name: "fetch-content",
description: "Fetch content from a URL",
parameters: z.object({
url: z.string(),
}),
annotations: {
title: "Web Content Fetcher", // Human-readable title for UI display
readOnlyHint: true, // Tool doesn't modify its environment
openWorldHint: true, // Tool interacts with external entities
},
execute: async (args) => {
return await fetchWebpageContent(args.url);
},
});
As anotações disponíveis são:
| Anotação | Tipo | Padrão | Descrição |
|---|---|---|---|
title | string | - | Um título legível por humanos para a ferramenta, útil para exibição em UI |
readOnlyHint | boolean | false | Se verdadeiro, indica que a ferramenta não modifica seu ambiente |
destructiveHint | boolean | true | Se verdadeiro, a ferramenta pode realizar atualizações destrutivas (só é significativo quando readOnlyHint é falso) |
idempotentHint | boolean | false | Se verdadeiro, chamar a ferramenta repetidamente com os mesmos argumentos não tem efeito adicional (só é significativo quando readOnlyHint é falso) |
openWorldHint | boolean | true | Se verdadeiro, a ferramenta pode interagir com um "mundo aberto" de entidades externas |
Essas anotações ajudam clientes e LLMs a entender melhor como usar as ferramentas e o que esperar ao chamá-las.
Recursos
Recursos representam qualquer tipo de dado que um servidor MCP queira disponibilizar aos clientes. Isso pode incluir:
- Conteúdos de arquivos
- Capturas de tela e imagens
- Arquivos de log
- E mais
Cada recurso é identificado por um URI único e pode conter dados de texto ou binários.
server.addResource({
uri: "file:///logs/app.log",
name: "Application Logs",
mimeType: "text/plain",
async load() {
return {
text: await readLogFile(),
};
},
});
[!NOTE]
loadpode retornar múltiplos recursos. Isso poderia ser usado, por exemplo, para retornar uma lista de arquivos dentro de um diretório quando o diretório é lido.async load() { return [ { text: "Conteúdo do primeiro arquivo", }, { text: "Conteúdo do segundo arquivo", }, ]; }
Você também pode retornar conteúdos binários em load:
async load() {
return {
blob: 'base64-encoded-data'
};
}
load também recebe auth (o valor retornado pela sua função authenticate, se houver) e um objeto context como segundo e terceiro argumentos. context espelha os campos client, log, session e sessionId disponíveis para tool.execute (veja Rastreamento de ID de Sessão e ID de Requisição); reportProgress e streamContent não são incluídos, pois estão vinculados ao token de progresso de uma chamada de ferramenta:
server.addResource({
uri: "file:///logs/app.log",
name: "Application Logs",
mimeType: "text/plain",
async load(auth, context) {
context.log.info("loading application logs", { requestedBy: auth?.userId });
return {
text: await readLogFile(),
};
},
});
Assinando atualizações de recursos
Clientes podem assinar um recurso com o método MCP resources/subscribe para serem notificados sempre que seu conteúdo mudar. O FastMCP anuncia a capacidade subscribe automaticamente para qualquer servidor que exponha recursos, rastreia as assinaturas de cada cliente e permite que você emita uma atualização com sendResourceUpdated:
server.addResource({
uri: "file:///logs/app.log",
name: "Application Logs",
mimeType: "text/plain",
async load() {
return { text: await readLogFile() };
},
});
// Whenever the underlying data changes, notify subscribed clients:
await server.sendResourceUpdated("file:///logs/app.log");
sendResourceUpdated só notifica clientes que assinaram o URI fornecido, então é seguro chamar sempre que seus dados mudarem. O FastMCP também anuncia a capacidade listChanged para ferramentas, recursos e prompts e emite notifications/tools/list_changed / notifications/resources/list_changed / notifications/prompts/list_changed automaticamente quando você adiciona ou remove ferramentas, recursos, modelos de recursos ou prompts em tempo de execução.
Uma sessão não pode ser mostrada a um tipo de primitiva que nunca negociou. O MCP resolve capacidades durante initialize e elas permanecem pela vida da sessão, então um cliente que se conectou enquanto o servidor não tinha recursos nenhum não verá recursos adicionados depois — o FastMCP registra um aviso nomeando a capacidade e deixa essa sessão em paz. Registre um de cada tipo que você pretende adicionar antes de start(), ou faça o cliente reconectar; sessões que se conectarem depois verão tudo.
Modelos de recursos
Você também pode definir modelos de recursos:
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}`,
};
},
});
Assim como recursos simples, load também recebe auth e context como segundo e terceiro argumentos (veja Recursos).
Auto-completar argumentos de modelos de recursos
Forneça funções complete para argumentos de modelos de recursos para habilitar a conclusão automática:
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}`,
};
},
});
Recursos Incorporados
O FastMCP fornece um método conveniente embedded() que simplifica a inclusão de recursos em respostas de ferramentas. Esse recurso reduz a duplicação de código e facilita a referência a recursos de dentro das ferramentas.
Uso Básico
server.addTool({
name: "get_user_data",
description: "Retrieve user information",
parameters: z.object({
userId: z.string(),
}),
execute: async (args) => {
return {
content: [
{
type: "resource",
resource: await server.embedded(`user://profile/${args.userId}`),
},
],
};
},
});
Trabalhando com Modelos de Recursos
O método embedded() funciona perfeitamente com modelos de recursos:
// Define a resource template
server.addResourceTemplate({
uriTemplate: "docs://project/{section}",
name: "Project Documentation",
mimeType: "text/markdown",
arguments: [
{
name: "section",
required: true,
},
],
async load(args) {
const docs = {
"getting-started": "# Getting Started\n\nWelcome to our project!",
"api-reference": "# API Reference\n\nAuthentication is required.",
};
return {
text: docs[args.section] || "Documentation not found",
};
},
});
// Use embedded resources in a tool
server.addTool({
name: "get_documentation",
description: "Retrieve project documentation",
parameters: z.object({
section: z.enum(["getting-started", "api-reference"]),
}),
execute: async (args) => {
return {
content: [
{
type: "resource",
resource: await server.embedded(`docs://project/${args.section}`),
},
],
};
},
});
Trabalhando com Recursos Diretos
Também funciona com recursos definidos diretamente:
// Define a direct resource
server.addResource({
uri: "system://status",
name: "System Status",
mimeType: "text/plain",
async load() {
return {
text: "System operational",
};
},
});
// Use in a tool
server.addTool({
name: "get_system_status",
description: "Get current system status",
parameters: z.object({}),
execute: async () => {
return {
content: [
{
type: "resource",
resource: await server.embedded("system://status"),
},
],
};
},
});
Prompts
Prompts permitem que servidores definam modelos de prompts e fluxos de trabalho reutilizáveis que clientes podem facilmente apresentar a usuários e LLMs. Eles fornecem uma maneira poderosa de padronizar e compartilhar interações comuns com LLMs.
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}`;
},
});
Assim como recursos, load também recebe auth e context como segundo e terceiro argumentos (veja Recursos):
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, auth, context) => {
context.log.debug("generating git commit prompt", { user: auth?.userId });
return `Generate a concise but descriptive commit message for these changes:\n\n${args.changes}`;
},
});
Auto-completar argumentos de prompts
Prompts podem fornecer auto-completar para seus argumentos:
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-completar argumentos de prompts usando enum
Se você fornecer um array enum para um argumento, o servidor fornecerá automaticamente conclusões para o argumento.
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"],
},
],
});
OpenAPI
fromOpenAPI() (de fastmcp/openapi) converte um documento OpenAPI 3.x em um servidor FastMCP, uma ferramenta por operação — lidando com $refs externos (especificações multi-arquivo), resolução relativa de servers[0].url e colisões de achatamento de parâmetros ao longo do caminho:
import { fromOpenAPI } from "fastmcp/openapi";
const server = await fromOpenAPI({
spec: "https://petstore3.swagger.io/api/v3/openapi.json",
include: (operation) => operation.tags.includes("pet"),
});
await server.start({ transportType: "stdio" });
Veja OpenAPI para MCP para a referência completa de opções, autenticação e limitações conhecidas.
Autenticação
O FastMCP suporta autenticação OAuth 2.1 com provedores pré-configurados, permitindo que você proteja seu servidor com configuração mínima.
OAuth com Provedores Pré-configurados
Use a opção auth com um provedor para habilitar a autenticação OAuth:
import { FastMCP, getAuthSession, GoogleProvider, requireAuth } from "fastmcp";
const server = new FastMCP({
auth: new GoogleProvider({
baseUrl: "https://your-server.com",
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
name: "My Server",
version: "1.0.0",
});
server.addTool({
canAccess: requireAuth,
description: "Get user profile",
execute: async (_args, { session }) => {
const { accessToken } = getAuthSession(session);
const response = await fetch(
"https://www.googleapis.com/oauth2/v2/userinfo",
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
return JSON.stringify(await response.json());
},
name: "get-profile",
});
Provedores Disponíveis:
| Provedor | Importação | Caso de Uso |
|---|---|---|
GoogleProvider | fastmcp | Google OAuth |
GitHubProvider | fastmcp | GitHub OAuth |
AzureProvider | fastmcp | Azure/Entra ID |
OAuthProvider | fastmcp | Qualquer provedor OAuth 2.0 |
Provedor OAuth Genérico (para SAP, Auth0, Okta, etc.):
import { FastMCP, OAuthProvider } from "fastmcp";
const server = new FastMCP({
auth: new OAuthProvider({
authorizationEndpoint: process.env.OAUTH_AUTH_ENDPOINT!,
baseUrl: "https://your-server.com",
clientId: process.env.OAUTH_CLIENT_ID!,
clientSecret: process.env.OAUTH_CLIENT_SECRET!,
scopes: ["openid", "profile"],
tokenEndpoint: process.env.OAUTH_TOKEN_ENDPOINT!,
}),
name: "My Server",
version: "1.0.0",
});
Autorização de Ferramentas
Controle o acesso a ferramentas usando a propriedade canAccess com funções auxiliares integradas:
import {
requireAuth,
requireScopes,
requireRole,
requireAll,
requireAny,
getAuthSession,
} from "fastmcp";
// Require any authenticated user
server.addTool({
canAccess: requireAuth,
name: "user-tool",
// ...
});
// Require specific OAuth scopes
server.addTool({
canAccess: requireScopes("read:user", "write:data"),
name: "scoped-tool",
// ...
});
// Require specific role
server.addTool({
canAccess: requireRole("admin"),
name: "admin-tool",
// ...
});
// Combine with AND logic
server.addTool({
canAccess: requireAll(requireAuth, requireRole("admin")),
name: "admin-only",
// ...
});
// Combine with OR logic
server.addTool({
canAccess: requireAny(requireRole("admin"), requireRole("moderator")),
name: "staff-tool",
// ...
});
Autorização Personalizada:
Para lógica personalizada, passe uma função diretamente:
server.addTool({
name: "custom-auth-tool",
canAccess: (auth) =>
auth?.role === "admin" && auth?.department === "engineering",
execute: async () => "Access granted!",
});
Extraindo Dados de Sessão:
Use getAuthSession para acesso type-safe à sessão OAuth em suas funções de execução de ferramentas:
import { getAuthSession, GoogleSession } from "fastmcp";
server.addTool({
canAccess: requireAuth,
name: "get-profile",
execute: async (_args, { session }) => {
// Type-safe destructuring (throws if not authenticated)
const { accessToken } = getAuthSession(session);
// Or with provider-specific typing:
// const { accessToken } = getAuthSession<GoogleSession>(session);
const response = await fetch("https://api.example.com/user", {
headers: { Authorization: `Bearer ${accessToken}` },
});
return JSON.stringify(await response.json());
},
});
Nota: Você também pode acessar
session.accessTokendiretamente, mas deve lidar com o caso em quesessioné indefinido. O auxiliargetAuthSessionlança um erro claro se a sessão não estiver autenticada, tornando-o mais seguro quando usado comcanAccess: requireAuth.
Autenticação Personalizada
Para cenários não-OAuth (chaves de API, tokens personalizados), use a opção authenticate:
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",
});
}
return { id: 1, role: "user" };
},
});
server.addTool({
name: "sayHello",
execute: async (args, { session }) => {
return `Hello, ${session.id}!`;
},
});
Proxy OAuth
A opção auth usa o Proxy OAuth integrado do FastMCP, que atua como um intermediário seguro entre clientes MCP e provedores OAuth upstream. O proxy lida com o fluxo completo de autorização OAuth 2.1, incluindo Registro Dinâmico de Cliente (DCR), PKCE, gerenciamento de consentimento e gerenciamento de tokens com criptografia e padrões de troca de tokens habilitados por padrão.
Principais Recursos:
- 🔐 Seguro por Padrão: Criptografia automática (AES-256-GCM) e padrão de troca de tokens
- 🚀 Zero Configuração: Gera chaves automaticamente e lida com fluxos OAuth automaticamente
- 🔌 Provedores Pré-configurados: Suporte integrado para Google, GitHub e Azure
- 🎯 Conformidade RFC: Implementa DCR (RFC 7591), PKCE e OAuth 2.1
- 🔑 JWKS Opcional: Suporte para verificação de tokens RS256/ES256 (via dependência opcional
jose)
Início Rápido:
import { FastMCP, getAuthSession, GoogleProvider, requireAuth } from "fastmcp";
const server = new FastMCP({
auth: new GoogleProvider({
baseUrl: "https://your-server.com",
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
name: "My Server",
version: "1.0.0",
});
server.addTool({
canAccess: requireAuth,
name: "protected-tool",
execute: async (_args, { session }) => {
const { accessToken } = getAuthSession(session);
// Use accessToken to call upstream APIs
return "Authenticated!";
},
});
Configuração Avançada:
Para mais controle sobre o comportamento OAuth, você pode usar a opção oauth diretamente:
import { FastMCP } from "fastmcp";
import { GoogleProvider } from "fastmcp/auth";
const authProvider = new GoogleProvider({
baseUrl: "https://your-server.com",
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
scopes: ["openid", "profile", "email"],
});
const server = new FastMCP({
name: "My Server",
oauth: {
authorizationServer: authProvider
.getProxy()
.getAuthorizationServerMetadata(),
enabled: true,
proxy: authProvider.getProxy(),
},
version: "1.0.0",
});
Documentação:
- Recursos do Proxy OAuth - Lista completa de recursos e capacidades
- Guia de Implementação do Proxy OAuth - Configuração e definições
- Comparação Python vs TypeScript - Comparação de recursos
Endpoints de Descoberta OAuth
O FastMCP também suporta endpoints de descoberta OAuth para integração direta com provedores OAuth, suportando tanto a Especificação MCP 2025-03-26 quanto a Especificação MCP 2025-06-18. Isso fornece endpoints de descoberta padrão que estão em conformidade com RFC 8414 (Metadados do Servidor de Autorização OAuth 2.0) e RFC 9470 (Metadados de Recursos Protegidos OAuth 2.0):
import { FastMCP } from "fastmcp";
import buildGetJwks from "get-jwks";
import fastJwt, { type DecodedJwt } from "fast-jwt";
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
oauth: {
enabled: true,
authorizationServer: {
issuer: "https://auth.example.com",
authorizationEndpoint: "https://auth.example.com/oauth/authorize",
tokenEndpoint: "https://auth.example.com/oauth/token",
jwksUri: "https://auth.example.com/.well-known/jwks.json",
responseTypesSupported: ["code"],
},
protectedResource: {
resource: "mcp://my-server",
authorizationServers: ["https://auth.example.com"],
},
},
authenticate: async (request) => {
const authHeader = request.headers.authorization;
if (!authHeader?.startsWith("Bearer ")) {
throw new Response(null, {
status: 401,
statusText: "Missing or invalid authorization header",
});
}
const token = authHeader.slice(7); // Remove 'Bearer ' prefix
// Validate OAuth JWT access token using OpenID Connect discovery
try {
// Create JWKS client for token verification
const getJwks = buildGetJwks();
// Create JWT verifier
const verify = fastJwt.createVerifier({
async key({ header }: DecodedJwt) {
const publicKey = await getJwks.getPublicKey({
kid: header.kid,
alg: header.alg,
domain: "https://auth.example.com",
});
return publicKey;
},
algorithms: ["RS256"],
});
// Verify the JWT token
const payload = await verify(token);
return {
userId: payload.sub,
scope: payload.scope,
email: payload.email,
// Include other claims as needed
};
} catch (error) {
throw new Response(null, {
status: 401,
statusText: "Invalid OAuth token",
});
}
},
});
Se o seu servidor MCP for publicado abaixo de um caminho de emissor, configure também o caminho base do stream HTTP:
server.start({
transportType: "httpStream",
httpStream: {
basePath: "/issuer1",
endpoint: "/mcp",
port: 8080,
},
});
Com esta configuração, o FastMCP serve os metadados do servidor de autorização do caminho do emissor em /.well-known/oauth-authorization-server/issuer1, enquanto os metadados de recursos protegidos permanecem disponíveis para o endpoint MCP em /.well-known/oauth-protected-resource/issuer1/mcp.
Esta configuração expõe automaticamente os endpoints de descoberta OAuth:
/.well-known/oauth-authorization-server- Metadados do servidor de autorização (RFC 8414)/.well-known/oauth-authorization-server<basePath>- Metadados do servidor de autorização quandohttpStream.basePathestá definido (RFC 8414 Seção 3)/.well-known/oauth-protected-resource- Metadados de recursos protegidos (RFC 9728)/.well-known/oauth-protected-resource<endpoint>- Metadados de recursos protegidos em sub-caminho (MCP 2025-11-25)
Mecanismo de Descoberta (Especificação MCP 2025-11-25):
Os clientes descobrem metadados de recursos protegidos usando a seguinte ordem de busca:
- Cabeçalho WWW-Authenticate - Método principal (tratado automaticamente pelo mcp-proxy)
- Sub-caminho well-known -
/.well-known/oauth-protected-resource<endpoint>(ex.:/.well-known/oauth-protected-resource/mcp) - Raiz well-known -
/.well-known/oauth-protected-resource(fallback)
Tanto os endpoints de sub-caminho quanto os de raiz retornam metadados idênticos, garantindo compatibilidade com todas as implementações de clientes MCP.
Para validação de tokens JWT, você pode usar bibliotecas como get-jwks e fast-jwt para tokens JWT OAuth.
Passando Cabeçalhos Através do Contexto
Se você está expondo seu servidor MCP via HTTP, pode querer permitir que os clientes forneçam chaves sensíveis por meio de cabeçalhos, que podem então ser passadas para APIs com as quais suas ferramentas interagem, permitindo que cada cliente forneça suas próprias chaves de API. Isso pode ser feito capturando os cabeçalhos HTTP na seção authenticate e armazenando-os na sessão para serem referenciados pelas ferramentas posteriormente.
import { FastMCP } from "fastmcp";
import { IncomingHttpHeaders } from "http";
// Define the session data type
interface SessionData {
headers: IncomingHttpHeaders;
[key: string]: unknown; // Add index signature to satisfy Record<string, unknown>
}
// Create a server instance
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
authenticate: async (request: any): Promise<SessionData> => {
// Authentication logic
return {
headers: request.headers,
};
},
});
// Tool to display HTTP headers
server.addTool({
name: "headerTool",
description: "Reads HTTP headers from the request",
execute: async (args: any, context: any) => {
const session = context.session as SessionData;
const headers = session?.headers ?? {};
const getHeaderString = (header: string | string[] | undefined) =>
Array.isArray(header) ? header.join(", ") : (header ?? "N/A");
const userAgent = getHeaderString(headers["user-agent"]);
const authorization = getHeaderString(headers["authorization"]);
return `User-Agent: ${userAgent}\nAuthorization: ${authorization}\nAll Headers: ${JSON.stringify(headers, null, 2)}`;
},
});
// Start the server
server.start({
transportType: "httpStream",
httpStream: {
port: 8080,
},
});
Um cliente que se conectaria a isso pode parecer algo assim:
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
const transport = new StreamableHTTPClientTransport(
new URL(`http://localhost:8080/mcp`),
{
requestInit: {
headers: {
Authorization: "Test 123",
},
},
},
);
const client = new Client({
name: "example-client",
version: "1.0.0",
});
(async () => {
await client.connect(transport);
// Call a tool
const result = await client.callTool({
name: "headerTool",
arguments: {
arg1: "value",
},
});
console.log("Tool result:", result);
})().catch(console.error);
O que apareceria no console após a execução do cliente seria algo assim:
Tool result: {
content: [
{
type: 'text',
text: 'User-Agent: node\n' +
'Authorization: Test 123\n' +
'All Headers: {\n' +
' "host": "localhost:8080",\n' +
' "connection": "keep-alive",\n' +
' "authorization": "Test 123",\n' +
' "content-type": "application/json",\n' +
' "accept": "application/json, text/event-stream",\n' +
' "accept-language": "*",\n' +
' "sec-fetch-mode": "cors",\n' +
' "user-agent": "node",\n' +
' "accept-encoding": "gzip, deflate",\n' +
' "content-length": "163"\n' +
'}'
}
]
}
Rastreamento de ID de Sessão e ID de Solicitação
O FastMCP expõe automaticamente IDs de sessão e solicitação aos manipuladores de ferramentas por meio do parâmetro de contexto. Isso permite gerenciamento de estado por sessão e rastreamento de solicitações.
ID de Sessão (context.sessionId):
- Disponível apenas para transportes baseados em HTTP (HTTP Stream, SSE)
- Extraído do cabeçalho
Mcp-Session-Id - Permanece constante em múltiplas solicitações do mesmo cliente
- Útil para manter estado por sessão, contadores ou dados específicos do usuário
ID de Solicitação (context.requestId):
- Disponível para todos os transportes quando fornecido pelo cliente
- Único para cada solicitação individual
- Útil para rastreamento e depuração de solicitações
import { FastMCP } from "fastmcp";
import { z } from "zod";
const server = new FastMCP({
name: "Session Counter Server",
version: "1.0.0",
});
// Per-session counter storage
const sessionCounters = new Map<string, number>();
server.addTool({
name: "increment_counter",
description: "Increment a per-session counter",
parameters: z.object({}),
execute: async (args, context) => {
if (!context.sessionId) {
return "Session ID not available (requires HTTP transport)";
}
const counter = sessionCounters.get(context.sessionId) || 0;
const newCounter = counter + 1;
sessionCounters.set(context.sessionId, newCounter);
return `Counter for session ${context.sessionId}: ${newCounter}`;
},
});
server.addTool({
name: "show_ids",
description: "Display session and request IDs",
parameters: z.object({}),
execute: async (args, context) => {
return `Session ID: ${context.sessionId || "N/A"}
Request ID: ${context.requestId || "N/A"}`;
},
});
server.start({
transportType: "httpStream",
httpStream: {
port: 8080,
},
});
Casos de Uso:
- Gerenciamento de estado por sessão: Manter contadores, caches ou dados temporários exclusivos de cada sessão de cliente
- Autenticação e autorização de usuário: Rastrear usuários autenticados entre solicitações
- Gerenciamento de recursos específicos da sessão: Alocar e gerenciar recursos por sessão
- Implementações multi-tenant: Isolar dados e operações por sessão
- Rastreamento de solicitações: Rastrear solicitações individuais para depuração e monitoramento
Exemplo:
Veja src/examples/session-id-counter.ts para um exemplo completo demonstrando gerenciamento de contador baseado em sessão.
Notas:
- IDs de sessão são gerados automaticamente pela camada de transporte MCP
- No modo stateless, IDs de sessão não são persistidos entre solicitações
- Para transporte stdio,
sessionIdseráundefinedpois não há conceito de sessão HTTP
Fornecendo Instruções
Você pode fornecer instruções ao servidor usando a opção instructions:
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
instructions:
'Instructions describing how to use the server and its features.\n\nThis can be used by clients to improve the LLM\'s understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt.',
});
Ícones e metadados do servidor
Você pode anunciar ícones e metadados relacionados ao seu servidor. Clientes que suportam ícones podem exibi-los em sua interface:
const server = new FastMCP({
name: "My Server",
version: "1.0.0",
websiteUrl: "https://example.com",
icons: [
{
src: "https://example.com/icon.png",
mimeType: "image/png",
sizes: ["48x48"],
},
],
});
Um title opcional também é repassado ao MCP initialize (serverInfo), para clientes que preferem um nome de exibição em vez do name do servidor.
Sessões
O objeto session é uma instância de FastMCPSession e descreve sessões de clientes ativas.
server.sessions;
Alocamos uma nova instância de servidor para cada conexão de cliente para permitir comunicação 1:1 entre um cliente e o servidor.
Eventos de servidor tipados
Você pode ouvir eventos emitidos pelo servidor usando o método on:
server.on("connect", (event) => {
console.log("Client connected:", event.session);
});
server.on("disconnect", (event) => {
console.log("Client disconnected:", event.session);
});
FastMCPSession
FastMCPSession representa uma sessão de cliente e fornece métodos para interagir com o cliente.
Consulte Sessões para exemplos de como obter uma instância de FastMCPSession.
requestElicitation
requestElicitation cria uma solicitação de elicitação para coletar informações adicionais do usuário por meio do cliente e retorna a resposta. O cliente deve anunciar o modo de capacidade elicitation correspondente — elicitation: { form: {} } para solicitações de formulário (o padrão) e/ou elicitation: { url: {} } para solicitações de URL.
await session.requestElicitation({
message: "What is your name?",
requestedSchema: {
type: "object",
properties: {
name: { type: "string" },
},
required: ["name"],
},
});
Dentro de uma ferramenta, prefira o método elicit do objeto de contexto.
requestSampling
requestSampling cria uma solicitação de amostragem e retorna a resposta.
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,
});
Opções
requestSampling aceita um segundo parâmetro opcional para opções de solicitação:
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,
},
{
// Progress callback - called when progress notifications are received
onprogress: (progress) => {
console.log(`Progress: ${progress.progress}/${progress.total}`);
},
// Abort signal for cancelling the request
signal: abortController.signal,
// Request timeout in milliseconds (default: DEFAULT_REQUEST_TIMEOUT_MSEC)
timeout: 30000,
// Whether progress notifications reset the timeout (default: false)
resetTimeoutOnProgress: true,
// Maximum total timeout regardless of progress (no default)
maxTotalTimeout: 60000,
},
);
Opções:
onprogress?: (progress: Progress) => void- Callback para notificações de progresso do lado remotosignal?: AbortSignal- Sinal de abortamento para cancelar a solicitaçãotimeout?: number- Tempo limite de solicitação em milissegundosresetTimeoutOnProgress?: boolean- Se notificações de progresso redefinem o tempo limitemaxTotalTimeout?: number- Tempo limite total máximo independentemente das notificações de progresso
clientCapabilities
A propriedade clientCapabilities contém as capacidades do cliente.
session.clientCapabilities;
loggingLevel
A propriedade loggingLevel descreve o nível de registro conforme definido pelo cliente. Ela lê info até que o cliente envie logging/setLevel; mensagens menos severas que o nível definido pelo cliente são descartadas em vez de enviadas.
session.loggingLevel;
roots
A propriedade roots contém as raízes conforme definidas pelo cliente.
session.roots;
server
A propriedade server contém uma instância do servidor MCP associada à sessão.
session.server;
Eventos de sessão tipados
Você pode ouvir eventos emitidos pela sessão usando o método on:
session.on("rootsChanged", (event) => {
console.log("Roots changed:", event.roots);
});
session.on("error", (event) => {
console.error("Error:", event.error);
});
Executando Seu Servidor
Testes unitários com transporte em memória
server.connect(transport) anexa o servidor a um transporte que você mesmo constrói, em vez de deixar start() criar um. Combinado com o InMemoryTransport do SDK, isso permite que você execute um servidor em processo — sem porta para vincular, sem subprocesso para gerar — o que geralmente é o que você deseja para testar um servidor stdio:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
async function createTestClient(server: FastMCP) {
const [clientTransport, serverTransport] =
InMemoryTransport.createLinkedPair();
const client = new Client({ name: "test-client", version: "0.0.0" });
const [session] = await Promise.all([
server.connect(serverTransport),
client.connect(clientTransport),
]);
return { client, session };
}
test("adds two numbers", async () => {
const { client } = await createTestClient(server);
expect(
await client.callTool({ arguments: { a: 2, b: 3 }, name: "add" }),
).toEqual({
content: [{ text: "5", type: "text" }],
});
await client.close();
});
A sessão é construída a partir das ferramentas, recursos e prompts registrados na instância, exatamente como start() a constrói, então seus testes exercitam a mesma fiação que o servidor real usa — incluindo filtragem canAccess e os eventos connect/disconnect.
Passe a autenticação da sessão como segundo argumento, equivalente ao que sua função authenticate retornaria:
await server.connect(serverTransport, { id: 7, role: "admin" });
connect retorna o FastMCPSession, então você pode fazer asserções em session.clientCapabilities, session.roots e o restante. O ciclo de vida do transporte pertence a você: stop() não fecha transportes passados para connect, então feche o cliente (e a sessão, se você precisar que disconnect seja disparado) quando o teste terminar.
Teste com mcp-cli
A maneira mais rápida de testar e depurar seu servidor é com fastmcp dev:
npx fastmcp dev server.js
npx fastmcp dev server.ts
Isso executará seu servidor com mcp-cli para testar e depurar seu servidor MCP no terminal.
Para chamar uma ferramenta de forma não interativa (por exemplo, em scripts ou testes automatizados), passe --tool e JSON --args opcional:
npx fastmcp dev server.ts --tool add --args '{"a":1,"b":2}'
Isso imprime o resultado da ferramenta como JSON e sai, em vez de abrir o inspetor interativo. --watch não tem efeito neste modo, pois o servidor é iniciado para uma única chamada.
Inspecione com MCP Inspector
Outra maneira é usar o MCP Inspector oficial para inspecionar seu servidor com uma interface web:
npx fastmcp inspect server.ts
FAQ
Como usar com Claude Desktop?
Siga o guia https://modelcontextprotocol.io/quickstart/user e adicione a seguinte configuração:
{
"mcpServers": {
"my-mcp-server": {
"command": "npx",
"args": ["tsx", "/PATH/TO/YOUR_PROJECT/src/index.ts"],
"env": {
"YOUR_ENV_VAR": "value"
}
}
}
}
Como executar FastMCP atrás de um proxy?
Consulte esta issue para um exemplo de uso do FastMCP com express e http-proxy-middleware.
Vitrine
[!NOTE]
Se você desenvolveu um servidor usando FastMCP, envie um PR para exibi-lo aqui!
[!NOTE]
Se você está procurando um repositório boilerplate para construir seu próprio servidor MCP, confira fastmcp-boilerplate.
- apinetwork/piapi-mcp-server - gera mídia usando Midjourney/Flux/Kling/LumaLabs/Udio/Chrip/Trellis
- domdomegg/computer-use-mcp - controla seu computador
- LiterallyBlah/Dradis-MCP – gerencia projetos e vulnerabilidades no Dradis
- Meeting-Baas/meeting-mcp - cria bots de reunião, pesquisa transcrições e gerencia dados de gravação
- drumnation/unsplash-smart-mcp-server – permite que agentes de IA pesquisem, recomendem e entreguem fotos profissionais de stock do Unsplash de forma integrada
- ssmanji89/halopsa-workflows-mcp - Integração de fluxos de trabalho HaloPSA com assistentes de IA
- aiamblichus/mcp-chat-adapter – fornece uma interface limpa para LLMs usarem conclusão de chat
- eyaltoledano/claude-task-master – gerenciador avançado de projetos/tarefas de IA alimentado por FastMCP
- cswkim/discogs-mcp-server - conecta-se à API Discogs para interagir com sua coleção de música
- Panzer-Jack/feuse-mcp - Ferramentas MCP Úteis de Frontend - Utilitários essenciais para desenvolvedores web automatizarem integração de API e geração de código
- sunra-ai/sunra-clients - Sunra.ai é uma plataforma de mídia generativa construída para desenvolvedores, fornecendo capacidades de inferência de modelos de IA de alto desempenho.
- foxtrottwist/shortcuts-mcp - conecta Claude a Atalhos do macOS para automação de sistema, integração de aplicativos e fluxos de trabalho interativos
Agradecimentos
- FastMCP é inspirado na implementação em Python por Jonathan Lowin.
- Partes do código foram adotadas do LiteMCP.
- Partes do código foram adotadas de Model Context protocolでSSEをやってみる.
Este projeto é testado com BrowserStack.