tmcp 1.1.0 → 1.2.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/package.json CHANGED
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "name": "tmcp",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "The main tmcp library",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "keywords": [],
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/paoloricciuti/tmcp.git",
11
+ "directory": "packages/tmcp"
12
+ },
8
13
  "author": "Paolo Ricciuti",
9
14
  "license": "MIT",
10
15
  "files": [
package/src/index.d.ts CHANGED
@@ -26,34 +26,36 @@ export class McpServer<StandardSchema extends StandardSchemaV1> {
26
26
  }, execute: TSchema extends undefined ? (() => Promise<unknown> | unknown) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown)): void;
27
27
  /**
28
28
  * @template {StandardSchema | undefined} [TSchema=undefined]
29
- * @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: Completion }} options
29
+ * @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: TSchema extends undefined ? never : Partial<Record<keyof (StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>), Completion>> }} options
30
30
  * @param {TSchema extends undefined ? (()=>Promise<unknown> | unknown) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown} execute
31
31
  */
32
32
  prompt<TSchema extends StandardSchema | undefined = undefined>({ name, description, schema, complete }: {
33
33
  name: string;
34
34
  description: string;
35
35
  schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never;
36
- complete?: Completion;
36
+ complete?: TSchema extends undefined ? never : Partial<Record<keyof StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>, Completion>>;
37
37
  }, execute: TSchema extends undefined ? (() => Promise<unknown> | unknown) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown): void;
38
38
  /**
39
39
  * @param {{ name: string; description: string; uri: string }} options
40
- * @param {(uri: string, params?: unknown) => Promise<unknown> | unknown} execute
40
+ * @param {(uri: string) => Promise<unknown> | unknown} execute
41
41
  */
42
42
  resource({ name, description, uri }: {
43
43
  name: string;
44
44
  description: string;
45
45
  uri: string;
46
- }, execute: (uri: string, params?: unknown) => Promise<unknown> | unknown): void;
46
+ }, execute: (uri: string) => Promise<unknown> | unknown): void;
47
47
  /**
48
- * @param {{ name: string; description: string; uri: string; complete?: Completion }} options
49
- * @param {(uri: string, params?: unknown) => Promise<unknown> | unknown} execute
48
+ * @template {string} TUri
49
+ * @template {ExtractURITemplateVariables<TUri>} TVariables
50
+ * @param {{ name: string; description: string; uri: TUri; complete?: TVariables extends never ? never : Partial<Record<TVariables, Completion>> }} options
51
+ * @param {(uri: string, params: Record<TVariables, string | string[]>) => Promise<unknown> | unknown} execute
50
52
  */
51
- template({ name, description, uri, complete }: {
53
+ template<TUri extends string, TVariables extends ExtractURITemplateVariables<TUri>>({ name, description, uri, complete }: {
52
54
  name: string;
53
55
  description: string;
54
- uri: string;
55
- complete?: Completion;
56
- }, execute: (uri: string, params?: unknown) => Promise<unknown> | unknown): void;
56
+ uri: TUri;
57
+ complete?: TVariables extends never ? never : Partial<Record<TVariables, Completion>>;
58
+ }, execute: (uri: string, params: Record<TVariables, string | string[]>) => Promise<unknown> | unknown): void;
57
59
  /**
58
60
  * @param {JSONRPCRequest} request
59
61
  * @returns {ReturnType<JSONRPCServer['receive']>}
@@ -70,6 +72,7 @@ export class McpServer<StandardSchema extends StandardSchemaV1> {
70
72
  import type { StandardSchemaV1 } from "@standard-schema/spec";
71
73
  import type { McpEvents } from "./internal/internal.js";
72
74
  import type { Completion } from "./internal/internal.js";
75
+ import type { ExtractURITemplateVariables } from "./internal/uri-template.js";
73
76
  import type { JSONRPCRequest } from "json-rpc-2.0";
74
77
  import { JSONRPCServer } from 'json-rpc-2.0';
75
78
  import type { SubscriptionsKeys } from "./internal/internal.js";
package/src/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @import { StandardSchemaV1 } from "@standard-schema/spec";
3
3
  * @import { JSONRPCRequest, JSONRPCParams } from "json-rpc-2.0";
4
+ * @import { ExtractURITemplateVariables } from "./internal/uri-template.js";
4
5
  * @import { Tool, Completion, Prompt, Resource, ServerOptions, ServerInfo, SubscriptionsKeys, McpEvents } from "./internal/internal.js";
5
6
  */
