@mcp-ts/sdk 1.6.2 → 2.1.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.
Files changed (113) hide show
  1. package/README.md +12 -6
  2. package/dist/adapters/agui-adapter.d.mts +3 -3
  3. package/dist/adapters/agui-adapter.d.ts +3 -3
  4. package/dist/adapters/agui-adapter.js +5 -5
  5. package/dist/adapters/agui-adapter.js.map +1 -1
  6. package/dist/adapters/agui-adapter.mjs +5 -5
  7. package/dist/adapters/agui-adapter.mjs.map +1 -1
  8. package/dist/adapters/agui-middleware.d.mts +3 -3
  9. package/dist/adapters/agui-middleware.d.ts +3 -3
  10. package/dist/adapters/ai-adapter.d.mts +9 -3
  11. package/dist/adapters/ai-adapter.d.ts +9 -3
  12. package/dist/adapters/ai-adapter.js +21 -6
  13. package/dist/adapters/ai-adapter.js.map +1 -1
  14. package/dist/adapters/ai-adapter.mjs +21 -6
  15. package/dist/adapters/ai-adapter.mjs.map +1 -1
  16. package/dist/adapters/langchain-adapter.d.mts +3 -3
  17. package/dist/adapters/langchain-adapter.d.ts +3 -3
  18. package/dist/adapters/langchain-adapter.js +10 -6
  19. package/dist/adapters/langchain-adapter.js.map +1 -1
  20. package/dist/adapters/langchain-adapter.mjs +10 -6
  21. package/dist/adapters/langchain-adapter.mjs.map +1 -1
  22. package/dist/adapters/mastra-adapter.d.mts +1 -1
  23. package/dist/adapters/mastra-adapter.d.ts +1 -1
  24. package/dist/adapters/mastra-adapter.js +5 -1
  25. package/dist/adapters/mastra-adapter.js.map +1 -1
  26. package/dist/adapters/mastra-adapter.mjs +5 -1
  27. package/dist/adapters/mastra-adapter.mjs.map +1 -1
  28. package/dist/bin/mcp-ts.js +7 -1
  29. package/dist/bin/mcp-ts.js.map +1 -1
  30. package/dist/bin/mcp-ts.mjs +7 -1
  31. package/dist/bin/mcp-ts.mjs.map +1 -1
  32. package/dist/client/index.d.mts +2 -2
  33. package/dist/client/index.d.ts +2 -2
  34. package/dist/client/index.js +9 -13
  35. package/dist/client/index.js.map +1 -1
  36. package/dist/client/index.mjs +9 -13
  37. package/dist/client/index.mjs.map +1 -1
  38. package/dist/client/react.d.mts +7 -7
  39. package/dist/client/react.d.ts +7 -7
  40. package/dist/client/react.js +15 -19
  41. package/dist/client/react.js.map +1 -1
  42. package/dist/client/react.mjs +15 -19
  43. package/dist/client/react.mjs.map +1 -1
  44. package/dist/client/vue.d.mts +7 -7
  45. package/dist/client/vue.d.ts +7 -7
  46. package/dist/client/vue.js +14 -18
  47. package/dist/client/vue.js.map +1 -1
  48. package/dist/client/vue.mjs +14 -18
  49. package/dist/client/vue.mjs.map +1 -1
  50. package/dist/{index-bFL4ZF2N.d.mts → index-Cfjsme-a.d.mts} +5 -5
  51. package/dist/{index-DhA-OEAe.d.ts → index-CmjMd2ac.d.ts} +5 -5
  52. package/dist/index.d.mts +6 -6
  53. package/dist/index.d.ts +6 -6
  54. package/dist/index.js +643 -374
  55. package/dist/index.js.map +1 -1
  56. package/dist/index.mjs +642 -374
  57. package/dist/index.mjs.map +1 -1
  58. package/dist/{multi-session-client-CHE8QpVE.d.ts → multi-session-client-BYtguGJm.d.ts} +22 -22
  59. package/dist/{multi-session-client-CQsRbxYI.d.mts → multi-session-client-DYNe6az3.d.mts} +22 -22
  60. package/dist/server/index.d.mts +31 -34
  61. package/dist/server/index.d.ts +31 -34
  62. package/dist/server/index.js +531 -256
  63. package/dist/server/index.js.map +1 -1
  64. package/dist/server/index.mjs +530 -256
  65. package/dist/server/index.mjs.map +1 -1
  66. package/dist/shared/index.d.mts +7 -7
  67. package/dist/shared/index.d.ts +7 -7
  68. package/dist/shared/index.js +103 -105
  69. package/dist/shared/index.js.map +1 -1
  70. package/dist/shared/index.mjs +103 -105
  71. package/dist/shared/index.mjs.map +1 -1
  72. package/dist/{tool-router-BVaV1udm.d.mts → tool-router-BP8TT9mI.d.ts} +87 -73
  73. package/dist/{tool-router-Dh2804tM.d.ts → tool-router-BkQnso27.d.mts} +87 -73
  74. package/dist/{types-rIuN1CQi.d.mts → types-CxFaaZrM.d.mts} +6 -5
  75. package/dist/{types-rIuN1CQi.d.ts → types-CxFaaZrM.d.ts} +6 -5
  76. package/dist/{utils-0qmYrqoa.d.mts → utils-DELRKQPU.d.mts} +1 -1
  77. package/dist/{utils-0qmYrqoa.d.ts → utils-DELRKQPU.d.ts} +1 -1
  78. package/migrations/neon/20260513010000_install_mcp_sessions.sql +69 -0
  79. package/migrations/neon/20260513020000_add_session_cleanup_cron.sql +35 -0
  80. package/{supabase/migrations → migrations/supabase}/20260330195700_install_mcp_sessions.sql +7 -9
  81. package/package.json +14 -5
  82. package/src/adapters/ai-adapter.ts +30 -1
  83. package/src/adapters/langchain-adapter.ts +6 -2
  84. package/src/adapters/mastra-adapter.ts +6 -2
  85. package/src/bin/mcp-ts.ts +8 -1
  86. package/src/client/core/app-host.ts +1 -1
  87. package/src/client/core/sse-client.ts +12 -14
  88. package/src/client/core/types.ts +1 -1
  89. package/src/client/react/use-mcp-apps.tsx +1 -1
  90. package/src/client/react/use-mcp.ts +11 -11
  91. package/src/client/vue/use-mcp.ts +10 -10
  92. package/src/server/handlers/nextjs-handler.ts +18 -15
  93. package/src/server/handlers/sse-handler.ts +29 -29
  94. package/src/server/index.ts +1 -1
  95. package/src/server/mcp/multi-session-client.ts +17 -17
  96. package/src/server/mcp/oauth-client.ts +37 -37
  97. package/src/server/mcp/storage-oauth-provider.ts +17 -17
  98. package/src/server/storage/file-backend.ts +25 -25
  99. package/src/server/storage/index.ts +67 -10
  100. package/src/server/storage/memory-backend.ts +34 -34
  101. package/src/server/storage/neon-backend.ts +281 -0
  102. package/src/server/storage/redis-backend.ts +64 -64
  103. package/src/server/storage/sqlite-backend.ts +33 -33
  104. package/src/server/storage/supabase-backend.ts +23 -24
  105. package/src/server/storage/types.ts +18 -21
  106. package/src/shared/errors.ts +1 -1
  107. package/src/shared/index.ts +1 -2
  108. package/src/shared/meta-tools.ts +9 -10
  109. package/src/shared/schema-compressor.ts +2 -42
  110. package/src/shared/tool-index.ts +90 -84
  111. package/src/shared/tool-router.ts +38 -27
  112. package/src/shared/types.ts +6 -5
  113. /package/{supabase/migrations → migrations/supabase}/20260421010000_add_session_cleanup_cron.sql +0 -0
