@mcp-ts/sdk 2.5.0 → 2.5.2

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 (63) hide show
  1. package/dist/adapters/agui-adapter.d.mts +3 -3
  2. package/dist/adapters/agui-adapter.d.ts +3 -3
  3. package/dist/adapters/agui-middleware.d.mts +3 -3
  4. package/dist/adapters/agui-middleware.d.ts +3 -3
  5. package/dist/adapters/ai-adapter.d.mts +3 -3
  6. package/dist/adapters/ai-adapter.d.ts +3 -3
  7. package/dist/adapters/langchain-adapter.d.mts +3 -3
  8. package/dist/adapters/langchain-adapter.d.ts +3 -3
  9. package/dist/adapters/mastra-adapter.d.mts +2 -2
  10. package/dist/adapters/mastra-adapter.d.ts +2 -2
  11. package/dist/client/index.d.mts +2 -2
  12. package/dist/client/index.d.ts +2 -2
  13. package/dist/client/index.js +3 -0
  14. package/dist/client/index.js.map +1 -1
  15. package/dist/client/index.mjs +3 -0
  16. package/dist/client/index.mjs.map +1 -1
  17. package/dist/client/react.d.mts +4 -4
  18. package/dist/client/react.d.ts +4 -4
  19. package/dist/client/react.js +23 -38
  20. package/dist/client/react.js.map +1 -1
  21. package/dist/client/react.mjs +23 -38
  22. package/dist/client/react.mjs.map +1 -1
  23. package/dist/client/vue.d.mts +5 -4
  24. package/dist/client/vue.d.ts +5 -4
  25. package/dist/client/vue.js +4 -1
  26. package/dist/client/vue.js.map +1 -1
  27. package/dist/client/vue.mjs +4 -1
  28. package/dist/client/vue.mjs.map +1 -1
  29. package/dist/{index-CTURVnom.d.mts → index-B3GPsPsi.d.mts} +2 -1
  30. package/dist/{index-sVcqrhf7.d.ts → index-gNdSQTSz.d.ts} +2 -1
  31. package/dist/index.d.mts +4 -4
  32. package/dist/index.d.ts +4 -4
  33. package/dist/index.js +214 -13
  34. package/dist/index.js.map +1 -1
  35. package/dist/index.mjs +214 -13
  36. package/dist/index.mjs.map +1 -1
  37. package/dist/{multi-session-client-CSPSHHla.d.ts → multi-session-client-CB4oDrQX.d.ts} +36 -10
  38. package/dist/{multi-session-client-DMZGVABI.d.mts → multi-session-client-xj3iQIv6.d.mts} +36 -10
  39. package/dist/server/index.d.mts +39 -7
  40. package/dist/server/index.d.ts +39 -7
  41. package/dist/server/index.js +211 -13
  42. package/dist/server/index.js.map +1 -1
  43. package/dist/server/index.mjs +211 -13
  44. package/dist/server/index.mjs.map +1 -1
  45. package/dist/shared/index.d.mts +4 -4
  46. package/dist/shared/index.d.ts +4 -4
  47. package/dist/shared/index.js.map +1 -1
  48. package/dist/shared/index.mjs.map +1 -1
  49. package/dist/{tool-router-CS9l0w4a.d.ts → tool-router-C-Mw1_BQ.d.ts} +1 -1
  50. package/dist/{tool-router-CVLBaCwH.d.mts → tool-router-CsKVXbQB.d.mts} +1 -1
  51. package/dist/{types-DK_NGWd4.d.mts → types-6SmXege4.d.mts} +4 -2
  52. package/dist/{types-DK_NGWd4.d.ts → types-6SmXege4.d.ts} +4 -2
  53. package/package.json +2 -2
  54. package/src/client/core/sse-client.ts +4 -0
  55. package/src/client/react/oauth-popup.tsx +17 -24
  56. package/src/client/react/use-mcp.ts +7 -16
  57. package/src/client/vue/use-mcp.ts +2 -1
  58. package/src/server/handlers/sse-handler.ts +121 -3
  59. package/src/server/mcp/oauth-client.ts +44 -20
  60. package/src/server/mcp/tool-policy-gateway.ts +98 -2
  61. package/src/server/storage/tool-policy.ts +102 -0
  62. package/src/shared/events.ts +1 -0
  63. package/src/shared/types.ts +4 -0
