@mcp-ts/sdk 2.2.0 → 2.3.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.
Files changed (48) hide show
  1. package/dist/adapters/agui-adapter.d.mts +1 -1
  2. package/dist/adapters/agui-adapter.d.ts +1 -1
  3. package/dist/adapters/agui-adapter.js +9 -5
  4. package/dist/adapters/agui-adapter.js.map +1 -1
  5. package/dist/adapters/agui-adapter.mjs +9 -5
  6. package/dist/adapters/agui-adapter.mjs.map +1 -1
  7. package/dist/adapters/agui-middleware.d.mts +1 -1
  8. package/dist/adapters/agui-middleware.d.ts +1 -1
  9. package/dist/adapters/ai-adapter.d.mts +1 -1
  10. package/dist/adapters/ai-adapter.d.ts +1 -1
  11. package/dist/adapters/ai-adapter.js +9 -5
  12. package/dist/adapters/ai-adapter.js.map +1 -1
  13. package/dist/adapters/ai-adapter.mjs +9 -5
  14. package/dist/adapters/ai-adapter.mjs.map +1 -1
  15. package/dist/adapters/langchain-adapter.d.mts +1 -1
  16. package/dist/adapters/langchain-adapter.d.ts +1 -1
  17. package/dist/adapters/langchain-adapter.js +9 -5
  18. package/dist/adapters/langchain-adapter.js.map +1 -1
  19. package/dist/adapters/langchain-adapter.mjs +9 -5
  20. package/dist/adapters/langchain-adapter.mjs.map +1 -1
  21. package/dist/client/react.js.map +1 -1
  22. package/dist/client/react.mjs.map +1 -1
  23. package/dist/index.d.mts +2 -2
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +83 -6
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +83 -7
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/server/index.d.mts +13 -1
  30. package/dist/server/index.d.ts +13 -1
  31. package/dist/server/index.js +66 -1
  32. package/dist/server/index.js.map +1 -1
  33. package/dist/server/index.mjs +66 -2
  34. package/dist/server/index.mjs.map +1 -1
  35. package/dist/shared/index.d.mts +2 -2
  36. package/dist/shared/index.d.ts +2 -2
  37. package/dist/shared/index.js +17 -5
  38. package/dist/shared/index.js.map +1 -1
  39. package/dist/shared/index.mjs +17 -5
  40. package/dist/shared/index.mjs.map +1 -1
  41. package/dist/{tool-router-C8hIA-tg.d.mts → tool-router-BfE_9tay.d.mts} +5 -0
  42. package/dist/{tool-router-CdX8e9bk.d.ts → tool-router-CP7qLk1J.d.ts} +5 -0
  43. package/package.json +1 -1
  44. package/src/server/index.ts +8 -1
  45. package/src/server/storage/index.ts +77 -2
  46. package/src/server/storage/types.ts +14 -0
  47. package/src/shared/meta-tools.ts +16 -7
  48. package/src/shared/tool-router.ts +13 -0