6
7
  import { JSONRPCServer, JSONRPCClient } from 'json-rpc-2.0';
@@ -30,7 +31,7 @@ export class McpServer {
30
31
  #resources = new Map();
31
32
  #templates = new UriTemplateMatcher();
32
33
  /**
33
- * @type {{ [ref: string]: Map<string, Completion> }}
34
+ * @type {{ [ref: string]: Map<string, Partial<Record<string, Completion>>> }}
34
35
  */
35
36
  #completions = {
36
37
  'ref/prompt': new Map(),
@@ -277,7 +278,12 @@ export class McpServer {
277
278
  throw new Error(`Resource ${uri} not found`);
278
279
  }
279
280
  }
280
- return resource.execute(uri, params);
281
+ if (resource.template) {
282
+ if (!params)
283
+ throw new Error('Missing parameters for template resource');
284
+ return resource.execute(uri, params);
285
+ }
286
+ return resource.execute(uri);
281
287
  });
282
288
  }
283
289
  /**
@@ -291,9 +297,11 @@ export class McpServer {
291
297
  if (!completions) return null;
292
298
  const complete = completions.get(ref.uri ?? ref.name);
293
299
  if (!complete) return null;
300
+ const actual_complete = complete[argument.name];
301
+ if (!actual_complete) return null;
294
302
  return {
295
303
  completion: {
296
- values: complete(argument, context),
304
+ values: actual_complete(argument.value, context),
297
305
  hasMore: false,
298
306
  },
299
307
  };
@@ -317,7 +325,7 @@ export class McpServer {
317
325
  }
318
326
  /**
319
327
  * @template {StandardSchema | undefined} [TSchema=undefined]
320
- * @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: Completion }} options
328
+ * @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: TSchema extends undefined ? never : Partial<Record<keyof (StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>), Completion>> }} options
321
329
  * @param {TSchema extends undefined ? (()=>Promise<unknown> | unknown) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown} execute
322
330
  */
323
331
  prompt({ name, description, schema, complete }, execute) {
@@ -334,43 +342,48 @@ export class McpServer {
334
342
  });
335
343
  }
336
344
  /**
337
- * @param {string} name
338
- * @param {string} description
339
- * @param {string} uri
340
- * @param {(uri: string, params?: unknown) => Promise<unknown> | unknown} execute
341
- * @param {Completion} [complete]
342
- * @param {boolean} [template=false]
345
+ * @type {(resource: Resource & {complete?: Partial<Record<string,Completion>>, uri: string})=> void}
343
346
  */
344
- #resource(name, description, uri, execute, complete, template = false) {
347
+ #resource({ uri, complete, ...resource }) {
345
348
  if (complete) {
346
349
  this.#completions['ref/resource'].set(uri, complete);
347
350
  }
348
- if (template) {
351
+ if (resource.template) {
349
352
  this.#templates.add(uri);
350
353
  }
351
354
  if (this.#options.capabilities?.resources?.listChanged) {
352
355
  this.#notify('notifications/resources/list_changed', {});
353
356
  }
354
- this.#resources.set(uri, {
355
- description,
356
- name,
357
- execute,
358
- template,
359
- });
357
+ this.#resources.set(uri, resource);
360
358
  }
361
359
  /**
362
360
  * @param {{ name: string; description: string; uri: string }} options
363
- * @param {(uri: string, params?: unknown) => Promise<unknown> | unknown} execute
361
+ * @param {(uri: string) => Promise<unknown> | unknown} execute
364
362
  */
365
363
  resource({ name, description, uri }, execute) {
366
- this.#resource(name, description, uri, execute, undefined, false);
364
+ this.#resource({
365
+ name,
366
+ description,
367
+ uri,
368
+ execute,
369
+ template: false,
370
+ });
367
371
  }
368
372
  /**
369
- * @param {{ name: string; description: string; uri: string; complete?: Completion }} options
370
- * @param {(uri: string, params?: unknown) => Promise<unknown> | unknown} execute
373
+ * @template {string} TUri
374
+ * @template {ExtractURITemplateVariables<TUri>} TVariables
375
+ * @param {{ name: string; description: string; uri: TUri; complete?: TVariables extends never ? never : Partial<Record<TVariables, Completion>> }} options
376
+ * @param {(uri: string, params: Record<TVariables, string | string[]>) => Promise<unknown> | unknown} execute
371
377
  */