@@ -2174,6 +2174,15 @@ var MCPClient = class {
2174
2174
  __publicField(this, "_onObservabilityEvent", new Emitter());
2175
2175
  __publicField(this, "onObservabilityEvent", this._onObservabilityEvent.event);
2176
2176
  __publicField(this, "currentState", "DISCONNECTED");
2177
+ /**
2178
+ * In-memory cache for the remote server's full tools list.
2179
+ *
2180
+ * Populated on the first `fetchTools()` call and reused for the lifetime of
2181
+ * the connection. Cleared to `null` at the start of `connect()` so that a
2182
+ * reconnect always retrieves a fresh list, and also in `dispose()` to release
2183
+ * the memory when the client is no longer needed.
2184
+ */
2185
+ __publicField(this, "cachedTools", null);
2177
2186
  this.serverUrl = options.serverUrl;
2178
2187
  this.serverName = options.serverName;
2179
2188
  this.callbackUrl = options.callbackUrl;
@@ -2494,13 +2503,20 @@ var MCPClient = class {
2494
2503
  throw lastError || new Error("No transports available");
2495
2504
  }
2496
2505
  /**
2497
- * Connects to the MCP server
2498
- * Automatically validates and refreshes OAuth tokens if needed
2499
- * Saves session to Redis on first successful connection
2500
- * @throws {UnauthorizedError} When OAuth authorization is required
2501
- * @throws {Error} When connection fails for other reasons
2506
+ * Connects to the MCP server.
2507
+ *
2508
+ * Automatically validates and refreshes OAuth tokens if needed.
2509
+ * Saves the session to Redis on first successful connection.
2510
+ *
2511
+ * The in-memory tools cache (`cachedTools`) is cleared at the start of every
2512
+ * call so that a reconnection always fetches a fresh tool list from the remote
2513
+ * server — even if the same `MCPClient` instance is reused.
2514
+ *
2515
+ * @throws {UnauthorizedError} When OAuth authorization is required.
2516
+ * @throws {Error} When connection fails for other reasons.
2502
2517
  */
2503
2518
  async connect() {
2519
+ this.cachedTools = null;
2504
2520
  if (this.client.transport) {
2505
2521
  this.transport = null;
2506
2522
  try {
@@ -2683,17 +2699,30 @@ var MCPClient = class {
2683
2699
  }
2684
2700
  }
2685
2701
  /**
2686
- * Lists all available tools from the connected MCP server without emitting discovery events.
2687
- * Gateways use this to apply policy before publishing tools to agents or UI state.
2702
+ * Lists all available tools from the connected MCP server without emitting
2703
+ * discovery events. The result is cached in memory for the lifetime of the
2704
+ * connection — subsequent callers (e.g. `gateway.listTools()` running right
2705
+ * after `fetchTools()`) pay zero extra network cost.
2706
+ *
2707
+ * Gateways use this to apply a tool-access policy before publishing the
2708
+ * filtered list to agents or UI state.
2709
+ *
2710
+ * @returns The full `ListToolsResult` from the remote server.
2711
+ * @throws {Error} When the client is not connected or the request times out.
2688
2712
  */
2689
2713
  async fetchTools() {
2714
+ if (this.cachedTools) {
2715
+ return this.cachedTools;
2716
+ }
2690
2717
  const request = {
2691
2718
  method: "tools/list",
2692
2719
  params: {}
2693
2720
  };
2694
- return await this.withRetry(
2721
+ const result = await this.withRetry(
2695
2722
  () => this.client.request(request, ListToolsResultSchema)
2696
2723
  );
2724
+ this.cachedTools = result;
2725
+ return result;
2697
2726
  }
2698
2727
  /**
2699
2728
  * Lists all available tools from the connected MCP server
@@ -2979,10 +3008,14 @@ var MCPClient = class {
2979
3008
  this.emitStateChange("DISCONNECTED");
2980
3009
  }
2981
3010
  /**
2982
- * Dispose of all event emitters
2983
- * Call this when the client is no longer needed
3011
+ * Disposes all event emitters and releases cached state.
3012
+ *
3013
+ * Clears `cachedTools` to free memory, and disposes the connection and
3014
+ * observability event emitters so downstream listeners are unsubscribed.
3015
+ * Call this when the client is permanently shut down (not just disconnected).
2984
3016
  */
2985
3017
  dispose() {
3018
+ this.cachedTools = null;
2986
3019
  this._onConnectionEvent.dispose();
2987
3020
  this._onObservabilityEvent.dispose();
2988
3021
  }
@@ -3037,21 +3070,47 @@ var ToolPolicyGateway = class {
3037
3070
  this.sessionId = sessionId;
3038
3071
  this.client = client;
3039
3072
  }
3073
+ /**
3074
+ * Returns whether the underlying MCP client transport is currently connected.
3075
+ */
3040
3076
  isConnected() {
3041
3077
  return this.client.isConnected();
3042
3078
  }
3079
+ /**
3080
+ * Returns the server ID from the underlying client, if available.
3081
+ */
3043
3082
  getServerId() {
3044
3083
  return this.client.getServerId?.();
3045
3084
  }
3085
+ /**
3086
+ * Returns the human-readable server name from the underlying client, if available.
3087
+ */
3046
3088
  getServerName() {
3047
3089
  return this.client.getServerName?.();
3048
3090
  }
3091
+ /**
3092
+ * Returns the server URL from the underlying client, if available.
3093
+ */
3049
3094
  getServerUrl() {
3050
3095
  return this.client.getServerUrl?.();
3051
3096
  }
3097
+ /**
3098
+ * Returns the session ID — prefers the value reported by the underlying
3099
+ * client, falling back to the one injected at construction time.
3100
+ */
3052
3101
  getSessionId() {
3053
3102
  return this.client.getSessionId?.() ?? this.sessionId;
3054
3103
  }
3104
+ /**
3105
+ * Returns the **policy-filtered** list of tools that the current session
3106
+ * is allowed to call.
3107
+ *
3108
+ * Internally calls `client.fetchTools()` (which is cache-backed) so no
3109
+ * extra network round-trip is incurred when called after `fetchTools()`.
3110
+ *
3111
+ * @returns A `ListToolsResult` containing only the permitted tools.
3112
+ * @throws {Error} When the session does not exist in the store.
3113
+ */
3055
3114
  async listTools() {
3056
3115
  const session = await this.getSession();
3057
3116
  const result = await this.client.fetchTools();
@@ -3061,20 +3120,61 @@ var ToolPolicyGateway = class {
3061
3120
  tools
3062
3121
  };
3063
3122
  }
3123
+ /**
3124
+ * Returns the **complete, unfiltered** list of tools from the remote server,
3125
+ * bypassing any tool-access policy.
3126
+ *
3127
+ * Used by the management UI to show all available tools (including blocked
3128
+ * ones) so users can toggle individual tool access in the dialog.
3129
+ *
3130
+ * @returns The raw `ListToolsResult` from the remote server.
3131
+ */
3064
3132
  async listAllTools() {
3065
3133
  return await this.client.fetchTools();
3066
3134
  }
3135
+ /**
3136
+ * Executes a tool call on the remote server after verifying that the tool
3137
+ * is permitted by the current session's policy.
3138
+ *
3139
+ * @param name - The exact tool name to invoke.
3140
+ * @param args - Key/value arguments to pass to the tool.
3141
+ * @returns The tool's `CallToolResult`.
3142
+ * @throws {Error} When the tool is blocked by the session's policy.
3143
+ * @throws {Error} When the session does not exist in the store.
3144
+ */
3067
3145
  async callTool(name, args) {
3068
3146
  const session = await this.getSession();
3069
3147
  this.assertAllowed(session, name);
3070
3148
  return await this.client.callTool(name, args);
3071
3149
  }
3150
+ /**
3151
+ * Filters a raw tools array down to only those permitted by the session's
3152
+ * `toolPolicy`.
3153
+ *
3154
+ * @param session - The session whose policy should be applied.
3155
+ * @param tools - The unfiltered list of tools from the remote server.
3156
+ * @returns A subset of `tools` that the policy allows.
3157
+ */
3072
3158
  filterTools(session, tools) {
3073
3159
  return filterToolsByPolicy(tools, session.toolPolicy, this.getPolicyServerId(session));
3074
3160
  }
3161
+ /**
3162
+ * Throws if `toolName` is blocked by the session's policy.
3163
+ * Call this before proxying a `callTool` request to the remote server.
3164
+ *
3165
+ * @param session - The session whose policy should be enforced.
3166
+ * @param toolName - The tool being invoked.
3167
+ * @throws {Error} When the tool is not permitted.
3168
+ */
3075
3169
  assertAllowed(session, toolName) {
3076
3170
  assertToolAllowed(session.toolPolicy, toolName, this.getPolicyServerId(session));
3077
3171
  }
3172
+ /**
3173
+ * Loads the session from the store and throws if it does not exist.
3174
+ *
3175
+ * @returns The fully-hydrated `Session` record.
3176
+ * @throws {Error} When the session cannot be found.
3177
+ */
3078
3178
  async getSession() {
3079
3179
  const session = await sessions.get(this.userId, this.sessionId);
3080
3180
  if (!session) {
@@ -3082,6 +3182,11 @@ var ToolPolicyGateway = class {
3082
3182
  }
3083
3183
  return session;
3084
3184
  }
3185
+ /**
3186
+ * Resolves the server ID to use when evaluating tool policy.
3187
+ * Prefers the value from the live client (most accurate) and falls back
3188
+ * to the server ID stored on the session record.
3189
+ */
3085
3190
  getPolicyServerId(session) {
3086
3191
  return this.client.getServerId?.() ?? session.serverId;
3087
3192
  }
@@ -3386,6 +3491,9 @@ var SSEConnectionManager = class {
3386
3491
  case "connect":
3387
3492
  result = await this.connect(request.params);
3388
3493
  break;
3494
+ case "reconnect":
3495
+ result = await this.reconnect(request.params);
3496
+ break;
3389
3497
  case "disconnect":
3390
3498
  result = await this.disconnect(request.params);
3391
3499
  break;
@@ -3528,6 +3636,66 @@ var SSEConnectionManager = class {
3528
3636
  throw error;
3529
3637
  }
3530
3638
  }
3639
+ /**
3640
+ * Reconnect to an MCP server — tears down the active client transport/connection
3641
+ * and creates a fresh connection while reusing the existing session credentials in a single RPC call.
3642
+ */
3643
+ async reconnect(params) {
3644
+ const { serverId: rawServerId, serverName, serverUrl, callbackUrl, transportType } = params;
3645
+ const headers = normalizeHeaders(params.headers);
3646
+ const serverId = rawServerId && rawServerId.length <= 12 ? rawServerId : generateServerId();
3647
+ const existingSessions = await sessions.list(this.userId);
3648
+ const duplicate = existingSessions.find(
3649
+ (s) => s.serverId === serverId || s.serverUrl === serverUrl
3650
+ );
3651
+ const sessionId = duplicate ? duplicate.sessionId : await sessions.generateSessionId();
3652
+ if (duplicate) {
3653
+ const existingClient = this.clients.get(duplicate.sessionId);
3654
+ if (existingClient) {
3655
+ await existingClient.disconnect();
3656
+ this.clients.delete(duplicate.sessionId);
3657
+ }
3658
+ }
3659
+ try {
3660
+ const clientMetadata = await this.getResolvedClientMetadata();
3661
+ const client = new MCPClient({
3662
+ userId: this.userId,
3663
+ sessionId,
3664
+ serverId,
3665
+ serverName,
3666
+ serverUrl,
3667
+ callbackUrl,
3668
+ transportType,
3669
+ headers,
3670
+ ...clientMetadata
3671
+ });
3672
+ this.clients.set(sessionId, client);
3673
+ client.onConnectionEvent((event) => {
3674
+ this.emitConnectionEvent(event);
3675
+ });
3676
+ client.onObservabilityEvent((event) => {
3677
+ this.sendEvent(event);
3678
+ });
3679
+ await client.connect();
3680
+ await this.listPolicyFilteredTools(sessionId);
3681
+ return { sessionId, success: true };
3682
+ } catch (error) {
3683
+ if (error instanceof UnauthorizedError) {
3684
+ this.clients.delete(sessionId);
3685
+ return { sessionId, success: true };
3686
+ }
3687
+ this.emitConnectionEvent({
3688
+ type: "error",
3689
+ sessionId,
3690
+ serverId,
3691
+ error: error instanceof Error ? error.message : "Connection failed",
3692
+ errorType: "connection",
3693
+ timestamp: Date.now()
3694
+ });
3695
+ this.clients.delete(sessionId);
3696
+ throw error;
3697
+ }
3698
+ }
3531
3699
  /**
3532
3700
  * Disconnect from an MCP server
3533
3701
  */
@@ -3574,12 +3742,29 @@ var SSEConnectionManager = class {
3574
3742
  this.clients.set(sessionId, client);
3575
3743
  return client;
3576
3744
  }
3745
+ /**
3746
+ * Fetches all tools from the remote MCP server and emits a `tools_discovered` event.
3747
+ *
3748
+ * Two lists are always published together:
3749
+ * - `tools` — policy-filtered list that agents are allowed to call.
3750
+ * - `allTools` — the complete, unfiltered list used by the management UI so
3751
+ * that blocked tools still appear as checkboxes in the dialog.
3752
+ *
3753
+ * `fetchTools()` is called first (populates the in-memory cache), then
3754
+ * `gateway.listTools()` re-uses that cache internally — so only one remote
3755
+ * network round-trip is made regardless of how many callers follow.
3756
+ *
3757
+ * @param sessionId - The session whose tools should be discovered.
3758
+ * @returns The session record and the policy-filtered tool list.
3759
+ * @throws {Error} When the session does not exist in the store.
3760
+ */
3577
3761
  async listPolicyFilteredTools(sessionId) {
3578
3762
  const session = await sessions.get(this.userId, sessionId);
3579
3763
  if (!session) {
3580
3764
  throw new Error("Session not found");
3581
3765
  }
3582
3766
  const client = await this.getOrCreateClient(sessionId);
3767
+ const allTools = await client.fetchTools().catch(() => ({ tools: [] }));
3583
3768
  const gateway = createToolPolicyGateway(this.userId, sessionId, client);
3584
3769
  const result = await gateway.listTools();
3585
3770
  this.emitConnectionEvent({
@@ -3588,12 +3773,15 @@ var SSEConnectionManager = class {
3588
3773
  serverId: session.serverId ?? "unknown",
3589
3774
  toolCount: result.tools.length,
3590
3775
  tools: result.tools,
3776
+ allTools: allTools.tools,
3591
3777
  timestamp: Date.now()
3592
3778
  });
3593
3779
  return { session, result };
3594
3780
  }
3595
3781
  /**
3596
- * List tools from a session
3782
+ * Returns the policy-filtered tool list for a session (agent-facing).
3783
+ * Internally re-uses `listPolicyFilteredTools` which also emits a
3784
+ * `tools_discovered` SSE event to keep client state up to date.
3597
3785
  */
3598
3786
  async listTools(params) {
3599
3787
  const { sessionId } = params;
@@ -3630,7 +3818,15 @@ var SSEConnectionManager = class {
3630
3818
  };
3631
3819
  }
3632
3820
  /**
3633
- * Update per-session tool access policy.
3821
+ * Persists a new tool access policy for a session and broadcasts the updated
3822
+ * filtered tool list to all connected browser clients via a `tools_discovered` event.
3823
+ *
3824
+ * Both `tools` (policy-filtered) and `allTools` (complete list) are emitted
3825
+ * so the management UI can immediately reflect the new checkbox states without
3826
+ * an additional round-trip to the server.
3827
+ *
3828
+ * @param params - Session ID and the new `{ mode, toolIds }` policy to apply.
3829
+ * @throws {Error} When the session does not exist or the policy references unknown tool IDs.
3634
3830
  */
3635
3831
  async setToolPolicy(params) {
3636
3832
  const { sessionId } = params;
@@ -3650,6 +3846,7 @@ var SSEConnectionManager = class {
3650
3846
  serverId: session.serverId ?? "unknown",
3651
3847
  toolCount: filteredTools.length,
3652
3848
  tools: filteredTools,
3849
+ allTools: allTools.tools,
3653
3850
  timestamp: Date.now()
3654
3851
  });
3655
3852
  return {
@@ -3660,7 +3857,8 @@ var SSEConnectionManager = class {
3660
3857
  };
3661
3858
  }
3662
3859
  /**
3663
- * Call a tool on the MCP server
3860
+ * Proxies a tool invocation to the remote MCP server.
3861
+ * Resolves the client for the given session and delegates to the tool router.
3664
3862
  */
3665
3863
  async callTool(params) {
3666
3864
  const { sessionId, toolName, toolArgs } = params;