快速MCP 用于构建能够处理客户端会话的 MCP 服务器的 TypeScript 框架。-开源码库社区-开源分享-6协议-村兔网

快速MCP 用于构建能够处理客户端会话的 MCP 服务器的 TypeScript 框架。

特征

  • 简单的工具、资源、提示定义
  • 认证
  • 通过上下文传递标头
  • 会话 ID 和请求 ID 跟踪
  • 会话
  • 图片内容
  • 音频内容
  • 嵌入式
  • 伐木
  • 错误处理
  • HTTP 流(与 SSE 兼容)
  • 用于无服务器部署的无状态模式
  • CORS(默认启用)
  • 进度通知
  • 流式输出
  • 类型化的服务器事件
  • 提示参数自动完成
  • 采样
  • 可配置的 ping 行为
  • 运行状况检查终结点
  • 用于测试和调试的 CLI

何时在官方 SDK 上使用 FastMCP?

 

FastMCP 构建在官方 SDK 之上。

官方 SDK 提供了用于构建 MCP 的基础块,但将许多实现细节留给您:

  • 启动和配置所有服务器组件
  • 连接处理
  • 工具的处理
  • 响应处理
  • 资源处理
  • 添加提示、资源、资源模板
  • 嵌入资源、图像和音频内容块

FastMCP 通过提供一个固执己见的框架来消除这种复杂性,该框架:

  • 自动处理所有样板
  • 为常见任务提供简单、直观的 API
  • 包括内置的最佳实践和错误处理
  • 让您专注于 MCP 的核心功能

何时选择 FastMCP:您希望快速构建 MCP 服务器,而无需处理低级实现细节。

何时使用官方 SDK:您需要最大程度的控制或具有特定的架构要求。在这种情况下,我们鼓励引用 FastMCP 的实现以避免常见的陷阱。

安装

 

npm install fastmcp
 

快速入门

 

注意

有许多在野外使用 FastMCP 的真实示例。有关示例,请参阅展示。

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",
});
 

就是这样!您有一个正在工作的 MCP 服务器。

您可以使用以下命令在终端中测试服务器:

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
 

如果您正在寻找一个样板存储库来构建自己的 MCP 服务器,请查看 fastmcp-boilerplate。

远程服务器选项

 

FastMCP 支持多种远程通信传输选项,允许通过网络访问托管在远程机器上的 MCP。

HTTP 流式处理

 

在支持 HTTP 流的环境中,HTTP 流式处理为 SSE 提供了更有效的替代方案,对于更大的有效负载,可能会具有更好的性能。

您可以使用 HTTP 流支持运行服务器:

server.start({
  transportType: "httpStream",
  httpStream: {
    port: 8080,
  },
});
 

这将启动服务器并侦听 上的 HTTP 流连接。http://localhost:8080/mcp

注意:您还可以使用选项自定义端点路径(默认值为 )。httpStream.endpoint/mcp

注意:这也会在 上启动 SSE 服务器。http://localhost:8080/sse

您可以使用适当的客户端传输连接到这些服务器。

对于 HTTP 流式连接:

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);
 

对于 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);
 

无状态模式

 

FastMCP 支持 HTTP 流的无状态作,其中每个请求都是独立处理的,而无需维护持久会话。这非常适合无服务器环境、负载均衡部署或不需要会话状态的情况。

在无状态模式下:

  • 服务器上不跟踪任何会话
  • 每个请求都会创建一个临时会话,该会话在响应后被丢弃
  • 减少内存使用并提高可扩展性
  • 非常适合无状态部署环境

您可以通过添加以下选项来启用无状态模式:stateless: true

server.start({
  transportType: "httpStream",
  httpStream: {
    port: 8080,
    stateless: true,
  },
});
 

注意:无状态模式仅适用于 HTTP 流传输。依赖于持久性会话的功能(如特定于会话的状态)在无状态模式下不可用。

您还可以使用 CLI 参数或环境变量启用无状态模式:

# 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
 

运行状况检查端点将指示服务器何时以无状态模式运行:/ready

{
  "mode": "stateless",
  "ready": 1,
  "status": "ready",
  "total": 1
}
 

核心概念

 

工具

 

MCP 中的工具允许服务器公开可执行函数,这些函数可以由客户端调用并由 LLM 用于执行作。

