fastmcp 1.4.0 → 1.5.1

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
@@ -9,6 +9,7 @@ A TypeScript framework for building [MCP](https://modelcontextprotocol.io/) serv
9
9
 
10
10
  - Simple Tool, Resource, Prompt definition
11
11
  - Full TypeScript support
12
+ - Built-in support for [image content](#returning-an-image)
12
13
  - Built-in [logging](#logging)
13
14
  - Built-in [error handling](#errors)
14
15
  - Built-in CLI for [testing](#test-with-mcp-cli) and [debugging](#inspect-with-mcp-inspector)
@@ -171,6 +172,75 @@ server.addTool({
171
172
  });
172
173
  ```
173
174
 
175
+ #### Returning an image
176
+
177
+ Use the `imageContent` to create a content object for an image:
178
+
179
+ ```js
180
+ import { imageContent } from "fastmcp";
181
+
182
+ server.addTool({
183
+ name: "download",
184
+ description: "Download a file",
185
+ parameters: z.object({
186
+ url: z.string(),
187
+ }),
188
+ execute: async (args) => {
189
+ return imageContent({
190
+ url: "https://example.com/image.png",
191
+ });
192
+
193
+ // or...
194
+ // return imageContent({
195
+ // path: "/path/to/image.png",
196
+ // });
197
+
198
+ // or...
199
+ // return imageContent({
200
+ // buffer: Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", "base64"),
201
+ // });
202
+
203
+ // or...
204
+ // return {
205
+ // content: [
206
+ // await imageContent(...)
207
+ // ],
208
+ // };
209
+ },
210
+ });
211
+ ```
212
+
213
+ The `imageContent` function takes the following options:
214
+
215
+ - `url`: The URL of the image.
216
+ - `path`: The path to the image file.
217
+ - `buffer`: The image data as a buffer.
218
+
219
+ Only one of `url`, `path`, or `buffer` must be specified.
220
+
221
+ The above example is equivalent to:
222
+
223
+ ```js
224
+ server.addTool({
225
+ name: "download",
226
+ description: "Download a file",
227
+ parameters: z.object({
228
+ url: z.string(),
229
+ }),
230
+ execute: async (args) => {
231
+ return {
232
+ content: [
233
+ {
234
+ type: "image",
235
+ data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
236
+ mimeType: "image/png",
237
+ },
238
+ ],
239
+ };
240
+ },
241
+ });
242
+ ```
243
+
174
244
  #### Logging
175
245
 
176
246
  Tools can log messages to the client using the `log` object in the context object:
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,66 +42,25 @@ type Context = {
35
42
  warn: (message: string, data?: SerializableValue) => void;
36
43
  };
37
44
  };
38
- declare const ContentResultZodSchema: z.ZodObject<{
39
- content: z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
40
- type: z.ZodLiteral<"text">;
41
- /**
42
- * The text content of the message.
43
- */
44
- text: z.ZodString;
45
- }, "strict", z.ZodTypeAny, {
46
- type: "text";
47
- text: string;
48
- }, {
49
- type: "text";
50
- text: string;
51
- }>, z.ZodObject<{
52
- type: z.ZodLiteral<"image">;
53
- /**
54
- * The base64-encoded image data.
55
- */
56
- data: z.ZodString;
57
- /**
58
- * The MIME type of the image. Different providers may support different image types.
59
- */
60
- mimeType: z.ZodString;
61
- }, "strict", z.ZodTypeAny, {
62
- type: "image";
63
- data: string;
64
- mimeType: string;
65
- }, {
66
- type: "image";
67
- data: string;
68
- mimeType: string;
69
- }>]>, "many">;
70
- isError: z.ZodOptional<z.ZodBoolean>;
71
- }, "strict", z.ZodTypeAny, {
72
- content: ({
73
- type: "text";
74
- text: string;
75
- } | {
76
- type: "image";
77
- data: string;
78
- mimeType: string;
79
- })[];
80
- isError?: boolean | undefined;
81
- }, {
82
- content: ({
83
- type: "text";
84
- text: string;
85
- } | {
86
- type: "image";
87
- data: string;
88
- mimeType: string;
89
- })[];
90
- isError?: boolean | undefined;
91
- }>;
92
- type ContentResult = z.infer<typeof ContentResultZodSchema>;
45
+ type TextContent = {
46
+ type: "text";
47
+ text: string;
48
+ };
49
+ type ImageContent = {
50
+ type: "image";
51
+ data: string;
52
+ mimeType: string;
53
+ };
54
+ type Content = TextContent | ImageContent;
55
+ type ContentResult = {
56
+ content: Content[];
57
+ isError?: boolean;
58
+ };
93
59
  type Tool<Params extends ToolParameters = ToolParameters> = {
94
60
  name: string;
95
61
  description?: string;
96
62
  parameters?: Params;
97
- execute: (args: z.infer<Params>, context: Context) => Promise<string | ContentResult>;
63
+ execute: (args: z.infer<Params>, context: Context) => Promise<string | ContentResult | TextContent | ImageContent>;
98
64
  };
99
65
  type Resource = {
100
66
  uri: string;
@@ -127,13 +93,14 @@ type ServerOptions = {
127
93
  name: string;
128
94
  version: `${number}.${number}.${number}`;
129
95
  };
96
+ type LoggingLevel = "debug" | "info" | "notice" | "warning" | "error" | "critical" | "alert" | "emergency";
130
97
  declare class FastMCP {
131
98
  #private;
132
99
  options: ServerOptions;
133
100
  constructor(options: ServerOptions);
134
101
  private setupHandlers;
135
102
  private setupErrorHandling;
136
- get loggingLevel(): "debug" | "info" | "notice" | "warning" | "error" | "critical" | "alert" | "emergency";
103
+ get loggingLevel(): LoggingLevel;
137
104
  private setupToolHandlers;
138
105
  private setupResourceHandlers;
139
106
  private setupPromptHandlers;
@@ -156,4 +123,4 @@ declare class FastMCP {
156
123
  stop(): Promise<void>;
157
124
  }
158
125
 
159
- export { FastMCP, UserError };
126
+ export { FastMCP, UserError, imageContent };
package/dist/FastMCP.js CHANGED
@@ -16,6 +16,33 @@ import {
16
16
  import { zodToJsonSchema } from "zod-to-json-schema";
17
17
  import { z } from "zod";
18
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
+ };
19
46
  var FastMCPError = class extends Error {
20
47
  constructor(message) {
21
48
  super(message);
@@ -112,105 +139,106 @@ var FastMCP = class {
112
139
  })
113
140
  };
114
141
  });
115
- server.setRequestHandler(
116
- CallToolRequestSchema,
117
- async (request, ...extra) => {
118
- const tool = this.#tools.find(
119
- (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}`
120
150
  );
121
- 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) {
122
156
  throw new McpError(
123
- ErrorCode.MethodNotFound,
124
- `Unknown tool: ${request.params.name}`
157
+ ErrorCode.InvalidRequest,
158
+ `Invalid ${request.params.name} arguments`
125
159
  );
126
160
  }
127
- let args = void 0;
128
- if (tool.parameters) {
129
- const parsed = tool.parameters.safeParse(request.params.arguments);
130
- if (!parsed.success) {
131
- throw new McpError(
132
- ErrorCode.InvalidRequest,
133
- `Invalid ${request.params.name} arguments`
134
- );
135
- }
136
- args = parsed.data;
137
- }
138
- const progressToken = request.params?._meta?.progressToken;
139
- let result;
140
- try {
141
- const reportProgress = async (progress) => {
142
- await server.notification({
143
- method: "notifications/progress",
144
- params: {
145
- ...progress,
146
- progressToken
147
- }
148
- });
149
- };
150
- const log = {
151
- debug: (message, context) => {
152
- server.sendLoggingMessage({
153
- level: "debug",
154
- data: {
155
- message,
156
- context
157
- }
158
- });
159
- },
160
- error: (message, context) => {
161
- server.sendLoggingMessage({
162
- level: "error",
163
- data: {
164
- message,
165
- context
166
- }
167
- });
168
- },
169
- info: (message, context) => {
170
- server.sendLoggingMessage({
171
- level: "info",
172
- data: {
173
- message,
174
- context
175
- }
176
- });
177
- },
178
- warn: (message, context) => {
179
- server.sendLoggingMessage({
180
- level: "warning",
181
- data: {
182
- message,
183
- context
184
- }
185
- });
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
186
172
  }
187
- };
188
- const maybeStringResult = await tool.execute(args, {
189
- reportProgress,
190
- log
191
173
  });
192
- if (typeof maybeStringResult === "string") {
193
- result = ContentResultZodSchema.parse({
194
- content: [{ type: "text", text: maybeStringResult }]
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
+ }
195
210
  });
196
- } else {
197
- result = ContentResultZodSchema.parse(maybeStringResult);
198
- }
199
- } catch (error) {
200
- if (error instanceof UserError) {
201
- return {
202
- content: [{ type: "text", text: error.message }],
203
- isError: true
204
- };
205
211
  }
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);
227
+ }
228
+ } catch (error) {
229
+ if (error instanceof UserError) {
206
230
  return {
207
- content: [{ type: "text", text: `Error: ${error}` }],
231
+ content: [{ type: "text", text: error.message }],
208
232
  isError: true
209
233
  };
210
234
  }
211
- return result;
235
+ return {
236
+ content: [{ type: "text", text: `Error: ${error}` }],
237
+ isError: true
238
+ };
212
239
  }