@@ -28,8 +28,6 @@ export interface ToolSummary {
28
28
  serverId: string;
29
29
  /** Session the tool belongs to */
30
30
  sessionId: string;
31
- /** Estimated token cost of the full inputSchema */
32
- estimatedTokens: number;
33
31
  }
34
32
 
35
33
  /** Server-level summary derived from indexed tools. */
@@ -74,6 +72,7 @@ export interface IndexedTool extends Tool {
74
72
  sessionId: string;
75
73
  serverId: string;
76
74
  serverName: string;
75
+ outputSchema?: Tool['outputSchema'];
77
76
  }
78
77
 
79
78
  /**
@@ -106,41 +105,6 @@ export interface ToolIndexOptions {
106
105
  keywordWeight?: number;
107
106
  }
108
107
 
109
- // ---------------------------------------------------------------------------
110
- // Token Estimation
111
- // ---------------------------------------------------------------------------
112
-
113
- /**
114
- * Character-class weights for accurate-ish token estimation without a real
115
- * tokenizer. Empirically calibrated against cl100k_base on typical JSON
116
- * Schema payloads.
117
- *
118
- * | Char class | Approx chars per token |
119
- * |--------------------|------------------------|
120
- * | Whitespace / punct | 1–2 |
121
- * | English words | ~4 |
122
- * | JSON keys/values | ~3.5 |
123
- *
124
- * We walk the string once and accumulate a weighted character count, then
125
- * divide by a calibrated divisor.
126
- */
127
- const CALIBRATION_DIVISOR = 3.6;
128
-
129
- function classifyChar(ch: string): number {
130
- const code = ch.charCodeAt(0);
131
- // whitespace / common JSON structural chars → high token density
132
- if (code <= 0x20 || ch === '{' || ch === '}' || ch === '[' || ch === ']' || ch === ':' || ch === ',') return 1.0;
133
- // digits and symbols
134
- if (code >= 0x21 && code <= 0x2f) return 1.5;
135
- if (code >= 0x30 && code <= 0x39) return 2.0;
136
- // uppercase (often JSON keys)
137
- if (code >= 0x41 && code <= 0x5a) return 3.5;
138
- // lowercase (natural language in descriptions)
139
- if (code >= 0x61 && code <= 0x7a) return 4.0;
140
- // everything else (unicode, emojis, etc.)
141
- return 2.5;
142
- }
143
-
144
108
  // ---------------------------------------------------------------------------