FastMCP 使用标准模式规范来定义工具参数。这允许您使用您首选的模式验证库(如 Zod、ArkType 或 Valibot),只要它实现了规范。

佐德示例:

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 示例:

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 示例:

Valibot 需要对等依赖项 @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);
  },
});
 

没有参数的工具

 

创建不需要参数的工具时,有两个选项:

  1. 完全省略 parameters 属性:

    server.addTool({
      name: "sayHello",
      description: "Say hello",
      // No parameters property
      execute: async () => {
        return "Hello, world!";
      },
    });
     
  2. 显式定义空参数:

    import { z } from "zod";
    
    server.addTool({
      name: "sayHello",
      description: "Say hello",
      parameters: z.object({}), // Empty object
      execute: async () => {
        return "Hello, world!";
      },
    });
     

注意

这两种方法都与所有 MCP 客户端完全兼容,包括 Cursor。在这两种情况下,FastMCP 都会自动生成正确的模式。

工具授权

 

您可以通过向工具的定义添加可选函数来控制哪些工具可供经过身份验证的用户使用。此函数接收身份验证上下文,如果允许用户访问该工具,则应返回。canAccesstrue

server.addTool({
  name: "admin-tool",
  description: "An admin-only tool",
  canAccess: (auth) => auth?.role === "admin",
  execute: async () => "Welcome, admin!",
});
 

返回字符串

 

execute可以返回一个字符串:

server.addTool({
  name: "download",
  description: "Download a file",
  parameters: z.object({
    url: z.string(),
  }),
  execute: async (args) => {
    return "Hello, world!";
  },
});
 

后者相当于:

server.addTool({
  name: "download",
  description: "Download a file",
  parameters: z.object({
    url: z.string(),
  }),
  execute: async (args) => {
    return {
      content: [
        {
          type: "text",
          text: "Hello, world!",
        },
      ],
    };
  },
});
 

返回列表

 

如果要返回消息列表,可以返回一个具有属性的对象: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" },
      ],
    };
  },
});
 

返回图像

 

使用 为 图像创建内容对象:imageContent

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(...)
    //   ],
    // };
  },
});
 

该函数采用以下选项:imageContent

  • url:图像的 URL。
  • path:图像文件的路径。
  • buffer:作为缓冲区的图像数据。

只能指定 、 或 中的一个。urlpathbuffer

上面的例子相当于:

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",
        },
      ],
    };
  },
});
 

可配置的 Ping 行为

 

FastMCP 包括一个可配置的 ping 机制来维护连接健康。可以通过服务器选项自定义 ping 行为:

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",
  },
});
 

默认情况下,ping 行为针对每种传输类型进行了优化:

  • 为 SSE 和 HTTP 流式连接启用(受益于保持活动状态)
  • 禁用连接(通常不需要 ping)stdio

这种可配置的方法有助于减少日志冗长性并优化不同使用场景的性能。

运行状况检查端点

 

当您使用传输运行 FastMCP 时,您可以选择公开 返回对负载均衡器有用的纯文本响应的简单 HTTP 端点 或容器编排活跃度检查。httpStream

通过服务器选项中的键启用(或自定义)端点:health

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 },
});
 

现在请求将返回:http://localhost:8080/healthz

HTTP/1.1 200 OK
content-type: text/plain

healthy
 

当服务器使用传输启动时,将忽略终结点。stdio

根管理

 

FastMCP 支持根 – 允许客户端提供一组类似文件系统的根位置的功能,这些根位置可以列出和动态更新。可以在服务器选项中配置或禁用根功能:

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)
  },
});
 

这提供了以下好处:

  • 与可能不支持 Roots 的不同客户端更好地兼容
  • 减少连接到未实现根功能的客户端时的错误日志
  • 更明确地控制 MCP 服务器功能
  • 根功能不可用时的正常降级

您可以监听服务器中的根更改:

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);
  });
});
 

当客户端不支持根或显式禁用根功能时,这些作将正常处理这种情况而不会引发错误。

返回音频

 

使用 为 音频创建内容对象:audioContent

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(...)
    //   ],
    // };
  },
});
 