@@ -355,6 +355,11 @@ declare class ToolRouter {
355
355
  * If tool name is ambiguous, use namespace to specify the server.
356
356
  */
357
357
  getToolSchema(toolName: string, namespace?: string, options?: ToolLookupOptions): IndexedTool | undefined;
358
+ /**
359
+ * Resolve the full tool definition by name, ensuring the router index has
360
+ * been initialized first.
361
+ */
362
+ resolveToolSchema(toolName: string, namespace?: string, options?: ToolLookupOptions): Promise<IndexedTool | undefined>;
358
363
  /**
359
364
  * Get compact (schema-less) summaries for all tools.
360
365
  */
@@ -355,6 +355,11 @@ declare class ToolRouter {
355
355
  * If tool name is ambiguous, use namespace to specify the server.
356
356
  */
357
357
  getToolSchema(toolName: string, namespace?: string, options?: ToolLookupOptions): IndexedTool | undefined;
358
+ /**
359
+ * Resolve the full tool definition by name, ensuring the router index has
360
+ * been initialized first.
361
+ */
362
+ resolveToolSchema(toolName: string, namespace?: string, options?: ToolLookupOptions): Promise<IndexedTool | undefined>;
358
363
  /**
359
364
  * Get compact (schema-less) summaries for all tools.
360
365
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-ts/sdk",
3
- "version": "2.2.0",
3
+ "version": "2.3.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -6,7 +6,7 @@
6
6
  /** Core MCP client and session management */
7
7
  export { MCPClient } from './mcp/oauth-client.js';
8
8
  export { UnauthorizedError } from '../shared/errors.js';
9
- export { sessions, type SessionStore } from './storage/index.js';
9
+ export { sessions, onSessionMutation, type SessionStore } from './storage/index.js';
10
10
  export { StorageOAuthClientProvider } from './mcp/storage-oauth-provider.js';
11
11
  export { MultiSessionClient } from './mcp/multi-session-client.js';
12
12
 
@@ -44,6 +44,13 @@ export type {
44
44
  CallToolResponse,
45
45
  } from '../shared/types';
46
46
 
47
+ export type {
48
+ Session,
49
+ SessionMutationEvent,
50
+ SessionMutationListener,
51
+ SessionMutationType,
52
+ } from './storage/types.js';
53
+
47
54
  /** Re-export MCP SDK types for convenience */
48
55
  export type {
49
56
  OAuthClientMetadata,
@@ -5,7 +5,7 @@ import { FileStorageBackend } from './file-backend';
5
5
  import { SqliteStorage } from './sqlite-backend.js';
6
6
  import { SupabaseStorageBackend } from './supabase-backend.js';
7
7
  import { NeonStorageBackend, type NeonStorageOptions } from './neon-backend.js';
8
- import type { SessionStore } from './types.js';
8
+ import type { SessionStore, Session, SessionMutationEvent, SessionMutationListener } from './types.js';
9
9
 
10
10
  // Re-export types
11
11
  export * from './types.js';
@@ -42,6 +42,65 @@ function warnIfNeonConnectionStringIsInsecure(connectionString: string): void {
42
42
 
43
43
  let storageInstance: SessionStore | null = null;
44
44
  let storagePromise: Promise<SessionStore> | null = null;
45
+ const sessionMutationListeners = new Set<SessionMutationListener>();
46
+
47
+ function emitSessionMutation(event: SessionMutationEvent): void {
48
+ for (const listener of sessionMutationListeners) {
49
+ try {
50
+ const result = listener(event);
51
+ if (result && typeof (result as Promise<void>).catch === 'function') {
52
+ void (result as Promise<void>).catch((error) => {
53
+ console.error('[mcp-ts][Storage] Session mutation listener failed:', error);
54
+ });
55
+ }
56
+ } catch (error) {
57
+ console.error('[mcp-ts][Storage] Session mutation listener failed:', error);
58
+ }
59
+ }
60
+ }
61
+
62
+ function createSessionMutationEvent(prop: PropertyKey, args: any[]): SessionMutationEvent | null {
63
+ const timestamp = Date.now();
64
+
65
+ if (prop === 'create') {
66
+ const [session, ttl] = args as [Session, number | undefined];
67
+ if (!session?.userId || !session?.sessionId) return null;
68
+ return {
69
+ type: 'create',
70
+ userId: session.userId,
71
+ sessionId: session.sessionId,
72
+ session,
73
+ ttl,
74
+ timestamp,
75
+ };
76
+ }
77
+
78
+ if (prop === 'update') {
79
+ const [userId, sessionId, patch, ttl] = args as [string, string, Partial<Session>, number | undefined];
80
+ if (!userId || !sessionId) return null;
81
+ return {
82
+ type: 'update',
83
+ userId,
84
+ sessionId,
85
+ patch,
86
+ ttl,
87
+ timestamp,
88
+ };
89
+ }
90
+
91
+ if (prop === 'delete') {
92
+ const [userId, sessionId] = args as [string, string];
93
+ if (!userId || !sessionId) return null;
94
+ return {
95
+ type: 'delete',
96
+ userId,
97
+ sessionId,
98
+ timestamp,
99
+ };
100
+ }
101
+
102
+ return null;
103
+ }
45
104
 
46
105
  async function initializeStorage<T extends SessionStore>(store: T): Promise<T> {
47
106
  if (typeof store.init === 'function') {
@@ -215,6 +274,17 @@ export function _setStorageInstanceForTesting(instance: SessionStore | null): vo
215
274
  }
216
275
  }
217
276
 
277
+ export function onSessionMutation(listener: SessionMutationListener): () => void {
278
+ sessionMutationListeners.add(listener);
279
+ return () => {
280
+ sessionMutationListeners.delete(listener);
281
+ };
282
+ }
283
+
284
+ export function _resetSessionMutationListenersForTesting(): void {
285
+ sessionMutationListeners.clear();
286
+ }
287
+
218
288
  /**
219
289
  * Global session store instance
220
290
  * Uses lazy initialization with a Proxy to handle async setup transparently
@@ -225,7 +295,12 @@ export const sessions: SessionStore = new Proxy({} as SessionStore, {
225
295
  const instance = await getStorage();
226
296
  const value = (instance as any)[prop];
227
297
  if (typeof value === 'function') {
228
- return value.apply(instance, args);
298
+ const result = await value.apply(instance, args);
299
+ const event = createSessionMutationEvent(prop, args);
300
+ if (event) {
301
+ emitSessionMutation(event);
302
+ }
303
+ return result;
229
304
  }
230
305
  return value;
231
306
  };
@@ -29,6 +29,20 @@ export interface Session {
29
29
  clientId?: string;
30
30
  }
31
31
 
32
+ export type SessionMutationType = 'create' | 'update' | 'delete';
33
+
34
+ export interface SessionMutationEvent {
35
+ type: SessionMutationType;
36
+ userId: string;
37
+ sessionId: string;
38
+ timestamp: number;
39
+ session?: Session;
40
+ patch?: Partial<Session>;
41
+ ttl?: number;
42
+ }
43
+
44
+ export type SessionMutationListener = (event: SessionMutationEvent) => void | Promise<void>;
45
+
32
46
  export interface SetClientOptions {
33
47
  sessionId: string;
34
48
  serverId?: string; // Database server ID
@@ -235,12 +235,23 @@ export async function executeMetaTool(
235
235
  router: ToolRouter,
236
236
  callToolFn?: CallToolFn
237
237
  ): Promise<CallToolResult | null> {
238
- const resolveToolSchema = (
238
+ const resolveToolSchema = async (
239
239
  name: string,
240
240
  namespace?: string,
241
241
  options?: ToolLookupOptions
242
- ): { tool?: IndexedTool; error?: CallToolResult } => {
242
+ ): Promise<{ tool?: IndexedTool; error?: CallToolResult }> => {
243
243
  try {
244
+ if (typeof (router as ToolRouter & { resolveToolSchema?: unknown }).resolveToolSchema === 'function') {
245
+ return {
246
+ tool: await (router as ToolRouter & {
247
+ resolveToolSchema: (
248
+ toolName: string,
249
+ namespace?: string,
250
+ options?: ToolLookupOptions
251
+ ) => Promise<IndexedTool | undefined>;
252
+ }).resolveToolSchema(name, namespace, options),
253
+ };
254
+ }
244
255
  return { tool: router.getToolSchema(name, namespace, options) };
245
256
  } catch (err) {
246
257
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -307,8 +318,6 @@ export async function executeMetaTool(
307
318
  // Fast path: Check for select: prefix
308
319
  const selectMatch = query.match(/^select:(.+)$/i);
309
320
  if (selectMatch) {
310
- await router.listTools({ serverId, serverName, limit: 1 });
311
-
312
321
  const requested = selectMatch[1]!
313
322
  .split(',')
314
323
  .map((s) => s.trim())
@@ -320,7 +329,7 @@ export async function executeMetaTool(
320
329
  const namespace = serverId ?? serverName;
321
330
 
322
331
  for (const requestedToolName of requested) {
323
- const { tool, error } = resolveToolSchema(requestedToolName, namespace, {
332
+ const { tool, error } = await resolveToolSchema(requestedToolName, namespace, {
324
333
  allowServerNameFragment: Boolean(serverName && !serverId),
325
334
  });
326
335
  if (error) {
@@ -410,7 +419,7 @@ export async function executeMetaTool(
410
419
  case 'mcp_get_tool_schema': {
411
420
  const name = String(args.toolName ?? '');
412
421
  const namespace = String(args.serverId ?? '') || undefined;
413
- const { tool, error } = resolveToolSchema(name, namespace);
422
+ const { tool, error } = await resolveToolSchema(name, namespace);
414
423
 
415
424
  if (error) {
416
425
  return error;
@@ -461,7 +470,7 @@ export async function executeMetaTool(
461
470
  }
462
471
 
463
472
  // Verify the tool exists in our index
464
- const { tool, error } = resolveToolSchema(targetToolName, namespace);
473
+ const { tool, error } = await resolveToolSchema(targetToolName, namespace);
465
474
  if (error) {
466
475
  return error;
467
476
  }
@@ -289,6 +289,19 @@ export class ToolRouter {
289
289
  return matches[0];
290
290
  }
291
291
 
292
+ /**
293
+ * Resolve the full tool definition by name, ensuring the router index has
294
+ * been initialized first.
295
+ */
296
+ async resolveToolSchema(
297
+ toolName: string,
298
+ namespace?: string,
299
+ options: ToolLookupOptions = {}
300
+ ): Promise<IndexedTool | undefined> {
301
+ await this.ensureInitialized();
302
+ return this.getToolSchema(toolName, namespace, options);
303
+ }
304
+
292
305
  /**
293
306
  * Get compact (schema-less) summaries for all tools.
294
307
  */