@mcp-ts/sdk 2.5.1 → 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 (56) 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/react.d.mts +4 -4
  14. package/dist/client/react.d.ts +4 -4
  15. package/dist/client/react.js +1 -1
  16. package/dist/client/react.js.map +1 -1
  17. package/dist/client/react.mjs +1 -1
  18. package/dist/client/react.mjs.map +1 -1
  19. package/dist/client/vue.d.mts +5 -4
  20. package/dist/client/vue.d.ts +5 -4
  21. package/dist/client/vue.js +1 -1
  22. package/dist/client/vue.js.map +1 -1
  23. package/dist/client/vue.mjs +1 -1
  24. package/dist/client/vue.mjs.map +1 -1
  25. package/dist/{index-C99wE2Zf.d.mts → index-B3GPsPsi.d.mts} +1 -1
  26. package/dist/{index-BEdyuz1M.d.ts → index-gNdSQTSz.d.ts} +1 -1
  27. package/dist/index.d.mts +4 -4
  28. package/dist/index.d.ts +4 -4
  29. package/dist/index.js +148 -13
  30. package/dist/index.js.map +1 -1
  31. package/dist/index.mjs +148 -13
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/{multi-session-client-CclWRxTD.d.mts → multi-session-client-CB4oDrQX.d.ts} +36 -10
  34. package/dist/{multi-session-client-DzvZD6Tt.d.ts → multi-session-client-xj3iQIv6.d.mts} +36 -10
  35. package/dist/server/index.d.mts +34 -7
  36. package/dist/server/index.d.ts +34 -7
  37. package/dist/server/index.js +148 -13
  38. package/dist/server/index.js.map +1 -1
  39. package/dist/server/index.mjs +148 -13
  40. package/dist/server/index.mjs.map +1 -1
  41. package/dist/shared/index.d.mts +4 -4
  42. package/dist/shared/index.d.ts +4 -4
  43. package/dist/shared/index.js.map +1 -1
  44. package/dist/shared/index.mjs.map +1 -1
  45. package/dist/{tool-router-CUSSk6lT.d.ts → tool-router-C-Mw1_BQ.d.ts} +1 -1
  46. package/dist/{tool-router-C9ECn3FX.d.mts → tool-router-CsKVXbQB.d.mts} +1 -1
  47. package/dist/{types-CbFbPkfQ.d.mts → types-6SmXege4.d.mts} +1 -0
  48. package/dist/{types-CbFbPkfQ.d.ts → types-6SmXege4.d.ts} +1 -0
  49. package/package.json +1 -1
  50. package/src/client/react/use-mcp.ts +1 -1
  51. package/src/client/vue/use-mcp.ts +2 -1
  52. package/src/server/handlers/sse-handler.ts +35 -3
  53. package/src/server/mcp/oauth-client.ts +44 -20
  54. package/src/server/mcp/tool-policy-gateway.ts +98 -2
  55. package/src/server/storage/tool-policy.ts +102 -0
  56. package/src/shared/events.ts +1 -0
