fastmcp 1.3.0 → 1.5.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
@@ -40,7 +40,7 @@ server.addTool({
40
40
  b: z.number(),
41
41
  }),
42
42
  execute: async (args) => {
43
- return args.a + args.b;
43
+ return String(args.a + args.b);
44
44
  },
45
45
  });
46
46
 
@@ -105,8 +105,137 @@ server.addTool({
105
105
  url: z.string(),
106
106
  }),
107
107
  execute: async (args) => {
108
- const content = await fetchWebpageContent(args.url);
109
- return content;
108
+ return await fetchWebpageContent(args.url);
109
+ },
110
+ });
111
+ ```
112
+
113
+ #### Returning a string
114
+
115
+ `execute` can return a string:
116
+
117
+ ```js
118
+ server.addTool({
119
+ name: "download",
120
+ description: "Download a file",
121
+ parameters: z.object({
122
+ url: z.string(),
123
+ }),
124
+ execute: async (args) => {
125
+ return "Hello, world!";
126
+ },
127
+ });
128
+ ```
129
+
130
+ The latter is equivalent to:
131
+
132
+ ```js
133
+ server.addTool({
134
+ name: "download",
135
+ description: "Download a file",
136
+ parameters: z.object({
137
+ url: z.string(),
138
+ }),
139
+ execute: async (args) => {
140
+ return {
141
+ content: [
142
+ {
143
+ type: "text",
144
+ text: "Hello, world!",
145
+ },
146
+ ],
147
+ };
148
+ },
149
+ });
150
+ ```
151
+
152
+ #### Returning a list
153
+
154
+ If you want to return a list of messages, you can return an object with a `content` property:
155
+
156
+ ```js
157
+ server.addTool({
158
+ name: "download",
159
+ description: "Download a file",
160
+ parameters: z.object({
161
+ url: z.string(),
162
+ }),
163
+ execute: async (args) => {
164
+ return {
165
+ content: [
166
+ { type: "text", text: "First message" },
167
+ { type: "text", text: "Second message" },
168
+ ],
169
+ };
170
+ },
171
+ });
172
+ ```
173
+
174
+ #### Returning an image
175
+
176
+ Use the `imageContent` to create a content object for an image:
177
+
178
+ ```js
179
+ import { imageContent } from "fastmcp";
180
+
181
+ server.addTool({
182
+ name: "download",
183
+ description: "Download a file",
184
+ parameters: z.object({
185
+ url: z.string(),
186
+ }),
187
+ execute: async (args) => {
188
+ return imageContent({
189
+ url: "https://example.com/image.png",
190
+ });
191
+
192
+ // or...
193
+ // return imageContent({
194
+ // path: "/path/to/image.png",
195
+ // });
196
+
197
+ // or...
198
+ // return imageContent({
199
+ // buffer: Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", "base64"),
200
+ // });
201
+
202
+ // or...
203
+ // return {
204
+ // content: [
205
+ // imageContent()
206
+ // ],
207
+ // };
208
+ },
209
+ });
210
+ ```
211
+
212
+ The `imageContent` function takes the following options:
213
+
214
+ - `url`: The URL of the image.
215
+ - `path`: The path to the image file.
216
+ - `buffer`: The image data as a buffer.
217
+
218
+ Only one of `url`, `path`, or `buffer` must be specified.
219
+
220
+ The above example is equivalent to:
221
+
222
+ ```js
223
+ server.addTool({
224
+ name: "download",
225
+ description: "Download a file",
226
+ parameters: z.object({
227
+ url: z.string(),
228
+ }),
229
+ execute: async (args) => {
230
+ return {
231
+ content: [
232
+ {
233
+ type: "image",
234
+ data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
235
+ mimeType: "image/png",
236
+ },
237
+ ],
238
+ };
110
239
  },
111
240
  });