145
109
  // ToolIndex
146
110
  // ---------------------------------------------------------------------------
@@ -170,9 +134,6 @@ export class ToolIndex {
170
134
  /** BM25: average document length across the entire index. */
171
135
  private avgDocLength = 0;
172
136
 
173
- /** Cached total estimated token cost across all indexed tools. */
174
- private totalTokenCost = 0;
175
-
176
137
  private options: Required<ToolIndexOptions>;
177
138
 
178
139
  constructor(options: ToolIndexOptions = {}) {
@@ -199,7 +160,6 @@ export class ToolIndex {
199
160
  this.embeddings.clear();
200
161
  this.docLengths.clear();
201
162
  this.avgDocLength = 0;
202
- this.totalTokenCost = 0;
203
163
 
204
164
  // 1. Populate tool map + search text
205
165
  const allTokenSets: Map<string, Set<string>> = new Map();
@@ -212,21 +172,19 @@ export class ToolIndex {
212
172
  this.tools.set(tool.name, []);
213
173
  }
214
174
  this.tools.get(tool.name)!.push(tool);
215
- const estimatedTokens = ToolIndex.estimateTokens(tool);
216
175
  this.toolSummaries.set(docKey, {
217
176
  name: tool.name,
218
177
  description: tool.description ?? '',
219
178
  serverName: tool.serverName,
220
179
  serverId: tool.serverId,
221
180
  sessionId: tool.sessionId,
222
- estimatedTokens,
223
181
  });
224
- this.totalTokenCost += estimatedTokens;
225
182
 
226
- const text = this.buildSearchableText(tool).toLowerCase();
183
+ const rawText = this.buildSearchableText(tool);
184
+ const text = rawText.toLowerCase();
227
185
  this.searchTexts.set(docKey, text);
228
186
 
229
- const tokens = this.tokenize(text);
187
+ const tokens = this.tokenize(rawText);
230
188
  const tf = new Map<string, number>();
231
189
  const uniqueTokens = new Set<string>();
232
190
 
@@ -578,60 +536,107 @@ export class ToolIndex {
578
536
  return count;
579
537
  }
580
538
 
581
- /** Total estimated token cost of all indexed tool schemas. */
582
- getTotalTokenCost(): number {
583
- return this.totalTokenCost;
584
- }
585
-
586
539
  // -----------------------------------------------------------------------
587
- // Static Helpers
540
+ // Internals
588
541
  // -----------------------------------------------------------------------
589
542
 
590
- /**
591
- * Estimate token count of a tool's full schema (name + description + inputSchema).
592
- *
593
- * Uses character-class weighted counting calibrated against cl100k_base.
594
- * Accuracy is typically within ±10% for JSON Schema payloads.
595
- */
596
- static estimateTokens(tool: Tool): number {
543
+ /** Build a single searchable string from tool metadata. */
544
+ private buildSearchableText(tool: Tool): string {
597
545
  const parts: string[] = [tool.name];
598
546
  if (tool.description) parts.push(tool.description);
599
- if (tool.inputSchema) parts.push(JSON.stringify(tool.inputSchema));
600
547
 
601
- const text = parts.join(' ');
602
- let weightedLen = 0;
603
-
604
- for (let i = 0; i < text.length; i++) {
605
- weightedLen += 1 / classifyChar(text[i]);
548
+ if (tool.inputSchema && typeof tool.inputSchema === 'object') {
549
+ this.collectSchemaSearchText(tool.inputSchema, parts);
606
550
  }
607
551
 
608
- return Math.ceil(weightedLen / (1 / CALIBRATION_DIVISOR));
552
+ return parts.join(' ');
609
553
  }
610
554
 
611
- // -----------------------------------------------------------------------
612
- // Internals
613
- // -----------------------------------------------------------------------
555
+ /** Recursively collect JSON Schema argument names and descriptions. */
556
+ private collectSchemaSearchText(
557
+ schema: unknown,
558
+ parts: string[],
559
+ seen = new WeakSet<object>()
560
+ ): void {
561
+ if (!schema || typeof schema !== 'object') return;
562
+ if (seen.has(schema)) return;
563
+ seen.add(schema);
564
+
565
+ if (Array.isArray(schema)) {
566
+ for (const item of schema) {
567
+ this.collectSchemaSearchText(item, parts, seen);
568
+ }
569
+ return;
570
+ }
614
571
 
615
- /** Build a single searchable string from tool metadata. */
616
- private buildSearchableText(tool: Tool): string {
617
- const parts: string[] = [tool.name];
618
- if (tool.description) parts.push(tool.description);
572
+ const schemaObject = schema as Record<string, unknown>;
573
+ this.pushStringValue(schemaObject.description, parts);
574
+ this.pushStringValue(schemaObject.title, parts);
619
575
 
620
- // Include property names and descriptions from schema
621
- if (tool.inputSchema && typeof tool.inputSchema === 'object') {
622
- const schema = tool.inputSchema as Record<string, unknown>;
623
- const props = schema.properties as Record<string, { description?: string }> | undefined;
624
- if (props) {
625
- for (const [key, val] of Object.entries(props)) {
626
- parts.push(key);
627
- if (val && typeof val === 'object' && val.description) {
628
- parts.push(val.description);
629
- }
576
+ const properties = schemaObject.properties;
577
+ if (properties && typeof properties === 'object' && !Array.isArray(properties)) {
578
+ for (const [propertyName, propertySchema] of Object.entries(properties)) {
579
+ parts.push(propertyName);
580
+ this.collectSchemaSearchText(propertySchema, parts, seen);
581
+ }
582
+ }
583
+
584
+ const patternProperties = schemaObject.patternProperties;
585
+ if (
586
+ patternProperties &&
587
+ typeof patternProperties === 'object' &&
588
+ !Array.isArray(patternProperties)
589
+ ) {
590
+ for (const [propertyPattern, propertySchema] of Object.entries(patternProperties)) {
591
+ parts.push(propertyPattern);
592
+ this.collectSchemaSearchText(propertySchema, parts, seen);
593
+ }
594
+ }
595
+
596
+ const dependentSchemas = schemaObject.dependentSchemas;
597
+ if (
598
+ dependentSchemas &&
599
+ typeof dependentSchemas === 'object' &&
600
+ !Array.isArray(dependentSchemas)
601
+ ) {
602
+ for (const [propertyName, dependentSchema] of Object.entries(dependentSchemas)) {
603
+ parts.push(propertyName);
604
+ this.collectSchemaSearchText(dependentSchema, parts, seen);
605
+ }
606
+ }
607
+
608
+ for (const key of [
609
+ 'items',
610
+ 'additionalProperties',
611
+ 'contains',
612
+ 'propertyNames',
613
+ 'if',
614
+ 'then',
615
+ 'else',
616
+ 'not',
617
+ ]) {
618
+ this.collectSchemaSearchText(schemaObject[key], parts, seen);
619
+ }
620
+
621
+ for (const key of ['allOf', 'anyOf', 'oneOf', 'prefixItems']) {
622
+ this.collectSchemaSearchText(schemaObject[key], parts, seen);
623
+ }
624
+
625
+ for (const key of ['$defs', 'definitions']) {
626
+ const definitions = schemaObject[key];
627
+ if (definitions && typeof definitions === 'object' && !Array.isArray(definitions)) {
628
+ for (const [definitionName, definitionSchema] of Object.entries(definitions)) {
629
+ parts.push(definitionName);
630
+ this.collectSchemaSearchText(definitionSchema, parts, seen);
630
631
  }
631
632
  }
632
633
  }
634
+ }
633
635
 
634
- return parts.join(' ');
636
+ private pushStringValue(value: unknown, parts: string[]): void {
637
+ if (typeof value === 'string' && value.trim()) {
638
+ parts.push(value);
639
+ }
635
640
  }
636
641
 
637
642
  private getDocumentKey(tool: IndexedTool): string {
@@ -662,6 +667,7 @@ export class ToolIndex {
662
667
  .replace(/([a-z])([A-Z])/g, '$1 $2')
663
668
  // Split snake_case / kebab-case
664
669
  .replace(/[_-]/g, ' ')
670
+ .toLowerCase()
665
671
  // Remove non-alphanumeric (except spaces)
666
672
  .replace(/[^a-z0-9\s]/g, '')
667
673
  // Split on whitespace
@@ -99,6 +99,17 @@ export interface ToolRouterOptions {
99
99
  */
100
100
  compactSchemas?: boolean;
101
101
 
102
+ /**
103
+ * Tool names to expose directly when using `search` strategy.
104
+ * Pinned tools are removed from discovery results and should be called directly.
105
+ */
106
+ pinnedTools?: string[];
107
+
108
+ /**
109
+ * Tool names or glob-style patterns to omit entirely from the router catalog.
110
+ */
111
+ excludeTools?: string[];
112
+
102
113
  /**
103
114
  * Optional embedding function for semantic search.
104
115
  * When not provided, keyword TF-IDF matching is used.
@@ -136,12 +147,16 @@ export type ToolRouterClientInput = ToolClientProvider | ToolClient[];
136
147
  export class ToolRouter {
137
148
  private index: ToolIndex;
138
149
  private allTools: IndexedTool[] = [];
150
+ private pinnedTools: IndexedTool[] = [];
151
+ private discoverableTools: IndexedTool[] = [];
139
152
  private groupsMap = new Map<string, ToolGroupInfo>();
140
153
  private strategy: ToolRouterStrategy;
141
154
  private maxTools: number;
142
155
  private compactSchemas: boolean;
143
156
  private activeGroups: Set<string>;
144
157
  private customGroups?: Record<string, string[]>;
158
+ private pinnedToolNames: Set<string>;
159
+ private excludeToolMatchers: RegExp[];
145
160
  private initialized = false;
146
161
 
147
162
  constructor(
@@ -153,6 +168,10 @@ export class ToolRouter {
153
168
  this.compactSchemas = options.compactSchemas ?? false;
154
169
  this.activeGroups = new Set(options.activeGroups ?? []);
155
170
  this.customGroups = options.groups;
171
+ this.pinnedToolNames = new Set(options.pinnedTools ?? []);
172
+ this.excludeToolMatchers = (options.excludeTools ?? []).map((pattern) =>
173
+ globToRegExp(pattern)
174
+ );
156
175
 
157
176
  this.index = new ToolIndex({
158
177
  embedFn: options.embedFn,
@@ -177,7 +196,7 @@ export class ToolRouter {
177
196
 
178
197
  switch (this.strategy) {
179
198
  case 'search':
180
- return this.getMetaToolDefinitions();
199
+ return [...this.getMetaToolDefinitions(), ...this.pinnedTools];
181
200
 
182
201
  case 'groups':
183
202
  return this.getGroupFilteredTools();
@@ -290,30 +309,6 @@ export class ToolRouter {
290
309
  return [...this.activeGroups];
291
310
  }
292
311
 
293
- // -----------------------------------------------------------------------
294
- // Stats & Introspection
295
- // -----------------------------------------------------------------------
296
-
297
- /** Total token cost of all tools if loaded without filtering. */
298
- getTotalTokenCost(): number {
299
- return this.index.getTotalTokenCost();
300
- }
301
-
302
- /** Estimate token cost of the currently filtered tool set. */
303
- async getFilteredTokenCost(): Promise<number> {
304
- const tools = await this.getFilteredTools();
305
- let total = 0;
306
- for (const tool of tools) {
307
- total += ToolIndex.estimateTokens(tool);
308
- }
309
- return total;
310
- }
311
-
312
- /** Get compression stats showing savings from current strategy. */
313
- getCompressionStats() {
314
- return SchemaCompressor.estimateSavings(this.allTools);
315
- }
316
-
317
312
  /** Number of total indexed tools. */
318
313
  get totalToolCount(): number {
319
314
  return this.allTools.length;
@@ -376,8 +371,11 @@ export class ToolRouter {
376
371
  private async ensureInitialized(): Promise<void> {
377
372
  if (this.initialized) return;
378
373
 
379
- this.allTools = await this.fetchAllTools();
380
- await this.index.buildIndex(this.allTools);
374
+ const fetchedTools = await this.fetchAllTools();
375
+ this.allTools = fetchedTools.filter((tool) => !this.matchesExcludedTool(tool.name));
376
+ this.pinnedTools = this.allTools.filter((tool) => this.matchesPinnedTool(tool.name));
377
+ this.discoverableTools = this.allTools.filter((tool) => !this.matchesPinnedTool(tool.name));
378
+ await this.index.buildIndex(this.discoverableTools);
381
379
  this.buildGroups();
382
380
  this.initialized = true;
383
381
  }
@@ -500,4 +498,17 @@ export class ToolRouter {
500
498
  ];
501
499
  }
502
500
 
501
+ private matchesPinnedTool(toolName: string): boolean {
502
+ return this.pinnedToolNames.has(toolName);
503
+ }
504
+
505
+ private matchesExcludedTool(toolName: string): boolean {
506
+ return this.excludeToolMatchers.some((matcher) => matcher.test(toolName));
507
+ }
508
+ }
509
+
510
+ function globToRegExp(pattern: string): RegExp {
511
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
512
+ const regexPattern = `^${escaped.replace(/\*/g, '.*')}$`;
513
+ return new RegExp(regexPattern);
503
514
  }
@@ -162,11 +162,12 @@ export function isCallToolSuccess(
162
162
  export type ToolInfo = {
163
163
  name: string;
164
164
  description?: string;
165
- inputSchema?: unknown;
165
+ inputSchema?: Tool['inputSchema'];
166
+ outputSchema?: Tool['outputSchema'];
166
167
  };
167
168
 
168
169
  // Transport type
169
- export type TransportType = 'sse' | 'streamable_http';
170
+ export type TransportType = 'sse' | 'streamable-http';
170
171
 
171
172
  // SSE/RPC types
172
173
  export type McpRpcMethod =
@@ -174,8 +175,8 @@ export type McpRpcMethod =
174
175
  | 'disconnect'
175
176
  | 'listTools'
176
177
  | 'callTool'
177
- | 'getSessions'
178
- | 'restoreSession'
178
+ | 'listSessions'
179
+ | 'getSession'
179
180
  | 'finishAuth'
180
181
  | 'listPrompts'
181
182
  | 'getPrompt'
@@ -275,7 +276,7 @@ export interface DisconnectResult {
275
276
  success: boolean;
276
277
  }
277
278
 
278
- export interface RestoreSessionResult {
279
+ export interface GetSessionResult {
279
280
  success: boolean;
280
281
  toolCount: number;
281
282
  }