213
- );
240
+ return result;
241
+ });
214
242
  }
215
243
  setupResourceHandlers(server) {
216
244
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
@@ -427,6 +455,7 @@ var FastMCP = class {
427
455
  };
428
456
  export {
429
457
  FastMCP,
430
- UserError
458
+ UserError,
459
+ imageContent
431
460
  };
432
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 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\";\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>;\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: 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 {\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\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 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\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 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\n process.on(\"SIGINT\", async () => {\n await server.close();\n process.exit(0);\n });\n }\n\n public get loggingLevel(): 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":"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","names":["tool","resource","prompt"]}
package/jsr.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@glama/fastmcp",
3
+ "version": "1.5.1",
4
+ "exports": "./src/FastMCP.ts"
5
+ }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "fastmcp",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
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"
@@ -33,9 +34,17 @@
33
34
  "release": {
34
35
  "branches": [
35
36
  "main"
37
+ ],
38
+ "plugins": [
39
+ "@semantic-release/commit-analyzer",
40
+ "@semantic-release/release-notes-generator",
41
+ "@semantic-release/npm",
42
+ "@semantic-release/github",
43
+ "@sebbo2002/semantic-release-jsr"
36
44
  ]
37
45
  },
38
46
  "devDependencies": {
47
+ "@sebbo2002/semantic-release-jsr": "^2.0.2",
39
48
  "@tsconfig/node22": "^22.0.0",
40
49
  "@types/node": "^22.10.2",
41
50
  "@types/yargs": "^17.0.33",
@@ -46,6 +55,7 @@
46
55
  "prettier": "^3.4.2",
47
56
  "semantic-release": "^24.2.0",
48
57
  "tsup": "^8.3.5",
58
+ "typescript": "^5.7.2",
49
59
  "vitest": "^2.1.8"
50
60
  },
