FastMCP

repository·main·Indexed 25 days ago

https://github.com/punkpeye/fastmcp

A high-level TypeScript framework for building Model Context Protocol (MCP) servers. It abstracts the official MCP SDK to simplify the creation of Tools, Resources, and Prompts, providing built-in support for session management, rich media (image and audio), and multiple transport protocols including stdio, SSE, and HTTP Streaming. It features Edge Runtime support via EdgeFastMCP for Cloudflare Workers and Deno Deploy, stateless mode for serverless environments, and integration with Hono for custom HTTP routes.

Tokens
40K
Snippets
114
Records
163
Agent score
82%

What's inside fastmcp

  1. Overview of FastMCP features and use cases

    main

    FastMCP is a TypeScript framework designed for building Model Context Protocol (MCP) servers that support client sessions. It is built on top of the official MCP SDK but abstracts away low-level implementation details.

    Key Features

    • Simplified Definitions: Easy setup for Tools, Resources, and Prompts.
    • Session Management: Supports Session ID/Request ID tracking and full session handling.
    • Rich Content: Native support for returning image, audio, and embedded resource content blocks.
    • Advanced Networking: HTTP Streaming (SSE compatible), HTTPS support, CORS (enabled by default), and custom HTTP routes for REST APIs or webhooks.
    • Deployment Flexibility: Edge Runtime support (Cloudflare Workers, Deno Deploy) and a Stateless mode for serverless environments.
    • Developer Experience: Built-in logging, error handling, progress notifications, streaming output, and prompt argument auto-completion.
    • Testing: In-memory transport for unit testing and a CLI for debugging.

    When to use FastMCP vs. the Official SDK

    • Use FastMCP if you want to build MCP servers quickly by automating boilerplate for connection handling, tool/resource/prompt management, and response handling.
    • Use the Official SDK if you require maximum control over low-level architectural details or specific implementation requirements.
  2. Quickstart: Create a basic MCP server

    main

    To create a working MCP server, import FastMCP and use addTool to define tools. You can use validation libraries like zod to define parameter schemas. Finally, call server.start() with a transportType (e.g., stdio).

    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",
    });
  3. Production Security Checklist for OAuth

    main

    When deploying an OAuth proxy in production, follow these security best practices:

    • Use DiskStore for persistent storage.
    • Wrap storage with EncryptedTokenStorage.
    • Derive JWT signing keys using JWTIssuer.deriveKey().
    • Use strong secrets (minimum 32 bytes).
    • Enable the token swap pattern.
    • Set appropriate TTL values.
    • Use HTTPS for all proxy endpoints.
    • Implement rate limiting on token endpoints.
  4. Integrate OAuth with FastMCP Server

    main

    To enable OAuth integration in a FastMCP server, pass an OAuth provider instance (e.g., GoogleProvider) to the auth option in the FastMCP constructor. This automatically handles endpoint registration and route setup.

    import { FastMCP, 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",
    });
  5. Unit test a server with in-memory transport

    main

    To test a stdio server in-process without binding ports or spawning subprocesses, use server.connect(transport) with the MCP SDK's InMemoryTransport. This allows you to drive the server and client within the same process for testing.

    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();
    });
  6. Integrate OAuth Proxy with FastMCP

    main

    The OAuth Proxy allows FastMCP servers to authenticate with traditional OAuth providers by presenting a DCR-compliant (Dynamic Client Registration) interface to MCP clients. This enables seamless integration with providers that do not natively support DCR. When you provide an auth provider to the FastMCP constructor, the following endpoints are automatically registered:

    • /oauth/register - DCR endpoint
    • /oauth/authorize - Authorization endpoint
    • /oauth/token - Token exchange
    • /oauth/callback - OAuth callback handler
    • /oauth/consent - User consent screen
    import { FastMCP, getAuthSession, GoogleProvider, requireAuth } from "fastmcp";
    
    // 1. Create FastMCP with OAuth provider
    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",
    });
    
    // 2. Add protected tools
    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",
    });
    
    await server.start({
      transportType: "httpStream",
      httpStream: { port: 3000 },
    });
  7. Implement the Token Swap Pattern with OAuthProxy

    main

    The Token Swap pattern prevents upstream tokens from reaching the client by issuing a proxy-specific JWT. This is enabled by default in OAuthProxy. To use the upstream tokens within your MCP tools, use authProxy.loadUpstreamTokens(clientToken) where clientToken is the Bearer token from the session headers.

    import { OAuthProxy, DiskStore, JWTIssuer } from "fastmcp/auth";
    
    const authProxy = new OAuthProxy({
      baseUrl: "https://your-server.com",
      upstreamAuthorizationEndpoint: "https://provider.com/oauth/authorize",
      upstreamTokenEndpoint: "https://provider.com/oauth/token",
      upstreamClientId: process.env.OAUTH_CLIENT_ID,
      upstreamClientSecret: process.env.OAUTH_CLIENT_SECRET,
    
      // Optionally provide your own signing key (recommended for production)
      jwtSigningKey: await JWTIssuer.deriveKey(process.env.JWT_SECRET, 100000),
    
      tokenStorage: new DiskStore({
        directory: "/var/lib/fastmcp/oauth",
      }),
    });
    
    // Inside a tool execution:
    // const upstreamTokens = await authProxy.loadUpstreamTokens(clientToken);
  8. Configure OAuth 2.1 authentication

    main

    FastMCP supports OAuth 2.1 via pre-configured providers. You can secure specific tools by using the canAccess: requireAuth option in addTool. Use getAuthSession(session) to retrieve the accessToken from the session.

    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",
    });
  9. Enable encrypted token storage

    main

    For enhanced security, wrap a DiskStore instance with EncryptedTokenStorage. This uses AES-256-GCM encryption with scrypt-derived keys to protect stored tokens.

    import { DiskStore, EncryptedTokenStorage } from "fastmcp/auth";
    
    const diskStore = new DiskStore({ directory: "/var/lib/fastmcp/oauth" });
    const encryptedStorage = new EncryptedTokenStorage(
      diskStore,
      "your-encryption-key",
    );
    
    const proxy = new OAuthProxy({
      // ... other config
      tokenStorage: encryptedStorage,
    });
  10. Create tools without parameters

    main

    You can create tools that require no arguments by either omitting the parameters property entirely or by providing an empty schema object. Both methods are fully compatible with MCP clients like Cursor.

    // Option 1: Omit parameters
    server.addTool({
      name: "sayHello",
      description: "Say hello",
      execute: async () => {
        return "Hello, world!";
      },
    });
    
    // Option 2: Explicit empty parameters
    import { z } from "zod";
    server.addTool({
      name: "sayHello",
      description: "Say hello",
      parameters: z.object({}),
      execute: async () => {
        return "Hello, world!";
      },
    });
  11. Advanced OAuth configuration with OAuthProxy

    main

    For granular control over OAuth behavior, use the OAuthProxy class from fastmcp/auth and pass it to the oauth option in the FastMCP constructor. This allows you to manually manage the authorizationServer metadata and the proxy instance.

    import { FastMCP } from "fastmcp";
    import { OAuthProxy } from "fastmcp/auth";
    
    const authProxy = new OAuthProxy({
      upstreamAuthorizationEndpoint: "https://provider.com/oauth/authorize",
      upstreamTokenEndpoint: "https://provider.com/oauth/token",
      upstreamClientId: process.env.OAUTH_CLIENT_ID!,
      upstreamClientSecret: process.env.OAUTH_CLIENT_SECRET!,
      baseUrl: "https://your-server.com",
      scopes: ["openid", "profile"],
    });
    
    const server = new FastMCP({
      name: "My Server",
      oauth: {
        enabled: true,
        authorizationServer: authProxy.getAuthorizationServerMetadata(),
        proxy: authProxy,
      },
    });
    
    await server.start({
      transportType: "httpStream",
      httpStream: { port: 3000 },
    });