372
378
  template({ name, description, uri, complete }, execute) {
373
- this.#resource(name, description, uri, execute, complete, true);
379
+ this.#resource({
380
+ name,
381
+ description,
382
+ uri,
383
+ execute,
384
+ complete,
385
+ template: true,
386
+ });
374
387
  }
375
388
  /**
376
389
  * @param {JSONRPCRequest} request
@@ -11,7 +11,7 @@ export type Tool<TSchema extends StandardSchemaV1 = StandardSchemaV1<any>> = {
11
11
  };
12
12
 
13
13
  export type Completion = (
14
- argument: { name: string; value: string },
14
+ query: string,
15
15
  context: { arguments: Record<string, string> },
16
16
  ) => string[];
17
17
 
@@ -23,12 +23,22 @@ export type Prompt<TSchema extends StandardSchemaV1 = StandardSchemaV1<any>> = {
23
23
  ) => Promise<unknown> | unknown;
24
24
  };
25
25
 
26
- export type Resource = {
27
- description: string;
28
- name: string;
29
- template: boolean;
30
- execute: (uri: string, params?: unknown) => Promise<unknown> | unknown;
31
- };
26
+ export type Resource =
27
+ | {
28
+ description: string;
29
+ name: string;
30
+ template: true;
31
+ execute: (
32
+ uri: string,
33
+ params: Record<string, string | string[]>,
34
+ ) => Promise<unknown> | unknown;
35
+ }
36
+ | {
37
+ description: string;
38
+ name: string;
39
+ template: false;
40
+ execute: (uri: string) => Promise<unknown> | unknown;
41
+ };
32
42
 
33
43
  export type ServerOptions<TSchema extends StandardSchemaV1> = {
34
44
  capabilities?: {
@@ -0,0 +1,45 @@
1
+ // Helper type to remove whitespace
2
+ type Trim<S extends string> = S extends ` ${infer R}`
3
+ ? Trim<R>
4
+ : S extends `${infer L} `
5
+ ? Trim<L>
6
+ : S;
7
+
8
+ // Helper type to extract variable name, removing modifiers
9
+ type ExtractVarName<S extends string> = S extends `${infer Name}:${string}`
10
+ ? Trim<Name> // Remove prefix modifier
11
+ : S extends `${infer Name}*`
12
+ ? Trim<Name> // Remove explode modifier
13
+ : Trim<S>;
14
+
15
+ // Helper type to split comma-separated variables
16
+ type SplitVariables<S extends string> = S extends `${infer First},${infer Rest}`
17
+ ? ExtractVarName<First> | SplitVariables<Rest>
18
+ : ExtractVarName<S>;
19
+
20
+ // Helper type to extract content from braces and handle operators
21
+ type ExtractFromExpression<S extends string> = S extends `+${infer Vars}`
22
+ ? SplitVariables<Vars> // Reserved {+var}
23
+ : S extends `#${infer Vars}`
24
+ ? SplitVariables<Vars> // Fragment {#var}
25
+ : S extends `.${infer Vars}`
26
+ ? SplitVariables<Vars> // Label {.var}
27
+ : S extends `/${infer Vars}`
28
+ ? SplitVariables<Vars> // Path {/var}
29
+ : S extends `;${infer Vars}`
30
+ ? SplitVariables<Vars> // Path-style {;var}
31
+ : S extends `?${infer Vars}`
32
+ ? SplitVariables<Vars> // Query {?var}
33
+ : S extends `&${infer Vars}`
34
+ ? SplitVariables<Vars> // Query continuation {&var}
35
+ : SplitVariables<S>; // Simple {var}
36
+
37
+ // Main recursive type to extract all variables from URI template
38
+ type ExtractVariablesFromTemplate<S extends string> =
39
+ S extends `${string}{${infer Expression}}${infer Rest}`
40
+ ? ExtractFromExpression<Expression> | ExtractVariablesFromTemplate<Rest>
41
+ : never;
42
+
43
+ // Main exported type
44
+ export type ExtractURITemplateVariables<T extends string> =
45
+ ExtractVariablesFromTemplate<T>;