package/dist/index.mjs CHANGED
@@ -2246,6 +2246,15 @@ var MCPClient = class {
2246
2246
  __publicField(this, "_onObservabilityEvent", new Emitter());
2247
2247
  __publicField(this, "onObservabilityEvent", this._onObservabilityEvent.event);
2248
2248
  __publicField(this, "currentState", "DISCONNECTED");
2249
+ /**
2250
+ * In-memory cache for the remote server's full tools list.
2251
+ *
2252
+ * Populated on the first `fetchTools()` call and reused for the lifetime of
2253
+ * the connection. Cleared to `null` at the start of `connect()` so that a
2254
+ * reconnect always retrieves a fresh list, and also in `dispose()` to release
2255
+ * the memory when the client is no longer needed.
2256
+ */
2257
+ __publicField(this, "cachedTools", null);
2249
2258
  this.serverUrl = options.serverUrl;
2250
2259
  this.serverName = options.serverName;
2251
2260
  this.callbackUrl = options.callbackUrl;
@@ -2566,13 +2575,20 @@ var MCPClient = class {
2566
2575
  throw lastError || new Error("No transports available");
2567
2576
  }
2568
2577
  /**
2569
- * Connects to the MCP server
2570
- * Automatically validates and refreshes OAuth tokens if needed
2571
- * Saves session to Redis on first successful connection
2572
- * @throws {UnauthorizedError} When OAuth authorization is required
2573
- * @throws {Error} When connection fails for other reasons
2578
+ * Connects to the MCP server.
2579
+ *
2580
+ * Automatically validates and refreshes OAuth tokens if needed.
2581
+ * Saves the session to Redis on first successful connection.
2582
+ *
2583
+ * The in-memory tools cache (`cachedTools`) is cleared at the start of every
2584
+ * call so that a reconnection always fetches a fresh tool list from the remote
2585
+ * server — even if the same `MCPClient` instance is reused.
2586
+ *
2587
+ * @throws {UnauthorizedError} When OAuth authorization is required.
2588
+ * @throws {Error} When connection fails for other reasons.
2574
2589
  */
2575
2590
  async connect() {
2591
+ this.cachedTools = null;
2576
2592
  if (this.client.transport) {
2577
2593
  this.transport = null;
2578
2594
  try {
@@ -2755,17 +2771,30 @@ var MCPClient = class {
2755
2771
  }
2756
2772
  }
2757
2773
  /**
2758
- * Lists all available tools from the connected MCP server without emitting discovery events.
2759
- * Gateways use this to apply policy before publishing tools to agents or UI state.
2774
+ * Lists all available tools from the connected MCP server without emitting
2775
+ * discovery events. The result is cached in memory for the lifetime of the
2776
+ * connection — subsequent callers (e.g. `gateway.listTools()` running right
2777
+ * after `fetchTools()`) pay zero extra network cost.
2778
+ *
2779
+ * Gateways use this to apply a tool-access policy before publishing the
2780
+ * filtered list to agents or UI state.
2781
+ *
2782
+ * @returns The full `ListToolsResult` from the remote server.
2783
+ * @throws {Error} When the client is not connected or the request times out.
2760
2784
  */
2761
2785
  async fetchTools() {
2786
+ if (this.cachedTools) {
2787
+ return this.cachedTools;
2788
+ }
2762
2789
  const request = {
2763
2790
  method: "tools/list",
2764
2791
  params: {}
2765
2792
  };
2766
- return await this.withRetry(
2793
+ const result = await this.withRetry(
2767
2794
  () => this.client.request(request, ListToolsResultSchema)
2768
2795
  );
2796
+ this.cachedTools = result;
2797
+ return result;
2769
2798
  }
2770
2799
  /**
2771
2800
  * Lists all available tools from the connected MCP server
@@ -3051,10 +3080,14 @@ var MCPClient = class {
3051
3080
  this.emitStateChange("DISCONNECTED");
3052
3081
  }
3053
3082
  /**
3054
- * Dispose of all event emitters
3055
- * Call this when the client is no longer needed
3083
+ * Disposes all event emitters and releases cached state.
3084
+ *
3085
+ * Clears `cachedTools` to free memory, and disposes the connection and
3086
+ * observability event emitters so downstream listeners are unsubscribed.
3087
+ * Call this when the client is permanently shut down (not just disconnected).
3056
3088
  */
3057
3089
  dispose() {
3090
+ this.cachedTools = null;
3058
3091
  this._onConnectionEvent.dispose();
3059
3092
  this._onObservabilityEvent.dispose();
3060
3093
  }
@@ -3109,21 +3142,47 @@ var ToolPolicyGateway = class {
3109
3142
  this.sessionId = sessionId;
3110
3143
  this.client = client;
3111
3144
  }
3145
+ /**
3146
+ * Returns whether the underlying MCP client transport is currently connected.
3147
+ */
3112
3148
  isConnected() {
3113
3149
  return this.client.isConnected();
3114
3150
  }
3151
+ /**
3152
+ * Returns the server ID from the underlying client, if available.
3153
+ */
3115
3154
  getServerId() {
3116
3155
  return this.client.getServerId?.();
3117
3156
  }
3157
+ /**
3158
+ * Returns the human-readable server name from the underlying client, if available.
3159
+ */
3118
3160
  getServerName() {
3119
3161
  return this.client.getServerName?.();
3120
3162
  }
3163
+ /**
3164
+ * Returns the server URL from the underlying client, if available.
3165
+ */
3121
3166
  getServerUrl() {
3122
3167
  return this.client.getServerUrl?.();
3123
3168
  }
3169
+ /**
3170
+ * Returns the session ID — prefers the value reported by the underlying
3171
+ * client, falling back to the one injected at construction time.
3172
+ */
3124
3173
  getSessionId() {
3125
3174
  return this.client.getSessionId?.() ?? this.sessionId;
3126
3175
  }
3176
+ /**
3177
+ * Returns the **policy-filtered** list of tools that the current session
3178
+ * is allowed to call.
3179
+ *
3180
+ * Internally calls `client.fetchTools()` (which is cache-backed) so no
3181
+ * extra network round-trip is incurred when called after `fetchTools()`.
3182
+ *
3183
+ * @returns A `ListToolsResult` containing only the permitted tools.
3184
+ * @throws {Error} When the session does not exist in the store.
3185
+ */
3127
3186
  async listTools() {
3128
3187
  const session = await this.getSession();
3129
3188
  const result = await this.client.fetchTools();
@@ -3133,20 +3192,61 @@ var ToolPolicyGateway = class {
3133
3192
  tools
3134
3193
  };
3135
3194
  }
3195
+ /**
3196
+ * Returns the **complete, unfiltered** list of tools from the remote server,
3197
+ * bypassing any tool-access policy.
3198
+ *
3199
+ * Used by the management UI to show all available tools (including blocked
3200
+ * ones) so users can toggle individual tool access in the dialog.
3201
+ *
3202
+ * @returns The raw `ListToolsResult` from the remote server.
3203
+ */
3136
3204
  async listAllTools() {
3137
3205
  return await this.client.fetchTools();
3138
3206
  }
3207
+ /**
3208
+ * Executes a tool call on the remote server after verifying that the tool
3209
+ * is permitted by the current session's policy.
3210
+ *
3211
+ * @param name - The exact tool name to invoke.
3212
+ * @param args - Key/value arguments to pass to the tool.
3213
+ * @returns The tool's `CallToolResult`.
3214
+ * @throws {Error} When the tool is blocked by the session's policy.
3215
+ * @throws {Error} When the session does not exist in the store.
3216
+ */
3139
3217
  async callTool(name, args) {
3140
3218
  const session = await this.getSession();
3141
3219
  this.assertAllowed(session, name);
3142
3220
  return await this.client.callTool(name, args);
3143
3221
  }
3222
+ /**
3223
+ * Filters a raw tools array down to only those permitted by the session's
3224
+ * `toolPolicy`.
3225
+ *
3226
+ * @param session - The session whose policy should be applied.
3227
+ * @param tools - The unfiltered list of tools from the remote server.
3228
+ * @returns A subset of `tools` that the policy allows.
3229
+ */
3144
3230
  filterTools(session, tools) {
3145
3231
  return filterToolsByPolicy(tools, session.toolPolicy, this.getPolicyServerId(session));
3146
3232
  }
3233
+ /**
3234
+ * Throws if `toolName` is blocked by the session's policy.
3235
+ * Call this before proxying a `callTool` request to the remote server.
3236
+ *
3237
+ * @param session - The session whose policy should be enforced.
3238
+ * @param toolName - The tool being invoked.
3239
+ * @throws {Error} When the tool is not permitted.
3240
+ */
3147
3241
  assertAllowed(session, toolName) {
3148
3242
  assertToolAllowed(session.toolPolicy, toolName, this.getPolicyServerId(session));
3149
3243
  }
3244
+ /**
3245
+ * Loads the session from the store and throws if it does not exist.
3246
+ *
3247
+ * @returns The fully-hydrated `Session` record.
3248
+ * @throws {Error} When the session cannot be found.
3249
+ */
3150
3250
  async getSession() {
3151
3251
  const session = await sessions.get(this.userId, this.sessionId);
3152
3252
  if (!session) {
@@ -3154,6 +3254,11 @@ var ToolPolicyGateway = class {
3154
3254
  }
3155
3255
  return session;
3156
3256
  }
3257
+ /**
3258
+ * Resolves the server ID to use when evaluating tool policy.
3259
+ * Prefers the value from the live client (most accurate) and falls back
3260
+ * to the server ID stored on the session record.
3261
+ */
3157
3262
  getPolicyServerId(session) {
3158
3263
  return this.client.getServerId?.() ?? session.serverId;
3159
3264
  }
@@ -3709,12 +3814,29 @@ var SSEConnectionManager = class {
3709
3814
  this.clients.set(sessionId, client);
3710
3815
  return client;
3711
3816
  }
3817
+ /**
3818
+ * Fetches all tools from the remote MCP server and emits a `tools_discovered` event.
3819
+ *
3820
+ * Two lists are always published together:
3821
+ * - `tools` — policy-filtered list that agents are allowed to call.
3822
+ * - `allTools` — the complete, unfiltered list used by the management UI so
3823
+ * that blocked tools still appear as checkboxes in the dialog.
3824
+ *
3825
+ * `fetchTools()` is called first (populates the in-memory cache), then
3826
+ * `gateway.listTools()` re-uses that cache internally — so only one remote
3827
+ * network round-trip is made regardless of how many callers follow.
3828
+ *
3829
+ * @param sessionId - The session whose tools should be discovered.
3830
+ * @returns The session record and the policy-filtered tool list.
3831
+ * @throws {Error} When the session does not exist in the store.
3832
+ */
3712
3833
  async listPolicyFilteredTools(sessionId) {
3713
3834
  const session = await sessions.get(this.userId, sessionId);
3714
3835
  if (!session) {
3715
3836
  throw new Error("Session not found");
3716
3837
  }
3717
3838
  const client = await this.getOrCreateClient(sessionId);
3839
+ const allTools = await client.fetchTools().catch(() => ({ tools: [] }));
3718
3840
  const gateway = createToolPolicyGateway(this.userId, sessionId, client);
3719
3841
  const result = await gateway.listTools();
3720
3842
  this.emitConnectionEvent({
@@ -3723,12 +3845,15 @@ var SSEConnectionManager = class {
3723
3845
  serverId: session.serverId ?? "unknown",
3724
3846
  toolCount: result.tools.length,
3725
3847
  tools: result.tools,
3848
+ allTools: allTools.tools,
3726
3849
  timestamp: Date.now()
3727
3850
  });
3728
3851
  return { session, result };
3729
3852
  }
3730
3853
  /**
3731
- * List tools from a session
3854
+ * Returns the policy-filtered tool list for a session (agent-facing).
3855
+ * Internally re-uses `listPolicyFilteredTools` which also emits a
3856
+ * `tools_discovered` SSE event to keep client state up to date.
3732
3857
  */
3733
3858
  async listTools(params) {
3734
3859
  const { sessionId } = params;
@@ -3765,7 +3890,15 @@ var SSEConnectionManager = class {
3765
3890
  };
3766
3891
  }
3767
3892
  /**
3768
- * Update per-session tool access policy.
3893
+ * Persists a new tool access policy for a session and broadcasts the updated
3894
+ * filtered tool list to all connected browser clients via a `tools_discovered` event.
3895
+ *
3896
+ * Both `tools` (policy-filtered) and `allTools` (complete list) are emitted
3897
+ * so the management UI can immediately reflect the new checkbox states without
3898
+ * an additional round-trip to the server.
3899
+ *
3900
+ * @param params - Session ID and the new `{ mode, toolIds }` policy to apply.
3901
+ * @throws {Error} When the session does not exist or the policy references unknown tool IDs.
3769
3902
  */
3770
3903
  async setToolPolicy(params) {
3771
3904
  const { sessionId } = params;
@@ -3785,6 +3918,7 @@ var SSEConnectionManager = class {
3785
3918
  serverId: session.serverId ?? "unknown",
3786
3919
  toolCount: filteredTools.length,
3787
3920
  tools: filteredTools,
3921
+ allTools: allTools.tools,
3788
3922
  timestamp: Date.now()
3789
3923
  });
3790
3924
  return {
@@ -3795,7 +3929,8 @@ var SSEConnectionManager = class {
3795
3929
  };
3796
3930
  }
3797
3931
  /**
3798
- * Call a tool on the MCP server
3932
+ * Proxies a tool invocation to the remote MCP server.
3933
+ * Resolves the client for the given session and delegates to the tool router.
3799
3934
  */
3800
3935
  async callTool(params) {
3801
3936
  const { sessionId, toolName, toolArgs } = params;