该函数采用以下选项:audioContent

  • url:音频的 URL。
  • path:音频文件的路径。
  • buffer:将音频数据作为缓冲区。

只能指定 、 或 中的一个。urlpathbuffer

上面的例子相当于:

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",
        },
      ],
    };
  },
});
 

返回组合类型

 

您可以通过这种方式组合各种类型并将它们发送回 AI

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,
  //     ],
  //   };
  // },
});
 

定制记录仪

 

FastMCP 允许您提供自定义记录器实现来控制服务器记录消息的方式。这对于与现有日志基础设施集成或自定义日志格式非常有用。

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(),
});
 

有关 Winston、Pino 和基于文件的日志记录的示例。src/examples/custom-logger.ts

伐木

 

工具可以使用上下文对象中的对象将消息记录到客户端:log

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";
  },
});
 

该对象具有以下方法:log

  • debug(message: string, data?: SerializableValue)
  • error(message: string, data?: SerializableValue)
  • info(message: string, data?: SerializableValue)
  • warn(message: string, data?: SerializableValue)

错误

 

应该向用户显示的错误应该作为实例抛出: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";
  },
});
 

进展

 

工具可以通过调用上下文对象来报告进度:reportProgress

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";
  },
});
 

流式输出

 

FastMCP 支持在工具仍在执行时从工具流式传输部分结果,从而实现响应式 UI 和实时反馈。这对于以下情况特别有用:

  • 以增量方式生成内容的长时间运行的作
  • 逐步生成文本、图像或其他媒体
  • 用户从立即看到部分结果中受益的作

要为工具启用流式处理,请添加注释并使用以下方法:streamingHintstreamContent

server.addTool({
  name: "generateText",
  description: "Generate text incrementally",
  parameters: z.object({
    prompt: z.string(),
  }),
  annotations: {
    streamingHint: true, // Signals this tool uses streaming
    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
    }

    // When using streamContent, you can:
    // 1. Return void (if all content was streamed)
    // 2. Return a final result (which will be appended to streamed content)

    // Option 1: All content was streamed, so return void
    return;

    // Option 2: Return final content that will be appended
    // return "Generation complete!";
  },
});
 

Streaming works with all content types (text, image, audio) and can be combined with progress reporting:

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++) {
      // Report numeric progress
      await reportProgress({ progress: i, total });

      // Stream intermediate results
      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!";
  },
});
 

Tool Annotations

 

As of the MCP Specification (2025-03-26), tools can include annotations that provide richer context and control by adding metadata about a tool’s behavior:

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);
  },
});
 

The available annotations are:

Annotation Type Default Description
title string A human-readable title for the tool, useful for UI display
readOnlyHint boolean false If true, indicates the tool does not modify its environment
destructiveHint boolean true If true, the tool may perform destructive updates (only meaningful when is false)readOnlyHint
idempotentHint boolean false If true, calling the tool repeatedly with the same arguments has no additional effect (only meaningful when is false)readOnlyHint
openWorldHint boolean true If true, the tool may interact with an “open world” of external entities

These annotations help clients and LLMs better understand how to use the tools and what to expect when calling them.

Resources

 

Resources represent any kind of data that an MCP server wants to make available to clients. This can include:

  • File contents
  • Screenshots and images
  • Log files
  • And more

Each resource is identified by a unique URI and can contain either text or binary data.

server.addResource({
  uri: "file:///logs/app.log",
  name: "Application Logs",
  mimeType: "text/plain",
  async load() {
    return {
      text: await readLogFile(),
    };
  },
});
 

注意

load可以返回多个资源。例如,这可用于在读取目录时返回目录内的文件列表。

async load() {
  return [
    {
      text: "First file content",
    },
    {
      text: "Second file content",
    },
  ];
}
 

您还可以在 中返回二进制内容:load

async load() {
  return {
    blob: 'base64-encoded-data'
  };
}
 

资源模板

 

您还可以定义资源模板:

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}`,
    };
  },
});
 

资源模板参数自动完成

 

为资源模板参数提供函数,实现自动补全:complete

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}`,
    };
  },
});
 

嵌入式资源

 

FastMCP 提供了一种方便的方法,可以简化在工具响应中包含资源的过程。此功能减少了代码重复,并使从工具中引用资源变得更加容易。embedded()

