fastmcp 1.23.0 → 1.23.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/dist/FastMCP.d.ts +165 -165
- package/dist/FastMCP.js +275 -275
- package/dist/FastMCP.js.map +1 -1
- package/dist/bin/fastmcp.js +9 -9
- package/dist/bin/fastmcp.js.map +1 -1
- package/eslint.config.ts +14 -0
- package/jsr.json +1 -1
- package/package.json +6 -1
- package/src/FastMCP.test.ts +396 -394
- package/src/FastMCP.ts +510 -510
- package/src/bin/fastmcp.ts +7 -7
- package/src/examples/addition.ts +25 -24
- package/eslint.config.js +0 -3
package/src/FastMCP.test.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import { FastMCP, FastMCPSession, UserError, imageContent } from "./FastMCP.js";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { test, expect, vi } from "vitest";
|
|
4
1
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
5
2
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
6
|
-
import { getRandomPort } from "get-port-please";
|
|
7
|
-
import { setTimeout as delay } from "timers/promises";
|
|
8
3
|
import {
|
|
9
4
|
CreateMessageRequestSchema,
|
|
10
5
|
ErrorCode,
|
|
@@ -15,13 +10,18 @@ import {
|
|
|
15
10
|
Root,
|
|
16
11
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
17
12
|
import { createEventSource, EventSourceClient } from "eventsource-client";
|
|
13
|
+
import { getRandomPort } from "get-port-please";
|
|
14
|
+
import { setTimeout as delay } from "timers/promises";
|
|
15
|
+
import { expect, test, vi } from "vitest";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
|
|
18
|
+
import { FastMCP, FastMCPSession, imageContent, UserError } from "./FastMCP.js";
|
|
18
19
|
|
|
19
20
|
const runWithTestServer = async ({
|
|
20
|
-
run,
|
|
21
21
|
client: createClient,
|
|
22
|
+
run,
|
|
22
23
|
server: createServer,
|
|
23
24
|
}: {
|
|
24
|
-
server?: () => Promise<FastMCP>;
|
|
25
25
|
client?: () => Promise<Client>;
|
|
26
26
|
run: ({
|
|
27
27
|
client,
|
|
@@ -31,6 +31,7 @@ const runWithTestServer = async ({
|
|
|
31
31
|
server: FastMCP;
|
|
32
32
|
session: FastMCPSession;
|
|
33
33
|
}) => Promise<void>;
|
|
34
|
+
server?: () => Promise<FastMCP>;
|
|
34
35
|
}) => {
|
|
35
36
|
const port = await getRandomPort();
|
|
36
37
|
|
|
@@ -42,11 +43,11 @@ const runWithTestServer = async ({
|
|
|
42
43
|
});
|
|
43
44
|
|
|
44
45
|
await server.start({
|
|
45
|
-
transportType: "sse",
|
|
46
46
|
sse: {
|
|
47
47
|
endpoint: "/sse",
|
|
48
48
|
port,
|
|
49
49
|
},
|
|
50
|
+
transportType: "sse",
|
|
50
51
|
});
|
|
51
52
|
|
|
52
53
|
try {
|
|
@@ -84,51 +85,26 @@ const runWithTestServer = async ({
|
|
|
84
85
|
|
|
85
86
|
test("adds tools", async () => {
|
|
86
87
|
await runWithTestServer({
|
|
87
|
-
server: async () => {
|
|
88
|
-
const server = new FastMCP({
|
|
89
|
-
name: "Test",
|
|
90
|
-
version: "1.0.0",
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
server.addTool({
|
|
94
|
-
name: "add",
|
|
95
|
-
description: "Add two numbers",
|
|
96
|
-
parameters: z.object({
|
|
97
|
-
a: z.number(),
|
|
98
|
-
b: z.number(),
|
|
99
|
-
}),
|
|
100
|
-
execute: async (args) => {
|
|
101
|
-
return String(args.a + args.b);
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
return server;
|
|
106
|
-
},
|
|
107
88
|
run: async ({ client }) => {
|
|
108
89
|
expect(await client.listTools()).toEqual({
|
|
109
90
|
tools: [
|
|
110
91
|
{
|
|
111
|
-
name: "add",
|
|
112
92
|
description: "Add two numbers",
|
|
113
93
|
inputSchema: {
|
|
114
|
-
additionalProperties: false,
|
|
115
94
|
$schema: "http://json-schema.org/draft-07/schema#",
|
|
116
|
-
|
|
95
|
+
additionalProperties: false,
|
|
117
96
|
properties: {
|
|
118
97
|
a: { type: "number" },
|
|
119
98
|
b: { type: "number" },
|
|
120
99
|
},
|
|
121
100
|
required: ["a", "b"],
|
|
101
|
+
type: "object",
|
|
122
102
|
},
|
|
103
|
+
name: "add",
|
|
123
104
|
},
|
|
124
105
|
],
|
|
125
106
|
});
|
|
126
107
|
},
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test("calls a tool", async () => {
|
|
131
|
-
await runWithTestServer({
|
|
132
108
|
server: async () => {
|
|
133
109
|
const server = new FastMCP({
|
|
134
110
|
name: "Test",
|
|
@@ -136,37 +112,37 @@ test("calls a tool", async () => {
|
|
|
136
112
|
});
|
|
137
113
|
|
|
138
114
|
server.addTool({
|
|
139
|
-
name: "add",
|
|
140
115
|
description: "Add two numbers",
|
|
116
|
+
execute: async (args) => {
|
|
117
|
+
return String(args.a + args.b);
|
|
118
|
+
},
|
|
119
|
+
name: "add",
|
|
141
120
|
parameters: z.object({
|
|
142
121
|
a: z.number(),
|
|
143
122
|
b: z.number(),
|
|
144
123
|
}),
|
|
145
|
-
execute: async (args) => {
|
|
146
|
-
return String(args.a + args.b);
|
|
147
|
-
},
|
|
148
124
|
});
|
|
149
125
|
|
|
150
126
|
return server;
|
|
151
127
|
},
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("calls a tool", async () => {
|
|
132
|
+
await runWithTestServer({
|
|
152
133
|
run: async ({ client }) => {
|
|
153
134
|
expect(
|
|
154
135
|
await client.callTool({
|
|
155
|
-
name: "add",
|
|
156
136
|
arguments: {
|
|
157
137
|
a: 1,
|
|
158
138
|
b: 2,
|
|
159
139
|
},
|
|
140
|
+
name: "add",
|
|
160
141
|
}),
|
|
161
142
|
).toEqual({
|
|
162
|
-
content: [{
|
|
143
|
+
content: [{ text: "3", type: "text" }],
|
|
163
144
|
});
|
|
164
145
|
},
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test("returns a list", async () => {
|
|
169
|
-
await runWithTestServer({
|
|
170
146
|
server: async () => {
|
|
171
147
|
const server = new FastMCP({
|
|
172
148
|
name: "Test",
|
|
@@ -174,45 +150,40 @@ test("returns a list", async () => {
|
|
|
174
150
|
});
|
|
175
151
|
|
|
176
152
|
server.addTool({
|
|
177
|
-
name: "add",
|
|
178
153
|
description: "Add two numbers",
|
|
154
|
+
execute: async (args) => {
|
|
155
|
+
return String(args.a + args.b);
|
|
156
|
+
},
|
|
157
|
+
name: "add",
|
|
179
158
|
parameters: z.object({
|
|
180
159
|
a: z.number(),
|
|
181
160
|
b: z.number(),
|
|
182
161
|
}),
|
|
183
|
-
execute: async () => {
|
|
184
|
-
return {
|
|
185
|
-
content: [
|
|
186
|
-
{ type: "text", text: "a" },
|
|
187
|
-
{ type: "text", text: "b" },
|
|
188
|
-
],
|
|
189
|
-
};
|
|
190
|
-
},
|
|
191
162
|
});
|
|
192
163
|
|
|
193
164
|
return server;
|
|
194
165
|
},
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("returns a list", async () => {
|
|
170
|
+
await runWithTestServer({
|
|
195
171
|
run: async ({ client }) => {
|
|
196
172
|
expect(
|
|
197
173
|
await client.callTool({
|
|
198
|
-
name: "add",
|
|
199
174
|
arguments: {
|
|
200
175
|
a: 1,
|
|
201
176
|
b: 2,
|
|
202
177
|
},
|
|
178
|
+
name: "add",
|
|
203
179
|
}),
|
|
204
180
|
).toEqual({
|
|
205
181
|
content: [
|
|
206
|
-
{
|
|
207
|
-
{
|
|
182
|
+
{ text: "a", type: "text" },
|
|
183
|
+
{ text: "b", type: "text" },
|
|
208
184
|
],
|
|
209
185
|
});
|
|
210
186
|
},
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
test("returns an image", async () => {
|
|
215
|
-
await runWithTestServer({
|
|
216
187
|
server: async () => {
|
|
217
188
|
const server = new FastMCP({
|
|
218
189
|
name: "Test",
|
|
@@ -220,48 +191,48 @@ test("returns an image", async () => {
|
|
|
220
191
|
});
|
|
221
192
|
|
|
222
193
|
server.addTool({
|
|
223
|
-
name: "add",
|
|
224
194
|
description: "Add two numbers",
|
|
195
|
+
execute: async () => {
|
|
196
|
+
return {
|
|
197
|
+
content: [
|
|
198
|
+
{ text: "a", type: "text" },
|
|
199
|
+
{ text: "b", type: "text" },
|
|
200
|
+
],
|
|
201
|
+
};
|
|
202
|
+
},
|
|
203
|
+
name: "add",
|
|
225
204
|
parameters: z.object({
|
|
226
205
|
a: z.number(),
|
|
227
206
|
b: z.number(),
|
|
228
207
|
}),
|
|
229
|
-
execute: async () => {
|
|
230
|
-
return imageContent({
|
|
231
|
-
buffer: Buffer.from(
|
|
232
|
-
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
|
|
233
|
-
"base64",
|
|
234
|
-
),
|
|
235
|
-
});
|
|
236
|
-
},
|
|
237
208
|
});
|
|
238
209
|
|
|
239
210
|
return server;
|
|
240
211
|
},
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("returns an image", async () => {
|
|
216
|
+
await runWithTestServer({
|
|
241
217
|
run: async ({ client }) => {
|
|
242
218
|
expect(
|
|
243
219
|
await client.callTool({
|
|
244
|
-
name: "add",
|
|
245
220
|
arguments: {
|
|
246
221
|
a: 1,
|
|
247
222
|
b: 2,
|
|
248
223
|
},
|
|
224
|
+
name: "add",
|
|
249
225
|
}),
|
|
250
226
|
).toEqual({
|
|
251
227
|
content: [
|
|
252
228
|
{
|
|
253
|
-
type: "image",
|
|
254
229
|
data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
|
|
255
230
|
mimeType: "image/png",
|
|
231
|
+
type: "image",
|
|
256
232
|
},
|
|
257
233
|
],
|
|
258
234
|
});
|
|
259
235
|
},
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
test("handles UserError errors", async () => {
|
|
264
|
-
await runWithTestServer({
|
|
265
236
|
server: async () => {
|
|
266
237
|
const server = new FastMCP({
|
|
267
238
|
name: "Test",
|
|
@@ -269,54 +240,76 @@ test("handles UserError errors", async () => {
|
|
|
269
240
|
});
|
|
270
241
|
|
|
271
242
|
server.addTool({
|
|
272
|
-
name: "add",
|
|
273
243
|
description: "Add two numbers",
|
|
244
|
+
execute: async () => {
|
|
245
|
+
return imageContent({
|
|
246
|
+
buffer: Buffer.from(
|
|
247
|
+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
|
|
248
|
+
"base64",
|
|
249
|
+
),
|
|
250
|
+
});
|
|
251
|
+
},
|
|
252
|
+
name: "add",
|
|
274
253
|
parameters: z.object({
|
|
275
254
|
a: z.number(),
|
|
276
255
|
b: z.number(),
|
|
277
256
|
}),
|
|
278
|
-
execute: async () => {
|
|
279
|
-
throw new UserError("Something went wrong");
|
|
280
|
-
},
|
|
281
257
|
});
|
|
282
258
|
|
|
283
259
|
return server;
|
|
284
260
|
},
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("handles UserError errors", async () => {
|
|
265
|
+
await runWithTestServer({
|
|
285
266
|
run: async ({ client }) => {
|
|
286
267
|
expect(
|
|
287
268
|
await client.callTool({
|
|
288
|
-
name: "add",
|
|
289
269
|
arguments: {
|
|
290
270
|
a: 1,
|
|
291
271
|
b: 2,
|
|
292
272
|
},
|
|
273
|
+
name: "add",
|
|
293
274
|
}),
|
|
294
275
|
).toEqual({
|
|
295
|
-
content: [{
|
|
276
|
+
content: [{ text: "Something went wrong", type: "text" }],
|
|
296
277
|
isError: true,
|
|
297
278
|
});
|
|
298
279
|
},
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
test("calling an unknown tool throws McpError with MethodNotFound code", async () => {
|
|
303
|
-
await runWithTestServer({
|
|
304
280
|
server: async () => {
|
|
305
281
|
const server = new FastMCP({
|
|
306
282
|
name: "Test",
|
|
307
283
|
version: "1.0.0",
|
|
308
284
|
});
|
|
309
285
|
|
|
286
|
+
server.addTool({
|
|
287
|
+
description: "Add two numbers",
|
|
288
|
+
execute: async () => {
|
|
289
|
+
throw new UserError("Something went wrong");
|
|
290
|
+
},
|
|
291
|
+
name: "add",
|
|
292
|
+
parameters: z.object({
|
|
293
|
+
a: z.number(),
|
|
294
|
+
b: z.number(),
|
|
295
|
+
}),
|
|
296
|
+
});
|
|
297
|
+
|
|
310
298
|
return server;
|
|
311
299
|
},
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("calling an unknown tool throws McpError with MethodNotFound code", async () => {
|
|
304
|
+
await runWithTestServer({
|
|
312
305
|
run: async ({ client }) => {
|
|
313
306
|
try {
|
|
314
307
|
await client.callTool({
|
|
315
|
-
name: "add",
|
|
316
308
|
arguments: {
|
|
317
309
|
a: 1,
|
|
318
310
|
b: 2,
|
|
319
311
|
},
|
|
312
|
+
name: "add",
|
|
320
313
|
});
|
|
321
314
|
} catch (error) {
|
|
322
315
|
expect(error).toBeInstanceOf(McpError);
|
|
@@ -325,48 +318,29 @@ test("calling an unknown tool throws McpError with MethodNotFound code", async (
|
|
|
325
318
|
expect(error.code).toBe(ErrorCode.MethodNotFound);
|
|
326
319
|
}
|
|
327
320
|
},
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
test("tracks tool progress", async () => {
|
|
332
|
-
await runWithTestServer({
|
|
333
321
|
server: async () => {
|
|
334
322
|
const server = new FastMCP({
|
|
335
323
|
name: "Test",
|
|
336
324
|
version: "1.0.0",
|
|
337
325
|
});
|
|
338
326
|
|
|
339
|
-
server.addTool({
|
|
340
|
-
name: "add",
|
|
341
|
-
description: "Add two numbers",
|
|
342
|
-
parameters: z.object({
|
|
343
|
-
a: z.number(),
|
|
344
|
-
b: z.number(),
|
|
345
|
-
}),
|
|
346
|
-
execute: async (args, { reportProgress }) => {
|
|
347
|
-
reportProgress({
|
|
348
|
-
progress: 0,
|
|
349
|
-
total: 10,
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
await delay(100);
|
|
353
|
-
|
|
354
|
-
return String(args.a + args.b);
|
|
355
|
-
},
|
|
356
|
-
});
|
|
357
|
-
|
|
358
327
|
return server;
|
|
359
328
|
},
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test("tracks tool progress", async () => {
|
|
333
|
+
await runWithTestServer({
|
|
360
334
|
run: async ({ client }) => {
|
|
361
335
|
const onProgress = vi.fn();
|
|
362
336
|
|
|
363
337
|
await client.callTool(
|
|
364
338
|
{
|
|
365
|
-
name: "add",
|
|
366
339
|
arguments: {
|
|
367
340
|
a: 1,
|
|
368
341
|
b: 2,
|
|
369
342
|
},
|
|
343
|
+
name: "add",
|
|
370
344
|
},
|
|
371
345
|
undefined,
|
|
372
346
|
{
|
|
@@ -380,6 +354,33 @@ test("tracks tool progress", async () => {
|
|
|
380
354
|
total: 10,
|
|
381
355
|
});
|
|
382
356
|
},
|
|
357
|
+
server: async () => {
|
|
358
|
+
const server = new FastMCP({
|
|
359
|
+
name: "Test",
|
|
360
|
+
version: "1.0.0",
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
server.addTool({
|
|
364
|
+
description: "Add two numbers",
|
|
365
|
+
execute: async (args, { reportProgress }) => {
|
|
366
|
+
reportProgress({
|
|
367
|
+
progress: 0,
|
|
368
|
+
total: 10,
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
await delay(100);
|
|
372
|
+
|
|
373
|
+
return String(args.a + args.b);
|
|
374
|
+
},
|
|
375
|
+
name: "add",
|
|
376
|
+
parameters: z.object({
|
|
377
|
+
a: z.number(),
|
|
378
|
+
b: z.number(),
|
|
379
|
+
}),
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
return server;
|
|
383
|
+
},
|
|
383
384
|
});
|
|
384
385
|
});
|
|
385
386
|
|
|
@@ -399,33 +400,6 @@ test("sets logging levels", async () => {
|
|
|
399
400
|
|
|
400
401
|
test("sends logging messages to the client", async () => {
|
|
401
402
|
await runWithTestServer({
|
|
402
|
-
server: async () => {
|
|
403
|
-
const server = new FastMCP({
|
|
404
|
-
name: "Test",
|
|
405
|
-
version: "1.0.0",
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
server.addTool({
|
|
409
|
-
name: "add",
|
|
410
|
-
description: "Add two numbers",
|
|
411
|
-
parameters: z.object({
|
|
412
|
-
a: z.number(),
|
|
413
|
-
b: z.number(),
|
|
414
|
-
}),
|
|
415
|
-
execute: async (args, { log }) => {
|
|
416
|
-
log.debug("debug message", {
|
|
417
|
-
foo: "bar",
|
|
418
|
-
});
|
|
419
|
-
log.error("error message");
|
|
420
|
-
log.info("info message");
|
|
421
|
-
log.warn("warn message");
|
|
422
|
-
|
|
423
|
-
return String(args.a + args.b);
|
|
424
|
-
},
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
return server;
|
|
428
|
-
},
|
|
429
403
|
run: async ({ client }) => {
|
|
430
404
|
const onLog = vi.fn();
|
|
431
405
|
|
|
@@ -442,20 +416,20 @@ test("sends logging messages to the client", async () => {
|
|
|
442
416
|
);
|
|
443
417
|
|
|
444
418
|
await client.callTool({
|
|
445
|
-
name: "add",
|
|
446
419
|
arguments: {
|
|
447
420
|
a: 1,
|
|
448
421
|
b: 2,
|
|
449
422
|
},
|
|
423
|
+
name: "add",
|
|
450
424
|
});
|
|
451
425
|
|
|
452
426
|
expect(onLog).toHaveBeenCalledTimes(4);
|
|
453
427
|
expect(onLog).toHaveBeenNthCalledWith(1, {
|
|
454
|
-
level: "debug",
|
|
455
|
-
message: "debug message",
|
|
456
428
|
context: {
|
|
457
429
|
foo: "bar",
|
|
458
430
|
},
|
|
431
|
+
level: "debug",
|
|
432
|
+
message: "debug message",
|
|
459
433
|
});
|
|
460
434
|
expect(onLog).toHaveBeenNthCalledWith(2, {
|
|
461
435
|
level: "error",
|
|
@@ -470,46 +444,49 @@ test("sends logging messages to the client", async () => {
|
|
|
470
444
|
message: "warn message",
|
|
471
445
|
});
|
|
472
446
|
},
|
|
473
|
-
});
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
test("adds resources", async () => {
|
|
477
|
-
await runWithTestServer({
|
|
478
447
|
server: async () => {
|
|
479
448
|
const server = new FastMCP({
|
|
480
449
|
name: "Test",
|
|
481
450
|
version: "1.0.0",
|
|
482
451
|
});
|
|
483
452
|
|
|
484
|
-
server.
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
453
|
+
server.addTool({
|
|
454
|
+
description: "Add two numbers",
|
|
455
|
+
execute: async (args, { log }) => {
|
|
456
|
+
log.debug("debug message", {
|
|
457
|
+
foo: "bar",
|
|
458
|
+
});
|
|
459
|
+
log.error("error message");
|
|
460
|
+
log.info("info message");
|
|
461
|
+
log.warn("warn message");
|
|
462
|
+
|
|
463
|
+
return String(args.a + args.b);
|
|
492
464
|
},
|
|
465
|
+
name: "add",
|
|
466
|
+
parameters: z.object({
|
|
467
|
+
a: z.number(),
|
|
468
|
+
b: z.number(),
|
|
469
|
+
}),
|
|
493
470
|
});
|
|
494
471
|
|
|
495
472
|
return server;
|
|
496
473
|
},
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
test("adds resources", async () => {
|
|
478
|
+
await runWithTestServer({
|
|
497
479
|
run: async ({ client }) => {
|
|
498
480
|
expect(await client.listResources()).toEqual({
|
|
499
481
|
resources: [
|
|
500
482
|
{
|
|
501
|
-
uri: "file:///logs/app.log",
|
|
502
|
-
name: "Application Logs",
|
|
503
483
|
mimeType: "text/plain",
|
|
484
|
+
name: "Application Logs",
|
|
485
|
+
uri: "file:///logs/app.log",
|
|
504
486
|
},
|
|
505
487
|
],
|
|
506
488
|
});
|
|
507
489
|
},
|
|
508
|
-
});
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
test("clients reads a resource", async () => {
|
|
512
|
-
await runWithTestServer({
|
|
513
490
|
server: async () => {
|
|
514
491
|
const server = new FastMCP({
|
|
515
492
|
name: "Test",
|
|
@@ -517,18 +494,23 @@ test("clients reads a resource", async () => {
|
|
|
517
494
|
});
|
|
518
495
|
|
|
519
496
|
server.addResource({
|
|
520
|
-
uri: "file:///logs/app.log",
|
|
521
|
-
name: "Application Logs",
|
|
522
|
-
mimeType: "text/plain",
|
|
523
497
|
async load() {
|
|
524
498
|
return {
|
|
525
499
|
text: "Example log content",
|
|
526
500
|
};
|
|
527
501
|
},
|
|
502
|
+
mimeType: "text/plain",
|
|
503
|
+
name: "Application Logs",
|
|
504
|
+
uri: "file:///logs/app.log",
|
|
528
505
|
});
|
|
529
506
|
|
|
530
507
|
return server;
|
|
531
508
|
},
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
test("clients reads a resource", async () => {
|
|
513
|
+
await runWithTestServer({
|
|
532
514
|
run: async ({ client }) => {
|
|
533
515
|
expect(
|
|
534
516
|
await client.readResource({
|
|
@@ -537,19 +519,14 @@ test("clients reads a resource", async () => {
|
|
|
537
519
|
).toEqual({
|
|
538
520
|
contents: [
|
|
539
521
|
{
|
|
540
|
-
|
|
522
|
+
mimeType: "text/plain",
|
|
541
523
|
name: "Application Logs",
|
|
542
524
|
text: "Example log content",
|
|
543
|
-
|
|
525
|
+
uri: "file:///logs/app.log",
|
|
544
526
|
},
|
|
545
527
|
],
|
|
546
528
|
});
|
|
547
529
|
},
|
|
548
|
-
});
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
test("clients reads a resource that returns multiple resources", async () => {
|
|
552
|
-
await runWithTestServer({
|
|
553
530
|
server: async () => {
|
|
554
531
|
const server = new FastMCP({
|
|
555
532
|
name: "Test",
|
|
@@ -557,23 +534,23 @@ test("clients reads a resource that returns multiple resources", async () => {
|
|
|
557
534
|
});
|
|
558
535
|
|
|
559
536
|
server.addResource({
|
|
560
|
-
uri: "file:///logs/app.log",
|
|
561
|
-
name: "Application Logs",
|
|
562
|
-
mimeType: "text/plain",
|
|
563
537
|
async load() {
|
|
564
|
-
return
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
},
|
|
568
|
-
{
|
|
569
|
-
text: "b",
|
|
570
|
-
},
|
|
571
|
-
];
|
|
538
|
+
return {
|
|
539
|
+
text: "Example log content",
|
|
540
|
+
};
|
|
572
541
|
},
|
|
542
|
+
mimeType: "text/plain",
|
|
543
|
+
name: "Application Logs",
|
|
544
|
+
uri: "file:///logs/app.log",
|
|
573
545
|
});
|
|
574
546
|
|
|
575
547
|
return server;
|
|
576
548
|
},
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
test("clients reads a resource that returns multiple resources", async () => {
|
|
553
|
+
await runWithTestServer({
|
|
577
554
|
run: async ({ client }) => {
|
|
578
555
|
expect(
|
|
579
556
|
await client.readResource({
|
|
@@ -582,65 +559,66 @@ test("clients reads a resource that returns multiple resources", async () => {
|
|
|
582
559
|
).toEqual({
|
|
583
560
|
contents: [
|
|
584
561
|
{
|
|
585
|
-
|
|
562
|
+
mimeType: "text/plain",
|
|
586
563
|
name: "Application Logs",
|
|
587
564
|
text: "a",
|
|
588
|
-
|
|
565
|
+
uri: "file:///logs/app.log",
|
|
589
566
|
},
|
|
590
567
|
{
|
|
591
|
-
|
|
568
|
+
mimeType: "text/plain",
|
|
592
569
|
name: "Application Logs",
|
|
593
570
|
text: "b",
|
|
594
|
-
|
|
571
|
+
uri: "file:///logs/app.log",
|
|
595
572
|
},
|
|
596
573
|
],
|
|
597
574
|
});
|
|
598
575
|
},
|
|
599
|
-
});
|
|
600
|
-
});
|
|
601
|
-
|
|
602
|
-
test("adds prompts", async () => {
|
|
603
|
-
await runWithTestServer({
|
|
604
576
|
server: async () => {
|
|
605
577
|
const server = new FastMCP({
|
|
606
578
|
name: "Test",
|
|
607
579
|
version: "1.0.0",
|
|
608
580
|
});
|
|
609
581
|
|
|
610
|
-
server.
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
load: async (args) => {
|
|
621
|
-
return `Generate a concise but descriptive commit message for these changes:\n\n${args.changes}`;
|
|
582
|
+
server.addResource({
|
|
583
|
+
async load() {
|
|
584
|
+
return [
|
|
585
|
+
{
|
|
586
|
+
text: "a",
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
text: "b",
|
|
590
|
+
},
|
|
591
|
+
];
|
|
622
592
|
},
|
|
593
|
+
mimeType: "text/plain",
|
|
594
|
+
name: "Application Logs",
|
|
595
|
+
uri: "file:///logs/app.log",
|
|
623
596
|
});
|
|
624
597
|
|
|
625
598
|
return server;
|
|
626
599
|
},
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
test("adds prompts", async () => {
|
|
604
|
+
await runWithTestServer({
|
|
627
605
|
run: async ({ client }) => {
|
|
628
606
|
expect(
|
|
629
607
|
await client.getPrompt({
|
|
630
|
-
name: "git-commit",
|
|
631
608
|
arguments: {
|
|
632
609
|
changes: "foo",
|
|
633
610
|
},
|
|
611
|
+
name: "git-commit",
|
|
634
612
|
}),
|
|
635
613
|
).toEqual({
|
|
636
614
|
description: "Generate a Git commit message",
|
|
637
615
|
messages: [
|
|
638
616
|
{
|
|
639
|
-
role: "user",
|
|
640
617
|
content: {
|
|
641
|
-
type: "text",
|
|
642
618
|
text: "Generate a concise but descriptive commit message for these changes:\n\nfoo",
|
|
619
|
+
type: "text",
|
|
643
620
|
},
|
|
621
|
+
role: "user",
|
|
644
622
|
},
|
|
645
623
|
],
|
|
646
624
|
});
|
|
@@ -648,19 +626,42 @@ test("adds prompts", async () => {
|
|
|
648
626
|
expect(await client.listPrompts()).toEqual({
|
|
649
627
|
prompts: [
|
|
650
628
|
{
|
|
651
|
-
name: "git-commit",
|
|
652
|
-
description: "Generate a Git commit message",
|
|
653
629
|
arguments: [
|
|
654
630
|
{
|
|
655
|
-
name: "changes",
|
|
656
631
|
description: "Git diff or description of changes",
|
|
632
|
+
name: "changes",
|
|
657
633
|
required: true,
|
|
658
634
|
},
|
|
659
635
|
],
|
|
636
|
+
description: "Generate a Git commit message",
|
|
637
|
+
name: "git-commit",
|
|
660
638
|
},
|
|
661
639
|
],
|
|
662
640
|
});
|
|
663
641
|
},
|
|
642
|
+
server: async () => {
|
|
643
|
+
const server = new FastMCP({
|
|
644
|
+
name: "Test",
|
|
645
|
+
version: "1.0.0",
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
server.addPrompt({
|
|
649
|
+
arguments: [
|
|
650
|
+
{
|
|
651
|
+
description: "Git diff or description of changes",
|
|
652
|
+
name: "changes",
|
|
653
|
+
required: true,
|
|
654
|
+
},
|
|
655
|
+
],
|
|
656
|
+
description: "Generate a Git commit message",
|
|
657
|
+
load: async (args) => {
|
|
658
|
+
return `Generate a concise but descriptive commit message for these changes:\n\n${args.changes}`;
|
|
659
|
+
},
|
|
660
|
+
name: "git-commit",
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
return server;
|
|
664
|
+
},
|
|
664
665
|
});
|
|
665
666
|
});
|
|
666
667
|
|
|
@@ -679,11 +680,11 @@ test("uses events to notify server of client connect/disconnect", async () => {
|
|
|
679
680
|
server.on("disconnect", onDisconnect);
|
|
680
681
|
|
|
681
682
|
await server.start({
|
|
682
|
-
transportType: "sse",
|
|
683
683
|
sse: {
|
|
684
684
|
endpoint: "/sse",
|
|
685
685
|
port,
|
|
686
686
|
},
|
|
687
|
+
transportType: "sse",
|
|
687
688
|
});
|
|
688
689
|
|
|
689
690
|
const client = new Client(
|
|
@@ -728,11 +729,11 @@ test("handles multiple clients", async () => {
|
|
|
728
729
|
});
|
|
729
730
|
|
|
730
731
|
await server.start({
|
|
731
|
-
transportType: "sse",
|
|
732
732
|
sse: {
|
|
733
733
|
endpoint: "/sse",
|
|
734
734
|
port,
|
|
735
735
|
},
|
|
736
|
+
transportType: "sse",
|
|
736
737
|
});
|
|
737
738
|
|
|
738
739
|
const client1 = new Client(
|
|
@@ -798,8 +799,8 @@ test("session knows about client capabilities", async () => {
|
|
|
798
799
|
return {
|
|
799
800
|
roots: [
|
|
800
801
|
{
|
|
801
|
-
uri: "file:///home/user/projects/frontend",
|
|
802
802
|
name: "Frontend Repository",
|
|
803
|
+
uri: "file:///home/user/projects/frontend",
|
|
803
804
|
},
|
|
804
805
|
],
|
|
805
806
|
};
|
|
@@ -838,8 +839,8 @@ test("session knows about roots", async () => {
|
|
|
838
839
|
return {
|
|
839
840
|
roots: [
|
|
840
841
|
{
|
|
841
|
-
uri: "file:///home/user/projects/frontend",
|
|
842
842
|
name: "Frontend Repository",
|
|
843
|
+
uri: "file:///home/user/projects/frontend",
|
|
843
844
|
},
|
|
844
845
|
],
|
|
845
846
|
};
|
|
@@ -850,8 +851,8 @@ test("session knows about roots", async () => {
|
|
|
850
851
|
run: async ({ session }) => {
|
|
851
852
|
expect(session.roots).toEqual([
|
|
852
853
|
{
|
|
853
|
-
uri: "file:///home/user/projects/frontend",
|
|
854
854
|
name: "Frontend Repository",
|
|
855
|
+
uri: "file:///home/user/projects/frontend",
|
|
855
856
|
},
|
|
856
857
|
]);
|
|
857
858
|
},
|
|
@@ -859,10 +860,10 @@ test("session knows about roots", async () => {
|
|
|
859
860
|
});
|
|
860
861
|
|
|
861
862
|
test("session listens to roots changes", async () => {
|
|
862
|
-
|
|
863
|
+
const clientRoots: Root[] = [
|
|
863
864
|
{
|
|
864
|
-
uri: "file:///home/user/projects/frontend",
|
|
865
865
|
name: "Frontend Repository",
|
|
866
|
+
uri: "file:///home/user/projects/frontend",
|
|
866
867
|
},
|
|
867
868
|
];
|
|
868
869
|
|
|
@@ -890,17 +891,17 @@ test("session listens to roots changes", async () => {
|
|
|
890
891
|
|
|
891
892
|
return client;
|
|
892
893
|
},
|
|
893
|
-
run: async ({
|
|
894
|
+
run: async ({ client, session }) => {
|
|
894
895
|
expect(session.roots).toEqual([
|
|
895
896
|
{
|
|
896
|
-
uri: "file:///home/user/projects/frontend",
|
|
897
897
|
name: "Frontend Repository",
|
|
898
|
+
uri: "file:///home/user/projects/frontend",
|
|
898
899
|
},
|
|
899
900
|
]);
|
|
900
901
|
|
|
901
902
|
clientRoots.push({
|
|
902
|
-
uri: "file:///home/user/projects/backend",
|
|
903
903
|
name: "Backend Repository",
|
|
904
|
+
uri: "file:///home/user/projects/backend",
|
|
904
905
|
});
|
|
905
906
|
|
|
906
907
|
await client.sendRootsListChanged();
|
|
@@ -913,12 +914,12 @@ test("session listens to roots changes", async () => {
|
|
|
913
914
|
|
|
914
915
|
expect(session.roots).toEqual([
|
|
915
916
|
{
|
|
916
|
-
uri: "file:///home/user/projects/frontend",
|
|
917
917
|
name: "Frontend Repository",
|
|
918
|
+
uri: "file:///home/user/projects/frontend",
|
|
918
919
|
},
|
|
919
920
|
{
|
|
920
|
-
uri: "file:///home/user/projects/backend",
|
|
921
921
|
name: "Backend Repository",
|
|
922
|
+
uri: "file:///home/user/projects/backend",
|
|
922
923
|
},
|
|
923
924
|
]);
|
|
924
925
|
|
|
@@ -926,12 +927,12 @@ test("session listens to roots changes", async () => {
|
|
|
926
927
|
expect(onRootsChanged).toHaveBeenCalledWith({
|
|
927
928
|
roots: [
|
|
928
929
|
{
|
|
929
|
-
uri: "file:///home/user/projects/frontend",
|
|
930
930
|
name: "Frontend Repository",
|
|
931
|
+
uri: "file:///home/user/projects/frontend",
|
|
931
932
|
},
|
|
932
933
|
{
|
|
933
|
-
uri: "file:///home/user/projects/backend",
|
|
934
934
|
name: "Backend Repository",
|
|
935
|
+
uri: "file:///home/user/projects/backend",
|
|
935
936
|
},
|
|
936
937
|
],
|
|
937
938
|
});
|
|
@@ -955,6 +956,24 @@ test("session sends pings to the client", async () => {
|
|
|
955
956
|
|
|
956
957
|
test("completes prompt arguments", async () => {
|
|
957
958
|
await runWithTestServer({
|
|
959
|
+
run: async ({ client }) => {
|
|
960
|
+
const response = await client.complete({
|
|
961
|
+
argument: {
|
|
962
|
+
name: "name",
|
|
963
|
+
value: "Germ",
|
|
964
|
+
},
|
|
965
|
+
ref: {
|
|
966
|
+
name: "countryPoem",
|
|
967
|
+
type: "ref/prompt",
|
|
968
|
+
},
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
expect(response).toEqual({
|
|
972
|
+
completion: {
|
|
973
|
+
values: ["Germany"],
|
|
974
|
+
},
|
|
975
|
+
});
|
|
976
|
+
},
|
|
958
977
|
server: async () => {
|
|
959
978
|
const server = new FastMCP({
|
|
960
979
|
name: "Test",
|
|
@@ -962,16 +981,8 @@ test("completes prompt arguments", async () => {
|
|
|
962
981
|
});
|
|
963
982
|
|
|
964
983
|
server.addPrompt({
|
|
965
|
-
name: "countryPoem",
|
|
966
|
-
description: "Writes a poem about a country",
|
|
967
|
-
load: async ({ name }) => {
|
|
968
|
-
return `Hello, ${name}!`;
|
|
969
|
-
},
|
|
970
984
|
arguments: [
|
|
971
985
|
{
|
|
972
|
-
name: "name",
|
|
973
|
-
description: "Name of the country",
|
|
974
|
-
required: true,
|
|
975
986
|
complete: async (value) => {
|
|
976
987
|
if (value === "Germ") {
|
|
977
988
|
return {
|
|
@@ -983,35 +994,44 @@ test("completes prompt arguments", async () => {
|
|
|
983
994
|
values: [],
|
|
984
995
|
};
|
|
985
996
|
},
|
|
997
|
+
description: "Name of the country",
|
|
998
|
+
name: "name",
|
|
999
|
+
required: true,
|
|
986
1000
|
},
|
|
987
1001
|
],
|
|
1002
|
+
description: "Writes a poem about a country",
|
|
1003
|
+
load: async ({ name }) => {
|
|
1004
|
+
return `Hello, ${name}!`;
|
|
1005
|
+
},
|
|
1006
|
+
name: "countryPoem",
|
|
988
1007
|
});
|
|
989
1008
|
|
|
990
1009
|
return server;
|
|
991
1010
|
},
|
|
1011
|
+
});
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
test("adds automatic prompt argument completion when enum is provided", async () => {
|
|
1015
|
+
await runWithTestServer({
|
|
992
1016
|
run: async ({ client }) => {
|
|
993
1017
|
const response = await client.complete({
|
|
994
|
-
ref: {
|
|
995
|
-
type: "ref/prompt",
|
|
996
|
-
name: "countryPoem",
|
|
997
|
-
},
|
|
998
1018
|
argument: {
|
|
999
1019
|
name: "name",
|
|
1000
1020
|
value: "Germ",
|
|
1001
1021
|
},
|
|
1022
|
+
ref: {
|
|
1023
|
+
name: "countryPoem",
|
|
1024
|
+
type: "ref/prompt",
|
|
1025
|
+
},
|
|
1002
1026
|
});
|
|
1003
1027
|
|
|
1004
1028
|
expect(response).toEqual({
|
|
1005
1029
|
completion: {
|
|
1030
|
+
total: 1,
|
|
1006
1031
|
values: ["Germany"],
|
|
1007
1032
|
},
|
|
1008
1033
|
});
|
|
1009
1034
|
},
|
|
1010
|
-
});
|
|
1011
|
-
});
|
|
1012
|
-
|
|
1013
|
-
test("adds automatic prompt argument completion when enum is provided", async () => {
|
|
1014
|
-
await runWithTestServer({
|
|
1015
1035
|
server: async () => {
|
|
1016
1036
|
const server = new FastMCP({
|
|
1017
1037
|
name: "Test",
|
|
@@ -1019,47 +1039,46 @@ test("adds automatic prompt argument completion when enum is provided", async ()
|
|
|
1019
1039
|
});
|
|
1020
1040
|
|
|
1021
1041
|
server.addPrompt({
|
|
1022
|
-
name: "countryPoem",
|
|
1023
|
-
description: "Writes a poem about a country",
|
|
1024
|
-
load: async ({ name }) => {
|
|
1025
|
-
return `Hello, ${name}!`;
|
|
1026
|
-
},
|
|
1027
1042
|
arguments: [
|
|
1028
1043
|
{
|
|
1029
|
-
name: "name",
|
|
1030
1044
|
description: "Name of the country",
|
|
1031
|
-
required: true,
|
|
1032
1045
|
enum: ["Germany", "France", "Italy"],
|
|
1046
|
+
name: "name",
|
|
1047
|
+
required: true,
|
|
1033
1048
|
},
|
|
1034
1049
|
],
|
|
1050
|
+
description: "Writes a poem about a country",
|
|
1051
|
+
load: async ({ name }) => {
|
|
1052
|
+
return `Hello, ${name}!`;
|
|
1053
|
+
},
|
|
1054
|
+
name: "countryPoem",
|
|
1035
1055
|
});
|
|
1036
1056
|
|
|
1037
1057
|
return server;
|
|
1038
1058
|
},
|
|
1059
|
+
});
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
test("completes template resource arguments", async () => {
|
|
1063
|
+
await runWithTestServer({
|
|
1039
1064
|
run: async ({ client }) => {
|
|
1040
1065
|
const response = await client.complete({
|
|
1041
|
-
ref: {
|
|
1042
|
-
type: "ref/prompt",
|
|
1043
|
-
name: "countryPoem",
|
|
1044
|
-
},
|
|
1045
1066
|
argument: {
|
|
1046
|
-
name: "
|
|
1047
|
-
value: "
|
|
1067
|
+
name: "issueId",
|
|
1068
|
+
value: "123",
|
|
1069
|
+
},
|
|
1070
|
+
ref: {
|
|
1071
|
+
type: "ref/resource",
|
|
1072
|
+
uri: "issue:///{issueId}",
|
|
1048
1073
|
},
|
|
1049
1074
|
});
|
|
1050
1075
|
|
|
1051
1076
|
expect(response).toEqual({
|
|
1052
1077
|
completion: {
|
|
1053
|
-
values: ["
|
|
1054
|
-
total: 1,
|
|
1078
|
+
values: ["123456"],
|
|
1055
1079
|
},
|
|
1056
1080
|
});
|
|
1057
1081
|
},
|
|
1058
|
-
});
|
|
1059
|
-
});
|
|
1060
|
-
|
|
1061
|
-
test("completes template resource arguments", async () => {
|
|
1062
|
-
await runWithTestServer({
|
|
1063
1082
|
server: async () => {
|
|
1064
1083
|
const server = new FastMCP({
|
|
1065
1084
|
name: "Test",
|
|
@@ -1067,13 +1086,8 @@ test("completes template resource arguments", async () => {
|
|
|
1067
1086
|
});
|
|
1068
1087
|
|
|
1069
1088
|
server.addResourceTemplate({
|
|
1070
|
-
uriTemplate: "issue:///{issueId}",
|
|
1071
|
-
name: "Issue",
|
|
1072
|
-
mimeType: "text/plain",
|
|
1073
1089
|
arguments: [
|
|
1074
1090
|
{
|
|
1075
|
-
name: "issueId",
|
|
1076
|
-
description: "ID of the issue",
|
|
1077
1091
|
complete: async (value) => {
|
|
1078
1092
|
if (value === "123") {
|
|
1079
1093
|
return {
|
|
@@ -1085,6 +1099,8 @@ test("completes template resource arguments", async () => {
|
|
|
1085
1099
|
values: [],
|
|
1086
1100
|
};
|
|
1087
1101
|
},
|
|
1102
|
+
description: "ID of the issue",
|
|
1103
|
+
name: "issueId",
|
|
1088
1104
|
},
|
|
1089
1105
|
],
|
|
1090
1106
|
load: async ({ issueId }) => {
|
|
@@ -1092,33 +1108,28 @@ test("completes template resource arguments", async () => {
|
|
|
1092
1108
|
text: `Issue ${issueId}`,
|
|
1093
1109
|
};
|
|
1094
1110
|
},
|
|
1111
|
+
mimeType: "text/plain",
|
|
1112
|
+
name: "Issue",
|
|
1113
|
+
uriTemplate: "issue:///{issueId}",
|
|
1095
1114
|
});
|
|
1096
1115
|
|
|
1097
1116
|
return server;
|
|
1098
1117
|
},
|
|
1099
|
-
run: async ({ client }) => {
|
|
1100
|
-
const response = await client.complete({
|
|
1101
|
-
ref: {
|
|
1102
|
-
type: "ref/resource",
|
|
1103
|
-
uri: "issue:///{issueId}",
|
|
1104
|
-
},
|
|
1105
|
-
argument: {
|
|
1106
|
-
name: "issueId",
|
|
1107
|
-
value: "123",
|
|
1108
|
-
},
|
|
1109
|
-
});
|
|
1110
|
-
|
|
1111
|
-
expect(response).toEqual({
|
|
1112
|
-
completion: {
|
|
1113
|
-
values: ["123456"],
|
|
1114
|
-
},
|
|
1115
|
-
});
|
|
1116
|
-
},
|
|
1117
1118
|
});
|
|
1118
1119
|
});
|
|
1119
1120
|
|
|
1120
1121
|
test("lists resource templates", async () => {
|
|
1121
1122
|
await runWithTestServer({
|
|
1123
|
+
run: async ({ client }) => {
|
|
1124
|
+
expect(await client.listResourceTemplates()).toEqual({
|
|
1125
|
+
resourceTemplates: [
|
|
1126
|
+
{
|
|
1127
|
+
name: "Application Logs",
|
|
1128
|
+
uriTemplate: "file:///logs/{name}.log",
|
|
1129
|
+
},
|
|
1130
|
+
],
|
|
1131
|
+
});
|
|
1132
|
+
},
|
|
1122
1133
|
server: async () => {
|
|
1123
1134
|
const server = new FastMCP({
|
|
1124
1135
|
name: "Test",
|
|
@@ -1126,13 +1137,10 @@ test("lists resource templates", async () => {
|
|
|
1126
1137
|
});
|
|
1127
1138
|
|
|
1128
1139
|
server.addResourceTemplate({
|
|
1129
|
-
uriTemplate: "file:///logs/{name}.log",
|
|
1130
|
-
name: "Application Logs",
|
|
1131
|
-
mimeType: "text/plain",
|
|
1132
1140
|
arguments: [
|
|
1133
1141
|
{
|
|
1134
|
-
name: "name",
|
|
1135
1142
|
description: "Name of the log",
|
|
1143
|
+
name: "name",
|
|
1136
1144
|
required: true,
|
|
1137
1145
|
},
|
|
1138
1146
|
],
|
|
@@ -1141,24 +1149,18 @@ test("lists resource templates", async () => {
|
|
|
1141
1149
|
text: `Example log content for ${name}`,
|
|
1142
1150
|
};
|
|
1143
1151
|
},
|
|
1152
|
+
mimeType: "text/plain",
|
|
1153
|
+
name: "Application Logs",
|
|
1154
|
+
uriTemplate: "file:///logs/{name}.log",
|
|
1144
1155
|
});
|
|
1145
1156
|
|
|
1146
1157
|
return server;
|
|
1147
1158
|
},
|
|
1148
|
-
run: async ({ client }) => {
|
|
1149
|
-
expect(await client.listResourceTemplates()).toEqual({
|
|
1150
|
-
resourceTemplates: [
|
|
1151
|
-
{
|
|
1152
|
-
name: "Application Logs",
|
|
1153
|
-
uriTemplate: "file:///logs/{name}.log",
|
|
1154
|
-
},
|
|
1155
|
-
],
|
|
1156
|
-
});
|
|
1157
|
-
},
|
|
1158
1159
|
});
|
|
1159
1160
|
});
|
|
1160
1161
|
|
|
1161
1162
|
test("clients reads a resource accessed via a resource template", async () => {
|
|
1163
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1162
1164
|
const loadSpy = vi.fn((_args) => {
|
|
1163
1165
|
return {
|
|
1164
1166
|
text: "Example log content",
|
|
@@ -1166,6 +1168,26 @@ test("clients reads a resource accessed via a resource template", async () => {
|
|
|
1166
1168
|
});
|
|
1167
1169
|
|
|
1168
1170
|
await runWithTestServer({
|
|
1171
|
+
run: async ({ client }) => {
|
|
1172
|
+
expect(
|
|
1173
|
+
await client.readResource({
|
|
1174
|
+
uri: "file:///logs/app.log",
|
|
1175
|
+
}),
|
|
1176
|
+
).toEqual({
|
|
1177
|
+
contents: [
|
|
1178
|
+
{
|
|
1179
|
+
mimeType: "text/plain",
|
|
1180
|
+
name: "Application Logs",
|
|
1181
|
+
text: "Example log content",
|
|
1182
|
+
uri: "file:///logs/app.log",
|
|
1183
|
+
},
|
|
1184
|
+
],
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
expect(loadSpy).toHaveBeenCalledWith({
|
|
1188
|
+
name: "app",
|
|
1189
|
+
});
|
|
1190
|
+
},
|
|
1169
1191
|
server: async () => {
|
|
1170
1192
|
const server = new FastMCP({
|
|
1171
1193
|
name: "Test",
|
|
@@ -1173,54 +1195,34 @@ test("clients reads a resource accessed via a resource template", async () => {
|
|
|
1173
1195
|
});
|
|
1174
1196
|
|
|
1175
1197
|
server.addResourceTemplate({
|
|
1176
|
-
uriTemplate: "file:///logs/{name}.log",
|
|
1177
|
-
name: "Application Logs",
|
|
1178
|
-
mimeType: "text/plain",
|
|
1179
1198
|
arguments: [
|
|
1180
1199
|
{
|
|
1181
|
-
name: "name",
|
|
1182
1200
|
description: "Name of the log",
|
|
1201
|
+
name: "name",
|
|
1183
1202
|
},
|
|
1184
1203
|
],
|
|
1185
1204
|
async load(args) {
|
|
1186
1205
|
return loadSpy(args);
|
|
1187
1206
|
},
|
|
1207
|
+
mimeType: "text/plain",
|
|
1208
|
+
name: "Application Logs",
|
|
1209
|
+
uriTemplate: "file:///logs/{name}.log",
|
|
1188
1210
|
});
|
|
1189
1211
|
|
|
1190
1212
|
return server;
|
|
1191
1213
|
},
|
|
1192
|
-
run: async ({ client }) => {
|
|
1193
|
-
expect(
|
|
1194
|
-
await client.readResource({
|
|
1195
|
-
uri: "file:///logs/app.log",
|
|
1196
|
-
}),
|
|
1197
|
-
).toEqual({
|
|
1198
|
-
contents: [
|
|
1199
|
-
{
|
|
1200
|
-
uri: "file:///logs/app.log",
|
|
1201
|
-
name: "Application Logs",
|
|
1202
|
-
text: "Example log content",
|
|
1203
|
-
mimeType: "text/plain",
|
|
1204
|
-
},
|
|
1205
|
-
],
|
|
1206
|
-
});
|
|
1207
|
-
|
|
1208
|
-
expect(loadSpy).toHaveBeenCalledWith({
|
|
1209
|
-
name: "app",
|
|
1210
|
-
});
|
|
1211
|
-
},
|
|
1212
1214
|
});
|
|
1213
1215
|
});
|
|
1214
1216
|
|
|
1215
1217
|
test("makes a sampling request", async () => {
|
|
1216
1218
|
const onMessageRequest = vi.fn(() => {
|
|
1217
1219
|
return {
|
|
1218
|
-
model: "gpt-3.5-turbo",
|
|
1219
|
-
role: "assistant",
|
|
1220
1220
|
content: {
|
|
1221
|
-
type: "text",
|
|
1222
1221
|
text: "The files are in the current directory.",
|
|
1222
|
+
type: "text",
|
|
1223
1223
|
},
|
|
1224
|
+
model: "gpt-3.5-turbo",
|
|
1225
|
+
role: "assistant",
|
|
1224
1226
|
};
|
|
1225
1227
|
});
|
|
1226
1228
|
|
|
@@ -1243,27 +1245,27 @@ test("makes a sampling request", async () => {
|
|
|
1243
1245
|
client.setRequestHandler(CreateMessageRequestSchema, onMessageRequest);
|
|
1244
1246
|
|
|
1245
1247
|
const response = await session.requestSampling({
|
|
1248
|
+
includeContext: "thisServer",
|
|
1249
|
+
maxTokens: 100,
|
|
1246
1250
|
messages: [
|
|
1247
1251
|
{
|
|
1248
|
-
role: "user",
|
|
1249
1252
|
content: {
|
|
1250
|
-
type: "text",
|
|
1251
1253
|
text: "What files are in the current directory?",
|
|
1254
|
+
type: "text",
|
|
1252
1255
|
},
|
|
1256
|
+
role: "user",
|
|
1253
1257
|
},
|
|
1254
1258
|
],
|
|
1255
1259
|
systemPrompt: "You are a helpful file system assistant.",
|
|
1256
|
-
includeContext: "thisServer",
|
|
1257
|
-
maxTokens: 100,
|
|
1258
1260
|
});
|
|
1259
1261
|
|
|
1260
1262
|
expect(response).toEqual({
|
|
1261
|
-
model: "gpt-3.5-turbo",
|
|
1262
|
-
role: "assistant",
|
|
1263
1263
|
content: {
|
|
1264
|
-
type: "text",
|
|
1265
1264
|
text: "The files are in the current directory.",
|
|
1265
|
+
type: "text",
|
|
1266
1266
|
},
|
|
1267
|
+
model: "gpt-3.5-turbo",
|
|
1268
|
+
role: "assistant",
|
|
1267
1269
|
});
|
|
1268
1270
|
|
|
1269
1271
|
expect(onMessageRequest).toHaveBeenCalledTimes(1);
|
|
@@ -1273,34 +1275,14 @@ test("makes a sampling request", async () => {
|
|
|
1273
1275
|
|
|
1274
1276
|
test("throws ErrorCode.InvalidParams if tool parameters do not match zod schema", async () => {
|
|
1275
1277
|
await runWithTestServer({
|
|
1276
|
-
server: async () => {
|
|
1277
|
-
const server = new FastMCP({
|
|
1278
|
-
name: "Test",
|
|
1279
|
-
version: "1.0.0",
|
|
1280
|
-
});
|
|
1281
|
-
|
|
1282
|
-
server.addTool({
|
|
1283
|
-
name: "add",
|
|
1284
|
-
description: "Add two numbers",
|
|
1285
|
-
parameters: z.object({
|
|
1286
|
-
a: z.number(),
|
|
1287
|
-
b: z.number(),
|
|
1288
|
-
}),
|
|
1289
|
-
execute: async (args) => {
|
|
1290
|
-
return String(args.a + args.b);
|
|
1291
|
-
},
|
|
1292
|
-
});
|
|
1293
|
-
|
|
1294
|
-
return server;
|
|
1295
|
-
},
|
|
1296
1278
|
run: async ({ client }) => {
|
|
1297
1279
|
try {
|
|
1298
1280
|
await client.callTool({
|
|
1299
|
-
name: "add",
|
|
1300
1281
|
arguments: {
|
|
1301
1282
|
a: 1,
|
|
1302
1283
|
b: "invalid",
|
|
1303
1284
|
},
|
|
1285
|
+
name: "add",
|
|
1304
1286
|
});
|
|
1305
1287
|
} catch (error) {
|
|
1306
1288
|
expect(error).toBeInstanceOf(McpError);
|
|
@@ -1314,11 +1296,6 @@ test("throws ErrorCode.InvalidParams if tool parameters do not match zod schema"
|
|
|
1314
1296
|
);
|
|
1315
1297
|
}
|
|
1316
1298
|
},
|
|
1317
|
-
});
|
|
1318
|
-
});
|
|
1319
|
-
|
|
1320
|
-
test("server remains usable after InvalidParams error", async () => {
|
|
1321
|
-
await runWithTestServer({
|
|
1322
1299
|
server: async () => {
|
|
1323
1300
|
const server = new FastMCP({
|
|
1324
1301
|
name: "Test",
|
|
@@ -1326,27 +1303,32 @@ test("server remains usable after InvalidParams error", async () => {
|
|
|
1326
1303
|
});
|
|
1327
1304
|
|
|
1328
1305
|
server.addTool({
|
|
1329
|
-
name: "add",
|
|
1330
1306
|
description: "Add two numbers",
|
|
1307
|
+
execute: async (args) => {
|
|
1308
|
+
return String(args.a + args.b);
|
|
1309
|
+
},
|
|
1310
|
+
name: "add",
|
|
1331
1311
|
parameters: z.object({
|
|
1332
1312
|
a: z.number(),
|
|
1333
1313
|
b: z.number(),
|
|
1334
1314
|
}),
|
|
1335
|
-
execute: async (args) => {
|
|
1336
|
-
return String(args.a + args.b);
|
|
1337
|
-
},
|
|
1338
1315
|
});
|
|
1339
1316
|
|
|
1340
1317
|
return server;
|
|
1341
1318
|
},
|
|
1319
|
+
});
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
test("server remains usable after InvalidParams error", async () => {
|
|
1323
|
+
await runWithTestServer({
|
|
1342
1324
|
run: async ({ client }) => {
|
|
1343
1325
|
try {
|
|
1344
1326
|
await client.callTool({
|
|
1345
|
-
name: "add",
|
|
1346
1327
|
arguments: {
|
|
1347
1328
|
a: 1,
|
|
1348
1329
|
b: "invalid",
|
|
1349
1330
|
},
|
|
1331
|
+
name: "add",
|
|
1350
1332
|
});
|
|
1351
1333
|
} catch (error) {
|
|
1352
1334
|
expect(error).toBeInstanceOf(McpError);
|
|
@@ -1362,15 +1344,35 @@ test("server remains usable after InvalidParams error", async () => {
|
|
|
1362
1344
|
|
|
1363
1345
|
expect(
|
|
1364
1346
|
await client.callTool({
|
|
1365
|
-
name: "add",
|
|
1366
1347
|
arguments: {
|
|
1367
1348
|
a: 1,
|
|
1368
1349
|
b: 2,
|
|
1369
1350
|
},
|
|
1351
|
+
name: "add",
|
|
1370
1352
|
}),
|
|
1371
1353
|
).toEqual({
|
|
1372
|
-
content: [{
|
|
1354
|
+
content: [{ text: "3", type: "text" }],
|
|
1355
|
+
});
|
|
1356
|
+
},
|
|
1357
|
+
server: async () => {
|
|
1358
|
+
const server = new FastMCP({
|
|
1359
|
+
name: "Test",
|
|
1360
|
+
version: "1.0.0",
|
|
1361
|
+
});
|
|
1362
|
+
|
|
1363
|
+
server.addTool({
|
|
1364
|
+
description: "Add two numbers",
|
|
1365
|
+
execute: async (args) => {
|
|
1366
|
+
return String(args.a + args.b);
|
|
1367
|
+
},
|
|
1368
|
+
name: "add",
|
|
1369
|
+
parameters: z.object({
|
|
1370
|
+
a: z.number(),
|
|
1371
|
+
b: z.number(),
|
|
1372
|
+
}),
|
|
1373
1373
|
});
|
|
1374
|
+
|
|
1375
|
+
return server;
|
|
1374
1376
|
},
|
|
1375
1377
|
});
|
|
1376
1378
|
});
|
|
@@ -1384,23 +1386,23 @@ test("allows new clients to connect after a client disconnects", async () => {
|
|
|
1384
1386
|
});
|
|
1385
1387
|
|
|
1386
1388
|
server.addTool({
|
|
1387
|
-
name: "add",
|
|
1388
1389
|
description: "Add two numbers",
|
|
1390
|
+
execute: async (args) => {
|
|
1391
|
+
return String(args.a + args.b);
|
|
1392
|
+
},
|
|
1393
|
+
name: "add",
|
|
1389
1394
|
parameters: z.object({
|
|
1390
1395
|
a: z.number(),
|
|
1391
1396
|
b: z.number(),
|
|
1392
1397
|
}),
|
|
1393
|
-
execute: async (args) => {
|
|
1394
|
-
return String(args.a + args.b);
|
|
1395
|
-
},
|
|
1396
1398
|
});
|
|
1397
1399
|
|
|
1398
1400
|
await server.start({
|
|
1399
|
-
transportType: "sse",
|
|
1400
1401
|
sse: {
|
|
1401
1402
|
endpoint: "/sse",
|
|
1402
1403
|
port,
|
|
1403
1404
|
},
|
|
1405
|
+
transportType: "sse",
|
|
1404
1406
|
});
|
|
1405
1407
|
|
|
1406
1408
|
const client1 = new Client(
|
|
@@ -1421,14 +1423,14 @@ test("allows new clients to connect after a client disconnects", async () => {
|
|
|
1421
1423
|
|
|
1422
1424
|
expect(
|
|
1423
1425
|
await client1.callTool({
|
|
1424
|
-
name: "add",
|
|
1425
1426
|
arguments: {
|
|
1426
1427
|
a: 1,
|
|
1427
1428
|
b: 2,
|
|
1428
1429
|
},
|
|
1430
|
+
name: "add",
|
|
1429
1431
|
}),
|
|
1430
1432
|
).toEqual({
|
|
1431
|
-
content: [{
|
|
1433
|
+
content: [{ text: "3", type: "text" }],
|
|
1432
1434
|
});
|
|
1433
1435
|
|
|
1434
1436
|
await client1.close();
|
|
@@ -1451,14 +1453,14 @@ test("allows new clients to connect after a client disconnects", async () => {
|
|
|
1451
1453
|
|
|
1452
1454
|
expect(
|
|
1453
1455
|
await client2.callTool({
|
|
1454
|
-
name: "add",
|
|
1455
1456
|
arguments: {
|
|
1456
1457
|
a: 1,
|
|
1457
1458
|
b: 2,
|
|
1458
1459
|
},
|
|
1460
|
+
name: "add",
|
|
1459
1461
|
}),
|
|
1460
1462
|
).toEqual({
|
|
1461
|
-
content: [{
|
|
1463
|
+
content: [{ text: "3", type: "text" }],
|
|
1462
1464
|
});
|
|
1463
1465
|
|
|
1464
1466
|
await client2.close();
|
|
@@ -1475,11 +1477,11 @@ test("able to close server immediately after starting it", async () => {
|
|
|
1475
1477
|
});
|
|
1476
1478
|
|
|
1477
1479
|
await server.start({
|
|
1478
|
-
transportType: "sse",
|
|
1479
1480
|
sse: {
|
|
1480
1481
|
endpoint: "/sse",
|
|
1481
1482
|
port,
|
|
1482
1483
|
},
|
|
1484
|
+
transportType: "sse",
|
|
1483
1485
|
});
|
|
1484
1486
|
|
|
1485
1487
|
// We were previously not waiting for the server to start.
|
|
@@ -1496,23 +1498,23 @@ test("closing event source does not produce error", async () => {
|
|
|
1496
1498
|
});
|
|
1497
1499
|
|
|
1498
1500
|
server.addTool({
|
|
1499
|
-
name: "add",
|
|
1500
1501
|
description: "Add two numbers",
|
|
1502
|
+
execute: async (args) => {
|
|
1503
|
+
return String(args.a + args.b);
|
|
1504
|
+
},
|
|
1505
|
+
name: "add",
|
|
1501
1506
|
parameters: z.object({
|
|
1502
1507
|
a: z.number(),
|
|
1503
1508
|
b: z.number(),
|
|
1504
1509
|
}),
|
|
1505
|
-
execute: async (args) => {
|
|
1506
|
-
return String(args.a + args.b);
|
|
1507
|
-
},
|
|
1508
1510
|
});
|
|
1509
1511
|
|
|
1510
1512
|
await server.start({
|
|
1511
|
-
transportType: "sse",
|
|
1512
1513
|
sse: {
|
|
1513
1514
|
endpoint: "/sse",
|
|
1514
1515
|
port,
|
|
1515
1516
|
},
|
|
1517
|
+
transportType: "sse",
|
|
1516
1518
|
});
|
|
1517
1519
|
|
|
1518
1520
|
const eventSource = await new Promise<EventSourceClient>((onMessage) => {
|
|
@@ -1551,9 +1553,9 @@ test("provides auth to tools", async () => {
|
|
|
1551
1553
|
});
|
|
1552
1554
|
|
|
1553
1555
|
const server = new FastMCP<{ id: number }>({
|
|
1556
|
+
authenticate,
|
|
1554
1557
|
name: "Test",
|
|
1555
1558
|
version: "1.0.0",
|
|
1556
|
-
authenticate,
|
|
1557
1559
|
});
|
|
1558
1560
|
|
|
1559
1561
|
const execute = vi.fn(async (args) => {
|
|
@@ -1561,21 +1563,21 @@ test("provides auth to tools", async () => {
|
|
|
1561
1563
|
});
|
|
1562
1564
|
|
|
1563
1565
|
server.addTool({
|
|
1564
|
-
name: "add",
|
|
1565
1566
|
description: "Add two numbers",
|
|
1567
|
+
execute,
|
|
1568
|
+
name: "add",
|
|
1566
1569
|
parameters: z.object({
|
|
1567
1570
|
a: z.number(),
|
|
1568
1571
|
b: z.number(),
|
|
1569
1572
|
}),
|
|
1570
|
-
execute,
|
|
1571
1573
|
});
|
|
1572
1574
|
|
|
1573
1575
|
await server.start({
|
|
1574
|
-
transportType: "sse",
|
|
1575
1576
|
sse: {
|
|
1576
1577
|
endpoint: "/sse",
|
|
1577
1578
|
port,
|
|
1578
1579
|
},
|
|
1580
|
+
transportType: "sse",
|
|
1579
1581
|
});
|
|
1580
1582
|
|
|
1581
1583
|
const client = new Client(
|
|
@@ -1614,14 +1616,14 @@ test("provides auth to tools", async () => {
|
|
|
1614
1616
|
|
|
1615
1617
|
expect(
|
|
1616
1618
|
await client.callTool({
|
|
1617
|
-
name: "add",
|
|
1618
1619
|
arguments: {
|
|
1619
1620
|
a: 1,
|
|
1620
1621
|
b: 2,
|
|
1621
1622
|
},
|
|
1623
|
+
name: "add",
|
|
1622
1624
|
}),
|
|
1623
1625
|
).toEqual({
|
|
1624
|
-
content: [{
|
|
1626
|
+
content: [{ text: "3", type: "text" }],
|
|
1625
1627
|
});
|
|
1626
1628
|
|
|
1627
1629
|
expect(execute, "execute should have been called").toHaveBeenCalledTimes(1);
|
|
@@ -1648,22 +1650,22 @@ test("blocks unauthorized requests", async () => {
|
|
|
1648
1650
|
const port = await getRandomPort();
|
|
1649
1651
|
|
|
1650
1652
|
const server = new FastMCP<{ id: number }>({
|
|
1651
|
-
name: "Test",
|
|
1652
|
-
version: "1.0.0",
|
|
1653
1653
|
authenticate: async () => {
|
|
1654
1654
|
throw new Response(null, {
|
|
1655
1655
|
status: 401,
|
|
1656
1656
|
statusText: "Unauthorized",
|
|
1657
1657
|
});
|
|
1658
1658
|
},
|
|
1659
|
+
name: "Test",
|
|
1660
|
+
version: "1.0.0",
|
|
1659
1661
|
});
|
|
1660
1662
|
|
|
1661
1663
|
await server.start({
|
|
1662
|
-
transportType: "sse",
|
|
1663
1664
|
sse: {
|
|
1664
1665
|
endpoint: "/sse",
|
|
1665
1666
|
port,
|
|
1666
1667
|
},
|
|
1668
|
+
transportType: "sse",
|
|
1667
1669
|
});
|
|
1668
1670
|
|
|
1669
1671
|
const client = new Client(
|