51
61
  "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";
@@ -163,7 +163,7 @@ test("returns a list", async () => {
163
163
  a: z.number(),
164
164
  b: z.number(),
165
165
  }),
166
- execute: async (args) => {
166
+ execute: async () => {
167
167
  return {
168
168
  content: [
169
169
  { type: "text", text: "a" },
@@ -194,6 +194,55 @@ test("returns a list", async () => {
194
194
  });
195
195
  });
196
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
+
197
246
  test("handles UserError errors", async () => {
198
247
  await runWithTestServer({
199
248
  start: async () => {
@@ -368,7 +417,7 @@ test("sends logging messages to the client", async () => {
368
417
 
369
418
  return server;
370
419
  },
371
- run: async ({ client, server }) => {
420
+ run: async ({ client }) => {
372
421
  const onLog = vi.fn();
373
422
 
374
423
  client.setNotificationHandler(
package/src/FastMCP.ts CHANGED
@@ -8,7 +8,6 @@ import {
8
8
  ListPromptsRequestSchema,
9
9
  ListResourcesRequestSchema,
10
10
  ListToolsRequestSchema,
11
- LoggingLevel,
12
11
  McpError,
13
12
  ReadResourceRequestSchema,
14
13
  ServerCapabilities,
@@ -17,6 +16,42 @@ import {
17
16
  import { zodToJsonSchema } from "zod-to-json-schema";
18
17
  import { z } from "zod";
19
18
  import http from "http";
19
+ import { readFile } from "fs/promises";
20
+ import { fileTypeFromBuffer } from "file-type";
21
+
22
+ export const imageContent = async (
23
+ input: { url: string } | { path: string } | { buffer: Buffer },
24
+ ): Promise<ImageContent> => {
25
+ let rawData: Buffer;
26
+
27
+ if ("url" in input) {
28
+ const response = await fetch(input.url);
29
+
30
+ if (!response.ok) {
31
+ throw new Error(`Failed to fetch image from URL: ${response.statusText}`);
32
+ }
33
+
34
+ rawData = Buffer.from(await response.arrayBuffer());
35
+ } else if ("path" in input) {
36
+ rawData = await readFile(input.path);
37
+ } else if ("buffer" in input) {
38
+ rawData = input.buffer;
39
+ } else {
40
+ throw new Error(
41
+ "Invalid input: Provide a valid 'url', 'path', or 'buffer'",
42
+ );
43
+ }
44
+
45
+ const mimeType = await fileTypeFromBuffer(rawData);
46
+
47
+ const base64Data = rawData.toString("base64");
48
+
49
+ return {
50
+ type: "image",
51
+ data: base64Data,
52
+ mimeType: mimeType?.mime ?? "image/png",
53
+ } as const;
54
+ };
20
55
 
21
56
  abstract class FastMCPError extends Error {
22
57
  public constructor(message?: string) {
@@ -71,6 +106,11 @@ type Context = {
71
106
  };
72
107
  };
73
108
 
109
+ type TextContent = {
110
+ type: "text";
111
+ text: string;
112
+ };
113
+
74
114
  const TextContentZodSchema = z
75
115
  .object({
76
116
  type: z.literal("text"),
@@ -79,9 +119,13 @@ const TextContentZodSchema = z
79
119
  */
80
120
  text: z.string(),
81
121
  })
82
- .strict();
122
+ .strict() satisfies z.ZodType<TextContent>;
83
123
 
84
- type TextContent = z.infer<typeof TextContentZodSchema>;
124
+ type ImageContent = {
125
+ type: "image";
126
+ data: string;
127
+ mimeType: string;
128
+ };
85
129
 
86
130
  const ImageContentZodSchema = z
87
131
  .object({
@@ -95,23 +139,26 @@ const ImageContentZodSchema = z
95
139
  */
96
140
  mimeType: z.string(),
97
141
  })
98
- .strict();
142
+ .strict() satisfies z.ZodType<ImageContent>;
99
143
 
100
- type ImageContent = z.infer<typeof ImageContentZodSchema>;
144
+ type Content = TextContent | ImageContent;
101
145
 
102
146
  const ContentZodSchema = z.discriminatedUnion("type", [
103
147
  TextContentZodSchema,
104
148
  ImageContentZodSchema,
105
- ]);
149
+ ]) satisfies z.ZodType<Content>;
150
+
151
+ type ContentResult = {
152
+ content: Content[];
153
+ isError?: boolean;
154
+ };
106
155
 
107
156
  const ContentResultZodSchema = z
108
157
  .object({
109
158
  content: ContentZodSchema.array(),
110
159
  isError: z.boolean().optional(),
111
160
  })
112
- .strict();
113
-
114
- type ContentResult = z.infer<typeof ContentResultZodSchema>;
161
+ .strict() satisfies z.ZodType<ContentResult>;
115
162
 
116
163
  type Tool<Params extends ToolParameters = ToolParameters> = {
117
164
  name: string;
@@ -120,7 +167,7 @@ type Tool<Params extends ToolParameters = ToolParameters> = {
120
167
  execute: (
121
168
  args: z.infer<Params>,
122
169
  context: Context,
123
- ) => Promise<string | ContentResult>;
170
+ ) => Promise<string | ContentResult | TextContent | ImageContent>;
124
171
  };
125
172
 
126
173
  type Resource = {
@@ -161,6 +208,16 @@ type ServerOptions = {
161
208
  version: `${number}.${number}.${number}`;
162
209
  };
163
210
 
211
+ type LoggingLevel =
212
+ | "debug"
213
+ | "info"
214
+ | "notice"
215
+ | "warning"
216
+ | "error"
217
+ | "critical"
218
+ | "alert"
219
+ | "emergency";
220
+
164
221
  export class FastMCP {
165
222
  #tools: Tool[];
166
223
  #resources: Resource[];
@@ -202,13 +259,14 @@ export class FastMCP {
202
259
  server.onerror = (error) => {
203
260
  console.error("[MCP Error]", error);
204
261
  };
262
+
205
263
  process.on("SIGINT", async () => {
206
264
  await server.close();
207
265
  process.exit(0);
208
266
  });
209
267
  }
210
268
 
211
- public get loggingLevel() {
269
+ public get loggingLevel(): LoggingLevel {
212
270
  return this.#loggingLevel;
213
271
  }
214
272
 
@@ -227,118 +285,119 @@ export class FastMCP {
227
285
  };
228
286
  });
229
287
 
230
- server.setRequestHandler(
231
- CallToolRequestSchema,
232
- async (request, ...extra) => {
233
- const tool = this.#tools.find(
234
- (tool) => tool.name === request.params.name,
288
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
289
+ const tool = this.#tools.find(
290
+ (tool) => tool.name === request.params.name,
291
+ );
292
+
293
+ if (!tool) {
294
+ throw new McpError(
295
+ ErrorCode.MethodNotFound,
296
+ `Unknown tool: ${request.params.name}`,
235
297
  );
298
+ }
299
+
300
+ let args: any = undefined;
301
+
302
+ if (tool.parameters) {
303
+ const parsed = tool.parameters.safeParse(request.params.arguments);
236
304
 
237
- if (!tool) {
305
+ if (!parsed.success) {
238
306
  throw new McpError(
239
- ErrorCode.MethodNotFound,
240
- `Unknown tool: ${request.params.name}`,
307
+ ErrorCode.InvalidRequest,
308
+ `Invalid ${request.params.name} arguments`,
241
309
  );
242
310
  }
243
311
 
244
- let args: any = undefined;
245
-
246
- if (tool.parameters) {
247
- const parsed = tool.parameters.safeParse(request.params.arguments);
248
-
249
- if (!parsed.success) {
250
- throw new McpError(
251
- ErrorCode.InvalidRequest,
252
- `Invalid ${request.params.name} arguments`,
253
- );
254
- }
255
-
256
- args = parsed.data;
257
- }
312
+ args = parsed.data;
313
+ }
258
314
 
259
- const progressToken = request.params?._meta?.progressToken;
315
+ const progressToken = request.params?._meta?.progressToken;
260
316
 
261
- let result: ContentResult;
317
+ let result: ContentResult;
262
318
 
263
- try {
264
- const reportProgress = async (progress: Progress) => {
265
- await server.notification({
266
- method: "notifications/progress",
267
- params: {
268
- ...progress,
269
- progressToken,
319
+ try {
320
+ const reportProgress = async (progress: Progress) => {
321
+ await server.notification({
322
+ method: "notifications/progress",
323
+ params: {
324
+ ...progress,
325
+ progressToken,
326
+ },
327
+ });
328
+ };
329
+
330
+ const log = {
331
+ debug: (message: string, context?: SerializableValue) => {
332
+ server.sendLoggingMessage({
333
+ level: "debug",
334
+ data: {
335
+ message,
336
+ context,
270
337
  },
271
338
  });
272
- };
339
+ },
340
+ error: (message: string, context?: SerializableValue) => {
341
+ server.sendLoggingMessage({
342
+ level: "error",
343
+ data: {
344
+ message,
345
+ context,
346
+ },
347
+ });
348
+ },
349
+ info: (message: string, context?: SerializableValue) => {
350
+ server.sendLoggingMessage({
351
+ level: "info",
352
+ data: {
353
+ message,
354
+ context,
355
+ },
356
+ });
357
+ },
358
+ warn: (message: string, context?: SerializableValue) => {
359
+ server.sendLoggingMessage({
360
+ level: "warning",
361
+ data: {
362
+ message,
363
+ context,
364
+ },
365
+ });
366
+ },
367
+ };
273
368
 
274
- const log = {
275
- debug: (message: string, context?: SerializableValue) => {
276
- server.sendLoggingMessage({
277
- level: "debug",
278
- data: {
279
- message,
280
- context,
281
- },
282
- });
283
- },
284
- error: (message: string, context?: SerializableValue) => {
285
- server.sendLoggingMessage({
286
- level: "error",
287
- data: {
288
- message,
289
- context,
290
- },
291
- });
292
- },
293
- info: (message: string, context?: SerializableValue) => {
294
- server.sendLoggingMessage({
295
- level: "info",
296
- data: {
297
- message,
298
- context,
299
- },
300
- });
301
- },
302
- warn: (message: string, context?: SerializableValue) => {
303
- server.sendLoggingMessage({
304
- level: "warning",
305
- data: {
306
- message,
307
- context,
308
- },
309
- });
310
- },
311
- };
369
+ const maybeStringResult = await tool.execute(args, {
370
+ reportProgress,
371
+ log,
372
+ });
312
373
 
313
- const maybeStringResult = await tool.execute(args, {
314
- reportProgress,
315
- log,
374
+ if (typeof maybeStringResult === "string") {
375
+ result = ContentResultZodSchema.parse({
376
+ content: [{ type: "text", text: maybeStringResult }],
316
377
  });
317
-
318
- if (typeof maybeStringResult === "string") {
319
- result = ContentResultZodSchema.parse({
320
- content: [{ type: "text", text: maybeStringResult }],
321
- });
322
- } else {
323
- result = ContentResultZodSchema.parse(maybeStringResult);
324
- }
325
- } catch (error) {
326
- if (error instanceof UserError) {
327
- return {
328
- content: [{ type: "text", text: error.message }],
329
- isError: true,
330
- };
331
- }
332
-
378
+ } else if ("type" in maybeStringResult) {
379
+ result = ContentResultZodSchema.parse({
380
+ content: [maybeStringResult],
381
+ });
382
+ } else {
383
+ result = ContentResultZodSchema.parse(maybeStringResult);
384
+ }
385
+ } catch (error) {
386
+ if (error instanceof UserError) {
333
387
  return {
334
- content: [{ type: "text", text: `Error: ${error}` }],
388
+ content: [{ type: "text", text: error.message }],
335
389
  isError: true,
336
390
  };
337
391
  }
338
392
 
339
- return result;
340
- },
341
- );
393
+ return {
394
+ content: [{ type: "text", text: `Error: ${error}` }],
395
+ isError: true,
396
+ };
397
+ }
398
+
399
+ return result;
400
+ });
342
401
  }
343
402
 
344
403
  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
  }