112
241
  ```
package/dist/FastMCP.d.ts CHANGED
@@ -1,5 +1,12 @@
1
1
  import { z } from 'zod';
2
2
 
3
+ declare const imageContent: (input: {
4
+ url: string;
5
+ } | {
6
+ path: string;
7
+ } | {
8
+ buffer: Buffer;
9
+ }) => Promise<ImageContent>;
3
10
  declare abstract class FastMCPError extends Error {
4
11
  constructor(message?: string);
5
12
  }
@@ -35,11 +42,100 @@ type Context = {
35
42
  warn: (message: string, data?: SerializableValue) => void;
36
43
  };
37
44
  };
45
+ declare const TextContentZodSchema: z.ZodObject<{
46
+ type: z.ZodLiteral<"text">;
47
+ /**
48
+ * The text content of the message.
49
+ */
50
+ text: z.ZodString;
51
+ }, "strict", z.ZodTypeAny, {
52
+ type: "text";
53
+ text: string;
54
+ }, {
55
+ type: "text";
56
+ text: string;
57
+ }>;
58
+ type TextContent = z.infer<typeof TextContentZodSchema>;
59
+ declare const ImageContentZodSchema: z.ZodObject<{
60
+ type: z.ZodLiteral<"image">;
61
+ /**
62
+ * The base64-encoded image data.
63
+ */
64
+ data: z.ZodString;
65
+ /**
66
+ * The MIME type of the image. Different providers may support different image types.
67
+ */
68
+ mimeType: z.ZodString;
69
+ }, "strict", z.ZodTypeAny, {
70
+ type: "image";
71
+ data: string;
72
+ mimeType: string;
73
+ }, {
74
+ type: "image";
75
+ data: string;
76
+ mimeType: string;
77
+ }>;
78
+ type ImageContent = z.infer<typeof ImageContentZodSchema>;
79
+ declare const ContentResultZodSchema: z.ZodObject<{
80
+ content: z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
81
+ type: z.ZodLiteral<"text">;
82
+ /**
83
+ * The text content of the message.
84
+ */
85
+ text: z.ZodString;
86
+ }, "strict", z.ZodTypeAny, {
87
+ type: "text";
88
+ text: string;
89
+ }, {
90
+ type: "text";
91
+ text: string;
92
+ }>, z.ZodObject<{
93
+ type: z.ZodLiteral<"image">;
94
+ /**
95
+ * The base64-encoded image data.
96
+ */
97
+ data: z.ZodString;
98
+ /**
99
+ * The MIME type of the image. Different providers may support different image types.
100
+ */
101
+ mimeType: z.ZodString;
102
+ }, "strict", z.ZodTypeAny, {
103
+ type: "image";
104
+ data: string;
105
+ mimeType: string;
106
+ }, {
107
+ type: "image";
108
+ data: string;
109
+ mimeType: string;
110
+ }>]>, "many">;
111
+ isError: z.ZodOptional<z.ZodBoolean>;
112
+ }, "strict", z.ZodTypeAny, {
113
+ content: ({
114
+ type: "image";
115
+ data: string;
116
+ mimeType: string;
117
+ } | {
118
+ type: "text";
119
+ text: string;
120
+ })[];
121
+ isError?: boolean | undefined;
122
+ }, {
123
+ content: ({
124
+ type: "image";
125
+ data: string;
126
+ mimeType: string;
127
+ } | {
128
+ type: "text";
129
+ text: string;
130
+ })[];
131
+ isError?: boolean | undefined;
132
+ }>;
133
+ type ContentResult = z.infer<typeof ContentResultZodSchema>;
38
134
  type Tool<Params extends ToolParameters = ToolParameters> = {
39
135
  name: string;
40
136
  description?: string;
41
137
  parameters?: Params;
42
- execute: (args: z.infer<Params>, context: Context) => Promise<unknown>;
138
+ execute: (args: z.infer<Params>, context: Context) => Promise<string | ContentResult | TextContent | ImageContent>;
43
139
  };
44
140
  type Resource = {
45
141
  uri: string;
@@ -101,4 +197,4 @@ declare class FastMCP {
101
197
  stop(): Promise<void>;
102
198
  }
103
199
 
104
- export { FastMCP, UserError };
200
+ export { FastMCP, UserError, imageContent };
package/dist/FastMCP.js CHANGED
@@ -14,7 +14,35 @@ import {
14
14
  SetLevelRequestSchema
15
15
  } from "@modelcontextprotocol/sdk/types.js";
16
16
  import { zodToJsonSchema } from "zod-to-json-schema";
17
+ import { z } from "zod";
17
18
  import http from "http";
19
+ import { readFile } from "fs/promises";
20
+ import { fileTypeFromBuffer } from "file-type";
21
+ var imageContent = async (input) => {
22
+ let rawData;
23
+ if ("url" in input) {
24
+ const response = await fetch(input.url);
25
+ if (!response.ok) {
26
+ throw new Error(`Failed to fetch image from URL: ${response.statusText}`);
27
+ }
28
+ rawData = Buffer.from(await response.arrayBuffer());
29
+ } else if ("path" in input) {
30
+ rawData = await readFile(input.path);
31
+ } else if ("buffer" in input) {
32
+ rawData = input.buffer;
33
+ } else {
34
+ throw new Error(
35
+ "Invalid input: Provide a valid 'url', 'path', or 'buffer'"
36
+ );
37
+ }
38
+ const mimeType = await fileTypeFromBuffer(rawData);
39
+ const base64Data = rawData.toString("base64");
40
+ return {
41
+ type: "image",
42
+ data: base64Data,
43
+ mimeType: mimeType?.mime ?? "image/png"
44
+ };
45
+ };
18
46
  var FastMCPError = class extends Error {
19
47
  constructor(message) {
20
48
  super(message);
@@ -31,6 +59,32 @@ var UnexpectedStateError = class extends FastMCPError {
31
59
  };
32
60
  var UserError = class extends UnexpectedStateError {
33
61
  };
62
+ var TextContentZodSchema = z.object({
63
+ type: z.literal("text"),
64
+ /**
65
+ * The text content of the message.
66
+ */
67
+ text: z.string()
68
+ }).strict();
69
+ var ImageContentZodSchema = z.object({
70
+ type: z.literal("image"),
71
+ /**
72
+ * The base64-encoded image data.
73
+ */
74
+ data: z.string().base64(),
75
+ /**
76
+ * The MIME type of the image. Different providers may support different image types.
77
+ */
78
+ mimeType: z.string()
79
+ }).strict();
80
+ var ContentZodSchema = z.discriminatedUnion("type", [
81
+ TextContentZodSchema,
82
+ ImageContentZodSchema
83
+ ]);
84
+ var ContentResultZodSchema = z.object({
85
+ content: ContentZodSchema.array(),
86
+ isError: z.boolean().optional()
87
+ }).strict();
34
88
  var FastMCP = class {
35
89
  constructor(options) {
36
90
  this.options = options;
@@ -85,105 +139,106 @@ var FastMCP = class {
85
139
  })
86
140
  };
87
141
  });
88
- server.setRequestHandler(
89
- CallToolRequestSchema,
90
- async (request, ...extra) => {
91
- const tool = this.#tools.find(
92
- (tool2) => tool2.name === request.params.name
142
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
143
+ const tool = this.#tools.find(
144
+ (tool2) => tool2.name === request.params.name
145
+ );
146
+ if (!tool) {
147
+ throw new McpError(
148
+ ErrorCode.MethodNotFound,
149
+ `Unknown tool: ${request.params.name}`
93
150
  );
94
- if (!tool) {
151
+ }
152
+ let args = void 0;
153
+ if (tool.parameters) {
154
+ const parsed = tool.parameters.safeParse(request.params.arguments);
155
+ if (!parsed.success) {
95
156
  throw new McpError(
96
- ErrorCode.MethodNotFound,
97
- `Unknown tool: ${request.params.name}`
157
+ ErrorCode.InvalidRequest,
158
+ `Invalid ${request.params.name} arguments`
98
159
  );
99
160
  }
100
- let args = void 0;
101
- if (tool.parameters) {
102
- const parsed = tool.parameters.safeParse(request.params.arguments);
103
- if (!parsed.success) {
104
- throw new McpError(
105
- ErrorCode.InvalidRequest,
106
- `Invalid ${request.params.name} arguments`
107
- );
108
- }
109
- args = parsed.data;
110
- }
111
- const progressToken = request.params?._meta?.progressToken;
112
- let result;
113
- try {
114
- const reportProgress = async (progress) => {
115
- await server.notification({
116
- method: "notifications/progress",
117
- params: {
118
- ...progress,
119
- progressToken
120
- }
121
- });
122
- };
123
- const log = {
124
- debug: (message, context) => {
125
- server.sendLoggingMessage({
126
- level: "debug",
127
- data: {
128
- message,
129
- context
130
- }
131
- });
132
- },
133
- error: (message, context) => {
134
- server.sendLoggingMessage({
135
- level: "error",
136
- data: {
137
- message,
138
- context
139
- }
140
- });
141
- },
142
- info: (message, context) => {
143
- server.sendLoggingMessage({
144
- level: "info",
145
- data: {
146
- message,
147
- context
148
- }
149
- });
150
- },
151
- warn: (message, context) => {
152
- server.sendLoggingMessage({
153
- level: "warning",
154
- data: {
155
- message,
156
- context
157
- }
158
- });
161
+ args = parsed.data;
162
+ }
163
+ const progressToken = request.params?._meta?.progressToken;
164
+ let result;
165
+ try {
166
+ const reportProgress = async (progress) => {
167
+ await server.notification({
168
+ method: "notifications/progress",
169
+ params: {
170
+ ...progress,
171
+ progressToken
159
172
  }
160
- };
161
- result = await tool.execute(args, {
162
- reportProgress,
163
- log
164
173
  });
165
- } catch (error) {
166
- if (error instanceof UserError) {
167
- return {
168
- content: [{ type: "text", text: error.message }],
169
- isError: true
170
- };
174
+ };
175
+ const log = {
176
+ debug: (message, context) => {
177
+ server.sendLoggingMessage({
178
+ level: "debug",
179
+ data: {
180
+ message,
181
+ context
182
+ }
183
+ });
184
+ },
185
+ error: (message, context) => {
186
+ server.sendLoggingMessage({
187
+ level: "error",
188
+ data: {
189
+ message,
190
+ context
191
+ }
192
+ });
193
+ },
194
+ info: (message, context) => {
195
+ server.sendLoggingMessage({
196
+ level: "info",
197
+ data: {
198
+ message,
199
+ context
200
+ }
201
+ });
202
+ },
203
+ warn: (message, context) => {
204
+ server.sendLoggingMessage({
205
+ level: "warning",
206
+ data: {
207
+ message,
208
+ context
209
+ }
210
+ });
171
211
  }
172
- return {
173
- content: [{ type: "text", text: `Error: ${error}` }],
174
- isError: true
175
- };
212
+ };
213
+ const maybeStringResult = await tool.execute(args, {
214
+ reportProgress,
215
+ log
216
+ });
217
+ if (typeof maybeStringResult === "string") {
218
+ result = ContentResultZodSchema.parse({
219
+ content: [{ type: "text", text: maybeStringResult }]
220
+ });
221
+ } else if ("type" in maybeStringResult) {
222
+ result = ContentResultZodSchema.parse({
223
+ content: [maybeStringResult]
224
+ });
225
+ } else {
226
+ result = ContentResultZodSchema.parse(maybeStringResult);
176
227
  }
177
- if (typeof result === "string") {
228
+ } catch (error) {
229
+ if (error instanceof UserError) {
178
230
  return {
179
- content: [{ type: "text", text: result }]
231
+ content: [{ type: "text", text: error.message }],
232
+ isError: true
180
233
  };
181
234
  }
182
235
  return {
183
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
236
+ content: [{ type: "text", text: `Error: ${error}` }],
237
+ isError: true
184
238
  };
185
239
  }
186
- );
240
+ return result;
241
+ });
187
242
  }
188
243
  setupResourceHandlers(server) {
189
244
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
@@ -400,6 +455,7 @@ var FastMCP = class {
400
455
  };
401
456
  export {
402
457
  FastMCP,
403
- UserError
458
+ UserError,
459
+ imageContent
404
460
  };
405
461
  //# sourceMappingURL=FastMCP.js.map
@@ -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 { SSEServerTransport } from \"@modelcontextprotocol/sdk/server/sse.js\";\nimport {\n CallToolRequestSchema,\n ErrorCode,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n LoggingLevel,\n LoggingLevelSchema,\n LoggingMessageNotificationSchema,\n McpError,\n NotificationSchema,\n ReadResourceRequestSchema,\n ServerCapabilities,\n SetLevelRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { zodToJsonSchema } from \"zod-to-json-schema\";\nimport type { z } from \"zod\";\nimport http from \"http\";\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\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 Tool<Params extends ToolParameters = ToolParameters> = {\n name: string;\n description?: string;\n parameters?: Params;\n execute: (args: z.infer<Params>, context: Context) => Promise<unknown>;\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\nexport class FastMCP {\n #tools: Tool[];\n #resources: Resource[];\n #prompts: Prompt[];\n #server: Server | null = null;\n #options: ServerOptions;\n #loggingLevel: LoggingLevel = \"info\";\n\n constructor(public options: ServerOptions) {\n this.#options = options;\n this.#tools = [];\n this.#resources = [];\n this.#prompts = [];\n }\n\n private setupHandlers(server: Server) {\n this.setupErrorHandling(server);\n\n if (this.#tools.length) {\n this.setupToolHandlers(server);\n }\n\n if (this.#resources.length) {\n this.setupResourceHandlers(server);\n }\n\n if (this.#prompts.length) {\n this.setupPromptHandlers(server);\n }\n\n server.setRequestHandler(SetLevelRequestSchema, (request) => {\n this.#loggingLevel = request.params.level;\n\n return {};\n });\n }\n\n private setupErrorHandling(server: Server) {\n server.onerror = (error) => {\n console.error(\"[MCP Error]\", error);\n };\n process.on(\"SIGINT\", async () => {\n await server.close();\n process.exit(0);\n });\n }\n\n public get loggingLevel() {\n return this.#loggingLevel;\n }\n\n private setupToolHandlers(server: Server) {\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: this.#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 server.setRequestHandler(\n CallToolRequestSchema,\n async (request, ...extra) => {\n const tool = this.#tools.find(\n (tool) => tool.name === request.params.name,\n );\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: any;\n\n try {\n const reportProgress = async (progress: Progress) => {\n await 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 server.sendLoggingMessage({\n level: \"debug\",\n data: {\n message,\n context,\n },\n });\n },\n error: (message: string, context?: SerializableValue) => {\n server.sendLoggingMessage({\n level: \"error\",\n data: {\n message,\n context,\n },\n });\n },\n info: (message: string, context?: SerializableValue) => {\n server.sendLoggingMessage({\n level: \"info\",\n data: {\n message,\n context,\n },\n });\n },\n warn: (message: string, context?: SerializableValue) => {\n server.sendLoggingMessage({\n level: \"warning\",\n data: {\n message,\n context,\n },\n });\n },\n };\n\n result = await tool.execute(args, {\n reportProgress,\n log,\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 if (typeof result === \"string\") {\n return {\n content: [{ type: \"text\", text: result }],\n };\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\n };\n },\n );\n }\n\n private setupResourceHandlers(server: Server) {\n server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n resources: this.#resources.map((resource) => {\n return {\n uri: resource.uri,\n name: resource.name,\n mimeType: resource.mimeType,\n };\n }),\n };\n });\n\n server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n const resource = this.#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 private setupPromptHandlers(server: Server) {\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n return {\n prompts: this.#prompts.map((prompt) => {\n return {\n name: prompt.name,\n description: prompt.description,\n arguments: prompt.arguments,\n };\n }),\n };\n });\n\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const prompt = this.#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 public addTool<Params extends ToolParameters>(tool: Tool<Params>) {\n this.#tools.push(tool as unknown as Tool);\n }\n\n public addResource(resource: Resource) {\n this.#resources.push(resource);\n }\n\n public addPrompt<const Args extends PromptArgument[]>(prompt: Prompt<Args>) {\n this.#prompts.push(prompt);\n }\n\n #httpServer: http.Server | null = null;\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 const capabilities: ServerCapabilities = {};\n\n if (this.#tools.length) {\n capabilities.tools = {};\n }\n\n if (this.#resources.length) {\n capabilities.resources = {};\n }\n\n if (this.#prompts.length) {\n capabilities.prompts = {};\n }\n\n capabilities.logging = {};\n\n this.#server = new Server(\n { name: this.#options.name, version: this.#options.version },\n { capabilities },\n );\n\n this.setupHandlers(this.#server);\n\n if (options.transportType === \"stdio\") {\n const transport = new StdioServerTransport();\n\n await this.#server.connect(transport);\n\n console.error(`server is running on stdio`);\n } else if (options.transportType === \"sse\") {\n let activeTransport: SSEServerTransport | null = null;\n\n /**\n * Adopted from https://dev.classmethod.jp/articles/mcp-sse/\n */\n this.#httpServer = http.createServer(async (req, res) => {\n if (req.method === \"GET\" && req.url === options.sse.endpoint) {\n const transport = new SSEServerTransport(\"/messages\", res);\n\n activeTransport = transport;\n\n if (!this.#server) {\n throw new Error(\"Server not initialized\");\n }\n\n await this.#server.connect(transport);\n\n res.on(\"close\", () => {\n console.log(\"SSE connection closed\");\n if (activeTransport === transport) {\n activeTransport = null;\n }\n });\n\n this.startSending(transport);\n return;\n }\n\n if (req.method === \"POST\" && req.url?.startsWith(\"/messages\")) {\n if (!activeTransport) {\n res.writeHead(400).end(\"No active transport\");\n return;\n }\n await activeTransport.handlePostMessage(req, res);\n return;\n }\n\n res.writeHead(404).end();\n });\n\n this.#httpServer.listen(options.sse.port, \"0.0.0.0\");\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 * @see https://dev.classmethod.jp/articles/mcp-sse/\n */\n private async startSending(transport: SSEServerTransport) {\n try {\n await transport.send({\n jsonrpc: \"2.0\",\n method: \"sse/connection\",\n params: { message: \"SSE Connection established\" },\n });\n\n let messageCount = 0;\n const interval = setInterval(async () => {\n messageCount++;\n\n const message = `Message ${messageCount} at ${new Date().toISOString()}`;\n\n try {\n await transport.send({\n jsonrpc: \"2.0\",\n method: \"sse/message\",\n params: { data: message },\n });\n\n console.log(`Sent: ${message}`);\n\n if (messageCount === 10) {\n clearInterval(interval);\n\n await transport.send({\n jsonrpc: \"2.0\",\n method: \"sse/complete\",\n params: { message: \"Stream completed\" },\n });\n console.log(\"Stream completed\");\n }\n } catch (error) {\n console.error(\"Error sending message:\", error);\n clearInterval(interval);\n }\n }, 1000);\n } catch (error) {\n console.error(\"Error in startSending:\", error);\n }\n }\n\n public async stop() {\n if (this.#httpServer) {\n this.#httpServer.close();\n }\n }\n}\n"],"mappings":"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","names":["tool","resource","prompt"]}
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 { SSEServerTransport } from \"@modelcontextprotocol/sdk/server/sse.js\";\nimport {\n CallToolRequestSchema,\n ErrorCode,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n LoggingLevel,\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 http from \"http\";\nimport { readFile } from \"fs/promises\";\nimport { fileTypeFromBuffer } from \"file-type\";\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\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\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();\n\ntype TextContent = z.infer<typeof TextContentZodSchema>;\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();\n\ntype ImageContent = z.infer<typeof ImageContentZodSchema>;\n\nconst ContentZodSchema = z.discriminatedUnion(\"type\", [\n TextContentZodSchema,\n ImageContentZodSchema,\n]);\n\nconst ContentResultZodSchema = z\n .object({\n content: ContentZodSchema.array(),\n isError: z.boolean().optional(),\n })\n .strict();\n\ntype ContentResult = z.infer<typeof ContentResultZodSchema>;\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\nexport class FastMCP {\n #tools: Tool[];\n #resources: Resource[];\n #prompts: Prompt[];\n #server: Server | null = null;\n #options: ServerOptions;\n #loggingLevel: LoggingLevel = \"info\";\n\n constructor(public options: ServerOptions) {\n this.#options = options;\n this.#tools = [];\n this.#resources = [];\n this.#prompts = [];\n }\n\n private setupHandlers(server: Server) {\n this.setupErrorHandling(server);\n\n if (this.#tools.length) {\n this.setupToolHandlers(server);\n }\n\n if (this.#resources.length) {\n this.setupResourceHandlers(server);\n }\n\n if (this.#prompts.length) {\n this.setupPromptHandlers(server);\n }\n\n server.setRequestHandler(SetLevelRequestSchema, (request) => {\n this.#loggingLevel = request.params.level;\n\n return {};\n });\n }\n\n private setupErrorHandling(server: Server) {\n server.onerror = (error) => {\n console.error(\"[MCP Error]\", error);\n };\n process.on(\"SIGINT\", async () => {\n await server.close();\n process.exit(0);\n });\n }\n\n public get loggingLevel() {\n return this.#loggingLevel;\n }\n\n private setupToolHandlers(server: Server) {\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: this.#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 server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const tool = this.#tools.find(\n (tool) => tool.name === request.params.name,\n );\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 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 server.sendLoggingMessage({\n level: \"debug\",\n data: {\n message,\n context,\n },\n });\n },\n error: (message: string, context?: SerializableValue) => {\n server.sendLoggingMessage({\n level: \"error\",\n data: {\n message,\n context,\n },\n });\n },\n info: (message: string, context?: SerializableValue) => {\n server.sendLoggingMessage({\n level: \"info\",\n data: {\n message,\n context,\n },\n });\n },\n warn: (message: string, context?: SerializableValue) => {\n 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(server: Server) {\n server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n resources: this.#resources.map((resource) => {\n return {\n uri: resource.uri,\n name: resource.name,\n mimeType: resource.mimeType,\n };\n }),\n };\n });\n\n server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\n const resource = this.#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 private setupPromptHandlers(server: Server) {\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\n return {\n prompts: this.#prompts.map((prompt) => {\n return {\n name: prompt.name,\n description: prompt.description,\n arguments: prompt.arguments,\n };\n }),\n };\n });\n\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const prompt = this.#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 public addTool<Params extends ToolParameters>(tool: Tool<Params>) {\n this.#tools.push(tool as unknown as Tool);\n }\n\n public addResource(resource: Resource) {\n this.#resources.push(resource);\n }\n\n public addPrompt<const Args extends PromptArgument[]>(prompt: Prompt<Args>) {\n this.#prompts.push(prompt);\n }\n\n #httpServer: http.Server | null = null;\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 const capabilities: ServerCapabilities = {};\n\n if (this.#tools.length) {\n capabilities.tools = {};\n }\n\n if (this.#resources.length) {\n capabilities.resources = {};\n }\n\n if (this.#prompts.length) {\n capabilities.prompts = {};\n }\n\n capabilities.logging = {};\n\n this.#server = new Server(\n { name: this.#options.name, version: this.#options.version },\n { capabilities },\n );\n\n this.setupHandlers(this.#server);\n\n if (options.transportType === \"stdio\") {\n const transport = new StdioServerTransport();\n\n await this.#server.connect(transport);\n\n console.error(`server is running on stdio`);\n } else if (options.transportType === \"sse\") {\n let activeTransport: SSEServerTransport | null = null;\n\n /**\n * Adopted from https://dev.classmethod.jp/articles/mcp-sse/\n */\n this.#httpServer = http.createServer(async (req, res) => {\n if (req.method === \"GET\" && req.url === options.sse.endpoint) {\n const transport = new SSEServerTransport(\"/messages\", res);\n\n activeTransport = transport;\n\n if (!this.#server) {\n throw new Error(\"Server not initialized\");\n }\n\n await this.#server.connect(transport);\n\n res.on(\"close\", () => {\n console.log(\"SSE connection closed\");\n if (activeTransport === transport) {\n activeTransport = null;\n }\n });\n\n this.startSending(transport);\n return;\n }\n\n if (req.method === \"POST\" && req.url?.startsWith(\"/messages\")) {\n if (!activeTransport) {\n res.writeHead(400).end(\"No active transport\");\n return;\n }\n await activeTransport.handlePostMessage(req, res);\n return;\n }\n\n res.writeHead(404).end();\n });\n\n this.#httpServer.listen(options.sse.port, \"0.0.0.0\");\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 * @see https://dev.classmethod.jp/articles/mcp-sse/\n */\n private async startSending(transport: SSEServerTransport) {\n try {\n await transport.send({\n jsonrpc: \"2.0\",\n method: \"sse/connection\",\n params: { message: \"SSE Connection established\" },\n });\n\n let messageCount = 0;\n const interval = setInterval(async () => {\n messageCount++;\n\n const message = `Message ${messageCount} at ${new Date().toISOString()}`;\n\n try {\n await transport.send({\n jsonrpc: \"2.0\",\n method: \"sse/message\",\n params: { data: message },\n });\n\n console.log(`Sent: ${message}`);\n\n if (messageCount === 10) {\n clearInterval(interval);\n\n await transport.send({\n jsonrpc: \"2.0\",\n method: \"sse/complete\",\n params: { message: \"Stream completed\" },\n });\n console.log(\"Stream completed\");\n }\n } catch (error) {\n console.error(\"Error sending message:\", error);\n clearInterval(interval);\n }\n }, 1000);\n } catch (error) {\n console.error(\"Error in startSending:\", error);\n }\n }\n\n public async stop() {\n if (this.#httpServer) {\n this.#httpServer.close();\n }\n }\n}\n"],"mappings":";AAAA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AACP,SAAS,uBAAuB;AAChC,SAAS,SAAS;AAClB,OAAO,UAAU;AACjB,SAAS,gBAAgB;AACzB,SAAS,0BAA0B;AAE5B,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;AAEO,IAAM,YAAN,cAAwB,qBAAqB;AAAC;AAgCrD,IAAM,uBAAuB,EAC1B,OAAO;AAAA,EACN,MAAM,EAAE,QAAQ,MAAM;AAAA;AAAA;AAAA;AAAA,EAItB,MAAM,EAAE,OAAO;AACjB,CAAC,EACA,OAAO;AAIV,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;AAED,IAAM,yBAAyB,EAC5B,OAAO;AAAA,EACN,SAAS,iBAAiB,MAAM;AAAA,EAChC,SAAS,EAAE,QAAQ,EAAE,SAAS;AAChC,CAAC,EACA,OAAO;AAoDH,IAAM,UAAN,MAAc;AAAA,EAQnB,YAAmB,SAAwB;AAAxB;AACjB,SAAK,WAAW;AAChB,SAAK,SAAS,CAAC;AACf,SAAK,aAAa,CAAC;AACnB,SAAK,WAAW,CAAC;AAAA,EACnB;AAAA,EAZA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAyB;AAAA,EACzB;AAAA,EACA,gBAA8B;AAAA,EAStB,cAAc,QAAgB;AACpC,SAAK,mBAAmB,MAAM;AAE9B,QAAI,KAAK,OAAO,QAAQ;AACtB,WAAK,kBAAkB,MAAM;AAAA,IAC/B;AAEA,QAAI,KAAK,WAAW,QAAQ;AAC1B,WAAK,sBAAsB,MAAM;AAAA,IACnC;AAEA,QAAI,KAAK,SAAS,QAAQ;AACxB,WAAK,oBAAoB,MAAM;AAAA,IACjC;AAEA,WAAO,kBAAkB,uBAAuB,CAAC,YAAY;AAC3D,WAAK,gBAAgB,QAAQ,OAAO;AAEpC,aAAO,CAAC;AAAA,IACV,CAAC;AAAA,EACH;AAAA,EAEQ,mBAAmB,QAAgB;AACzC,WAAO,UAAU,CAAC,UAAU;AAC1B,cAAQ,MAAM,eAAe,KAAK;AAAA,IACpC;AACA,YAAQ,GAAG,UAAU,YAAY;AAC/B,YAAM,OAAO,MAAM;AACnB,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAAA,EACH;AAAA,EAEA,IAAW,eAAe;AACxB,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,kBAAkB,QAAgB;AACxC,WAAO,kBAAkB,wBAAwB,YAAY;AAC3D,aAAO;AAAA,QACL,OAAO,KAAK,OAAO,IAAI,CAAC,SAAS;AAC/B,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,WAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,YAAM,OAAO,KAAK,OAAO;AAAA,QACvB,CAACA,UAASA,MAAK,SAAS,QAAQ,OAAO;AAAA,MACzC;AAEA,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,OAAO,aAAa;AAAA,YACxB,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,mBAAO,mBAAmB;AAAA,cACxB,OAAO;AAAA,cACP,MAAM;AAAA,gBACJ;AAAA,gBACA;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH;AAAA,UACA,OAAO,CAAC,SAAiB,YAAgC;AACvD,mBAAO,mBAAmB;AAAA,cACxB,OAAO;AAAA,cACP,MAAM;AAAA,gBACJ;AAAA,gBACA;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH;AAAA,UACA,MAAM,CAAC,SAAiB,YAAgC;AACtD,mBAAO,mBAAmB;AAAA,cACxB,OAAO;AAAA,cACP,MAAM;AAAA,gBACJ;AAAA,gBACA;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH;AAAA,UACA,MAAM,CAAC,SAAiB,YAAgC;AACtD,mBAAO,mBAAmB;AAAA,cACxB,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,QAAgB;AAC5C,WAAO,kBAAkB,4BAA4B,YAAY;AAC/D,aAAO;AAAA,QACL,WAAW,KAAK,WAAW,IAAI,CAAC,aAAa;AAC3C,iBAAO;AAAA,YACL,KAAK,SAAS;AAAA,YACd,MAAM,SAAS;AAAA,YACf,UAAU,SAAS;AAAA,UACrB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAED,WAAO,kBAAkB,2BAA2B,OAAO,YAAY;AACrE,YAAM,WAAW,KAAK,WAAW;AAAA,QAC/B,CAACC,cAAaA,UAAS,QAAQ,QAAQ,OAAO;AAAA,MAChD;AAEA,UAAI,CAAC,UAAU;AACb,cAAM,IAAI;AAAA,UACR,UAAU;AAAA,UACV,qBAAqB,QAAQ,OAAO,GAAG;AAAA,QACzC;AAAA,MACF;AAEA,UAAI;AAEJ,UAAI;AACF,iBAAS,MAAM,SAAS,KAAK;AAAA,MAC/B,SAAS,OAAO;AACd,cAAM,IAAI;AAAA,UACR,UAAU;AAAA,UACV,2BAA2B,KAAK;AAAA,UAChC;AAAA,YACE,KAAK,SAAS;AAAA,UAChB;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,QACL,UAAU;AAAA,UACR;AAAA,YACE,KAAK,SAAS;AAAA,YACd,UAAU,SAAS;AAAA,YACnB,GAAG;AAAA,UACL;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,oBAAoB,QAAgB;AAC1C,WAAO,kBAAkB,0BAA0B,YAAY;AAC7D,aAAO;AAAA,QACL,SAAS,KAAK,SAAS,IAAI,CAAC,WAAW;AACrC,iBAAO;AAAA,YACL,MAAM,OAAO;AAAA,YACb,aAAa,OAAO;AAAA,YACpB,WAAW,OAAO;AAAA,UACpB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAED,WAAO,kBAAkB,wBAAwB,OAAO,YAAY;AAClE,YAAM,SAAS,KAAK,SAAS;AAAA,QAC3B,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;AAAA,EAEO,QAAuC,MAAoB;AAChE,SAAK,OAAO,KAAK,IAAuB;AAAA,EAC1C;AAAA,EAEO,YAAY,UAAoB;AACrC,SAAK,WAAW,KAAK,QAAQ;AAAA,EAC/B;AAAA,EAEO,UAA+C,QAAsB;AAC1E,SAAK,SAAS,KAAK,MAAM;AAAA,EAC3B;AAAA,EAEA,cAAkC;AAAA,EAElC,MAAa,MACX,UAKQ;AAAA,IACN,eAAe;AAAA,EACjB,GACA;AACA,UAAM,eAAmC,CAAC;AAE1C,QAAI,KAAK,OAAO,QAAQ;AACtB,mBAAa,QAAQ,CAAC;AAAA,IACxB;AAEA,QAAI,KAAK,WAAW,QAAQ;AAC1B,mBAAa,YAAY,CAAC;AAAA,IAC5B;AAEA,QAAI,KAAK,SAAS,QAAQ;AACxB,mBAAa,UAAU,CAAC;AAAA,IAC1B;AAEA,iBAAa,UAAU,CAAC;AAExB,SAAK,UAAU,IAAI;AAAA,MACjB,EAAE,MAAM,KAAK,SAAS,MAAM,SAAS,KAAK,SAAS,QAAQ;AAAA,MAC3D,EAAE,aAAa;AAAA,IACjB;AAEA,SAAK,cAAc,KAAK,OAAO;AAE/B,QAAI,QAAQ,kBAAkB,SAAS;AACrC,YAAM,YAAY,IAAI,qBAAqB;AAE3C,YAAM,KAAK,QAAQ,QAAQ,SAAS;AAEpC,cAAQ,MAAM,4BAA4B;AAAA,IAC5C,WAAW,QAAQ,kBAAkB,OAAO;AAC1C,UAAI,kBAA6C;AAKjD,WAAK,cAAc,KAAK,aAAa,OAAO,KAAK,QAAQ;AACvD,YAAI,IAAI,WAAW,SAAS,IAAI,QAAQ,QAAQ,IAAI,UAAU;AAC5D,gBAAM,YAAY,IAAI,mBAAmB,aAAa,GAAG;AAEzD,4BAAkB;AAElB,cAAI,CAAC,KAAK,SAAS;AACjB,kBAAM,IAAI,MAAM,wBAAwB;AAAA,UAC1C;AAEA,gBAAM,KAAK,QAAQ,QAAQ,SAAS;AAEpC,cAAI,GAAG,SAAS,MAAM;AACpB,oBAAQ,IAAI,uBAAuB;AACnC,gBAAI,oBAAoB,WAAW;AACjC,gCAAkB;AAAA,YACpB;AAAA,UACF,CAAC;AAED,eAAK,aAAa,SAAS;AAC3B;AAAA,QACF;AAEA,YAAI,IAAI,WAAW,UAAU,IAAI,KAAK,WAAW,WAAW,GAAG;AAC7D,cAAI,CAAC,iBAAiB;AACpB,gBAAI,UAAU,GAAG,EAAE,IAAI,qBAAqB;AAC5C;AAAA,UACF;AACA,gBAAM,gBAAgB,kBAAkB,KAAK,GAAG;AAChD;AAAA,QACF;AAEA,YAAI,UAAU,GAAG,EAAE,IAAI;AAAA,MACzB,CAAC;AAED,WAAK,YAAY,OAAO,QAAQ,IAAI,MAAM,SAAS;AAEnD,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,MAAc,aAAa,WAA+B;AACxD,QAAI;AACF,YAAM,UAAU,KAAK;AAAA,QACnB,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,QAAQ,EAAE,SAAS,6BAA6B;AAAA,MAClD,CAAC;AAED,UAAI,eAAe;AACnB,YAAM,WAAW,YAAY,YAAY;AACvC;AAEA,cAAM,UAAU,WAAW,YAAY,QAAO,oBAAI,KAAK,GAAE,YAAY,CAAC;AAEtE,YAAI;AACF,gBAAM,UAAU,KAAK;AAAA,YACnB,SAAS;AAAA,YACT,QAAQ;AAAA,YACR,QAAQ,EAAE,MAAM,QAAQ;AAAA,UAC1B,CAAC;AAED,kBAAQ,IAAI,SAAS,OAAO,EAAE;AAE9B,cAAI,iBAAiB,IAAI;AACvB,0BAAc,QAAQ;AAEtB,kBAAM,UAAU,KAAK;AAAA,cACnB,SAAS;AAAA,cACT,QAAQ;AAAA,cACR,QAAQ,EAAE,SAAS,mBAAmB;AAAA,YACxC,CAAC;AACD,oBAAQ,IAAI,kBAAkB;AAAA,UAChC;AAAA,QACF,SAAS,OAAO;AACd,kBAAQ,MAAM,0BAA0B,KAAK;AAC7C,wBAAc,QAAQ;AAAA,QACxB;AAAA,MACF,GAAG,GAAI;AAAA,IACT,SAAS,OAAO;AACd,cAAQ,MAAM,0BAA0B,KAAK;AAAA,IAC/C;AAAA,EACF;AAAA,EAEA,MAAa,OAAO;AAClB,QAAI,KAAK,aAAa;AACpB,WAAK,YAAY,MAAM;AAAA,IACzB;AAAA,EACF;AACF;","names":["tool","resource","prompt"]}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "fastmcp",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "main": "dist/fastmcp.js",
5
5
  "scripts": {
6
6
  "build": "tsup",
7
- "test": "vitest run",
7
+ "test": "vitest run && tsc",
8
8
  "format": "prettier --write . && eslint --fix ."
9
9
  },
10
10
  "bin": {
@@ -23,6 +23,7 @@
23
23
  "dependencies": {
24
24
  "@modelcontextprotocol/sdk": "^1.0.4",
25
25
  "execa": "^9.5.2",
26
+ "file-type": "^19.6.0",
26
27
  "yargs": "^17.7.2",
27
28
  "zod": "^3.24.1",
28
29
  "zod-to-json-schema": "^3.24.1"
@@ -46,6 +47,7 @@
46
47
  "prettier": "^3.4.2",
47
48
  "semantic-release": "^24.2.0",
48
49
  "tsup": "^8.3.5",
50
+ "typescript": "^5.7.2",
49
51
  "vitest": "^2.1.8"
50
52
  },
51
53
  "tsup": {
@@ -1,4 +1,4 @@
1
- import { FastMCP, UserError } from "./FastMCP.js";
1
+ import { FastMCP, UserError, imageContent } from "./FastMCP.js";
2
2
  import { z } from "zod";
3
3
  import { test, expect, vi } from "vitest";
4
4
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -8,7 +8,6 @@ import { EventSource } from "eventsource";
8
8
  import { setTimeout as delay } from "timers/promises";
9
9
  import {
10
10
  ErrorCode,
11
- JSONRPCMessage,
12
11
  LoggingMessageNotificationSchema,
13
12
  McpError,
14
13
  } from "@modelcontextprotocol/sdk/types.js";
@@ -82,7 +81,7 @@ test("adds tools", async () => {
82
81
  b: z.number(),
83
82
  }),
84
83
  execute: async (args) => {
85
- return args.a + args.b;
84
+ return String(args.a + args.b);
86
85
  },
87
86
  });
88
87
 
@@ -127,7 +126,7 @@ test("calls a tool", async () => {
127
126
  b: z.number(),
128
127
  }),
129
128
  execute: async (args) => {
130
- return args.a + args.b;
129
+ return String(args.a + args.b);
131
130
  },
132
131
  });
133
132
 
@@ -149,6 +148,101 @@ test("calls a tool", async () => {
149
148
  });
150
149
  });
151
150
 
151
+ test("returns a list", async () => {
152
+ await runWithTestServer({
153
+ start: async () => {
154
+ const server = new FastMCP({
155
+ name: "Test",
156
+ version: "1.0.0",
157
+ });
158
+
159
+ server.addTool({
160
+ name: "add",
161
+ description: "Add two numbers",
162
+ parameters: z.object({
163
+ a: z.number(),
164
+ b: z.number(),
165
+ }),
166
+ execute: async () => {
167
+ return {
168
+ content: [
169
+ { type: "text", text: "a" },
170
+ { type: "text", text: "b" },
171
+ ],
172
+ };
173
+ },
174
+ });
175
+
176
+ return server;
177
+ },
178
+ run: async ({ client }) => {
179
+ expect(
180
+ await client.callTool({
181
+ name: "add",
182
+ arguments: {
183
+ a: 1,
184
+ b: 2,
185
+ },
186
+ }),
187
+ ).toEqual({
188
+ content: [
189
+ { type: "text", text: "a" },
190
+ { type: "text", text: "b" },
191
+ ],
192
+ });
193
+ },
194
+ });
195
+ });
196
+
197
+ test("returns an image", async () => {
198
+ await runWithTestServer({
199
+ start: async () => {
200
+ const server = new FastMCP({
201
+ name: "Test",
202
+ version: "1.0.0",
203
+ });
204
+
205
+ server.addTool({
206
+ name: "add",
207
+ description: "Add two numbers",
208
+ parameters: z.object({
209
+ a: z.number(),
210
+ b: z.number(),
211
+ }),
212
+ execute: async () => {
213
+ return imageContent({
214
+ buffer: Buffer.from(
215
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
216
+ "base64",
217
+ ),
218
+ });
219
+ },
220
+ });
221
+
222
+ return server;
223
+ },
224
+ run: async ({ client }) => {
225
+ expect(
226
+ await client.callTool({
227
+ name: "add",
228
+ arguments: {
229
+ a: 1,
230
+ b: 2,
231
+ },
232
+ }),
233
+ ).toEqual({
234
+ content: [
235
+ {
236
+ type: "image",
237
+ data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
238
+ mimeType: "image/png",
239
+ },
240
+ ],
241
+ });
242
+ },
243
+ });
244
+ });
245
+
152
246
  test("handles UserError errors", async () => {
153
247
  await runWithTestServer({
154
248
  start: async () => {
@@ -164,7 +258,7 @@ test("handles UserError errors", async () => {
164
258
  a: z.number(),
165
259
  b: z.number(),
166
260
  }),
167
- execute: async (args) => {
261
+ execute: async () => {
168
262
  throw new UserError("Something went wrong");
169
263
  },
170
264
  });
@@ -240,7 +334,7 @@ test("tracks tool progress", async () => {
240
334
 
241
335
  await delay(100);
242
336
 
243
- return args.a + args.b;
337
+ return String(args.a + args.b);
244
338
  },
245
339
  });
246
340
 
@@ -294,29 +388,6 @@ test("sets logging levels", async () => {
294
388
  });
295
389
  });
296
390
 
297
- const onMessage = (
298
- client: Client,
299
- callback: (message: JSONRPCMessage) => void,
300
- ) => {
301
- if (!client.transport) {
302
- throw new Error("Transport not set");
303
- }
304
-
305
- const onmessage = client.transport.onmessage;
306
-
307
- if (!onmessage) {
308
- throw new Error("onmessage not set");
309
- }
310
-
311
- client.transport.onmessage = (message) => {
312
- console.log("message", message);
313
-
314
- onmessage(message);
315
-
316
- callback(message);
317
- };
318
- };
319
-
320
391
  test("sends logging messages to the client", async () => {
321
392
  await runWithTestServer({
322
393
  start: async () => {
@@ -340,13 +411,13 @@ test("sends logging messages to the client", async () => {
340
411
  log.info("info message");
341
412
  log.warn("warn message");
342
413
 
343
- return args.a + args.b;
414
+ return String(args.a + args.b);
344
415
  },
345
416
  });
346
417
 
347
418
  return server;
348
419
  },
349
- run: async ({ client, server }) => {
420
+ run: async ({ client }) => {
350
421
  const onLog = vi.fn();
351
422
 
352
423
  client.setNotificationHandler(
package/src/FastMCP.ts CHANGED
@@ -9,17 +9,50 @@ import {
9
9
  ListResourcesRequestSchema,
10
10
  ListToolsRequestSchema,
11
11
  LoggingLevel,
12
- LoggingLevelSchema,
13
- LoggingMessageNotificationSchema,
14
12
  McpError,
15
- NotificationSchema,
16
13
  ReadResourceRequestSchema,
17
14
  ServerCapabilities,
18
15
  SetLevelRequestSchema,
19
16
  } from "@modelcontextprotocol/sdk/types.js";
20
17
  import { zodToJsonSchema } from "zod-to-json-schema";
21
- import type { z } from "zod";
18
+ import { z } from "zod";
22
19
  import http from "http";
20
+ import { readFile } from "fs/promises";
21
+ import { fileTypeFromBuffer } from "file-type";
22
+
23
+ export const imageContent = async (
24
+ input: { url: string } | { path: string } | { buffer: Buffer },
25
+ ): Promise<ImageContent> => {
26
+ let rawData: Buffer;
27
+
28
+ if ("url" in input) {
29
+ const response = await fetch(input.url);
30
+
31
+ if (!response.ok) {
32
+ throw new Error(`Failed to fetch image from URL: ${response.statusText}`);
33
+ }
34
+
35
+ rawData = Buffer.from(await response.arrayBuffer());
36
+ } else if ("path" in input) {
37
+ rawData = await readFile(input.path);
38
+ } else if ("buffer" in input) {
39
+ rawData = input.buffer;
40
+ } else {
41
+ throw new Error(
42
+ "Invalid input: Provide a valid 'url', 'path', or 'buffer'",
43
+ );
44
+ }
45
+
46
+ const mimeType = await fileTypeFromBuffer(rawData);
47
+
48
+ const base64Data = rawData.toString("base64");
49
+
50
+ return {
51
+ type: "image",
52
+ data: base64Data,
53
+ mimeType: mimeType?.mime ?? "image/png",
54
+ } as const;
55
+ };
23
56
 
24
57
  abstract class FastMCPError extends Error {
25
58
  public constructor(message?: string) {
@@ -74,11 +107,56 @@ type Context = {
74
107
  };
75
108
  };
76
109
 
110
+ const TextContentZodSchema = z
111
+ .object({
112
+ type: z.literal("text"),
113
+ /**
114
+ * The text content of the message.
115
+ */
116
+ text: z.string(),
117
+ })
118
+ .strict();
119
+
120
+ type TextContent = z.infer<typeof TextContentZodSchema>;
121
+
122
+ const ImageContentZodSchema = z
123
+ .object({
124
+ type: z.literal("image"),
125
+ /**
126
+ * The base64-encoded image data.
127
+ */
128
+ data: z.string().base64(),
129
+ /**
130
+ * The MIME type of the image. Different providers may support different image types.
131
+ */
132
+ mimeType: z.string(),
133
+ })
134
+ .strict();
135
+
136
+ type ImageContent = z.infer<typeof ImageContentZodSchema>;
137
+
138
+ const ContentZodSchema = z.discriminatedUnion("type", [
139
+ TextContentZodSchema,
140
+ ImageContentZodSchema,
141
+ ]);
142
+
143
+ const ContentResultZodSchema = z
144
+ .object({
145
+ content: ContentZodSchema.array(),
146
+ isError: z.boolean().optional(),
147
+ })
148
+ .strict();
149
+
150
+ type ContentResult = z.infer<typeof ContentResultZodSchema>;
151
+
77
152
  type Tool<Params extends ToolParameters = ToolParameters> = {
78
153
  name: string;
79
154
  description?: string;
80
155
  parameters?: Params;
81
- execute: (args: z.infer<Params>, context: Context) => Promise<unknown>;
156
+ execute: (
157
+ args: z.infer<Params>,
158
+ context: Context,
159
+ ) => Promise<string | ContentResult | TextContent | ImageContent>;
82
160
  };
83
161
 
84
162
  type Resource = {
@@ -185,118 +263,119 @@ export class FastMCP {
185
263
  };
186
264
  });
187
265
 
188
- server.setRequestHandler(
189
- CallToolRequestSchema,
190
- async (request, ...extra) => {
191
- const tool = this.#tools.find(
192
- (tool) => tool.name === request.params.name,
266
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
267
+ const tool = this.#tools.find(
268
+ (tool) => tool.name === request.params.name,
269
+ );
270
+
271
+ if (!tool) {
272
+ throw new McpError(
273
+ ErrorCode.MethodNotFound,
274
+ `Unknown tool: ${request.params.name}`,
193
275
  );
276
+ }
277
+
278
+ let args: any = undefined;
194
279
 
195
- if (!tool) {
280
+ if (tool.parameters) {
281
+ const parsed = tool.parameters.safeParse(request.params.arguments);
282
+
283
+ if (!parsed.success) {
196
284
  throw new McpError(
197
- ErrorCode.MethodNotFound,
198
- `Unknown tool: ${request.params.name}`,
285
+ ErrorCode.InvalidRequest,
286
+ `Invalid ${request.params.name} arguments`,
199
287
  );
200
288
  }
201
289
 
202
- let args: any = undefined;
203
-
204
- if (tool.parameters) {
205
- const parsed = tool.parameters.safeParse(request.params.arguments);
206
-
207
- if (!parsed.success) {
208
- throw new McpError(
209
- ErrorCode.InvalidRequest,
210
- `Invalid ${request.params.name} arguments`,
211
- );
212
- }
290
+ args = parsed.data;
291
+ }
213
292
 
214
- args = parsed.data;
215
- }
293
+ const progressToken = request.params?._meta?.progressToken;
216
294
 
217
- const progressToken = request.params?._meta?.progressToken;
295
+ let result: ContentResult;
218
296
 
219
- let result: any;
297
+ try {
298
+ const reportProgress = async (progress: Progress) => {
299
+ await server.notification({
300
+ method: "notifications/progress",
301
+ params: {
302
+ ...progress,
303
+ progressToken,
304
+ },
305
+ });
306
+ };
220
307
 
221
- try {
222
- const reportProgress = async (progress: Progress) => {
223
- await server.notification({
224
- method: "notifications/progress",
225
- params: {
226
- ...progress,
227
- progressToken,
308
+ const log = {
309
+ debug: (message: string, context?: SerializableValue) => {
310
+ server.sendLoggingMessage({
311
+ level: "debug",
312
+ data: {
313
+ message,
314
+ context,
228
315
  },
229
316
  });
230
- };
317
+ },
318
+ error: (message: string, context?: SerializableValue) => {
319
+ server.sendLoggingMessage({
320
+ level: "error",
321
+ data: {
322
+ message,
323
+ context,
324
+ },
325
+ });
326
+ },
327
+ info: (message: string, context?: SerializableValue) => {
328
+ server.sendLoggingMessage({
329
+ level: "info",
330
+ data: {
331
+ message,
332
+ context,
333
+ },
334
+ });
335
+ },
336
+ warn: (message: string, context?: SerializableValue) => {
337
+ server.sendLoggingMessage({
338
+ level: "warning",
339
+ data: {
340
+ message,
341
+ context,
342
+ },
343
+ });
344
+ },
345
+ };
231
346
 
232
- const log = {
233
- debug: (message: string, context?: SerializableValue) => {
234
- server.sendLoggingMessage({
235
- level: "debug",
236
- data: {
237
- message,
238
- context,
239
- },
240
- });
241
- },
242
- error: (message: string, context?: SerializableValue) => {
243
- server.sendLoggingMessage({
244
- level: "error",
245
- data: {
246
- message,
247
- context,
248
- },
249
- });
250
- },
251
- info: (message: string, context?: SerializableValue) => {
252
- server.sendLoggingMessage({
253
- level: "info",
254
- data: {
255
- message,
256
- context,
257
- },
258
- });
259
- },
260
- warn: (message: string, context?: SerializableValue) => {
261
- server.sendLoggingMessage({
262
- level: "warning",
263
- data: {
264
- message,
265
- context,
266
- },
267
- });
268
- },
269
- };
347
+ const maybeStringResult = await tool.execute(args, {
348
+ reportProgress,
349
+ log,
350
+ });
270
351
 
271
- result = await tool.execute(args, {
272
- reportProgress,
273
- log,
352
+ if (typeof maybeStringResult === "string") {
353
+ result = ContentResultZodSchema.parse({
354
+ content: [{ type: "text", text: maybeStringResult }],
274
355
  });
275
- } catch (error) {
276
- if (error instanceof UserError) {
277
- return {
278
- content: [{ type: "text", text: error.message }],
279
- isError: true,
280
- };
281
- }
282
-
283
- return {
284
- content: [{ type: "text", text: `Error: ${error}` }],
285
- isError: true,
286
- };
356
+ } else if ("type" in maybeStringResult) {
357
+ result = ContentResultZodSchema.parse({
358
+ content: [maybeStringResult],
359
+ });
360
+ } else {
361
+ result = ContentResultZodSchema.parse(maybeStringResult);
287
362
  }
288
-
289
- if (typeof result === "string") {
363
+ } catch (error) {
364
+ if (error instanceof UserError) {
290
365
  return {
291
- content: [{ type: "text", text: result }],
366
+ content: [{ type: "text", text: error.message }],
367
+ isError: true,
292
368
  };
293
369
  }
294
370
 
295
371
  return {
296
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
372
+ content: [{ type: "text", text: `Error: ${error}` }],
373
+ isError: true,
297
374
  };
298
- },
299
- );
375
+ }
376
+
377
+ return result;
378
+ });
300
379
  }
301
380
 
302
381
  private setupResourceHandlers(server: Server) {
package/tsconfig.json CHANGED
@@ -1,3 +1,8 @@
1
1
  {
2
- "extends": "@tsconfig/node22/tsconfig.json"
2
+ "extends": "@tsconfig/node22/tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "noUnusedLocals": true,
6
+ "noUnusedParameters": true
7
+ }
3
8
  }