tmcp 1.18.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -212,11 +212,169 @@ tool.mix(
212
212
 
213
213
  - `tool` – build `CallToolResult` objects via `text`, `error`, `media`, `resource`, `resourceLink`, `structured`, and `mix` (merge multiple kind of tool response).
214
214
  - `resource` – return `ReadResourceResult` through `text`, `blob`, or `mix` (merge `resource.text` and `resource.blob`).
215
- - `prompt` – generate `GetPromptResult` using `message` (single string), `messages` (array of strings), or `various` (for prompts with string, audio, images, resources etc).
215
+ - `prompt` – generate `GetPromptResult` using `message` (single string), `messages` (array of strings), `text`, `media`, `resource`, `resourceLink` and `mix` (merge multiple kind of tool response).
216
216
  - `complete` – create `CompleteResult` payloads with `values(list, hasMore?, total?)`.
217
217
 
218
218
  All helpers are typed, so TypeScript will prevent you from returning malformed payloads while cutting down the repetitive envelopes that used to appear in almost every handler.
219
219
 
220
+ ## Defining Tools, Prompts, Resources, and Templates in Separate Files
221
+
222
+ For better code organization and reusability, you can define your tools, prompts, resources, and templates in separate files using the `defineTool`, `definePrompt`, `defineResource`, and `defineTemplate` utilities. This approach allows you to:
223
+
224
+ - **Organize by feature**: Group related tools in feature-specific modules
225
+ - **Reuse across servers**: Share common tools between different MCP servers
226
+
227
+ ### Example Structure
228
+
229
+ ```javascript
230
+ // tools/calculator.js
231
+ import { defineTool } from 'tmcp/tool';
232
+ import { z } from 'zod';
233
+
234
+ export const addTool = defineTool(
235
+ {
236
+ name: 'add',
237
+ description: 'Add two numbers',
238
+ schema: z.object({
239
+ a: z.number(),
240
+ b: z.number(),
241
+ }),
242
+ },
243
+ async ({ a, b }) => ({
244
+ content: [{ type: 'text', text: `${a} + ${b} = ${a + b}` }],
245
+ }),
246
+ );
247
+
248
+ export const multiplyTool = defineTool(
249
+ {
250
+ name: 'multiply',
251
+ description: 'Multiply two numbers',
252
+ schema: z.object({
253
+ a: z.number(),
254
+ b: z.number(),
255
+ }),
256
+ },
257
+ async ({ a, b }) => ({
258
+ content: [{ type: 'text', text: `${a} × ${b} = ${a * b}` }],
259
+ }),
260
+ );
261
+ ```
262
+
263
+ ```javascript
264
+ // prompts/code-review.js
265
+ import { definePrompt } from 'tmcp/prompt';
266
+ import { z } from 'zod';
267
+
268
+ export const codeReviewPrompt = definePrompt(
269
+ {
270
+ name: 'code-review',
271
+ description: 'Generate code review prompt',
272
+ schema: z.object({
273
+ code: z.string(),
274
+ language: z.string(),
275
+ }),
276
+ },
277
+ async ({ code, language }) => ({
278
+ messages: [
279
+ {
280
+ role: 'user',
281
+ content: {
282
+ type: 'text',
283
+ text: `Review this ${language} code:\n\n${code}`,
284
+ },
285
+ },
286
+ ],
287
+ }),
288
+ );
289
+ ```
290
+
291
+ ```javascript
292
+ // resources/files.js
293
+ import { defineResource } from 'tmcp/resource';
294
+ import { readFile } from 'node:fs/promises';
295
+
296
+ export const readmeResource = defineResource(
297
+ {
298
+ name: 'readme',
299
+ description: 'Project README file',
300
+ uri: 'file://README.md',
301
+ },
302
+ async (uri) => ({
303
+ contents: [
304
+ {
305
+ uri,
306
+ mimeType: 'text/markdown',
307
+ text: await readFile(uri.replace('file://', ''), 'utf8'),
308
+ },
309
+ ],
310
+ }),
311
+ );
312
+ ```
313
+
314
+ ```javascript
315
+ // templates/user-files.js
316
+ import { defineTemplate } from 'tmcp/template';
317
+ import { getUserFile } from '../api/users.js';
318
+
319
+ export const userFileTemplate = defineTemplate(
320
+ {
321
+ name: 'user-files',
322
+ description: 'Access user files by ID',
323
+ uri: 'users/{userId}/files/{filename}',
324
+ complete: {
325
+ userId: async (arg) => ({
326
+ completion: {
327
+ values: await searchUserIds(arg),
328
+ hasMore: false,
329
+ },
330
+ }),
331
+ },
332
+ },
333
+ async (uri, { userId, filename }) => ({
334
+ contents: [
335
+ {
336
+ uri,
337
+ mimeType: 'text/plain',
338
+ text: await getUserFile(userId, filename),
339
+ },
340
+ ],
341
+ }),
342
+ );
343
+ ```
344
+
345
+ ```javascript
346
+ // server.js
347
+ import { McpServer } from 'tmcp';
348
+ import { ZodJsonSchemaAdapter } from '@tmcp/adapter-zod';
349
+ import { addTool, multiplyTool } from './tools/calculator.js';
350
+ import { codeReviewPrompt } from './prompts/code-review.js';
351
+ import { readmeResource } from './resources/files.js';
352
+ import { userFileTemplate } from './templates/user-files.js';
353
+
354
+ const server = new McpServer(
355
+ {
356
+ name: 'my-server',
357
+ version: '1.0.0',
358
+ },
359
+ {
360
+ adapter: new ZodJsonSchemaAdapter(),
361
+ },
362
+ );
363
+
364
+ // Register multiple tools at once
365
+ server.tools([addTool, multiplyTool]);
366
+
367
+ // Or register individually
368
+ server.tool(addTool);
369
+
370
+ // Same for prompts, resources, and templates
371
+ server.prompts([codeReviewPrompt]);
372
+ server.resources([readmeResource]);
373
+ server.templates([userFileTemplate]);
374
+ ```
375
+
376
+ Adding the primitive to the server will error if the tool uses a different validation library than the one expressed in the adapter.
377
+
220
378
  ## API Reference
221
379
 
222
380
  ### McpServer
@@ -237,11 +395,12 @@ new McpServer(serverInfo, options);
237
395
 
238
396
  #### Methods
239
397
 
240
- ##### `tool(definition, handler)`
398
+ ##### `tool(definition, handler)` / `tools(definitions)`
241
399
 
242
- Register a tool with optional schema validation.
400
+ Register one or more tools with optional schema validation.
243
401
 
244
402
  ```javascript
403
+ // Register a single tool inline
245
404
  server.tool(
246
405
  {
247
406
  name: 'tool-name',
@@ -250,83 +409,146 @@ server.tool(
250
409
  },
251
410
  async (input) => {
252
411
  // Tool implementation
253
- return { content: [{ type: 'text', text: 'Tool result' }] };
412
+ return result;
413
+ },
414
+ );
415
+
416
+ // Register a tool created with defineTool
417
+ import { defineTool } from 'tmcp/tool';
418
+
419
+ const myTool = defineTool(
420
+ {
421
+ name: 'tool-name',
422
+ description: 'Tool description',
423
+ schema: yourSchema,
424
+ },
425
+ async (input) => {
426
+ return result;
254
427
  },
255
428
  );
429
+
430
+ server.tool(myTool);
431
+
432
+ // Register multiple tools at once
433
+ server.tools([tool1, tool2, tool3]);
256
434
  ```
257
435
 
258
436
  `sessionInfo` contains the latest `clientCapabilities`, `clientInfo`, and `logLevel` reported by the connected client. Built-in transports populate it automatically, so every handler can branch on client features without additional bookkeeping.
259
437
 
260
- ##### `prompt(definition, handler)`
438
+ ##### `prompt(definition, handler)` / `prompts(definitions)`
261
439
 
262
- Register a prompt template with optional schema validation.
440
+ Register one or more prompt templates with optional schema validation.
263
441
 
264
442
  ```javascript
443
+ // Register a single prompt inline
265
444
  server.prompt(
266
445
  {
267
- name: 'prompt-name',
268
- description: 'Prompt description',
269
- schema: yourSchema, // optional
270
- complete: {
271
- paramName: (arg, context) => ({
272
- completion: {
273
- values: ['completion1', 'completion2'],
274
- total: 2,
275
- hasMore: false
276
- }
277
- })
278
- } // optional
446
+ name: 'prompt-name',
447
+ description: 'Prompt description',
448
+ schema: yourSchema, // optional
449
+ complete: (arg, context) => ['completion1', 'completion2'] // optional
450
+ },
451
+ async (input) => {
452
+ // Prompt implementation
453
+ return { messages: [...] };
454
+ }
455
+ );
456
+
457
+ // Register a prompt created with definePrompt
458
+ import { definePrompt } from 'tmcp/prompt';
459
+
460
+ const myPrompt = definePrompt(
461
+ {
462
+ name: 'prompt-name',
463
+ description: 'Prompt description',
464
+ schema: yourSchema,
279
465
  },
280
466
  async (input) => {
281
- // Prompt implementation
282
- return { messages: [...] };
467
+ return { messages: [...] };
283
468
  }
284
469
  );
470
+
471
+ server.prompt(myPrompt);
472
+
473
+ // Register multiple prompts at once
474
+ server.prompts([prompt1, prompt2, prompt3]);
285
475
  ```
286
476
 
287
- ##### `resource(definition, handler)`
477
+ ##### `resource(definition, handler)` / `resources(definitions)`
288
478
 
289
- Register a static resource.
479
+ Register one or more static resources.
290
480
 
291
481
  ```javascript
482
+ // Register a single resource inline
292
483
  server.resource(
293
484
  {
294
- name: 'resource-name',
295
- description: 'Resource description',
296
- uri: 'file://path/to/resource'
485
+ name: 'resource-name',
486
+ description: 'Resource description',
487
+ uri: 'file://path/to/resource'
297
488
  },
298
489
  async (uri, params) => {
299
- // Resource implementation
300
- return { contents: [...] };
490
+ // Resource implementation
491
+ return { contents: [...] };
301
492
  }
302
493
  );
494
+
495
+ // Register a resource created with defineResource
496
+ import { defineResource } from 'tmcp/resource';
497
+
498
+ const myResource = defineResource(
499
+ {
500
+ name: 'resource-name',
501
+ description: 'Resource description',
502
+ uri: 'file://path/to/resource'
503
+ },
504
+ async (uri) => {
505
+ return { contents: [...] };
506
+ }
507
+ );
508
+
509
+ server.resource(myResource);
510
+
511
+ // Register multiple resources at once
512
+ server.resources([resource1, resource2, resource3]);
303
513
  ```
304
514
 
305
- ##### `template(definition, handler)`
515
+ ##### `template(definition, handler)` / `templates(definitions)`
306
516
 
307
- Register a URI template for dynamic resources.
517
+ Register one or more URI templates for dynamic resources.
308
518
 
309
519
  ```javascript
520
+ // Register a single template inline
310
521
  server.template(
311
522
  {
312
- name: 'template-name',
313
- description: 'Template description',
314
- uri: 'file://path/{id}/resource',
315
- complete: {
316
- id: (arg, context) => ({
317
- completion: {
318
- values: ['id1', 'id2', 'id3'],
319
- total: 3,
320
- hasMore: false
321
- }
322
- })
323
- } // optional
523
+ name: 'template-name',
524
+ description: 'Template description',
525
+ uri: 'file://path/{id}/resource',
526
+ complete: (arg, context) => ['id1', 'id2'] // optional
324
527
  },
325
528
  async (uri, params) => {
326
- // Template implementation using params.id
327
- return { contents: [...] };
529
+ // Template implementation using params.id
530
+ return { contents: [...] };
328
531
  }
329
532
  );
533
+
534
+ // Register a template created with defineTemplate
535
+ import { defineTemplate } from 'tmcp/template';
536
+
537
+ const myTemplate = defineTemplate(
538
+ {
539
+ name: 'template-name',
540
+ description: 'Template description',
541
+ uri: 'file://path/{id}/resource',
542
+ },
543
+ async (uri, params) => {
544
+ return { contents: [...] };
545
+ }
546
+ );
547
+
548
+ server.template(myTemplate);
549
+
550
+ // Register multiple templates at once
551
+ server.templates([template1, template2, template3]);
330
552
  ```
331
553
 
332
554
  ##### `withContext<T>()`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmcp",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "description": "The main tmcp library",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -21,6 +21,22 @@
21
21
  "types": "./src/types/index.d.ts",
22
22
  "default": "./src/index.js"
23
23
  },
24
+ "./tool": {
25
+ "types": "./src/types/index.d.ts",
26
+ "default": "./src/tool.js"
27
+ },
28
+ "./prompt": {
29
+ "types": "./src/types/index.d.ts",
30
+ "default": "./src/prompt.js"
31
+ },
32
+ "./resource": {
33
+ "types": "./src/types/index.d.ts",
34
+ "default": "./src/resource.js"
35
+ },
36
+ "./template": {
37
+ "types": "./src/types/index.d.ts",
38
+ "default": "./src/template.js"
39
+ },
24
40
  "./adapter": {
25
41
  "types": "./src/types/index.d.ts",
26
42
  "default": "./src/adapter.js"
@@ -48,7 +64,7 @@
48
64
  "scripts": {
49
65
  "test": "vitest",
50
66
  "typecheck": "tsc --noEmit",
51
- "generate:types": "dts-buddy && publint",
67
+ "generate:types": "dts-buddy && node ./scripts/fix-unique-symbol.ts && publint",
52
68
  "prepublish": "pnpm generate:types"
53
69
  }
54
70
  }