fastmcp 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,7 @@ A TypeScript framework for building [MCP](https://modelcontextprotocol.io/) serv
15
15
  - [SSE](#sse)
16
16
  - [Progress notifications](#progress)
17
17
  - [Typed events](#typed-events)
18
+ - Roots
18
19
  - CLI for [testing](#test-with-mcp-cli) and [debugging](#inspect-with-mcp-inspector)
19
20
 
20
21
  ## Installation
@@ -51,6 +52,8 @@ server.start({
51
52
  });
52
53
  ```
53
54
 
55
+ _That's it!_ You have a working MCP server.
56
+
54
57
  You can test the server in terminal with:
55
58
 
56
59
  ```bash
@@ -413,6 +416,44 @@ server.on("disconnect", (event) => {
413
416
  });
414
417
  ```
415
418
 
419
+ ## `FastMCPSession`
420
+
421
+ `FastMCPSession` represents a client session and provides methods to interact with the client.
422
+
423
+ Refer to [Sessions](#sessions) for examples of how to obtain a `FastMCPSession` instance.
424
+
425
+ ### `clientCapabilities`
426
+
427
+ The `clientCapabilities` property contains the client capabilities.
428
+
429
+ ```ts
430
+ session.clientCapabilities;
431
+ ```
432
+
433
+ ### `loggingLevel`
434
+
435
+ The `loggingLevel` property describes the logging level as set by the client.
436
+
437
+ ```ts
438
+ session.loggingLevel;
439
+ ```
440
+
441
+ ### `roots`
442
+
443
+ The `roots` property contains the roots as set by the client.
444
+
445
+ ```ts
446
+ session.roots;
447
+ ```
448
+
449
+ ### `server`
450
+
451
+ The `server` property contains an instance of MCP server that is associated with the session.
452
+
453
+ ```ts
454
+ session.server;
455
+ ```
456
+
416
457
  ## Running Your Server
417
458
 
418
459
  ### Test with `mcp-cli`
package/dist/FastMCP.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { ClientCapabilities, Root } from '@modelcontextprotocol/sdk/types.js';
2
3
  import { z } from 'zod';
3
4
  import { StrictEventEmitter } from 'strict-event-emitter-types';
4
5
  import { EventEmitter } from 'events';
@@ -124,8 +125,11 @@ declare class FastMCPSession {
124
125
  resources: Resource[];
125
126
  prompts: Prompt[];
126
127
  });
128
+ get clientCapabilities(): ClientCapabilities | null;
127
129
  get server(): Server;
128
- connect(transport: Transport): void;
130
+ connect(transport: Transport): Promise<void>;
131
+ get roots(): Root[];
132
+ close(): Promise<void>;
129
133
  private setupErrorHandling;
130
134
  get loggingLevel(): LoggingLevel;
131
135
  private setupLoggingHandlers;
package/dist/FastMCP.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  } from "@modelcontextprotocol/sdk/types.js";
15
15
  import { zodToJsonSchema } from "zod-to-json-schema";
16
16
  import { z } from "zod";
17
+ import { setTimeout as delay } from "timers/promises";
17
18
  import { readFile } from "fs/promises";
18
19
  import { fileTypeFromBuffer } from "file-type";
19
20
  import { EventEmitter } from "events";
@@ -89,6 +90,8 @@ var FastMCPSession = class {
89
90
  #capabilities = {};
90
91
  #loggingLevel = "info";
91
92
  #server;
93
+ #clientCapabilities;
94
+ #roots = [];
92
95
  constructor({
93
96
  name,
94
97
  version,
@@ -122,11 +125,39 @@ var FastMCPSession = class {
122
125
  this.setupPromptHandlers(prompts);
123
126
  }
124
127
  }
128
+ get clientCapabilities() {
129
+ return this.#clientCapabilities ?? null;
130
+ }
125
131
  get server() {
126
132
  return this.#server;
127
133
  }
128
- connect(transport) {
129
- this.#server.connect(transport);
134
+ async connect(transport) {
135
+ if (this.#server.transport) {
136
+ throw new UnexpectedStateError("Server is already connected");
137
+ }
138
+ await this.#server.connect(transport);
139
+ let attempt = 0;
140
+ while (attempt++ < 10) {
141
+ const capabilities = await this.#server.getClientCapabilities();
142
+ if (capabilities) {
143
+ this.#clientCapabilities = capabilities;
144
+ break;
145
+ }
146
+ await delay(100);
147
+ }
148
+ if (this.#clientCapabilities?.roots) {
149
+ const roots = await this.#server.listRoots();
150
+ this.#roots = roots.roots;
151
+ }
152
+ if (!this.#clientCapabilities) {
153
+ throw new UnexpectedStateError("Server did not connect");
154
+ }
155
+ }
156
+ get roots() {
157
+ return this.#roots;
158
+ }
159
+ async close() {
160
+ await this.#server.close();
130
161
  }
131
162
  setupErrorHandling() {
132
163
  this.#server.onerror = (error) => {
@@ -417,42 +448,24 @@ var FastMCP = class extends FastMCPEventEmitter {
417
448
  endpoint: options.sse.endpoint,
418
449
  port: options.sse.port,
419
450
  createServer: async () => {
420
- const session = new FastMCPSession({
451
+ return new FastMCPSession({
421
452
  name: this.#options.name,
422
453
  version: this.#options.version,
423
454
  tools: this.#tools,
424
455
  resources: this.#resources,
425
456
  prompts: this.#prompts
426
457
  });
427
- this.#sessions.push(session);
428
- return session.server;
429
458
  },
430
- onClose: (server) => {
431
- const session = this.#sessions.find(
432
- (session2) => session2.server === server
433
- );
434
- if (!session) {
435
- throw new UnexpectedStateError("Server not found");
436
- }
437
- this.#sessions = this.#sessions.filter(
438
- (maybeOurSession) => maybeOurSession !== session
439
- );
459
+ onClose: (session) => {
440
460
  this.emit("disconnect", {
441
461
  session
442
462
  });
443
463
  },
444
- onConnect: async (server) => {
445
- const session = this.#sessions.find(
446
- (session2) => session2.server === server
447
- );
448
- if (!session) {
449
- throw new UnexpectedStateError("Server not found");
450
- }
451
- setTimeout(() => {
452
- this.emit("connect", {
453
- session
454
- });
455
- }, 100);
464
+ onConnect: async (session) => {
465
+ this.#sessions.push(session);
466
+ this.emit("connect", {
467
+ session
468
+ });
456
469
  }
457
470
  });
458
471
  console.error(
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/FastMCP.ts"],"sourcesContent":["import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ErrorCode,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema,\n ServerCapabilities,\n SetLevelRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { zodToJsonSchema } from \"zod-to-json-schema\";\nimport { z } from \"zod\";\nimport { readFile } from \"fs/promises\";\nimport { fileTypeFromBuffer } from \"file-type\";\nimport { StrictEventEmitter } from \"strict-event-emitter-types\";\nimport { EventEmitter } from \"events\";\nimport { startSSEServer } from \"mcp-proxy\";\nimport { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\n\nexport type SSEServer = {\n close: () => Promise<void>;\n};\n\ntype FastMCPEvents = {\n connect: (event: { session: FastMCPSession }) => void;\n disconnect: (event: { session: FastMCPSession }) => void;\n};\n\n/**\n * Generates an image content object from a URL, file path, or buffer.\n */\nexport const imageContent = async (\n input: { url: string } | { path: string } | { buffer: Buffer },\n): Promise<ImageContent> => {\n let rawData: Buffer;\n\n if (\"url\" in input) {\n const response = await fetch(input.url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch image from URL: ${response.statusText}`);\n }\n\n rawData = Buffer.from(await response.arrayBuffer());\n } else if (\"path\" in input) {\n rawData = await readFile(input.path);\n } else if (\"buffer\" in input) {\n rawData = input.buffer;\n } else {\n throw new Error(\n \"Invalid input: Provide a valid 'url', 'path', or 'buffer'\",\n );\n }\n\n const mimeType = await fileTypeFromBuffer(rawData);\n\n const base64Data = rawData.toString(\"base64\");\n\n return {\n type: \"image\",\n data: base64Data,\n mimeType: mimeType?.mime ?? \"image/png\",\n } as const;\n};\n\nabstract class FastMCPError extends Error {\n public constructor(message?: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\ntype Extra = unknown;\n\ntype Extras = Record<string, Extra>;\n\nclass UnexpectedStateError extends FastMCPError {\n public extras?: Extras;\n\n public constructor(message: string, extras?: Extras) {\n super(message);\n this.name = new.target.name;\n this.extras = extras;\n }\n}\n\n/**\n * An error that is meant to be surfaced to the user.\n */\nexport class UserError extends UnexpectedStateError {}\n\ntype ToolParameters = z.ZodTypeAny;\n\ntype Literal = boolean | null | number | string | undefined;\n\ntype SerializableValue =\n | Literal\n | SerializableValue[]\n | { [key: string]: SerializableValue };\n\ntype Progress = {\n /**\n * The progress thus far. This should increase every time progress is made, even if the total is unknown.\n */\n progress: number;\n /**\n * Total number of items to process (or total progress required), if known.\n */\n total?: number;\n};\n\ntype Context = {\n reportProgress: (progress: Progress) => Promise<void>;\n log: {\n debug: (message: string, data?: SerializableValue) => void;\n error: (message: string, data?: SerializableValue) => void;\n info: (message: string, data?: SerializableValue) => void;\n warn: (message: string, data?: SerializableValue) => void;\n };\n};\n\ntype TextContent = {\n type: \"text\";\n text: string;\n};\n\nconst TextContentZodSchema = z\n .object({\n type: z.literal(\"text\"),\n /**\n * The text content of the message.\n */\n text: z.string(),\n })\n .strict() satisfies z.ZodType<TextContent>;\n\ntype ImageContent = {\n type: \"image\";\n data: string;\n mimeType: string;\n};\n\nconst ImageContentZodSchema = z\n .object({\n type: z.literal(\"image\"),\n /**\n * The base64-encoded image data.\n */\n data: z.string().base64(),\n /**\n * The MIME type of the image. Different providers may support different image types.\n */\n mimeType: z.string(),\n })\n .strict() satisfies z.ZodType<ImageContent>;\n\ntype Content = TextContent | ImageContent;\n\nconst ContentZodSchema = z.discriminatedUnion(\"type\", [\n TextContentZodSchema,\n ImageContentZodSchema,\n]) satisfies z.ZodType<Content>;\n\ntype ContentResult = {\n content: Content[];\n isError?: boolean;\n};\n\nconst ContentResultZodSchema = z\n .object({\n content: ContentZodSchema.array(),\n isError: z.boolean().optional(),\n })\n .strict() satisfies z.ZodType<ContentResult>;\n\ntype Tool<Params extends ToolParameters = ToolParameters> = {\n name: string;\n description?: string;\n parameters?: Params;\n execute: (\n args: z.infer<Params>,\n context: Context,\n ) => Promise<string | ContentResult | TextContent | ImageContent>;\n};\n\ntype Resource = {\n uri: string;\n name: string;\n description?: string;\n mimeType?: string;\n load: () => Promise<{ text: string } | { blob: string }>;\n};\n\ntype PromptArgument = Readonly<{\n name: string;\n description?: string;\n required?: boolean;\n}>;\n\ntype ArgumentsToObject<T extends PromptArgument[]> = {\n [K in T[number][\"name\"]]: Extract<\n T[number],\n { name: K }\n >[\"required\"] extends true\n ? string\n : string | undefined;\n};\n\ntype Prompt<\n Arguments extends PromptArgument[] = PromptArgument[],\n Args = ArgumentsToObject<Arguments>,\n> = {\n name: string;\n description?: string;\n arguments?: Arguments;\n load: (args: Args) => Promise<string>;\n};\n\ntype ServerOptions = {\n name: string;\n version: `${number}.${number}.${number}`;\n};\n\ntype LoggingLevel =\n | \"debug\"\n | \"info\"\n | \"notice\"\n | \"warning\"\n | \"error\"\n | \"critical\"\n | \"alert\"\n | \"emergency\";\n\nexport class FastMCPSession {\n #capabilities: ServerCapabilities = {};\n #loggingLevel: LoggingLevel = \"info\";\n #server: Server;\n\n constructor({\n name,\n version,\n tools,\n resources,\n prompts,\n }: {\n name: string;\n version: string;\n tools: Tool[];\n resources: Resource[];\n prompts: Prompt[];\n }) {\n if (tools.length) {\n this.#capabilities.tools = {};\n }\n\n if (resources.length) {\n this.#capabilities.resources = {};\n }\n\n if (prompts.length) {\n this.#capabilities.prompts = {};\n }\n\n this.#capabilities.logging = {};\n\n this.#server = new Server(\n { name: name, version: version },\n { capabilities: this.#capabilities },\n );\n\n this.setupErrorHandling();\n this.setupLoggingHandlers();\n\n if (tools.length) {\n this.setupToolHandlers(tools);\n }\n\n if (resources.length) {\n this.setupResourceHandlers(resources);\n }\n\n if (prompts.length) {\n this.setupPromptHandlers(prompts);\n }\n }\n\n public get server(): Server {\n return this.#server;\n }\n\n public connect(transport: Transport) {\n this.#server.connect(transport);\n }\n\n private setupErrorHandling() {\n this.#server.onerror = (error) => {\n console.error(\"[MCP Error]\", error);\n };\n }\n\n public get loggingLevel(): LoggingLevel {\n return this.#loggingLevel;\n }\n\n private setupLoggingHandlers() {\n this.#server.setRequestHandler(SetLevelRequestSchema, (request) => {\n this.#loggingLevel = request.params.level;\n\n return {};\n });\n }\n\n private setupToolHandlers(tools: Tool[]) {\n this.#server.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: tools.map((tool) => {\n return {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.parameters\n ? zodToJsonSchema(tool.parameters)\n : undefined,\n };\n }),\n };\n });\n\n this.#server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const tool = tools.find((tool) => tool.name === request.params.name);\n\n if (!tool) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n\n let args: any = undefined;\n\n if (tool.parameters) {\n const parsed = tool.parameters.safeParse(request.params.arguments);\n\n if (!parsed.success) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Invalid ${request.params.name} arguments`,\n );\n }\n\n args = parsed.data;\n }\n\n const progressToken = request.params?._meta?.progressToken;\n\n let result: ContentResult;\n\n try {\n const reportProgress = async (progress: Progress) => {\n await this.#server.notification({\n method: \"notifications/progress\",\n params: {\n ...progress,\n progressToken,\n },\n });\n };\n\n const log = {\n debug: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"debug\",\n data: {\n message,\n context,\n },\n });\n },\n error: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"error\",\n data: {\n message,\n context,\n },\n });\n },\n info: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"info\",\n data: {\n message,\n context,\n },\n });\n },\n warn: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"warning\",\n data: {\n message,\n context,\n },\n });\n },\n };\n\n const maybeStringResult = await tool.execute(args, {\n reportProgress,\n log,\n });\n\n if (typeof maybeStringResult === \"string\") {\n result = ContentResultZodSchema.parse({\n content: [{ type: \"text\", text: maybeStringResult }],\n });\n } else if (\"type\" in maybeStringResult) {\n result = ContentResultZodSchema.parse({\n content: [maybeStringResult],\n });\n } else {\n result = ContentResultZodSchema.parse(maybeStringResult);\n }\n } catch (error) {\n if (error instanceof UserError) {\n return {\n content: [{ type: \"text\", text: error.message }],\n isError: true,\n };\n }\n\n return {\n content: [{ type: \"text\", text: `Error: ${error}` }],\n isError: true,\n };\n }\n\n return result;\n });\n }\n\n private setupResourceHandlers(resources: Resource[]) {\n this.#server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n resources: resources.map((resource) => {\n return {\n uri: resource.uri,\n name: resource.name,\n mimeType: resource.mimeType,\n };\n }),\n };\n });\n\n this.#server.setRequestHandler(\n ReadResourceRequestSchema,\n async (request) => {\n const resource = resources.find(\n (resource) => resource.uri === request.params.uri,\n );\n\n if (!resource) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown resource: ${request.params.uri}`,\n );\n }\n\n let result: Awaited<ReturnType<Resource[\"load\"]>>;\n\n try {\n result = await resource.load();\n } catch (error) {\n throw new McpError(\n ErrorCode.InternalError,\n `Error reading resource: ${error}`,\n {\n uri: resource.uri,\n },\n );\n }\n\n return {\n contents: [\n {\n uri: resource.uri,\n mimeType: resource.mimeType,\n ...result,\n },\n ],\n };\n },\n );\n }\n\n private setupPromptHandlers(prompts: Prompt[]) {\n this.#server.setRequestHandler(ListPromptsRequestSchema, async () => {\n return {\n prompts: prompts.map((prompt) => {\n return {\n name: prompt.name,\n description: prompt.description,\n arguments: prompt.arguments,\n };\n }),\n };\n });\n\n this.#server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const prompt = prompts.find(\n (prompt) => prompt.name === request.params.name,\n );\n\n if (!prompt) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown prompt: ${request.params.name}`,\n );\n }\n\n const args = request.params.arguments;\n\n if (prompt.arguments) {\n for (const arg of prompt.arguments) {\n if (arg.required && !(args && arg.name in args)) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Missing required argument: ${arg.name}`,\n );\n }\n }\n }\n\n let result: Awaited<ReturnType<Prompt[\"load\"]>>;\n\n try {\n result = await prompt.load(args as Record<string, string | undefined>);\n } catch (error) {\n throw new McpError(\n ErrorCode.InternalError,\n `Error loading prompt: ${error}`,\n );\n }\n\n return {\n description: prompt.description,\n messages: [\n {\n role: \"user\",\n content: { type: \"text\", text: result },\n },\n ],\n };\n });\n }\n}\n\nconst FastMCPEventEmitterBase: {\n new (): StrictEventEmitter<EventEmitter, FastMCPEvents>;\n} = EventEmitter;\n\nclass FastMCPEventEmitter extends FastMCPEventEmitterBase {}\n\nexport class FastMCP extends FastMCPEventEmitter {\n #options: ServerOptions;\n #prompts: Prompt[] = [];\n #resources: Resource[] = [];\n #sessions: FastMCPSession[] = [];\n #sseServer: SSEServer | null = null;\n #tools: Tool[] = [];\n\n constructor(public options: ServerOptions) {\n super();\n\n this.#options = options;\n }\n\n public get sessions(): FastMCPSession[] {\n return this.#sessions;\n }\n\n /**\n * Adds a tool to the server.\n */\n public addTool<Params extends ToolParameters>(tool: Tool<Params>) {\n this.#tools.push(tool as unknown as Tool);\n }\n\n /**\n * Adds a resource to the server.\n */\n public addResource(resource: Resource) {\n this.#resources.push(resource);\n }\n\n /**\n * Adds a prompt to the server.\n */\n public addPrompt<const Args extends PromptArgument[]>(prompt: Prompt<Args>) {\n this.#prompts.push(prompt);\n }\n\n /**\n * Starts the server.\n */\n public async start(\n options:\n | { transportType: \"stdio\" }\n | {\n transportType: \"sse\";\n sse: { endpoint: `/${string}`; port: number };\n } = {\n transportType: \"stdio\",\n },\n ) {\n if (options.transportType === \"stdio\") {\n const transport = new StdioServerTransport();\n\n const session = new FastMCPSession({\n name: this.#options.name,\n version: this.#options.version,\n tools: this.#tools,\n resources: this.#resources,\n prompts: this.#prompts,\n });\n\n await session.connect(transport);\n\n this.#sessions.push(session);\n\n this.emit(\"connect\", {\n session,\n });\n\n console.error(`server is running on stdio`);\n } else if (options.transportType === \"sse\") {\n this.#sseServer = await startSSEServer({\n endpoint: options.sse.endpoint as `/${string}`,\n port: options.sse.port,\n createServer: async () => {\n const session = new FastMCPSession({\n name: this.#options.name,\n version: this.#options.version,\n tools: this.#tools,\n resources: this.#resources,\n prompts: this.#prompts,\n });\n\n this.#sessions.push(session);\n\n return session.server;\n },\n onClose: (server) => {\n const session = this.#sessions.find(\n (session) => session.server === server,\n );\n\n if (!session) {\n throw new UnexpectedStateError(\"Server not found\");\n }\n\n this.#sessions = this.#sessions.filter(\n (maybeOurSession) => maybeOurSession !== session,\n );\n\n this.emit(\"disconnect\", {\n session,\n });\n },\n onConnect: async (server) => {\n const session = this.#sessions.find(\n (session) => session.server === server,\n );\n\n if (!session) {\n throw new UnexpectedStateError(\"Server not found\");\n }\n\n // TODO investigate where is the race condition\n setTimeout(() => {\n this.emit(\"connect\", {\n session,\n });\n }, 100);\n },\n });\n\n console.error(\n `server is running on SSE at http://localhost:${options.sse.port}${options.sse.endpoint}`,\n );\n } else {\n throw new Error(\"Invalid transport type\");\n }\n }\n\n /**\n * Stops the server.\n */\n public async stop() {\n if (this.#sseServer) {\n this.#sseServer.close();\n }\n }\n}\n"],"mappings":"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","names":["tool","resource","prompt","session"]}
1
+ {"version":3,"sources":["../src/FastMCP.ts"],"sourcesContent":["import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ClientCapabilities,\n ErrorCode,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema,\n Root,\n ServerCapabilities,\n SetLevelRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { zodToJsonSchema } from \"zod-to-json-schema\";\nimport { z } from \"zod\";\nimport { setTimeout as delay } from \"timers/promises\";\nimport { readFile } from \"fs/promises\";\nimport { fileTypeFromBuffer } from \"file-type\";\nimport { StrictEventEmitter } from \"strict-event-emitter-types\";\nimport { EventEmitter } from \"events\";\nimport { startSSEServer } from \"mcp-proxy\";\nimport { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\n\nexport type SSEServer = {\n close: () => Promise<void>;\n};\n\ntype FastMCPEvents = {\n connect: (event: { session: FastMCPSession }) => void;\n disconnect: (event: { session: FastMCPSession }) => void;\n};\n\n/**\n * Generates an image content object from a URL, file path, or buffer.\n */\nexport const imageContent = async (\n input: { url: string } | { path: string } | { buffer: Buffer },\n): Promise<ImageContent> => {\n let rawData: Buffer;\n\n if (\"url\" in input) {\n const response = await fetch(input.url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch image from URL: ${response.statusText}`);\n }\n\n rawData = Buffer.from(await response.arrayBuffer());\n } else if (\"path\" in input) {\n rawData = await readFile(input.path);\n } else if (\"buffer\" in input) {\n rawData = input.buffer;\n } else {\n throw new Error(\n \"Invalid input: Provide a valid 'url', 'path', or 'buffer'\",\n );\n }\n\n const mimeType = await fileTypeFromBuffer(rawData);\n\n const base64Data = rawData.toString(\"base64\");\n\n return {\n type: \"image\",\n data: base64Data,\n mimeType: mimeType?.mime ?? \"image/png\",\n } as const;\n};\n\nabstract class FastMCPError extends Error {\n public constructor(message?: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\ntype Extra = unknown;\n\ntype Extras = Record<string, Extra>;\n\nclass UnexpectedStateError extends FastMCPError {\n public extras?: Extras;\n\n public constructor(message: string, extras?: Extras) {\n super(message);\n this.name = new.target.name;\n this.extras = extras;\n }\n}\n\n/**\n * An error that is meant to be surfaced to the user.\n */\nexport class UserError extends UnexpectedStateError {}\n\ntype ToolParameters = z.ZodTypeAny;\n\ntype Literal = boolean | null | number | string | undefined;\n\ntype SerializableValue =\n | Literal\n | SerializableValue[]\n | { [key: string]: SerializableValue };\n\ntype Progress = {\n /**\n * The progress thus far. This should increase every time progress is made, even if the total is unknown.\n */\n progress: number;\n /**\n * Total number of items to process (or total progress required), if known.\n */\n total?: number;\n};\n\ntype Context = {\n reportProgress: (progress: Progress) => Promise<void>;\n log: {\n debug: (message: string, data?: SerializableValue) => void;\n error: (message: string, data?: SerializableValue) => void;\n info: (message: string, data?: SerializableValue) => void;\n warn: (message: string, data?: SerializableValue) => void;\n };\n};\n\ntype TextContent = {\n type: \"text\";\n text: string;\n};\n\nconst TextContentZodSchema = z\n .object({\n type: z.literal(\"text\"),\n /**\n * The text content of the message.\n */\n text: z.string(),\n })\n .strict() satisfies z.ZodType<TextContent>;\n\ntype ImageContent = {\n type: \"image\";\n data: string;\n mimeType: string;\n};\n\nconst ImageContentZodSchema = z\n .object({\n type: z.literal(\"image\"),\n /**\n * The base64-encoded image data.\n */\n data: z.string().base64(),\n /**\n * The MIME type of the image. Different providers may support different image types.\n */\n mimeType: z.string(),\n })\n .strict() satisfies z.ZodType<ImageContent>;\n\ntype Content = TextContent | ImageContent;\n\nconst ContentZodSchema = z.discriminatedUnion(\"type\", [\n TextContentZodSchema,\n ImageContentZodSchema,\n]) satisfies z.ZodType<Content>;\n\ntype ContentResult = {\n content: Content[];\n isError?: boolean;\n};\n\nconst ContentResultZodSchema = z\n .object({\n content: ContentZodSchema.array(),\n isError: z.boolean().optional(),\n })\n .strict() satisfies z.ZodType<ContentResult>;\n\ntype Tool<Params extends ToolParameters = ToolParameters> = {\n name: string;\n description?: string;\n parameters?: Params;\n execute: (\n args: z.infer<Params>,\n context: Context,\n ) => Promise<string | ContentResult | TextContent | ImageContent>;\n};\n\ntype Resource = {\n uri: string;\n name: string;\n description?: string;\n mimeType?: string;\n load: () => Promise<{ text: string } | { blob: string }>;\n};\n\ntype PromptArgument = Readonly<{\n name: string;\n description?: string;\n required?: boolean;\n}>;\n\ntype ArgumentsToObject<T extends PromptArgument[]> = {\n [K in T[number][\"name\"]]: Extract<\n T[number],\n { name: K }\n >[\"required\"] extends true\n ? string\n : string | undefined;\n};\n\ntype Prompt<\n Arguments extends PromptArgument[] = PromptArgument[],\n Args = ArgumentsToObject<Arguments>,\n> = {\n name: string;\n description?: string;\n arguments?: Arguments;\n load: (args: Args) => Promise<string>;\n};\n\ntype ServerOptions = {\n name: string;\n version: `${number}.${number}.${number}`;\n};\n\ntype LoggingLevel =\n | \"debug\"\n | \"info\"\n | \"notice\"\n | \"warning\"\n | \"error\"\n | \"critical\"\n | \"alert\"\n | \"emergency\";\n\nexport class FastMCPSession {\n #capabilities: ServerCapabilities = {};\n #loggingLevel: LoggingLevel = \"info\";\n #server: Server;\n #clientCapabilities?: ClientCapabilities;\n #roots: Root[] = [];\n\n constructor({\n name,\n version,\n tools,\n resources,\n prompts,\n }: {\n name: string;\n version: string;\n tools: Tool[];\n resources: Resource[];\n prompts: Prompt[];\n }) {\n if (tools.length) {\n this.#capabilities.tools = {};\n }\n\n if (resources.length) {\n this.#capabilities.resources = {};\n }\n\n if (prompts.length) {\n this.#capabilities.prompts = {};\n }\n\n this.#capabilities.logging = {};\n\n this.#server = new Server(\n { name: name, version: version },\n { capabilities: this.#capabilities },\n );\n\n this.setupErrorHandling();\n this.setupLoggingHandlers();\n\n if (tools.length) {\n this.setupToolHandlers(tools);\n }\n\n if (resources.length) {\n this.setupResourceHandlers(resources);\n }\n\n if (prompts.length) {\n this.setupPromptHandlers(prompts);\n }\n }\n\n public get clientCapabilities(): ClientCapabilities | null {\n return this.#clientCapabilities ?? null;\n }\n\n public get server(): Server {\n return this.#server;\n }\n\n public async connect(transport: Transport) {\n if (this.#server.transport) {\n throw new UnexpectedStateError(\"Server is already connected\");\n }\n\n await this.#server.connect(transport);\n\n let attempt = 0;\n\n while (attempt++ < 10) {\n const capabilities = await this.#server.getClientCapabilities();\n\n if (capabilities) {\n this.#clientCapabilities = capabilities;\n\n break;\n }\n\n await delay(100);\n }\n\n if (this.#clientCapabilities?.roots) {\n const roots = await this.#server.listRoots();\n\n this.#roots = roots.roots;\n }\n\n if (!this.#clientCapabilities) {\n throw new UnexpectedStateError(\"Server did not connect\");\n }\n }\n\n public get roots(): Root[] {\n return this.#roots;\n }\n\n public async close() {\n await this.#server.close();\n }\n\n private setupErrorHandling() {\n this.#server.onerror = (error) => {\n console.error(\"[MCP Error]\", error);\n };\n }\n\n public get loggingLevel(): LoggingLevel {\n return this.#loggingLevel;\n }\n\n private setupLoggingHandlers() {\n this.#server.setRequestHandler(SetLevelRequestSchema, (request) => {\n this.#loggingLevel = request.params.level;\n\n return {};\n });\n }\n\n private setupToolHandlers(tools: Tool[]) {\n this.#server.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: tools.map((tool) => {\n return {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.parameters\n ? zodToJsonSchema(tool.parameters)\n : undefined,\n };\n }),\n };\n });\n\n this.#server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const tool = tools.find((tool) => tool.name === request.params.name);\n\n if (!tool) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n\n let args: any = undefined;\n\n if (tool.parameters) {\n const parsed = tool.parameters.safeParse(request.params.arguments);\n\n if (!parsed.success) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Invalid ${request.params.name} arguments`,\n );\n }\n\n args = parsed.data;\n }\n\n const progressToken = request.params?._meta?.progressToken;\n\n let result: ContentResult;\n\n try {\n const reportProgress = async (progress: Progress) => {\n await this.#server.notification({\n method: \"notifications/progress\",\n params: {\n ...progress,\n progressToken,\n },\n });\n };\n\n const log = {\n debug: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"debug\",\n data: {\n message,\n context,\n },\n });\n },\n error: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"error\",\n data: {\n message,\n context,\n },\n });\n },\n info: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"info\",\n data: {\n message,\n context,\n },\n });\n },\n warn: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"warning\",\n data: {\n message,\n context,\n },\n });\n },\n };\n\n const maybeStringResult = await tool.execute(args, {\n reportProgress,\n log,\n });\n\n if (typeof maybeStringResult === \"string\") {\n result = ContentResultZodSchema.parse({\n content: [{ type: \"text\", text: maybeStringResult }],\n });\n } else if (\"type\" in maybeStringResult) {\n result = ContentResultZodSchema.parse({\n content: [maybeStringResult],\n });\n } else {\n result = ContentResultZodSchema.parse(maybeStringResult);\n }\n } catch (error) {\n if (error instanceof UserError) {\n return {\n content: [{ type: \"text\", text: error.message }],\n isError: true,\n };\n }\n\n return {\n content: [{ type: \"text\", text: `Error: ${error}` }],\n isError: true,\n };\n }\n\n return result;\n });\n }\n\n private setupResourceHandlers(resources: Resource[]) {\n this.#server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n resources: resources.map((resource) => {\n return {\n uri: resource.uri,\n name: resource.name,\n mimeType: resource.mimeType,\n };\n }),\n };\n });\n\n this.#server.setRequestHandler(\n ReadResourceRequestSchema,\n async (request) => {\n const resource = resources.find(\n (resource) => resource.uri === request.params.uri,\n );\n\n if (!resource) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown resource: ${request.params.uri}`,\n );\n }\n\n let result: Awaited<ReturnType<Resource[\"load\"]>>;\n\n try {\n result = await resource.load();\n } catch (error) {\n throw new McpError(\n ErrorCode.InternalError,\n `Error reading resource: ${error}`,\n {\n uri: resource.uri,\n },\n );\n }\n\n return {\n contents: [\n {\n uri: resource.uri,\n mimeType: resource.mimeType,\n ...result,\n },\n ],\n };\n },\n );\n }\n\n private setupPromptHandlers(prompts: Prompt[]) {\n this.#server.setRequestHandler(ListPromptsRequestSchema, async () => {\n return {\n prompts: prompts.map((prompt) => {\n return {\n name: prompt.name,\n description: prompt.description,\n arguments: prompt.arguments,\n };\n }),\n };\n });\n\n this.#server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const prompt = prompts.find(\n (prompt) => prompt.name === request.params.name,\n );\n\n if (!prompt) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown prompt: ${request.params.name}`,\n );\n }\n\n const args = request.params.arguments;\n\n if (prompt.arguments) {\n for (const arg of prompt.arguments) {\n if (arg.required && !(args && arg.name in args)) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Missing required argument: ${arg.name}`,\n );\n }\n }\n }\n\n let result: Awaited<ReturnType<Prompt[\"load\"]>>;\n\n try {\n result = await prompt.load(args as Record<string, string | undefined>);\n } catch (error) {\n throw new McpError(\n ErrorCode.InternalError,\n `Error loading prompt: ${error}`,\n );\n }\n\n return {\n description: prompt.description,\n messages: [\n {\n role: \"user\",\n content: { type: \"text\", text: result },\n },\n ],\n };\n });\n }\n}\n\nconst FastMCPEventEmitterBase: {\n new (): StrictEventEmitter<EventEmitter, FastMCPEvents>;\n} = EventEmitter;\n\nclass FastMCPEventEmitter extends FastMCPEventEmitterBase {}\n\nexport class FastMCP extends FastMCPEventEmitter {\n #options: ServerOptions;\n #prompts: Prompt[] = [];\n #resources: Resource[] = [];\n #sessions: FastMCPSession[] = [];\n #sseServer: SSEServer | null = null;\n #tools: Tool[] = [];\n\n constructor(public options: ServerOptions) {\n super();\n\n this.#options = options;\n }\n\n public get sessions(): FastMCPSession[] {\n return this.#sessions;\n }\n\n /**\n * Adds a tool to the server.\n */\n public addTool<Params extends ToolParameters>(tool: Tool<Params>) {\n this.#tools.push(tool as unknown as Tool);\n }\n\n /**\n * Adds a resource to the server.\n */\n public addResource(resource: Resource) {\n this.#resources.push(resource);\n }\n\n /**\n * Adds a prompt to the server.\n */\n public addPrompt<const Args extends PromptArgument[]>(prompt: Prompt<Args>) {\n this.#prompts.push(prompt);\n }\n\n /**\n * Starts the server.\n */\n public async start(\n options:\n | { transportType: \"stdio\" }\n | {\n transportType: \"sse\";\n sse: { endpoint: `/${string}`; port: number };\n } = {\n transportType: \"stdio\",\n },\n ) {\n if (options.transportType === \"stdio\") {\n const transport = new StdioServerTransport();\n\n const session = new FastMCPSession({\n name: this.#options.name,\n version: this.#options.version,\n tools: this.#tools,\n resources: this.#resources,\n prompts: this.#prompts,\n });\n\n await session.connect(transport);\n\n this.#sessions.push(session);\n\n this.emit(\"connect\", {\n session,\n });\n\n console.error(`server is running on stdio`);\n } else if (options.transportType === \"sse\") {\n this.#sseServer = await startSSEServer<FastMCPSession>({\n endpoint: options.sse.endpoint as `/${string}`,\n port: options.sse.port,\n createServer: async () => {\n return new FastMCPSession({\n name: this.#options.name,\n version: this.#options.version,\n tools: this.#tools,\n resources: this.#resources,\n prompts: this.#prompts,\n });\n },\n onClose: (session) => {\n this.emit(\"disconnect\", {\n session,\n });\n },\n onConnect: async (session) => {\n this.#sessions.push(session);\n\n this.emit(\"connect\", {\n session,\n });\n },\n });\n\n console.error(\n `server is running on SSE at http://localhost:${options.sse.port}${options.sse.endpoint}`,\n );\n } else {\n throw new Error(\"Invalid transport type\");\n }\n }\n\n /**\n * Stops the server.\n */\n public async stop() {\n if (this.#sseServer) {\n this.#sseServer.close();\n }\n }\n}\n"],"mappings":";AAAA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,OACK;AACP,SAAS,uBAAuB;AAChC,SAAS,SAAS;AAClB,SAAS,cAAc,aAAa;AACpC,SAAS,gBAAgB;AACzB,SAAS,0BAA0B;AAEnC,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAexB,IAAM,eAAe,OAC1B,UAC0B;AAC1B,MAAI;AAEJ,MAAI,SAAS,OAAO;AAClB,UAAM,WAAW,MAAM,MAAM,MAAM,GAAG;AAEtC,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,mCAAmC,SAAS,UAAU,EAAE;AAAA,IAC1E;AAEA,cAAU,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC;AAAA,EACpD,WAAW,UAAU,OAAO;AAC1B,cAAU,MAAM,SAAS,MAAM,IAAI;AAAA,EACrC,WAAW,YAAY,OAAO;AAC5B,cAAU,MAAM;AAAA,EAClB,OAAO;AACL,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,mBAAmB,OAAO;AAEjD,QAAM,aAAa,QAAQ,SAAS,QAAQ;AAE5C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU,UAAU,QAAQ;AAAA,EAC9B;AACF;AAEA,IAAe,eAAf,cAAoC,MAAM;AAAA,EACjC,YAAY,SAAkB;AACnC,UAAM,OAAO;AACb,SAAK,OAAO,WAAW;AAAA,EACzB;AACF;AAMA,IAAM,uBAAN,cAAmC,aAAa;AAAA,EACvC;AAAA,EAEA,YAAY,SAAiB,QAAiB;AACnD,UAAM,OAAO;AACb,SAAK,OAAO,WAAW;AACvB,SAAK,SAAS;AAAA,EAChB;AACF;AAKO,IAAM,YAAN,cAAwB,qBAAqB;AAAC;AAqCrD,IAAM,uBAAuB,EAC1B,OAAO;AAAA,EACN,MAAM,EAAE,QAAQ,MAAM;AAAA;AAAA;AAAA;AAAA,EAItB,MAAM,EAAE,OAAO;AACjB,CAAC,EACA,OAAO;AAQV,IAAM,wBAAwB,EAC3B,OAAO;AAAA,EACN,MAAM,EAAE,QAAQ,OAAO;AAAA;AAAA;AAAA;AAAA,EAIvB,MAAM,EAAE,OAAO,EAAE,OAAO;AAAA;AAAA;AAAA;AAAA,EAIxB,UAAU,EAAE,OAAO;AACrB,CAAC,EACA,OAAO;AAIV,IAAM,mBAAmB,EAAE,mBAAmB,QAAQ;AAAA,EACpD;AAAA,EACA;AACF,CAAC;AAOD,IAAM,yBAAyB,EAC5B,OAAO;AAAA,EACN,SAAS,iBAAiB,MAAM;AAAA,EAChC,SAAS,EAAE,QAAQ,EAAE,SAAS;AAChC,CAAC,EACA,OAAO;AA4DH,IAAM,iBAAN,MAAqB;AAAA,EAC1B,gBAAoC,CAAC;AAAA,EACrC,gBAA8B;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,SAAiB,CAAC;AAAA,EAElB,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAMG;AACD,QAAI,MAAM,QAAQ;AAChB,WAAK,cAAc,QAAQ,CAAC;AAAA,IAC9B;AAEA,QAAI,UAAU,QAAQ;AACpB,WAAK,cAAc,YAAY,CAAC;AAAA,IAClC;AAEA,QAAI,QAAQ,QAAQ;AAClB,WAAK,cAAc,UAAU,CAAC;AAAA,IAChC;AAEA,SAAK,cAAc,UAAU,CAAC;AAE9B,SAAK,UAAU,IAAI;AAAA,MACjB,EAAE,MAAY,QAAiB;AAAA,MAC/B,EAAE,cAAc,KAAK,cAAc;AAAA,IACrC;AAEA,SAAK,mBAAmB;AACxB,SAAK,qBAAqB;AAE1B,QAAI,MAAM,QAAQ;AAChB,WAAK,kBAAkB,KAAK;AAAA,IAC9B;AAEA,QAAI,UAAU,QAAQ;AACpB,WAAK,sBAAsB,SAAS;AAAA,IACtC;AAEA,QAAI,QAAQ,QAAQ;AAClB,WAAK,oBAAoB,OAAO;AAAA,IAClC;AAAA,EACF;AAAA,EAEA,IAAW,qBAAgD;AACzD,WAAO,KAAK,uBAAuB;AAAA,EACrC;AAAA,EAEA,IAAW,SAAiB;AAC1B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAa,QAAQ,WAAsB;AACzC,QAAI,KAAK,QAAQ,WAAW;AAC1B,YAAM,IAAI,qBAAqB,6BAA6B;AAAA,IAC9D;AAEA,UAAM,KAAK,QAAQ,QAAQ,SAAS;AAEpC,QAAI,UAAU;AAEd,WAAO,YAAY,IAAI;AACrB,YAAM,eAAe,MAAM,KAAK,QAAQ,sBAAsB;AAE9D,UAAI,cAAc;AAChB,aAAK,sBAAsB;AAE3B;AAAA,MACF;AAEA,YAAM,MAAM,GAAG;AAAA,IACjB;AAEA,QAAI,KAAK,qBAAqB,OAAO;AACnC,YAAM,QAAQ,MAAM,KAAK,QAAQ,UAAU;AAE3C,WAAK,SAAS,MAAM;AAAA,IACtB;AAEA,QAAI,CAAC,KAAK,qBAAqB;AAC7B,YAAM,IAAI,qBAAqB,wBAAwB;AAAA,IACzD;AAAA,EACF;AAAA,EAEA,IAAW,QAAgB;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAa,QAAQ;AACnB,UAAM,KAAK,QAAQ,MAAM;AAAA,EAC3B;AAAA,EAEQ,qBAAqB;AAC3B,SAAK,QAAQ,UAAU,CAAC,UAAU;AAChC,cAAQ,MAAM,eAAe,KAAK;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,IAAW,eAA6B;AACtC,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,uBAAuB;AAC7B,SAAK,QAAQ,kBAAkB,uBAAuB,CAAC,YAAY;AACjE,WAAK,gBAAgB,QAAQ,OAAO;AAEpC,aAAO,CAAC;AAAA,IACV,CAAC;AAAA,EACH;AAAA,EAEQ,kBAAkB,OAAe;AACvC,SAAK,QAAQ,kBAAkB,wBAAwB,YAAY;AACjE,aAAO;AAAA,QACL,OAAO,MAAM,IAAI,CAAC,SAAS;AACzB,iBAAO;AAAA,YACL,MAAM,KAAK;AAAA,YACX,aAAa,KAAK;AAAA,YAClB,aAAa,KAAK,aACd,gBAAgB,KAAK,UAAU,IAC/B;AAAA,UACN;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAED,SAAK,QAAQ,kBAAkB,uBAAuB,OAAO,YAAY;AACvE,YAAM,OAAO,MAAM,KAAK,CAACA,UAASA,MAAK,SAAS,QAAQ,OAAO,IAAI;AAEnE,UAAI,CAAC,MAAM;AACT,cAAM,IAAI;AAAA,UACR,UAAU;AAAA,UACV,iBAAiB,QAAQ,OAAO,IAAI;AAAA,QACtC;AAAA,MACF;AAEA,UAAI,OAAY;AAEhB,UAAI,KAAK,YAAY;AACnB,cAAM,SAAS,KAAK,WAAW,UAAU,QAAQ,OAAO,SAAS;AAEjE,YAAI,CAAC,OAAO,SAAS;AACnB,gBAAM,IAAI;AAAA,YACR,UAAU;AAAA,YACV,WAAW,QAAQ,OAAO,IAAI;AAAA,UAChC;AAAA,QACF;AAEA,eAAO,OAAO;AAAA,MAChB;AAEA,YAAM,gBAAgB,QAAQ,QAAQ,OAAO;AAE7C,UAAI;AAEJ,UAAI;AACF,cAAM,iBAAiB,OAAO,aAAuB;AACnD,gBAAM,KAAK,QAAQ,aAAa;AAAA,YAC9B,QAAQ;AAAA,YACR,QAAQ;AAAA,cACN,GAAG;AAAA,cACH;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AAEA,cAAM,MAAM;AAAA,UACV,OAAO,CAAC,SAAiB,YAAgC;AACvD,iBAAK,QAAQ,mBAAmB;AAAA,cAC9B,OAAO;AAAA,cACP,MAAM;AAAA,gBACJ;AAAA,gBACA;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH;AAAA,UACA,OAAO,CAAC,SAAiB,YAAgC;AACvD,iBAAK,QAAQ,mBAAmB;AAAA,cAC9B,OAAO;AAAA,cACP,MAAM;AAAA,gBACJ;AAAA,gBACA;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH;AAAA,UACA,MAAM,CAAC,SAAiB,YAAgC;AACtD,iBAAK,QAAQ,mBAAmB;AAAA,cAC9B,OAAO;AAAA,cACP,MAAM;AAAA,gBACJ;AAAA,gBACA;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH;AAAA,UACA,MAAM,CAAC,SAAiB,YAAgC;AACtD,iBAAK,QAAQ,mBAAmB;AAAA,cAC9B,OAAO;AAAA,cACP,MAAM;AAAA,gBACJ;AAAA,gBACA;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAEA,cAAM,oBAAoB,MAAM,KAAK,QAAQ,MAAM;AAAA,UACjD;AAAA,UACA;AAAA,QACF,CAAC;AAED,YAAI,OAAO,sBAAsB,UAAU;AACzC,mBAAS,uBAAuB,MAAM;AAAA,YACpC,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kBAAkB,CAAC;AAAA,UACrD,CAAC;AAAA,QACH,WAAW,UAAU,mBAAmB;AACtC,mBAAS,uBAAuB,MAAM;AAAA,YACpC,SAAS,CAAC,iBAAiB;AAAA,UAC7B,CAAC;AAAA,QACH,OAAO;AACL,mBAAS,uBAAuB,MAAM,iBAAiB;AAAA,QACzD;AAAA,MACF,SAAS,OAAO;AACd,YAAI,iBAAiB,WAAW;AAC9B,iBAAO;AAAA,YACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,MAAM,QAAQ,CAAC;AAAA,YAC/C,SAAS;AAAA,UACX;AAAA,QACF;AAEA,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,UAAU,KAAK,GAAG,CAAC;AAAA,UACnD,SAAS;AAAA,QACX;AAAA,MACF;AAEA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEQ,sBAAsB,WAAuB;AACnD,SAAK,QAAQ,kBAAkB,4BAA4B,YAAY;AACrE,aAAO;AAAA,QACL,WAAW,UAAU,IAAI,CAAC,aAAa;AACrC,iBAAO;AAAA,YACL,KAAK,SAAS;AAAA,YACd,MAAM,SAAS;AAAA,YACf,UAAU,SAAS;AAAA,UACrB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAED,SAAK,QAAQ;AAAA,MACX;AAAA,MACA,OAAO,YAAY;AACjB,cAAM,WAAW,UAAU;AAAA,UACzB,CAACC,cAAaA,UAAS,QAAQ,QAAQ,OAAO;AAAA,QAChD;AAEA,YAAI,CAAC,UAAU;AACb,gBAAM,IAAI;AAAA,YACR,UAAU;AAAA,YACV,qBAAqB,QAAQ,OAAO,GAAG;AAAA,UACzC;AAAA,QACF;AAEA,YAAI;AAEJ,YAAI;AACF,mBAAS,MAAM,SAAS,KAAK;AAAA,QAC/B,SAAS,OAAO;AACd,gBAAM,IAAI;AAAA,YACR,UAAU;AAAA,YACV,2BAA2B,KAAK;AAAA,YAChC;AAAA,cACE,KAAK,SAAS;AAAA,YAChB;AAAA,UACF;AAAA,QACF;AAEA,eAAO;AAAA,UACL,UAAU;AAAA,YACR;AAAA,cACE,KAAK,SAAS;AAAA,cACd,UAAU,SAAS;AAAA,cACnB,GAAG;AAAA,YACL;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,oBAAoB,SAAmB;AAC7C,SAAK,QAAQ,kBAAkB,0BAA0B,YAAY;AACnE,aAAO;AAAA,QACL,SAAS,QAAQ,IAAI,CAAC,WAAW;AAC/B,iBAAO;AAAA,YACL,MAAM,OAAO;AAAA,YACb,aAAa,OAAO;AAAA,YACpB,WAAW,OAAO;AAAA,UACpB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAED,SAAK,QAAQ,kBAAkB,wBAAwB,OAAO,YAAY;AACxE,YAAM,SAAS,QAAQ;AAAA,QACrB,CAACC,YAAWA,QAAO,SAAS,QAAQ,OAAO;AAAA,MAC7C;AAEA,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI;AAAA,UACR,UAAU;AAAA,UACV,mBAAmB,QAAQ,OAAO,IAAI;AAAA,QACxC;AAAA,MACF;AAEA,YAAM,OAAO,QAAQ,OAAO;AAE5B,UAAI,OAAO,WAAW;AACpB,mBAAW,OAAO,OAAO,WAAW;AAClC,cAAI,IAAI,YAAY,EAAE,QAAQ,IAAI,QAAQ,OAAO;AAC/C,kBAAM,IAAI;AAAA,cACR,UAAU;AAAA,cACV,8BAA8B,IAAI,IAAI;AAAA,YACxC;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,UAAI;AAEJ,UAAI;AACF,iBAAS,MAAM,OAAO,KAAK,IAA0C;AAAA,MACvE,SAAS,OAAO;AACd,cAAM,IAAI;AAAA,UACR,UAAU;AAAA,UACV,yBAAyB,KAAK;AAAA,QAChC;AAAA,MACF;AAEA,aAAO;AAAA,QACL,aAAa,OAAO;AAAA,QACpB,UAAU;AAAA,UACR;AAAA,YACE,MAAM;AAAA,YACN,SAAS,EAAE,MAAM,QAAQ,MAAM,OAAO;AAAA,UACxC;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,IAAM,0BAEF;AAEJ,IAAM,sBAAN,cAAkC,wBAAwB;AAAC;AAEpD,IAAM,UAAN,cAAsB,oBAAoB;AAAA,EAQ/C,YAAmB,SAAwB;AACzC,UAAM;AADW;AAGjB,SAAK,WAAW;AAAA,EAClB;AAAA,EAXA;AAAA,EACA,WAAqB,CAAC;AAAA,EACtB,aAAyB,CAAC;AAAA,EAC1B,YAA8B,CAAC;AAAA,EAC/B,aAA+B;AAAA,EAC/B,SAAiB,CAAC;AAAA,EAQlB,IAAW,WAA6B;AACtC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKO,QAAuC,MAAoB;AAChE,SAAK,OAAO,KAAK,IAAuB;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKO,YAAY,UAAoB;AACrC,SAAK,WAAW,KAAK,QAAQ;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKO,UAA+C,QAAsB;AAC1E,SAAK,SAAS,KAAK,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,MACX,UAKQ;AAAA,IACN,eAAe;AAAA,EACjB,GACA;AACA,QAAI,QAAQ,kBAAkB,SAAS;AACrC,YAAM,YAAY,IAAI,qBAAqB;AAE3C,YAAM,UAAU,IAAI,eAAe;AAAA,QACjC,MAAM,KAAK,SAAS;AAAA,QACpB,SAAS,KAAK,SAAS;AAAA,QACvB,OAAO,KAAK;AAAA,QACZ,WAAW,KAAK;AAAA,QAChB,SAAS,KAAK;AAAA,MAChB,CAAC;AAED,YAAM,QAAQ,QAAQ,SAAS;AAE/B,WAAK,UAAU,KAAK,OAAO;AAE3B,WAAK,KAAK,WAAW;AAAA,QACnB;AAAA,MACF,CAAC;AAED,cAAQ,MAAM,4BAA4B;AAAA,IAC5C,WAAW,QAAQ,kBAAkB,OAAO;AAC1C,WAAK,aAAa,MAAM,eAA+B;AAAA,QACrD,UAAU,QAAQ,IAAI;AAAA,QACtB,MAAM,QAAQ,IAAI;AAAA,QAClB,cAAc,YAAY;AACxB,iBAAO,IAAI,eAAe;AAAA,YACxB,MAAM,KAAK,SAAS;AAAA,YACpB,SAAS,KAAK,SAAS;AAAA,YACvB,OAAO,KAAK;AAAA,YACZ,WAAW,KAAK;AAAA,YAChB,SAAS,KAAK;AAAA,UAChB,CAAC;AAAA,QACH;AAAA,QACA,SAAS,CAAC,YAAY;AACpB,eAAK,KAAK,cAAc;AAAA,YACtB;AAAA,UACF,CAAC;AAAA,QACH;AAAA,QACA,WAAW,OAAO,YAAY;AAC5B,eAAK,UAAU,KAAK,OAAO;AAE3B,eAAK,KAAK,WAAW;AAAA,YACnB;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAED,cAAQ;AAAA,QACN,gDAAgD,QAAQ,IAAI,IAAI,GAAG,QAAQ,IAAI,QAAQ;AAAA,MACzF;AAAA,IACF,OAAO;AACL,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,OAAO;AAClB,QAAI,KAAK,YAAY;AACnB,WAAK,WAAW,MAAM;AAAA,IACxB;AAAA,EACF;AACF;","names":["tool","resource","prompt"]}
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glama/fastmcp",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "exports": "./src/FastMCP.ts",
5
5
  "include": ["src/FastMCP.ts", "src/bin/fastmcp.ts"]
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastmcp",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "main": "dist/FastMCP.js",
5
5
  "scripts": {
6
6
  "build": "tsup",
@@ -24,7 +24,7 @@
24
24
  "@modelcontextprotocol/sdk": "^1.0.4",
25
25
  "execa": "^9.5.2",
26
26
  "file-type": "^19.6.0",
27
- "mcp-proxy": "^2.0.2",
27
+ "mcp-proxy": "^2.2.0",
28
28
  "strict-event-emitter-types": "^2.0.0",
29
29
  "yargs": "^17.7.2",
30
30
  "zod": "^3.24.1",
@@ -7,15 +7,18 @@ import { getRandomPort } from "get-port-please";
7
7
  import { setTimeout as delay } from "timers/promises";
8
8
  import {
9
9
  ErrorCode,
10
+ ListRootsRequestSchema,
10
11
  LoggingMessageNotificationSchema,
11
12
  McpError,
12
13
  } from "@modelcontextprotocol/sdk/types.js";
13
14
 
14
15
  const runWithTestServer = async ({
15
16
  run,
17
+ client: clientOverride,
16
18
  start,
17
19
  }: {
18
20
  start: () => Promise<FastMCP>;
21
+ client?: Client;
19
22
  run: ({
20
23
  client,
21
24
  server,
@@ -38,15 +41,17 @@ const runWithTestServer = async ({
38
41
  });
39
42
 
40
43
  try {
41
- const client = new Client(
42
- {
43
- name: "example-client",
44
- version: "1.0.0",
45
- },
46
- {
47
- capabilities: {},
48
- },
49
- );
44
+ const client =
45
+ clientOverride ??
46
+ new Client(
47
+ {
48
+ name: "example-client",
49
+ version: "1.0.0",
50
+ },
51
+ {
52
+ capabilities: {},
53
+ },
54
+ );
50
55
 
51
56
  const transport = new SSEClientTransport(
52
57
  new URL(`http://localhost:${port}/sse`),
@@ -657,3 +662,96 @@ test("handles multiple clients", async () => {
657
662
 
658
663
  await server.stop();
659
664
  });
665
+
666
+ test("session knows about client capabilities", async () => {
667
+ const client = new Client(
668
+ {
669
+ name: "example-client",
670
+ version: "1.0.0",
671
+ },
672
+ {
673
+ capabilities: {
674
+ roots: {
675
+ listChanged: true,
676
+ },
677
+ },
678
+ },
679
+ );
680
+
681
+ client.setRequestHandler(ListRootsRequestSchema, () => {
682
+ return {
683
+ roots: [
684
+ {
685
+ uri: "file:///home/user/projects/frontend",
686
+ name: "Frontend Repository",
687
+ },
688
+ ],
689
+ };
690
+ });
691
+
692
+ await runWithTestServer({
693
+ client,
694
+ start: async () => {
695
+ const server = new FastMCP({
696
+ name: "Test",
697
+ version: "1.0.0",
698
+ });
699
+
700
+ return server;
701
+ },
702
+ run: async ({ session }) => {
703
+ expect(session.clientCapabilities).toEqual({
704
+ roots: {
705
+ listChanged: true,
706
+ },
707
+ });
708
+ },
709
+ });
710
+ });
711
+
712
+ test("session knows about roots", async () => {
713
+ const client = new Client(
714
+ {
715
+ name: "example-client",
716
+ version: "1.0.0",
717
+ },
718
+ {
719
+ capabilities: {
720
+ roots: {
721
+ listChanged: true,
722
+ },
723
+ },
724
+ },
725
+ );
726
+
727
+ client.setRequestHandler(ListRootsRequestSchema, () => {
728
+ return {
729
+ roots: [
730
+ {
731
+ uri: "file:///home/user/projects/frontend",
732
+ name: "Frontend Repository",
733
+ },
734
+ ],
735
+ };
736
+ });
737
+
738
+ await runWithTestServer({
739
+ client,
740
+ start: async () => {
741
+ const server = new FastMCP({
742
+ name: "Test",
743
+ version: "1.0.0",
744
+ });
745
+
746
+ return server;
747
+ },
748
+ run: async ({ session }) => {
749
+ expect(session.roots).toEqual([
750
+ {
751
+ uri: "file:///home/user/projects/frontend",
752
+ name: "Frontend Repository",
753
+ },
754
+ ]);
755
+ },
756
+ });
757
+ });
package/src/FastMCP.ts CHANGED
@@ -2,6 +2,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  import {
4
4
  CallToolRequestSchema,
5
+ ClientCapabilities,
5
6
  ErrorCode,
6
7
  GetPromptRequestSchema,
7
8
  ListPromptsRequestSchema,
@@ -9,11 +10,13 @@ import {
9
10
  ListToolsRequestSchema,
10
11
  McpError,
11
12
  ReadResourceRequestSchema,
13
+ Root,
12
14
  ServerCapabilities,
13
15
  SetLevelRequestSchema,
14
16
  } from "@modelcontextprotocol/sdk/types.js";
15
17
  import { zodToJsonSchema } from "zod-to-json-schema";
16
18
  import { z } from "zod";
19
+ import { setTimeout as delay } from "timers/promises";
17
20
  import { readFile } from "fs/promises";
18
21
  import { fileTypeFromBuffer } from "file-type";
19
22
  import { StrictEventEmitter } from "strict-event-emitter-types";
@@ -239,6 +242,8 @@ export class FastMCPSession {
239
242
  #capabilities: ServerCapabilities = {};
240
243
  #loggingLevel: LoggingLevel = "info";
241
244
  #server: Server;
245
+ #clientCapabilities?: ClientCapabilities;
246
+ #roots: Root[] = [];
242
247
 
243
248
  constructor({
244
249
  name,
@@ -288,12 +293,52 @@ export class FastMCPSession {
288
293
  }
289
294
  }
290
295
 
296
+ public get clientCapabilities(): ClientCapabilities | null {
297
+ return this.#clientCapabilities ?? null;
298
+ }
299
+
291
300
  public get server(): Server {
292
301
  return this.#server;
293
302
  }
294
303
 
295
- public connect(transport: Transport) {
296
- this.#server.connect(transport);
304
+ public async connect(transport: Transport) {
305
+ if (this.#server.transport) {
306
+ throw new UnexpectedStateError("Server is already connected");
307
+ }
308
+
309
+ await this.#server.connect(transport);
310
+
311
+ let attempt = 0;
312
+
313
+ while (attempt++ < 10) {
314
+ const capabilities = await this.#server.getClientCapabilities();
315
+
316
+ if (capabilities) {
317
+ this.#clientCapabilities = capabilities;
318
+
319
+ break;
320
+ }
321
+
322
+ await delay(100);
323
+ }
324
+
325
+ if (this.#clientCapabilities?.roots) {
326
+ const roots = await this.#server.listRoots();
327
+
328
+ this.#roots = roots.roots;
329
+ }
330
+
331
+ if (!this.#clientCapabilities) {
332
+ throw new UnexpectedStateError("Server did not connect");
333
+ }
334
+ }
335
+
336
+ public get roots(): Root[] {
337
+ return this.#roots;
338
+ }
339
+
340
+ public async close() {
341
+ await this.#server.close();
297
342
  }
298
343
 
299
344
  private setupErrorHandling() {
@@ -637,54 +682,29 @@ export class FastMCP extends FastMCPEventEmitter {
637
682
 
638
683
  console.error(`server is running on stdio`);
639
684
  } else if (options.transportType === "sse") {
640
- this.#sseServer = await startSSEServer({
685
+ this.#sseServer = await startSSEServer<FastMCPSession>({
641
686
  endpoint: options.sse.endpoint as `/${string}`,
642
687
  port: options.sse.port,
643
688
  createServer: async () => {
644
- const session = new FastMCPSession({
689
+ return new FastMCPSession({
645
690
  name: this.#options.name,
646
691
  version: this.#options.version,
647
692
  tools: this.#tools,
648
693
  resources: this.#resources,
649
694
  prompts: this.#prompts,
650
695
  });
651
-
652
- this.#sessions.push(session);
653
-
654
- return session.server;
655
696
  },
656
- onClose: (server) => {
657
- const session = this.#sessions.find(
658
- (session) => session.server === server,
659
- );
660
-
661
- if (!session) {
662
- throw new UnexpectedStateError("Server not found");
663
- }
664
-
665
- this.#sessions = this.#sessions.filter(
666
- (maybeOurSession) => maybeOurSession !== session,
667
- );
668
-
697
+ onClose: (session) => {
669
698
  this.emit("disconnect", {
670
699
  session,
671
700
  });
672
701
  },
673
- onConnect: async (server) => {
674
- const session = this.#sessions.find(
675
- (session) => session.server === server,
676
- );
677
-
678
- if (!session) {
679
- throw new UnexpectedStateError("Server not found");
680
- }
702
+ onConnect: async (session) => {
703
+ this.#sessions.push(session);
681
704
 
682
- // TODO investigate where is the race condition
683
- setTimeout(() => {
684
- this.emit("connect", {
685
- session,
686
- });
687
- }, 100);
705
+ this.emit("connect", {
706
+ session,
707
+ });
688
708
  },
689
709
  });
690
710