基本用法

 

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}`),
        },
      ],
    };
  },
});
 

使用资源模板

 

该方法与资源模板无缝协作:embedded()

// 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}`),
        },
      ],
    };
  },
});
 

Working with Direct Resources

 

It also works with directly defined resources:

// 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 enable servers to define reusable prompt templates and workflows that clients can easily surface to users and LLMs. They provide a powerful way to standardize and share common LLM interactions.

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 can provide auto-completion for their arguments:

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: [],
        };
      },
    },
  ],
});
 

Prompt argument auto-completion using enum

 

If you provide an array for an argument, the server will automatically provide completions for the argument.enum

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 session-based authentication, allowing you to secure your server and control access to its features.

Note

For more granular control over which tools are available to authenticated users, see the Tool Authorization section.

To enable authentication, provide an function in the server options. This function receives the incoming HTTP request and should return a promise that resolves with the authentication context.authenticate

If authentication fails, the function should throw a object, which will be sent to the client.Response

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",
      });
    }

    // Whatever you return here will be accessible in the `context.session` object.
    return {
      id: 1,
    };
  },
});
 

Now you can access the authenticated session data in your tools:

server.addTool({
  name: "sayHello",
  execute: async (args, { session }) => {
    return `Hello, ${session.id}!`;
  },
});
 

Tool Authorization

 

You can control which tools are available to authenticated users by adding an optional function to a tool’s definition. This function receives the authentication context and should return if the user is allowed to access the tool.canAccesstrue

If is not provided, the tool is accessible to all authenticated users by default. If no authentication is configured on the server, all tools are available to all clients.canAccess

Example:

const server = new FastMCP<{ role: "admin" | "user" }>({
  authenticate: async (request) => {
    const role = request.headers["x-role"] as string;
    return { role: role === "admin" ? "admin" : "user" };
  },
  name: "My Server",
  version: "1.0.0",
});

server.addTool({
  name: "admin-dashboard",
  description: "An admin-only tool",
  // Only users with the 'admin' role can see and execute this tool
  canAccess: (auth) => auth?.role === "admin",
  execute: async () => {
    return "Welcome to the admin dashboard!";
  },
});

server.addTool({
  name: "public-info",
  description: "A tool available to everyone",
  execute: async () => {
    return "This is public information.";
  },
});
 

In this example, only clients authenticating with the role will be able to list or call the tool. The tool will be available to all authenticated users.adminadmin-dashboardpublic-info

OAuth Support

 

FastMCP includes built-in support for OAuth discovery endpoints, supporting both MCP Specification 2025-03-26 and MCP Specification 2025-06-18 for OAuth integration. This makes it easy to integrate with OAuth authorization flows by providing standard discovery endpoints that comply with RFC 8414 (OAuth 2.0 Authorization Server Metadata) and RFC 9470 (OAuth 2.0 Protected Resource Metadata):

import { FastMCP } from "fastmcp";
import { buildGetJwks } from "get-jwks";
import fastJwt 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 {
      // TODO: Cache the discovery document to avoid repeated requests
      // Discover OAuth/OpenID configuration from well-known endpoint
      const discoveryUrl =
        "https://auth.example.com/.well-known/openid-configuration";
      // Alternative: Use OAuth authorization server metadata endpoint
      // const discoveryUrl = 'https://auth.example.com/.well-known/oauth-authorization-server';

      const discoveryResponse = await fetch(discoveryUrl);
      if (!discoveryResponse.ok) {
        throw new Error("Failed to fetch OAuth discovery document");
      }

      const config = await discoveryResponse.json();
      const jwksUri = config.jwks_uri;
      const issuer = config.issuer;

      // Create JWKS client for token verification using discovered endpoint
      const getJwks = buildGetJwks({
        jwksUrl: jwksUri,
        cache: true,
        rateLimit: true,
      });

      // Create JWT verifier with JWKS and discovered issuer
      const verify = fastJwt.createVerifier({
        key: async (token) => {
          const { header } = fastJwt.decode(token, { complete: true });
          const jwk = await getJwks.getJwk({
            kid: header.kid,
            alg: header.alg,
          });
          return jwk;
        },
        algorithms: ["RS256", "ES256"],
        issuer: issuer,
        audience: "mcp://my-server",
      });

      // 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",
      });
    }
  },
});
 

