[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-73756":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":23,"hasPages":23,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":28,"readmeContent":29,"aiSummary":30,"trendingCount":16,"starSnapshotCount":16,"syncStatus":31,"lastSyncTime":32,"discoverSource":33},73756,"fastmcp","punkpeye\u002Ffastmcp","punkpeye","A TypeScript framework for building MCP servers.","",null,"TypeScript",3185,277,18,39,0,10,32,77,30,103.03,"MIT License",false,"main",[26,27],"mcp","sse","2026-06-12 04:01:11","# FastMCP\n\nA TypeScript framework for building [MCP](https:\u002F\u002Fglama.ai\u002Fmcp) servers capable of handling client sessions.\n\n> [!NOTE]\n>\n> For a Python implementation, see [FastMCP](https:\u002F\u002Fgithub.com\u002Fjlowin\u002Ffastmcp).\n\n## Features\n\n- Simple Tool, Resource, Prompt definition\n- [Authentication](#authentication)\n- [Passing headers through context](#passing-headers-through-context)\n- [Session ID and Request ID tracking](#session-id-and-request-id-tracking)\n- [Sessions](#sessions)\n- [Image content](#returning-an-image)\n- [Audio content](#returning-an-audio)\n- [Embedded](#embedded-resources)\n- [Logging](#logging)\n- [Error handling](#errors)\n- [HTTP Streaming](#http-streaming) (with SSE compatibility)\n- [HTTPS Support](#https-support) for secure connections\n- [Custom HTTP routes](#custom-http-routes) for REST APIs, webhooks, and admin interfaces\n- [Edge Runtime Support](#edge-runtime-support) for Cloudflare Workers, Deno Deploy, and more\n- [Stateless mode](#stateless-mode) for serverless deployments\n- CORS (enabled by default)\n- [Progress notifications](#progress)\n- [Streaming output](#streaming-output)\n- [Typed server events](#typed-server-events)\n- [Prompt argument auto-completion](#prompt-argument-auto-completion)\n- [Sampling](#requestsampling)\n- [Configurable ping behavior](#configurable-ping-behavior)\n- [Health-check endpoint](#health-check-endpoint)\n- [Roots](#roots-management)\n- CLI for [testing](#test-with-mcp-cli) and [debugging](#inspect-with-mcp-inspector)\n\n## When to use FastMCP over the official SDK?\n\nFastMCP is built on top of the official SDK.\n\nThe official SDK provides foundational blocks for building MCPs, but leaves many implementation details to you:\n\n- [Initiating and configuring](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp\u002Fblob\u002F06c2af7a3d7e3d8c638deac1964ce269ce8e518b\u002Fsrc\u002FFastMCP.ts#L664-L744) all the server components\n- [Handling of connections](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp\u002Fblob\u002F06c2af7a3d7e3d8c638deac1964ce269ce8e518b\u002Fsrc\u002FFastMCP.ts#L760-L850)\n- [Handling of tools](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp\u002Fblob\u002F06c2af7a3d7e3d8c638deac1964ce269ce8e518b\u002Fsrc\u002FFastMCP.ts#L1303-L1498)\n- [Handling of responses](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp\u002Fblob\u002F06c2af7a3d7e3d8c638deac1964ce269ce8e518b\u002Fsrc\u002FFastMCP.ts#L989-L1060)\n- [Handling of resources](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp\u002Fblob\u002F06c2af7a3d7e3d8c638deac1964ce269ce8e518b\u002Fsrc\u002FFastMCP.ts#L1151-L1242)\n- Adding [prompts](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp\u002Fblob\u002F06c2af7a3d7e3d8c638deac1964ce269ce8e518b\u002Fsrc\u002FFastMCP.ts#L760-L850), [resources](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp\u002Fblob\u002F06c2af7a3d7e3d8c638deac1964ce269ce8e518b\u002Fsrc\u002FFastMCP.ts#L960-L962), [resource templates](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp\u002Fblob\u002F06c2af7a3d7e3d8c638deac1964ce269ce8e518b\u002Fsrc\u002FFastMCP.ts#L964-L987)\n- Embedding [resources](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp\u002Fblob\u002F06c2af7a3d7e3d8c638deac1964ce269ce8e518b\u002Fsrc\u002FFastMCP.ts#L1569-L1643), [image](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp\u002Fblob\u002F06c2af7a3d7e3d8c638deac1964ce269ce8e518b\u002Fsrc\u002FFastMCP.ts#L51-L111) and [audio](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp\u002Fblob\u002F06c2af7a3d7e3d8c638deac1964ce269ce8e518b\u002Fsrc\u002FFastMCP.ts#L113-L173) content blocks\n\nFastMCP eliminates this complexity by providing an opinionated framework that:\n\n- Handles all the boilerplate automatically\n- Provides simple, intuitive APIs for common tasks\n- Includes built-in best practices and error handling\n- Lets you focus on your MCP's core functionality\n\n**When to choose FastMCP:** You want to build MCP servers quickly without dealing with low-level implementation details.\n\n**When to use the official SDK:** You need maximum control or have specific architectural requirements. In this case, we encourage referencing FastMCP's implementation to avoid common pitfalls.\n\n## Installation\n\n```bash\nnpm install fastmcp\n```\n\n## Quickstart\n\n> [!NOTE]\n>\n> There are many real-world examples of using FastMCP in the wild. See the [Showcase](#showcase) for examples.\n\n```ts\nimport { FastMCP } from \"fastmcp\";\nimport { z } from \"zod\"; \u002F\u002F Or any validation library that supports Standard Schema\n\nconst server = new FastMCP({\n  name: \"My Server\",\n  version: \"1.0.0\",\n});\n\nserver.addTool({\n  name: \"add\",\n  description: \"Add two numbers\",\n  parameters: z.object({\n    a: z.number(),\n    b: z.number(),\n  }),\n  execute: async (args) => {\n    return String(args.a + args.b);\n  },\n});\n\nserver.start({\n  transportType: \"stdio\",\n});\n```\n\n_That's it!_ You have a working MCP server.\n\nYou can test the server in terminal with:\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp.git\ncd fastmcp\n\npnpm install\npnpm build\n\n# Test the addition server example using CLI:\nnpx fastmcp dev src\u002Fexamples\u002Faddition.ts\n# Test the addition server example using MCP Inspector:\nnpx fastmcp inspect src\u002Fexamples\u002Faddition.ts\n```\n\nIf you are looking for a boilerplate repository to build your own MCP server, check out [fastmcp-boilerplate](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp-boilerplate).\n\n### Remote Server Options\n\nFastMCP supports multiple transport options for remote communication, allowing an MCP hosted on a remote machine to be accessed over the network.\n\n#### HTTP Streaming\n\n[HTTP streaming](https:\u002F\u002Fwww.cloudflare.com\u002Flearning\u002Fvideo\u002Fwhat-is-http-live-streaming\u002F) provides a more efficient alternative to SSE in environments that support it, with potentially better performance for larger payloads.\n\nYou can run the server with HTTP streaming support:\n\n```ts\nserver.start({\n  transportType: \"httpStream\",\n  httpStream: {\n    port: 8080,\n  },\n});\n```\n\nThis will start the server and listen for HTTP streaming connections on `http:\u002F\u002Flocalhost:8080\u002Fmcp`.\n\n> **Note:** You can also customize the endpoint path using the `httpStream.endpoint` option (default is `\u002Fmcp`).\n\n> **Note:** This also starts an SSE server on `http:\u002F\u002Flocalhost:8080\u002Fsse`.\n\nYou can connect to these servers using the appropriate client transport.\n\nFor HTTP streaming connections:\n\n```ts\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol\u002Fsdk\u002Fclient\u002FstreamableHttp.js\";\n\nconst client = new Client(\n  {\n    name: \"example-client\",\n    version: \"1.0.0\",\n  },\n  {\n    capabilities: {},\n  },\n);\n\nconst transport = new StreamableHTTPClientTransport(\n  new URL(`http:\u002F\u002Flocalhost:8080\u002Fmcp`),\n);\n\nawait client.connect(transport);\n```\n\nFor SSE connections:\n\n```ts\nimport { SSEClientTransport } from \"@modelcontextprotocol\u002Fsdk\u002Fclient\u002Fsse.js\";\n\nconst client = new Client(\n  {\n    name: \"example-client\",\n    version: \"1.0.0\",\n  },\n  {\n    capabilities: {},\n  },\n);\n\nconst transport = new SSEClientTransport(new URL(`http:\u002F\u002Flocalhost:8080\u002Fsse`));\n\nawait client.connect(transport);\n```\n\n##### HTTPS Support\n\nFastMCP supports HTTPS for secure connections by providing SSL certificate options:\n\n```ts\nserver.start({\n  transportType: \"httpStream\",\n  httpStream: {\n    port: 8443,\n    sslCert: \".\u002Fpath\u002Fto\u002Fcert.pem\",\n    sslKey: \".\u002Fpath\u002Fto\u002Fkey.pem\",\n    sslCa: \".\u002Fpath\u002Fto\u002Fca.pem\", \u002F\u002F Optional: for client certificate authentication\n  },\n});\n```\n\nThis will start the server with HTTPS on `https:\u002F\u002Flocalhost:8443\u002Fmcp`.\n\n**SSL Options:**\n\n- `sslCert` - Path to SSL certificate file\n- `sslKey` - Path to SSL private key file\n- `sslCa` - (Optional) Path to CA certificate for mutual TLS authentication\n\n**For testing**, you can generate self-signed certificates:\n\n```bash\nopenssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj \"\u002FCN=localhost\"\n```\n\n**For production**, obtain certificates from a trusted CA like Let's Encrypt.\n\nSee the [https-server example](src\u002Fexamples\u002Fhttps-server.ts) for a complete demonstration.\n\n#### Custom HTTP Routes\n\nFastMCP allows you to add custom HTTP routes alongside MCP endpoints, enabling you to build comprehensive HTTP services that include REST APIs, webhooks, admin interfaces, and more - all within the same server process.\n\n```ts\n\u002F\u002F Add REST API endpoints\nserver.addRoute(\"GET\", \"\u002Fapi\u002Fusers\", async (req, res) => {\n  res.json({ users: [] });\n});\n\n\u002F\u002F Handle path parameters\nserver.addRoute(\"GET\", \"\u002Fapi\u002Fusers\u002F:id\", async (req, res) => {\n  res.json({\n    userId: req.params.id,\n    query: req.query, \u002F\u002F Access query parameters\n  });\n});\n\n\u002F\u002F Handle POST requests with body parsing\nserver.addRoute(\"POST\", \"\u002Fapi\u002Fusers\", async (req, res) => {\n  const body = await req.json();\n  res.status(201).json({ created: body });\n});\n\n\u002F\u002F Serve HTML content\nserver.addRoute(\"GET\", \"\u002Fadmin\", async (req, res) => {\n  res.send(\"\u003Chtml>\u003Cbody>\u003Ch1>Admin Panel\u003C\u002Fh1>\u003C\u002Fbody>\u003C\u002Fhtml>\");\n});\n\n\u002F\u002F Handle webhooks\nserver.addRoute(\"POST\", \"\u002Fwebhook\u002Fgithub\", async (req, res) => {\n  const payload = await req.json();\n  const event = req.headers[\"x-github-event\"];\n\n  \u002F\u002F Process webhook...\n  res.json({ received: true });\n});\n```\n\nCustom routes support:\n\n- All HTTP methods: GET, POST, PUT, DELETE, PATCH, OPTIONS\n- Path parameters (`:param`) and wildcards (`*`)\n- Query string parsing\n- JSON and text body parsing\n- Custom status codes and headers\n- Authentication via the same `authenticate` function as MCP\n- **Public routes** that bypass authentication\n\nRoutes are matched in the order they are registered, allowing you to define specific routes before catch-all patterns.\n\n##### Public Routes\n\nBy default, custom routes require authentication (if configured). You can make routes public by adding the `{ public: true }` option:\n\n```ts\n\u002F\u002F Public route - no authentication required\nserver.addRoute(\n  \"GET\",\n  \"\u002F.well-known\u002Fopenid-configuration\",\n  async (req, res) => {\n    res.json({\n      issuer: \"https:\u002F\u002Fexample.com\",\n      authorization_endpoint: \"https:\u002F\u002Fexample.com\u002Fauth\",\n      token_endpoint: \"https:\u002F\u002Fexample.com\u002Ftoken\",\n    });\n  },\n  { public: true },\n);\n\n\u002F\u002F Private route - requires authentication\nserver.addRoute(\"GET\", \"\u002Fapi\u002Fusers\", async (req, res) => {\n  \u002F\u002F req.auth contains authenticated user data\n  res.json({ users: [] });\n});\n\n\u002F\u002F Public static files\nserver.addRoute(\n  \"GET\",\n  \"\u002Fpublic\u002F*\",\n  async (req, res) => {\n    \u002F\u002F Serve static files without authentication\n    res.send(`File: ${req.url}`);\n  },\n  { public: true },\n);\n```\n\nPublic routes are perfect for:\n\n- OAuth discovery endpoints (`.well-known\u002F*`)\n- Health checks and status pages\n- Static assets and documentation\n- Webhook endpoints from external services\n- Public APIs that don't require user authentication\n\nSee the [custom-routes example](src\u002Fexamples\u002Fcustom-routes.ts) for a complete demonstration.\n\n#### Edge Runtime Support\n\nFastMCP supports edge runtimes like Cloudflare Workers, enabling deployment of MCP servers to the edge with minimal latency worldwide.\n\n##### Choosing Between FastMCP and EdgeFastMCP\n\n| Use Case                        | Class         | Import                                       |\n| ------------------------------- | ------------- | -------------------------------------------- |\n| Node.js, Express, Bun           | `FastMCP`     | `import { FastMCP } from \"fastmcp\"`          |\n| Cloudflare Workers, Deno Deploy | `EdgeFastMCP` | `import { EdgeFastMCP } from \"fastmcp\u002Fedge\"` |\n\n| Feature              | FastMCP                        | EdgeFastMCP                            |\n| -------------------- | ------------------------------ | -------------------------------------- |\n| Runtime              | Node.js                        | Edge (V8 isolates)                     |\n| Start method         | `server.start({ port })`       | `export default server`                |\n| Transport            | stdio, httpStream, SSE         | HTTP Streamable only                   |\n| Sessions             | Stateful or stateless          | Stateless only                         |\n| File system          | Yes                            | No                                     |\n| OAuth\u002FAuthentication | Built-in `authenticate` option | Use Hono middleware (built-in planned) |\n| Custom routes        | `server.getApp()`              | `server.getApp()`                      |\n\n> **Note:** Built-in authentication for EdgeFastMCP is planned for a future release. Both FastMCP and EdgeFastMCP use Hono internally, so there's no technical barrier—EdgeFastMCP was simply written before OAuth was added to FastMCP. PRs are welcome to add an `authenticate` option that accepts web `Request` instead of Node.js `http.IncomingMessage`.\n>\n> In the meantime, use Hono middleware:\n>\n> ```ts\n> const app = server.getApp();\n> app.use(\"\u002Fapi\u002F*\", async (c, next) => {\n>   if (c.req.header(\"authorization\") !== \"Bearer secret\") {\n>     return c.json({ error: \"Unauthorized\" }, 401);\n>   }\n>   await next();\n> });\n> ```\n\n##### Cloudflare Workers\n\nTo deploy FastMCP to Cloudflare Workers, use the `EdgeFastMCP` class from the `\u002Fedge` subpath:\n\n```ts\nimport { EdgeFastMCP } from \"fastmcp\u002Fedge\";\nimport { z } from \"zod\";\n\nconst server = new EdgeFastMCP({\n  name: \"My Edge Server\",\n  version: \"1.0.0\",\n  description: \"MCP server running on Cloudflare Workers\",\n});\n\n\u002F\u002F Add tools, resources, prompts as usual\nserver.addTool({\n  name: \"greet\",\n  description: \"Greet someone\",\n  parameters: z.object({\n    name: z.string(),\n  }),\n  execute: async ({ name }) => {\n    return `Hello, ${name}! Served from the edge.`;\n  },\n});\n\n\u002F\u002F Export the server as the default (required for Cloudflare Workers)\nexport default server;\n```\n\n##### Edge Runtime Differences\n\nWhen running on edge runtimes:\n\n- **Stateless by default**: Each request is handled independently\n- **No filesystem access**: Use fetch APIs for external data\n- **V8 Isolates**: Fast cold starts and efficient resource usage\n- **Global deployment**: Automatic distribution to edge locations\n\n##### Custom Routes on Edge\n\nYou can access the underlying Hono app to add custom HTTP routes:\n\n```ts\nconst app = server.getApp();\n\n\u002F\u002F Add a landing page\napp.get(\"\u002F\", (c) => c.html(\"\u003Ch1>Welcome to my MCP server\u003C\u002Fh1>\"));\n\n\u002F\u002F Add REST API endpoints\napp.get(\"\u002Fapi\u002Fstatus\", (c) => c.json({ status: \"ok\" }));\n```\n\n##### Deployment\n\nConfigure your `wrangler.toml`:\n\n```toml\nname = \"my-mcp-server\"\nmain = \"src\u002Findex.ts\"\ncompatibility_date = \"2024-01-01\"\n```\n\nDeploy with:\n\n```bash\nwrangler deploy\n```\n\nSee the [edge-cloudflare-worker example](src\u002Fexamples\u002Fedge-cloudflare-worker.ts) for a complete demonstration.\n\n#### Stateless Mode\n\nFastMCP supports stateless operation for HTTP streaming, where each request is handled independently without maintaining persistent sessions. This is ideal for serverless environments, load-balanced deployments, or when session state isn't required.\n\nIn stateless mode:\n\n- No sessions are tracked on the server\n- Each request creates a temporary session that's discarded after the response\n- Reduced memory usage and better scalability\n- Perfect for stateless deployment environments\n\nYou can enable stateless mode by adding the `stateless: true` option:\n\n```ts\nserver.start({\n  transportType: \"httpStream\",\n  httpStream: {\n    port: 8080,\n    stateless: true,\n  },\n});\n```\n\n> **Note:** Stateless mode is only available with HTTP streaming transport. Features that depend on persistent sessions (like session-specific state) will not be available in stateless mode.\n\nYou can also enable stateless mode using CLI arguments or environment variables:\n\n```bash\n# Via CLI argument\nnpx fastmcp dev src\u002Fserver.ts --transport http-stream --port 8080 --stateless true\n\n# Via environment variable\nFASTMCP_STATELESS=true npx fastmcp dev src\u002Fserver.ts\n```\n\nThe `\u002Fready` health check endpoint will indicate when the server is running in stateless mode:\n\n```json\n{\n  \"mode\": \"stateless\",\n  \"ready\": 1,\n  \"status\": \"ready\",\n  \"total\": 1\n}\n```\n\n## Core Concepts\n\n### Tools\n\n[Tools](https:\u002F\u002Fmodelcontextprotocol.io\u002Fdocs\u002Fconcepts\u002Ftools) in MCP allow servers to expose executable functions that can be invoked by clients and used by LLMs to perform actions.\n\nFastMCP uses the [Standard Schema](https:\u002F\u002Fstandardschema.dev) specification for defining tool parameters. This allows you to use your preferred schema validation library (like Zod, ArkType, or Valibot) as long as it implements the spec.\n\n**Zod Example:**\n\n```typescript\nimport { z } from \"zod\";\n\nserver.addTool({\n  name: \"fetch-zod\",\n  description: \"Fetch the content of a url (using Zod)\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  execute: async (args) => {\n    return await fetchWebpageContent(args.url);\n  },\n});\n```\n\n**ArkType Example:**\n\n```typescript\nimport { type } from \"arktype\";\n\nserver.addTool({\n  name: \"fetch-arktype\",\n  description: \"Fetch the content of a url (using ArkType)\",\n  parameters: type({\n    url: \"string\",\n  }),\n  execute: async (args) => {\n    return await fetchWebpageContent(args.url);\n  },\n});\n```\n\n**Valibot Example:**\n\nValibot requires the peer dependency @valibot\u002Fto-json-schema.\n\n```typescript\nimport * as v from \"valibot\";\n\nserver.addTool({\n  name: \"fetch-valibot\",\n  description: \"Fetch the content of a url (using Valibot)\",\n  parameters: v.object({\n    url: v.string(),\n  }),\n  execute: async (args) => {\n    return await fetchWebpageContent(args.url);\n  },\n});\n```\n\n#### Tools Without Parameters\n\nWhen creating tools that don't require parameters, you have two options:\n\n1. Omit the parameters property entirely:\n\n   ```typescript\n   server.addTool({\n     name: \"sayHello\",\n     description: \"Say hello\",\n     \u002F\u002F No parameters property\n     execute: async () => {\n       return \"Hello, world!\";\n     },\n   });\n   ```\n\n2. Explicitly define empty parameters:\n\n   ```typescript\n   import { z } from \"zod\";\n\n   server.addTool({\n     name: \"sayHello\",\n     description: \"Say hello\",\n     parameters: z.object({}), \u002F\u002F Empty object\n     execute: async () => {\n       return \"Hello, world!\";\n     },\n   });\n   ```\n\n> [!NOTE]\n>\n> Both approaches are fully compatible with all MCP clients, including Cursor. FastMCP automatically generates the proper schema in both cases.\n\n#### Tool Authorization\n\nYou can control which tools are available to authenticated users by adding an optional `canAccess` function to a tool's definition. This function receives the authentication context and should return `true` if the user is allowed to access the tool.\n\n```typescript\nserver.addTool({\n  name: \"admin-tool\",\n  description: \"An admin-only tool\",\n  canAccess: (auth) => auth?.role === \"admin\",\n  execute: async () => \"Welcome, admin!\",\n});\n```\n\n#### Returning a string\n\n`execute` can return a string:\n\n```js\nserver.addTool({\n  name: \"download\",\n  description: \"Download a file\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  execute: async (args) => {\n    return \"Hello, world!\";\n  },\n});\n```\n\nThe latter is equivalent to:\n\n```js\nserver.addTool({\n  name: \"download\",\n  description: \"Download a file\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  execute: async (args) => {\n    return {\n      content: [\n        {\n          type: \"text\",\n          text: \"Hello, world!\",\n        },\n      ],\n    };\n  },\n});\n```\n\n#### Returning a list\n\nIf you want to return a list of messages, you can return an object with a `content` property:\n\n```js\nserver.addTool({\n  name: \"download\",\n  description: \"Download a file\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  execute: async (args) => {\n    return {\n      content: [\n        { type: \"text\", text: \"First message\" },\n        { type: \"text\", text: \"Second message\" },\n      ],\n    };\n  },\n});\n```\n\n#### Returning an image\n\nUse the `imageContent` to create a content object for an image:\n\n```js\nimport { imageContent } from \"fastmcp\";\n\nserver.addTool({\n  name: \"download\",\n  description: \"Download a file\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  execute: async (args) => {\n    return imageContent({\n      url: \"https:\u002F\u002Fexample.com\u002Fimage.png\",\n    });\n\n    \u002F\u002F or...\n    \u002F\u002F return imageContent({\n    \u002F\u002F   path: \"\u002Fpath\u002Fto\u002Fimage.png\",\n    \u002F\u002F });\n\n    \u002F\u002F or...\n    \u002F\u002F return imageContent({\n    \u002F\u002F   buffer: Buffer.from(\"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\", \"base64\"),\n    \u002F\u002F });\n\n    \u002F\u002F or...\n    \u002F\u002F return {\n    \u002F\u002F   content: [\n    \u002F\u002F     await imageContent(...)\n    \u002F\u002F   ],\n    \u002F\u002F };\n  },\n});\n```\n\nThe `imageContent` function takes the following options:\n\n- `url`: The URL of the image.\n- `path`: The path to the image file.\n- `buffer`: The image data as a buffer.\n\nOnly one of `url`, `path`, or `buffer` must be specified.\n\nThe above example is equivalent to:\n\n```js\nserver.addTool({\n  name: \"download\",\n  description: \"Download a file\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  execute: async (args) => {\n    return {\n      content: [\n        {\n          type: \"image\",\n          data: \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\",\n          mimeType: \"image\u002Fpng\",\n        },\n      ],\n    };\n  },\n});\n```\n\n#### Configurable Ping Behavior\n\nFastMCP includes a configurable ping mechanism to maintain connection health. The ping behavior can be customized through server options:\n\n```ts\nconst server = new FastMCP({\n  name: \"My Server\",\n  version: \"1.0.0\",\n  ping: {\n    \u002F\u002F Explicitly enable or disable pings (defaults vary by transport)\n    enabled: true,\n    \u002F\u002F Configure ping interval in milliseconds (default: 5000ms)\n    intervalMs: 10000,\n    \u002F\u002F Set log level for ping-related messages (default: 'debug')\n    logLevel: \"debug\",\n  },\n});\n```\n\nBy default, ping behavior is optimized for each transport type:\n\n- Enabled for SSE and HTTP streaming connections (which benefit from keep-alive)\n- Disabled for `stdio` connections (where pings are typically unnecessary)\n\nThis configurable approach helps reduce log verbosity and optimize performance for different usage scenarios.\n\n### Health-check Endpoint\n\nWhen you run FastMCP with the `httpStream` transport you can optionally expose a\nsimple HTTP endpoint that returns a plain-text response useful for load-balancer\nor container orchestration liveness checks.\n\nEnable (or customise) the endpoint via the `health` key in the server options:\n\n```ts\nconst server = new FastMCP({\n  name: \"My Server\",\n  version: \"1.0.0\",\n  health: {\n    \u002F\u002F Enable \u002F disable (default: true)\n    enabled: true,\n    \u002F\u002F Body returned by the endpoint (default: 'ok')\n    message: \"healthy\",\n    \u002F\u002F Path that should respond (default: '\u002Fhealth')\n    path: \"\u002Fhealthz\",\n    \u002F\u002F HTTP status code to return (default: 200)\n    status: 200,\n  },\n});\n\nawait server.start({\n  transportType: \"httpStream\",\n  httpStream: { port: 8080 },\n});\n```\n\nNow a request to `http:\u002F\u002Flocalhost:8080\u002Fhealthz` will return:\n\n```\nHTTP\u002F1.1 200 OK\ncontent-type: text\u002Fplain\n\nhealthy\n```\n\nThe endpoint is ignored when the server is started with the `stdio` transport.\n\n#### Roots Management\n\nFastMCP supports [Roots](https:\u002F\u002Fmodelcontextprotocol.io\u002Fdocs\u002Fconcepts\u002Froots) - Feature that allows clients to provide a set of filesystem-like root locations that can be listed and dynamically updated. The Roots feature can be configured or disabled in server options:\n\n```ts\nconst server = new FastMCP({\n  name: \"My Server\",\n  version: \"1.0.0\",\n  roots: {\n    \u002F\u002F Set to false to explicitly disable roots support\n    enabled: false,\n    \u002F\u002F By default, roots support is enabled (true)\n  },\n});\n```\n\nThis provides the following benefits:\n\n- Better compatibility with different clients that may not support Roots\n- Reduced error logs when connecting to clients that don't implement roots capability\n- More explicit control over MCP server capabilities\n- Graceful degradation when roots functionality isn't available\n\nYou can listen for root changes in your server:\n\n```ts\nserver.on(\"connect\", (event) => {\n  const session = event.session;\n\n  \u002F\u002F Access the current roots\n  console.log(\"Initial roots:\", session.roots);\n\n  \u002F\u002F Listen for changes to the roots\n  session.on(\"rootsChanged\", (event) => {\n    console.log(\"Roots changed:\", event.roots);\n  });\n});\n```\n\nWhen a client doesn't support roots or when roots functionality is explicitly disabled, these operations will gracefully handle the situation without throwing errors.\n\n### Returning an audio\n\nUse the `audioContent` to create a content object for an audio:\n\n```js\nimport { audioContent } from \"fastmcp\";\n\nserver.addTool({\n  name: \"download\",\n  description: \"Download a file\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  execute: async (args) => {\n    return audioContent({\n      url: \"https:\u002F\u002Fexample.com\u002Faudio.mp3\",\n    });\n\n    \u002F\u002F or...\n    \u002F\u002F return audioContent({\n    \u002F\u002F   path: \"\u002Fpath\u002Fto\u002Faudio.mp3\",\n    \u002F\u002F });\n\n    \u002F\u002F or...\n    \u002F\u002F return audioContent({\n    \u002F\u002F   buffer: Buffer.from(\"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\", \"base64\"),\n    \u002F\u002F });\n\n    \u002F\u002F or...\n    \u002F\u002F return {\n    \u002F\u002F   content: [\n    \u002F\u002F     await audioContent(...)\n    \u002F\u002F   ],\n    \u002F\u002F };\n  },\n});\n```\n\nThe `audioContent` function takes the following options:\n\n- `url`: The URL of the audio.\n- `path`: The path to the audio file.\n- `buffer`: The audio data as a buffer.\n\nOnly one of `url`, `path`, or `buffer` must be specified.\n\nThe above example is equivalent to:\n\n```js\nserver.addTool({\n  name: \"download\",\n  description: \"Download a file\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  execute: async (args) => {\n    return {\n      content: [\n        {\n          type: \"audio\",\n          data: \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\",\n          mimeType: \"audio\u002Fmpeg\",\n        },\n      ],\n    };\n  },\n});\n```\n\n#### Return combination type\n\nYou can combine various types in this way and send them back to AI\n\n```js\nserver.addTool({\n  name: \"download\",\n  description: \"Download a file\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  execute: async (args) => {\n    return {\n      content: [\n        {\n          type: \"text\",\n          text: \"Hello, world!\",\n        },\n        {\n          type: \"image\",\n          data: \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\",\n          mimeType: \"image\u002Fpng\",\n        },\n        {\n          type: \"audio\",\n          data: \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\",\n          mimeType: \"audio\u002Fmpeg\",\n        },\n      ],\n    };\n  },\n\n  \u002F\u002F or...\n  \u002F\u002F execute: async (args) => {\n  \u002F\u002F   const imgContent = await imageContent({\n  \u002F\u002F     url: \"https:\u002F\u002Fexample.com\u002Fimage.png\",\n  \u002F\u002F   });\n  \u002F\u002F   const audContent = await audioContent({\n  \u002F\u002F     url: \"https:\u002F\u002Fexample.com\u002Faudio.mp3\",\n  \u002F\u002F   });\n  \u002F\u002F   return {\n  \u002F\u002F     content: [\n  \u002F\u002F       {\n  \u002F\u002F         type: \"text\",\n  \u002F\u002F         text: \"Hello, world!\",\n  \u002F\u002F       },\n  \u002F\u002F       imgContent,\n  \u002F\u002F       audContent,\n  \u002F\u002F     ],\n  \u002F\u002F   };\n  \u002F\u002F },\n});\n```\n\n#### Custom Logger\n\nFastMCP allows you to provide a custom logger implementation to control how the server logs messages. This is useful for integrating with existing logging infrastructure or customizing log formatting.\n\n```ts\nimport { FastMCP, Logger } from \"fastmcp\";\n\nclass CustomLogger implements Logger {\n  debug(...args: unknown[]): void {\n    console.log(\"[DEBUG]\", new Date().toISOString(), ...args);\n  }\n\n  error(...args: unknown[]): void {\n    console.error(\"[ERROR]\", new Date().toISOString(), ...args);\n  }\n\n  info(...args: unknown[]): void {\n    console.info(\"[INFO]\", new Date().toISOString(), ...args);\n  }\n\n  log(...args: unknown[]): void {\n    console.log(\"[LOG]\", new Date().toISOString(), ...args);\n  }\n\n  warn(...args: unknown[]): void {\n    console.warn(\"[WARN]\", new Date().toISOString(), ...args);\n  }\n}\n\nconst server = new FastMCP({\n  name: \"My Server\",\n  version: \"1.0.0\",\n  logger: new CustomLogger(),\n});\n```\n\nSee `src\u002Fexamples\u002Fcustom-logger.ts` for examples with Winston, Pino, and file-based logging.\n\n#### Logging\n\nTools can log messages to the client using the `log` object in the context object:\n\n```js\nserver.addTool({\n  name: \"download\",\n  description: \"Download a file\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  execute: async (args, { log }) => {\n    log.info(\"Downloading file...\", {\n      url,\n    });\n\n    \u002F\u002F ...\n\n    log.info(\"Downloaded file\");\n\n    return \"done\";\n  },\n});\n```\n\nThe `log` object has the following methods:\n\n- `debug(message: string, data?: SerializableValue)`\n- `error(message: string, data?: SerializableValue)`\n- `info(message: string, data?: SerializableValue)`\n- `warn(message: string, data?: SerializableValue)`\n\n#### Errors\n\nThe errors that are meant to be shown to the user should be thrown as `UserError` instances:\n\n```js\nimport { UserError } from \"fastmcp\";\n\nserver.addTool({\n  name: \"download\",\n  description: \"Download a file\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  execute: async (args) => {\n    if (args.url.startsWith(\"https:\u002F\u002Fexample.com\")) {\n      throw new UserError(\"This URL is not allowed\");\n    }\n\n    return \"done\";\n  },\n});\n```\n\n#### Progress\n\nTools can report progress by calling `reportProgress` in the context object:\n\n```js\nserver.addTool({\n  name: \"download\",\n  description: \"Download a file\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  execute: async (args, { reportProgress }) => {\n    await reportProgress({\n      progress: 0,\n      total: 100,\n    });\n\n    \u002F\u002F ...\n\n    await reportProgress({\n      progress: 100,\n      total: 100,\n    });\n\n    return \"done\";\n  },\n});\n```\n\n#### Streaming Output\n\nFastMCP supports streaming partial results from tools while they're still executing, enabling responsive UIs and real-time feedback. This is particularly useful for:\n\n- Long-running operations that generate content incrementally\n- Progressive generation of text, images, or other media\n- Operations where users benefit from seeing immediate partial results\n\nTo enable streaming for a tool, add the `streamingHint` annotation and use the `streamContent` method:\n\n```js\nserver.addTool({\n  name: \"generateText\",\n  description: \"Generate text incrementally\",\n  parameters: z.object({\n    prompt: z.string(),\n  }),\n  annotations: {\n    streamingHint: true, \u002F\u002F Signals this tool uses streaming\n    readOnlyHint: true,\n  },\n  execute: async (args, { streamContent }) => {\n    \u002F\u002F Send initial content immediately\n    await streamContent({ type: \"text\", text: \"Starting generation...\\n\" });\n\n    \u002F\u002F Simulate incremental content generation\n    const words = \"The quick brown fox jumps over the lazy dog.\".split(\" \");\n    for (const word of words) {\n      await streamContent({ type: \"text\", text: word + \" \" });\n      await new Promise((resolve) => setTimeout(resolve, 300)); \u002F\u002F Simulate delay\n    }\n\n    \u002F\u002F When using streamContent, you can:\n    \u002F\u002F 1. Return void (if all content was streamed)\n    \u002F\u002F 2. Return a final result (which will be appended to streamed content)\n\n    \u002F\u002F Option 1: All content was streamed, so return void\n    return;\n\n    \u002F\u002F Option 2: Return final content that will be appended\n    \u002F\u002F return \"Generation complete!\";\n  },\n});\n```\n\nStreaming works with all content types (text, image, audio) and can be combined with progress reporting:\n\n```js\nserver.addTool({\n  name: \"processData\",\n  description: \"Process data with streaming updates\",\n  parameters: z.object({\n    datasetSize: z.number(),\n  }),\n  annotations: {\n    streamingHint: true,\n  },\n  execute: async (args, { streamContent, reportProgress }) => {\n    const total = args.datasetSize;\n\n    for (let i = 0; i \u003C total; i++) {\n      \u002F\u002F Report numeric progress\n      await reportProgress({ progress: i, total });\n\n      \u002F\u002F Stream intermediate results\n      if (i % 10 === 0) {\n        await streamContent({\n          type: \"text\",\n          text: `Processed ${i} of ${total} items...\\n`,\n        });\n      }\n\n      await new Promise((resolve) => setTimeout(resolve, 50));\n    }\n\n    return \"Processing complete!\";\n  },\n});\n```\n\n#### Tool Annotations\n\nAs 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:\n\n```typescript\nserver.addTool({\n  name: \"fetch-content\",\n  description: \"Fetch content from a URL\",\n  parameters: z.object({\n    url: z.string(),\n  }),\n  annotations: {\n    title: \"Web Content Fetcher\", \u002F\u002F Human-readable title for UI display\n    readOnlyHint: true, \u002F\u002F Tool doesn't modify its environment\n    openWorldHint: true, \u002F\u002F Tool interacts with external entities\n  },\n  execute: async (args) => {\n    return await fetchWebpageContent(args.url);\n  },\n});\n```\n\nThe available annotations are:\n\n| Annotation        | Type    | Default | Description                                                                                                                          |\n| :---------------- | :------ | :------ | :----------------------------------------------------------------------------------------------------------------------------------- |\n| `title`           | string  | -       | A human-readable title for the tool, useful for UI display                                                                           |\n| `readOnlyHint`    | boolean | `false` | If true, indicates the tool does not modify its environment                                                                          |\n| `destructiveHint` | boolean | `true`  | If true, the tool may perform destructive updates (only meaningful when `readOnlyHint` is false)                                     |\n| `idempotentHint`  | boolean | `false` | If true, calling the tool repeatedly with the same arguments has no additional effect (only meaningful when `readOnlyHint` is false) |\n| `openWorldHint`   | boolean | `true`  | If true, the tool may interact with an \"open world\" of external entities                                                             |\n\nThese annotations help clients and LLMs better understand how to use the tools and what to expect when calling them.\n\n### Resources\n\n[Resources](https:\u002F\u002Fmodelcontextprotocol.io\u002Fdocs\u002Fconcepts\u002Fresources) represent any kind of data that an MCP server wants to make available to clients. This can include:\n\n- File contents\n- Screenshots and images\n- Log files\n- And more\n\nEach resource is identified by a unique URI and can contain either text or binary data.\n\n```ts\nserver.addResource({\n  uri: \"file:\u002F\u002F\u002Flogs\u002Fapp.log\",\n  name: \"Application Logs\",\n  mimeType: \"text\u002Fplain\",\n  async load() {\n    return {\n      text: await readLogFile(),\n    };\n  },\n});\n```\n\n> [!NOTE]\n>\n> `load` can return multiple resources. This could be used, for example, to return a list of files inside a directory when the directory is read.\n>\n> ```ts\n> async load() {\n>   return [\n>     {\n>       text: \"First file content\",\n>     },\n>     {\n>       text: \"Second file content\",\n>     },\n>   ];\n> }\n> ```\n\nYou can also return binary contents in `load`:\n\n```ts\nasync load() {\n  return {\n    blob: 'base64-encoded-data'\n  };\n}\n```\n\n### Resource templates\n\nYou can also define resource templates:\n\n```ts\nserver.addResourceTemplate({\n  uriTemplate: \"file:\u002F\u002F\u002Flogs\u002F{name}.log\",\n  name: \"Application Logs\",\n  mimeType: \"text\u002Fplain\",\n  arguments: [\n    {\n      name: \"name\",\n      description: \"Name of the log\",\n      required: true,\n    },\n  ],\n  async load({ name }) {\n    return {\n      text: `Example log content for ${name}`,\n    };\n  },\n});\n```\n\n#### Resource template argument auto-completion\n\nProvide `complete` functions for resource template arguments to enable automatic completion:\n\n```ts\nserver.addResourceTemplate({\n  uriTemplate: \"file:\u002F\u002F\u002Flogs\u002F{name}.log\",\n  name: \"Application Logs\",\n  mimeType: \"text\u002Fplain\",\n  arguments: [\n    {\n      name: \"name\",\n      description: \"Name of the log\",\n      required: true,\n      complete: async (value) => {\n        if (value === \"Example\") {\n          return {\n            values: [\"Example Log\"],\n          };\n        }\n\n        return {\n          values: [],\n        };\n      },\n    },\n  ],\n  async load({ name }) {\n    return {\n      text: `Example log content for ${name}`,\n    };\n  },\n});\n```\n\n### Embedded Resources\n\nFastMCP provides a convenient `embedded()` method that simplifies including resources in tool responses. This feature reduces code duplication and makes it easier to reference resources from within tools.\n\n#### Basic Usage\n\n```js\nserver.addTool({\n  name: \"get_user_data\",\n  description: \"Retrieve user information\",\n  parameters: z.object({\n    userId: z.string(),\n  }),\n  execute: async (args) => {\n    return {\n      content: [\n        {\n          type: \"resource\",\n          resource: await server.embedded(`user:\u002F\u002Fprofile\u002F${args.userId}`),\n        },\n      ],\n    };\n  },\n});\n```\n\n#### Working with Resource Templates\n\nThe `embedded()` method works seamlessly with resource templates:\n\n```js\n\u002F\u002F Define a resource template\nserver.addResourceTemplate({\n  uriTemplate: \"docs:\u002F\u002Fproject\u002F{section}\",\n  name: \"Project Documentation\",\n  mimeType: \"text\u002Fmarkdown\",\n  arguments: [\n    {\n      name: \"section\",\n      required: true,\n    },\n  ],\n  async load(args) {\n    const docs = {\n      \"getting-started\": \"# Getting Started\\n\\nWelcome to our project!\",\n      \"api-reference\": \"# API Reference\\n\\nAuthentication is required.\",\n    };\n    return {\n      text: docs[args.section] || \"Documentation not found\",\n    };\n  },\n});\n\n\u002F\u002F Use embedded resources in a tool\nserver.addTool({\n  name: \"get_documentation\",\n  description: \"Retrieve project documentation\",\n  parameters: z.object({\n    section: z.enum([\"getting-started\", \"api-reference\"]),\n  }),\n  execute: async (args) => {\n    return {\n      content: [\n        {\n          type: \"resource\",\n          resource: await server.embedded(`docs:\u002F\u002Fproject\u002F${args.section}`),\n        },\n      ],\n    };\n  },\n});\n```\n\n#### Working with Direct Resources\n\nIt also works with directly defined resources:\n\n```js\n\u002F\u002F Define a direct resource\nserver.addResource({\n  uri: \"system:\u002F\u002Fstatus\",\n  name: \"System Status\",\n  mimeType: \"text\u002Fplain\",\n  async load() {\n    return {\n      text: \"System operational\",\n    };\n  },\n});\n\n\u002F\u002F Use in a tool\nserver.addTool({\n  name: \"get_system_status\",\n  description: \"Get current system status\",\n  parameters: z.object({}),\n  execute: async () => {\n    return {\n      content: [\n        {\n          type: \"resource\",\n          resource: await server.embedded(\"system:\u002F\u002Fstatus\"),\n        },\n      ],\n    };\n  },\n});\n```\n\n### Prompts\n\n[Prompts](https:\u002F\u002Fmodelcontextprotocol.io\u002Fdocs\u002Fconcepts\u002Fprompts) 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.\n\n```ts\nserver.addPrompt({\n  name: \"git-commit\",\n  description: \"Generate a Git commit message\",\n  arguments: [\n    {\n      name: \"changes\",\n      description: \"Git diff or description of changes\",\n      required: true,\n    },\n  ],\n  load: async (args) => {\n    return `Generate a concise but descriptive commit message for these changes:\\n\\n${args.changes}`;\n  },\n});\n```\n\n#### Prompt argument auto-completion\n\nPrompts can provide auto-completion for their arguments:\n\n```js\nserver.addPrompt({\n  name: \"countryPoem\",\n  description: \"Writes a poem about a country\",\n  load: async ({ name }) => {\n    return `Hello, ${name}!`;\n  },\n  arguments: [\n    {\n      name: \"name\",\n      description: \"Name of the country\",\n      required: true,\n      complete: async (value) => {\n        if (value === \"Germ\") {\n          return {\n            values: [\"Germany\"],\n          };\n        }\n\n        return {\n          values: [],\n        };\n      },\n    },\n  ],\n});\n```\n\n#### Prompt argument auto-completion using `enum`\n\nIf you provide an `enum` array for an argument, the server will automatically provide completions for the argument.\n\n```js\nserver.addPrompt({\n  name: \"countryPoem\",\n  description: \"Writes a poem about a country\",\n  load: async ({ name }) => {\n    return `Hello, ${name}!`;\n  },\n  arguments: [\n    {\n      name: \"name\",\n      description: \"Name of the country\",\n      required: true,\n      enum: [\"Germany\", \"France\", \"Italy\"],\n    },\n  ],\n});\n```\n\n### Authentication\n\nFastMCP supports OAuth 2.1 authentication with pre-configured providers, allowing you to secure your server with minimal setup.\n\n#### OAuth with Pre-configured Providers\n\nUse the `auth` option with a provider to enable OAuth authentication:\n\n```ts\nimport { FastMCP, getAuthSession, GoogleProvider, requireAuth } from \"fastmcp\";\n\nconst server = new FastMCP({\n  auth: new GoogleProvider({\n    baseUrl: \"https:\u002F\u002Fyour-server.com\",\n    clientId: process.env.GOOGLE_CLIENT_ID!,\n    clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n  }),\n  name: \"My Server\",\n  version: \"1.0.0\",\n});\n\nserver.addTool({\n  canAccess: requireAuth,\n  description: \"Get user profile\",\n  execute: async (_args, { session }) => {\n    const { accessToken } = getAuthSession(session);\n    const response = await fetch(\n      \"https:\u002F\u002Fwww.googleapis.com\u002Foauth2\u002Fv2\u002Fuserinfo\",\n      {\n        headers: { Authorization: `Bearer ${accessToken}` },\n      },\n    );\n    return JSON.stringify(await response.json());\n  },\n  name: \"get-profile\",\n});\n```\n\n**Available Providers:**\n\n| Provider         | Import    | Use Case               |\n| :--------------- | :-------- | :--------------------- |\n| `GoogleProvider` | `fastmcp` | Google OAuth           |\n| `GitHubProvider` | `fastmcp` | GitHub OAuth           |\n| `AzureProvider`  | `fastmcp` | Azure\u002FEntra ID         |\n| `OAuthProvider`  | `fastmcp` | Any OAuth 2.0 provider |\n\n**Generic OAuth Provider** (for SAP, Auth0, Okta, etc.):\n\n```ts\nimport { FastMCP, OAuthProvider } from \"fastmcp\";\n\nconst server = new FastMCP({\n  auth: new OAuthProvider({\n    authorizationEndpoint: process.env.OAUTH_AUTH_ENDPOINT!,\n    baseUrl: \"https:\u002F\u002Fyour-server.com\",\n    clientId: process.env.OAUTH_CLIENT_ID!,\n    clientSecret: process.env.OAUTH_CLIENT_SECRET!,\n    scopes: [\"openid\", \"profile\"],\n    tokenEndpoint: process.env.OAUTH_TOKEN_ENDPOINT!,\n  }),\n  name: \"My Server\",\n  version: \"1.0.0\",\n});\n```\n\n#### Tool Authorization\n\nControl tool access using the `canAccess` property with built-in helper functions:\n\n```ts\nimport {\n  requireAuth,\n  requireScopes,\n  requireRole,\n  requireAll,\n  requireAny,\n  getAuthSession,\n} from \"fastmcp\";\n\n\u002F\u002F Require any authenticated user\nserver.addTool({\n  canAccess: requireAuth,\n  name: \"user-tool\",\n  \u002F\u002F ...\n});\n\n\u002F\u002F Require specific OAuth scopes\nserver.addTool({\n  canAccess: requireScopes(\"read:user\", \"write:data\"),\n  name: \"scoped-tool\",\n  \u002F\u002F ...\n});\n\n\u002F\u002F Require specific role\nserver.addTool({\n  canAccess: requireRole(\"admin\"),\n  name: \"admin-tool\",\n  \u002F\u002F ...\n});\n\n\u002F\u002F Combine with AND logic\nserver.addTool({\n  canAccess: requireAll(requireAuth, requireRole(\"admin\")),\n  name: \"admin-only\",\n  \u002F\u002F ...\n});\n\n\u002F\u002F Combine with OR logic\nserver.addTool({\n  canAccess: requireAny(requireRole(\"admin\"), requireRole(\"moderator\")),\n  name: \"staff-tool\",\n  \u002F\u002F ...\n});\n```\n\n**Custom Authorization:**\n\nFor custom logic, pass a function directly:\n\n```typescript\nserver.addTool({\n  name: \"custom-auth-tool\",\n  canAccess: (auth) =>\n    auth?.role === \"admin\" && auth?.department === \"engineering\",\n  execute: async () => \"Access granted!\",\n});\n```\n\n**Extracting Session Data:**\n\nUse `getAuthSession` for type-safe access to the OAuth session in your tool execute functions:\n\n```typescript\nimport { getAuthSession, GoogleSession } from \"fastmcp\";\n\nserver.addTool({\n  canAccess: requireAuth,\n  name: \"get-profile\",\n  execute: async (_args, { session }) => {\n    \u002F\u002F Type-safe destructuring (throws if not authenticated)\n    const { accessToken } = getAuthSession(session);\n\n    \u002F\u002F Or with provider-specific typing:\n    \u002F\u002F const { accessToken } = getAuthSession\u003CGoogleSession>(session);\n\n    const response = await fetch(\"https:\u002F\u002Fapi.example.com\u002Fuser\", {\n      headers: { Authorization: `Bearer ${accessToken}` },\n    });\n    return JSON.stringify(await response.json());\n  },\n});\n```\n\n> **Note:** You can also access `session.accessToken` directly, but you must handle the case where `session` is undefined. The `getAuthSession` helper throws a clear error if the session is not authenticated, making it safer when used with `canAccess: requireAuth`.\n\n#### Custom Authentication\n\nFor non-OAuth scenarios (API keys, custom tokens), use the `authenticate` option:\n\n```ts\nconst server = new FastMCP({\n  name: \"My Server\",\n  version: \"1.0.0\",\n  authenticate: (request) => {\n    const apiKey = request.headers[\"x-api-key\"];\n\n    if (apiKey !== \"123\") {\n      throw new Response(null, {\n        status: 401,\n        statusText: \"Unauthorized\",\n      });\n    }\n\n    return { id: 1, role: \"user\" };\n  },\n});\n\nserver.addTool({\n  name: \"sayHello\",\n  execute: async (args, { session }) => {\n    return `Hello, ${session.id}!`;\n  },\n});\n```\n\n#### OAuth Proxy\n\nThe `auth` option uses FastMCP's built-in **OAuth Proxy** that acts as a secure intermediary between MCP clients and upstream OAuth providers. The proxy handles the complete OAuth 2.1 authorization flow, including Dynamic Client Registration (DCR), PKCE, consent management, and token management with encryption and token swap patterns enabled by default.\n\n**Key Features:**\n\n- 🔐 **Secure by Default**: Automatic encryption (AES-256-GCM) and token swap pattern\n- 🚀 **Zero Configuration**: Auto-generates keys and handles OAuth flows automatically\n- 🔌 **Pre-configured Providers**: Built-in support for Google, GitHub, and Azure\n- 🎯 **RFC Compliant**: Implements DCR (RFC 7591), PKCE, and OAuth 2.1\n- 🔑 **Optional JWKS**: Support for RS256\u002FES256 token verification (via optional `jose` dependency)\n\n**Quick Start:**\n\n```ts\nimport { FastMCP, getAuthSession, GoogleProvider, requireAuth } from \"fastmcp\";\n\nconst server = new FastMCP({\n  auth: new GoogleProvider({\n    baseUrl: \"https:\u002F\u002Fyour-server.com\",\n    clientId: process.env.GOOGLE_CLIENT_ID!,\n    clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n  }),\n  name: \"My Server\",\n  version: \"1.0.0\",\n});\n\nserver.addTool({\n  canAccess: requireAuth,\n  name: \"protected-tool\",\n  execute: async (_args, { session }) => {\n    const { accessToken } = getAuthSession(session);\n    \u002F\u002F Use accessToken to call upstream APIs\n    return \"Authenticated!\";\n  },\n});\n```\n\n**Advanced Configuration:**\n\nFor more control over OAuth behavior, you can use the `oauth` option directly:\n\n```ts\nimport { FastMCP } from \"fastmcp\";\nimport { GoogleProvider } from \"fastmcp\u002Fauth\";\n\nconst authProxy = new GoogleProvider({\n  clientId: process.env.GOOGLE_CLIENT_ID,\n  clientSecret: process.env.GOOGLE_CLIENT_SECRET,\n  baseUrl: \"https:\u002F\u002Fyour-server.com\",\n  scopes: [\"openid\", \"profile\", \"email\"],\n});\n\nconst server = new FastMCP({\n  name: \"My Server\",\n  oauth: {\n    enabled: true,\n    authorizationServer: authProxy.getAuthorizationServerMetadata(),\n    proxy: authProxy,\n  },\n});\n```\n\n**Documentation:**\n\n- [OAuth Proxy Features](docs\u002Foauth-proxy-features.md) - Complete feature list and capabilities\n- [OAuth Proxy Implementation Guide](docs\u002Foauth-proxy-guide.md) - Setup and configuration\n- [Python vs TypeScript Comparison](docs\u002Foauth-python-typescript.md) - Feature comparison\n\n#### OAuth Discovery Endpoints\n\nFastMCP also supports OAuth discovery endpoints for direct integration with OAuth providers, supporting both **MCP Specification 2025-03-26** and **MCP Specification 2025-06-18**. This provides standard discovery endpoints that comply with RFC 8414 (OAuth 2.0 Authorization Server Metadata) and RFC 9470 (OAuth 2.0 Protected Resource Metadata):\n\n```ts\nimport { FastMCP, DiscoveryDocumentCache } from \"fastmcp\";\nimport { buildGetJwks } from \"get-jwks\";\nimport fastJwt from \"fast-jwt\";\n\n\u002F\u002F Create a cache for discovery documents (reuse across requests)\nconst discoveryCache = new DiscoveryDocumentCache({\n  ttl: 3600000, \u002F\u002F Cache for 1 hour (default)\n});\n\nconst server = new FastMCP({\n  name: \"My Server\",\n  version: \"1.0.0\",\n  oauth: {\n    enabled: true,\n    authorizationServer: {\n      issuer: \"https:\u002F\u002Fauth.example.com\",\n      authorizationEndpoint: \"https:\u002F\u002Fauth.example.com\u002Foauth\u002Fauthorize\",\n      tokenEndpoint: \"https:\u002F\u002Fauth.example.com\u002Foauth\u002Ftoken\",\n      jwksUri: \"https:\u002F\u002Fauth.example.com\u002F.well-known\u002Fjwks.json\",\n      responseTypesSupported: [\"code\"],\n    },\n    protectedResource: {\n      resource: \"mcp:\u002F\u002Fmy-server\",\n      authorizationServers: [\"https:\u002F\u002Fauth.example.com\"],\n    },\n  },\n  authenticate: async (request) => {\n    const authHeader = request.headers.authorization;\n\n    if (!authHeader?.startsWith(\"Bearer \")) {\n      throw new Response(null, {\n        status: 401,\n        statusText: \"Missing or invalid authorization header\",\n      });\n    }\n\n    const token = authHeader.slice(7); \u002F\u002F Remove 'Bearer ' prefix\n\n    \u002F\u002F Validate OAuth JWT access token using OpenID Connect discovery\n    try {\n      \u002F\u002F Fetch and cache the discovery document\n      const discoveryUrl =\n        \"https:\u002F\u002Fauth.example.com\u002F.well-known\u002Fopenid-configuration\";\n      \u002F\u002F Alternative: Use OAuth authorization server metadata endpoint\n      \u002F\u002F const discoveryUrl = 'https:\u002F\u002Fauth.example.com\u002F.well-known\u002Foauth-authorization-server';\n\n      const config = (await discoveryCache.get(discoveryUrl)) as {\n        jwks_uri: string;\n        issuer: string;\n      };\n      const jwksUri = config.jwks_uri;\n      const issuer = config.issuer;\n\n      \u002F\u002F Create JWKS client for token verification using discovered endpoint\n      const getJwks = buildGetJwks({\n        jwksUrl: jwksUri,\n        cache: true,\n        rateLimit: true,\n      });\n\n      \u002F\u002F Create JWT verifier with JWKS and discovered issuer\n      const verify = fastJwt.createVerifier({\n        key: async (token) => {\n          const { header } = fastJwt.decode(token, { complete: true });\n          const jwk = await getJwks.getJwk({\n            kid: header.kid,\n            alg: header.alg,\n          });\n          return jwk;\n        },\n        algorithms: [\"RS256\", \"ES256\"],\n        issuer: issuer,\n        audience: \"mcp:\u002F\u002Fmy-server\",\n      });\n\n      \u002F\u002F Verify the JWT token\n      const payload = await verify(token);\n\n      return {\n        userId: payload.sub,\n        scope: payload.scope,\n        email: payload.email,\n        \u002F\u002F Include other claims as needed\n      };\n    } catch (error) {\n      throw new Response(null, {\n        status: 401,\n        statusText: \"Invalid OAuth token\",\n      });\n    }\n  },\n});\n```\n\nThis configuration automatically exposes OAuth discovery endpoints:\n\n- `\u002F.well-known\u002Foauth-authorization-server` - Authorization server metadata (RFC 8414)\n- `\u002F.well-known\u002Foauth-protected-resource` - Protected resource metadata (RFC 9728)\n- `\u002F.well-known\u002Foauth-protected-resource\u003Cendpoint>` - Protected resource metadata at sub-path (MCP 2025-11-25)\n\n**Discovery Mechanism (MCP Specification 2025-11-25):**\n\nClients discover protected resource metadata using the following search order:\n\n1. **WWW-Authenticate header** - Primary method (handled automatically by mcp-proxy)\n2. **Sub-path well-known** - `\u002F.well-known\u002Foauth-protected-resource\u003Cendpoint>` (e.g., `\u002F.well-known\u002Foauth-protected-resource\u002Fmcp`)\n3. **Root well-known** - `\u002F.well-known\u002Foauth-protected-resource` (fallback)\n\nBoth the sub-path and root endpoints return identical metadata, ensuring compatibility with all MCP client implementations.\n\nFor JWT token validation, you can use libraries like [`get-jwks`](https:\u002F\u002Fgithub.com\u002Fnearform\u002Fget-jwks) and [`@fastify\u002Fjwt`](https:\u002F\u002Fgithub.com\u002Ffastify\u002Ffastify-jwt) for OAuth JWT tokens.\n\n#### Passing Headers Through Context\n\nIf 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 `authenticate` section and storing them in the session to be referenced by the tools later.\n\n```ts\nimport { FastMCP } from \"fastmcp\";\nimport { IncomingHttpHeaders } from \"http\";\n\n\u002F\u002F Define the session data type\ninterface SessionData {\n  headers: IncomingHttpHeaders;\n  [key: string]: unknown; \u002F\u002F Add index signature to satisfy Record\u003Cstring, unknown>\n}\n\n\u002F\u002F Create a server instance\nconst server = new FastMCP({\n  name: \"My Server\",\n  version: \"1.0.0\",\n  authenticate: async (request: any): Promise\u003CSessionData> => {\n    \u002F\u002F Authentication logic\n    return {\n      headers: request.headers,\n    };\n  },\n});\n\n\u002F\u002F Tool to display HTTP headers\nserver.addTool({\n  name: \"headerTool\",\n  description: \"Reads HTTP headers from the request\",\n  execute: async (args: any, context: any) => {\n    const session = context.session as SessionData;\n    const headers = session?.headers ?? {};\n\n    const getHeaderString = (header: string | string[] | undefined) =>\n      Array.isArray(header) ? header.join(\", \") : (header ?? \"N\u002FA\");\n\n    const userAgent = getHeaderString(headers[\"user-agent\"]);\n    const authorization = getHeaderString(headers[\"authorization\"]);\n    return `User-Agent: ${userAgent}\\nAuthorization: ${authorization}\\nAll Headers: ${JSON.stringify(headers, null, 2)}`;\n  },\n});\n\n\u002F\u002F Start the server\nserver.start({\n  transportType: \"httpStream\",\n  httpStream: {\n    port: 8080,\n  },\n});\n```\n\nA client that would connect to this may look something like this:\n\n```ts\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol\u002Fsdk\u002Fclient\u002FstreamableHttp.js\";\nimport { Client } from \"@modelcontextprotocol\u002Fsdk\u002Fclient\u002Findex.js\";\n\nconst transport = new StreamableHTTPClientTransport(\n  new URL(`http:\u002F\u002Flocalhost:8080\u002Fmcp`),\n  {\n    requestInit: {\n      headers: {\n        Authorization: \"Test 123\",\n      },\n    },\n  },\n);\n\nconst client = new Client({\n  name: \"example-client\",\n  version: \"1.0.0\",\n});\n\n(async () => {\n  await client.connect(transport);\n\n  \u002F\u002F Call a tool\n  const result = await client.callTool({\n    name: \"headerTool\",\n    arguments: {\n      arg1: \"value\",\n    },\n  });\n\n  console.log(\"Tool result:\", result);\n})().catch(console.error);\n```\n\nWhat would show up in the console after the client runs is something like this:\n\n```\nTool result: {\n  content: [\n    {\n      type: 'text',\n      text: 'User-Agent: node\\n' +\n        'Authorization: Test 123\\n' +\n        'All Headers: {\\n' +\n        '  \"host\": \"localhost:8080\",\\n' +\n        '  \"connection\": \"keep-alive\",\\n' +\n        '  \"authorization\": \"Test 123\",\\n' +\n        '  \"content-type\": \"application\u002Fjson\",\\n' +\n        '  \"accept\": \"application\u002Fjson, text\u002Fevent-stream\",\\n' +\n        '  \"accept-language\": \"*\",\\n' +\n        '  \"sec-fetch-mode\": \"cors\",\\n' +\n        '  \"user-agent\": \"node\",\\n' +\n        '  \"accept-encoding\": \"gzip, deflate\",\\n' +\n        '  \"content-length\": \"163\"\\n' +\n        '}'\n    }\n  ]\n}\n```\n\n#### Session ID and Request ID Tracking\n\nFastMCP automatically exposes session and request IDs to tool handlers through the context parameter. This enables per-session state management and request tracking.\n\n**Session ID** (`context.sessionId`):\n\n- Available only for HTTP-based transports (HTTP Stream, SSE)\n- Extracted from the `Mcp-Session-Id` header\n- Remains constant across multiple requests from the same client\n- Useful for maintaining per-session state, counters, or user-specific data\n\n**Request ID** (`context.requestId`):\n\n- Available for all transports when provided by the client\n- Unique for each individual request\n- Useful for request tracing and debugging\n\n```ts\nimport { FastMCP } from \"fastmcp\";\nimport { z } from \"zod\";\n\nconst server = new FastMCP({\n  name: \"Session Counter Server\",\n  version: \"1.0.0\",\n});\n\n\u002F\u002F Per-session counter storage\nconst sessionCounters = new Map\u003Cstring, number>();\n\nserver.addTool({\n  name: \"increment_counter\",\n  description: \"Increment a per-session counter\",\n  parameters: z.object({}),\n  execute: async (args, context) => {\n    if (!context.sessionId) {\n      return \"Session ID not available (requires HTTP transport)\";\n    }\n\n    const counter = sessionCounters.get(context.sessionId) || 0;\n    const newCounter = counter + 1;\n    sessionCounters.set(context.sessionId, newCounter);\n\n    return `Counter for session ${context.sessionId}: ${newCounter}`;\n  },\n});\n\nserver.addTool({\n  name: \"show_ids\",\n  description: \"Display session and request IDs\",\n  parameters: z.object({}),\n  execute: async (args, context) => {\n    return `Session ID: ${context.sessionId || \"N\u002FA\"}\nRequest ID: ${context.requestId || \"N\u002FA\"}`;\n  },\n});\n\nserver.start({\n  transportType: \"httpStream\",\n  httpStream: {\n    port: 8080,\n  },\n});\n```\n\n**Use Cases:**\n\n- **Per-session state management**: Maintain counters, caches, or temporary data unique to each client session\n- **User authentication and authorization**: Track authenticated users across requests\n- **Session-specific resource management**: Allocate and manage resources per session\n- **Multi-tenant implementations**: Isolate data and operations by session\n- **Request tracing**: Track individual requests for debugging and monitoring\n\n**Example:**\n\nSee [`src\u002Fexamples\u002Fsession-id-counter.ts`](src\u002Fexamples\u002Fsession-id-counter.ts) for a complete example demonstrating session-based counter management.\n\n**Notes:**\n\n- Session IDs are automatically generated by the MCP transport layer\n- In stateless mode, session IDs are not persisted across requests\n- For stdio transport, `sessionId` will be `undefined` as there's no HTTP session concept\n\n### Providing Instructions\n\nYou can provide instructions to the server using the `instructions` option:\n\n```ts\nconst server = new FastMCP({\n  name: \"My Server\",\n  version: \"1.0.0\",\n  instructions:\n    '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.',\n});\n```\n\n### Sessions\n\nThe `session` object is an instance of `FastMCPSession` and it describes active client sessions.\n\n```ts\nserver.sessions;\n```\n\nWe allocate a new server instance for each client connection to enable 1:1 communication between a client and the server.\n\n### Typed server events\n\nYou can listen to events emitted by the server using the `on` method:\n\n```ts\nserver.on(\"connect\", (event) => {\n  console.log(\"Client connected:\", event.session);\n});\n\nserver.on(\"disconnect\", (event) => {\n  console.log(\"Client disconnected:\", event.session);\n});\n```\n\n## `FastMCPSession`\n\n`FastMCPSession` represents a client session and provides methods to interact with the client.\n\nRefer to [Sessions](#sessions) for examples of how to obtain a `FastMCPSession` instance.\n\n### `requestSampling`\n\n`requestSampling` creates a [sampling](https:\u002F\u002Fmodelcontextprotocol.io\u002Fdocs\u002Fconcepts\u002Fsampling) request and returns the response.\n\n```ts\nawait session.requestSampling({\n  messages: [\n    {\n      role: \"user\",\n      content: {\n        type: \"text\",\n        text: \"What files are in the current directory?\",\n      },\n    },\n  ],\n  systemPrompt: \"You are a helpful file system assistant.\",\n  includeContext: \"thisServer\",\n  maxTokens: 100,\n});\n```\n\n#### Options\n\n`requestSampling` accepts an optional second parameter for request options:\n\n```ts\nawait session.requestSampling(\n  {\n    messages: [\n      {\n        role: \"user\",\n        content: {\n          type: \"text\",\n          text: \"What files are in the current directory?\",\n        },\n      },\n    ],\n    systemPrompt: \"You are a helpful file system assistant.\",\n    includeContext: \"thisServer\",\n    maxTokens: 100,\n  },\n  {\n    \u002F\u002F Progress callback - called when progress notifications are received\n    onprogress: (progress) => {\n      console.log(`Progress: ${progress.progress}\u002F${progress.total}`);\n    },\n\n    \u002F\u002F Abort signal for cancelling the request\n    signal: abortController.signal,\n\n    \u002F\u002F Request timeout in milliseconds (default: DEFAULT_REQUEST_TIMEOUT_MSEC)\n    timeout: 30000,\n\n    \u002F\u002F Whether progress notifications reset the timeout (default: false)\n    resetTimeoutOnProgress: true,\n\n    \u002F\u002F Maximum total timeout regardless of progress (no default)\n    maxTotalTimeout: 60000,\n  },\n);\n```\n\n**Options:**\n\n- `onprogress?: (progress: Progress) => void` - Callback for progress notifications from the remote end\n- `signal?: AbortSignal` - Abort signal to cancel the request\n- `timeout?: number` - Request timeout in milliseconds\n- `resetTimeoutOnProgress?: boolean` - Whether progress notifications reset the timeout\n- `maxTotalTimeout?: number` - Maximum total timeout regardless of progress notifications\n\n### `clientCapabilities`\n\nThe `clientCapabilities` property contains the client capabilities.\n\n```ts\nsession.clientCapabilities;\n```\n\n### `loggingLevel`\n\nThe `loggingLevel` property describes the logging level as set by the client.\n\n```ts\nsession.loggingLevel;\n```\n\n### `roots`\n\nThe `roots` property contains the roots as set by the client.\n\n```ts\nsession.roots;\n```\n\n### `server`\n\nThe `server` property contains an instance of MCP server that is associated with the session.\n\n```ts\nsession.server;\n```\n\n### Typed session events\n\nYou can listen to events emitted by the session using the `on` method:\n\n```ts\nsession.on(\"rootsChanged\", (event) => {\n  console.log(\"Roots changed:\", event.roots);\n});\n\nsession.on(\"error\", (event) => {\n  console.error(\"Error:\", event.error);\n});\n```\n\n## Running Your Server\n\n### Test with `mcp-cli`\n\nThe fastest way to test and debug your server is with `fastmcp dev`:\n\n```bash\nnpx fastmcp dev server.js\nnpx fastmcp dev server.ts\n```\n\nThis will run your server with [`mcp-cli`](https:\u002F\u002Fgithub.com\u002Fwong2\u002Fmcp-cli) for testing and debugging your MCP server in the terminal.\n\n### Inspect with `MCP Inspector`\n\nAnother way is to use the official [`MCP Inspector`](https:\u002F\u002Fmodelcontextprotocol.io\u002Fdocs\u002Ftools\u002Finspector) to inspect your server with a Web UI:\n\n```bash\nnpx fastmcp inspect server.ts\n```\n\n## FAQ\n\n### How to use with Claude Desktop?\n\nFollow the guide https:\u002F\u002Fmodelcontextprotocol.io\u002Fquickstart\u002Fuser and add the following configuration:\n\n```json\n{\n  \"mcpServers\": {\n    \"my-mcp-server\": {\n      \"command\": \"npx\",\n      \"args\": [\"tsx\", \"\u002FPATH\u002FTO\u002FYOUR_PROJECT\u002Fsrc\u002Findex.ts\"],\n      \"env\": {\n        \"YOUR_ENV_VAR\": \"value\"\n      }\n    }\n  }\n}\n```\n\n### How to run FastMCP behind a proxy?\n\nRefer to this [issue](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp\u002Fissues\u002F25#issuecomment-3004568732) for an example of using FastMCP with `express` and `http-proxy-middleware`.\n\n## Showcase\n\n> [!NOTE]\n>\n> If you've developed a server using FastMCP, please [submit a PR](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp) to showcase it here!\n\n> [!NOTE]\n>\n> If you are looking for a boilerplate repository to build your own MCP server, check out [fastmcp-boilerplate](https:\u002F\u002Fgithub.com\u002Fpunkpeye\u002Ffastmcp-boilerplate).\n\n- [apinetwork\u002Fpiapi-mcp-server](https:\u002F\u002Fgithub.com\u002Fapinetwork\u002Fpiapi-mcp-server) - generate media using Midjourney\u002FFlux\u002FKling\u002FLumaLabs\u002FUdio\u002FChrip\u002FTrellis\n- [domdomegg\u002Fcomputer-use-mcp](https:\u002F\u002Fgithub.com\u002Fdomdomegg\u002Fcomputer-use-mcp) - controls your computer\n- [LiterallyBlah\u002FDradis-MCP](https:\u002F\u002Fgithub.com\u002FLiterallyBlah\u002FDradis-MCP) – manages projects and vulnerabilities in Dradis\n- [Meeting-Baas\u002Fmeeting-mcp](https:\u002F\u002Fgithub.com\u002FMeeting-Baas\u002Fmeeting-mcp) - create meeting bots, search transcripts, and manage recording data\n- [drumnation\u002Funsplash-smart-mcp-server](https:\u002F\u002Fgithub.com\u002Fdrumnation\u002Funsplash-smart-mcp-server) – enables AI agents to seamlessly search, recommend, and deliver professional stock photos from Unsplash\n- [ssmanji89\u002Fhalopsa-workflows-mcp](https:\u002F\u002Fgithub.com\u002Fssmanji89\u002Fhalopsa-workflows-mcp) - HaloPSA Workflows integration with AI assistants\n- [aiamblichus\u002Fmcp-chat-adapter](https:\u002F\u002Fgithub.com\u002Faiamblichus\u002Fmcp-chat-adapter) – provides a clean interface for LLMs to use chat completion\n- [eyaltoledano\u002Fclaude-task-master](https:\u002F\u002Fgithub.com\u002Feyaltoledano\u002Fclaude-task-master) – advanced AI project\u002Ftask manager powered by FastMCP\n- [cswkim\u002Fdiscogs-mcp-server](https:\u002F\u002Fgithub.com\u002Fcswkim\u002Fdiscogs-mcp-server) - connects to the Discogs API for interacting with your music collection\n- [Panzer-Jack\u002Ffeuse-mcp](https:\u002F\u002Fgithub.com\u002FPanzer-Jack\u002Ffeuse-mcp) - Frontend Useful MCP Tools - Essential utilities for web developers to automate API integration and code generation\n- [sunra-ai\u002Fsunra-clients](https:\u002F\u002Fgithub.com\u002Fsunra-ai\u002Fsunra-clients\u002Ftree\u002Fmain\u002Fmcp-server) - Sunra.ai is a generative media platform built for developers, providing high-performance AI model inference capabilities.\n- [foxtrottwist\u002Fshortcuts-mcp](https:\u002F\u002Fgithub.com\u002Ffoxtrottwist\u002Fshortcuts-mcp) - connects Claude to macOS Shortcuts for system automation, app integration, and interactive workflows\n\n## Acknowledgements\n\n- FastMCP is inspired by the [Python implementation](https:\u002F\u002Fgithub.com\u002Fjlowin\u002Ffastmcp) by [Jonathan Lowin](https:\u002F\u002Fgithub.com\u002Fjlowin).\n- Parts of codebase were adopted from [LiteMCP](https:\u002F\u002Fgithub.com\u002Fwong2\u002Flitemcp).\n- Parts of codebase were adopted from [Model Context protocolでSSEをやってみる](https:\u002F\u002Fdev.classmethod.jp\u002Farticles\u002Fmcp-sse\u002F).\n\nThis project is tested with BrowserStack.\n","FastMCP 是一个用于构建 MCP 服务器的 TypeScript 框架，能够处理客户端会话。它提供了一系列核心功能，包括工具、资源和提示的简单定义，身份验证，请求头传递，会话和请求 ID 跟踪，图像与音频内容支持，以及 HTTP 流式传输（兼容 SSE）等。此外，还支持 HTTPS 安全连接、自定义 HTTP 路由、边缘运行时环境下的部署及无状态模式等高级特性。FastMCP 适用于需要快速搭建并管理复杂多客户端协议交互场景的应用开发，尤其是在那些对安全性、实时性和扩展性有较高要求的情况下。",2,"2026-06-11 03:47:15","high_star"]