This configuration automatically exposes OAuth discovery endpoints:

  • /.well-known/oauth-authorization-server – Authorization server metadata (RFC 8414)
  • /.well-known/oauth-protected-resource – Protected resource metadata (RFC 9470)

For JWT token validation, you can use libraries like get-jwks and @fastify/jwt for OAuth JWT tokens.

Passing Headers Through Context

 

If you are exposing your MCP server via HTTP, you may wish to allow clients to supply sensitive keys via headers, which can then be passed along to APIs that your tools interact with, allowing each client to supply their own API keys. This can be done by capturing the HTTP headers in the section and storing them in the session to be referenced by the tools later.authenticate

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,
  },
});
 

A client that would connect to this may look something like this:

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);
 

What would show up in the console after the client runs is something like this:

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' +
        '}'
    }
  ]
}
 

Session ID and Request ID Tracking

 

FastMCP automatically exposes session and request IDs to tool handlers through the context parameter. This enables per-session state management and request tracking.

Session ID ():context.sessionId

  • Available only for HTTP-based transports (HTTP Stream, SSE)
  • Extracted from the headerMcp-Session-Id
  • Remains constant across multiple requests from the same client
  • Useful for maintaining per-session state, counters, or user-specific data

Request ID ():context.requestId

  • Available for all transports when provided by the client
  • Unique for each individual request
  • Useful for request tracing and debugging
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,
  },
});
 

Use Cases:

  • Per-session state management: Maintain counters, caches, or temporary data unique to each client session
  • User authentication and authorization: Track authenticated users across requests
  • Session-specific resource management: Allocate and manage resources per session
  • Multi-tenant implementations: Isolate data and operations by session
  • Request tracing: Track individual requests for debugging and monitoring

Example:

See src/examples/session-id-counter.ts for a complete example demonstrating session-based counter management.

Notes:

  • Session IDs are automatically generated by the MCP transport layer
  • In stateless mode, session IDs are not persisted across requests
  • For stdio transport, will be as there’s no HTTP session conceptsessionIdundefined

Providing Instructions

 

You can provide instructions to the server using the option: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.',
});
 

Sessions

 

The object is an instance of and it describes active client sessions.sessionFastMCPSession

server.sessions;
 

We allocate a new server instance for each client connection to enable 1:1 communication between a client and the server.

Typed server events

 

You can listen to events emitted by the server using the method: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.

Refer to Sessions for examples of how to obtain a instance.FastMCPSession

requestSampling

 

requestSampling creates a sampling request and returns 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,
});
 

Options

 

requestSampling accepts an optional second parameter for request options:

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,
  },
);

Options:

  • onprogress?: (progress: Progress) => void – Callback for progress notifications from the remote end
  • signal?: AbortSignal – Abort signal to cancel the request
  • timeout?: number – Request timeout in milliseconds
  • resetTimeoutOnProgress?: boolean – Whether progress notifications reset the timeout
  • maxTotalTimeout?: number – Maximum total timeout regardless of progress notifications

clientCapabilities

The property contains the client capabilities.clientCapabilities

session.clientCapabilities;

loggingLevel

The property describes the logging level as set by the client.loggingLevel

session.loggingLevel;

roots

The property contains the roots as set by the client.roots

session.roots;

server

The property contains an instance of MCP server that is associated with the session.server

session.server;

Typed session events

You can listen to events emitted by the session using the method:on

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

The fastest way to test and debug your server is with :fastmcp dev

npx fastmcp dev server.js
npx fastmcp dev server.ts

This will run your server with mcp-cli for testing and debugging your MCP server in the terminal.

Inspect with MCP Inspector

Another way is to use the official MCP Inspector to inspect your server with a Web UI:

npx fastmcp inspect server.ts

FAQ

How to use with Claude Desktop?

按照指南 https://modelcontextprotocol.io/quickstart/user 添加以下配置:

{
  "mcpServers": {
    "my-mcp-server": {
      "command": "npx",
      "args": ["tsx", "/PATH/TO/YOUR_PROJECT/src/index.ts"],
      "env": {
        "YOUR_ENV_VAR": "value"
      }
    }
  }
}
请登录后发表评论

    没有回复内容