@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.js CHANGED
@@ -2320,6 +2320,15 @@ var MCPClient = class {
2320
2320
  __publicField(this, "_onObservabilityEvent", new Emitter());
2321
2321
  __publicField(this, "onObservabilityEvent", this._onObservabilityEvent.event);
2322
2322
  __publicField(this, "currentState", "DISCONNECTED");
2323
+ /**
2324
+ * In-memory cache for the remote server's full tools list.
2325
+ *
2326
+ * Populated on the first `fetchTools()` call and reused for the lifetime of
2327
+ * the connection. Cleared to `null` at the start of `connect()` so that a
2328
+ * reconnect always retrieves a fresh list, and also in `dispose()` to release
2329
+ * the memory when the client is no longer needed.
2330
+ */
2331
+ __publicField(this, "cachedTools", null);
2323
2332
  this.serverUrl = options.serverUrl;
2324
2333
  this.serverName = options.serverName;
2325
2334
  this.callbackUrl = options.callbackUrl;
@@ -2640,13 +2649,20 @@ var MCPClient = class {
2640
2649
  throw lastError || new Error("No transports available");
2641
2650
  }
2642
2651
  /**
2643
- * Connects to the MCP server
2644
- * Automatically validates and refreshes OAuth tokens if needed
2645
- * Saves session to Redis on first successful connection
2646
- * @throws {UnauthorizedError} When OAuth authorization is required
2647
- * @throws {Error} When connection fails for other reasons
2652
+ * Connects to the MCP server.
2653
+ *
2654
+ * Automatically validates and refreshes OAuth tokens if needed.
2655
+ * Saves the session to Redis on first successful connection.
2656
+ *
2657
+ * The in-memory tools cache (`cachedTools`) is cleared at the start of every
2658
+ * call so that a reconnection always fetches a fresh tool list from the remote
2659
+ * server — even if the same `MCPClient` instance is reused.
2660
+ *
2661
+ * @throws {UnauthorizedError} When OAuth authorization is required.
2662
+ * @throws {Error} When connection fails for other reasons.
2648
2663
  */
2649
2664
  async connect() {
2665
+ this.cachedTools = null;
2650
2666
  if (this.client.transport) {
2651
2667
  this.transport = null;
2652
2668
  try {
@@ -2829,17 +2845,30 @@ var MCPClient = class {
2829
2845
  }
2830
2846
  }
2831
2847
  /**
2832
- * Lists all available tools from the connected MCP server without emitting discovery events.
2833
- * Gateways use this to apply policy before publishing tools to agents or UI state.
2848
+ * Lists all available tools from the connected MCP server without emitting
2849
+ * discovery events. The result is cached in memory for the lifetime of the
2850
+ * connection — subsequent callers (e.g. `gateway.listTools()` running right
2851
+ * after `fetchTools()`) pay zero extra network cost.
2852
+ *
2853
+ * Gateways use this to apply a tool-access policy before publishing the
2854
+ * filtered list to agents or UI state.
2855
+ *
2856
+ * @returns The full `ListToolsResult` from the remote server.
2857
+ * @throws {Error} When the client is not connected or the request times out.
2834
2858
  */
2835
2859
  async fetchTools() {
2860
+ if (this.cachedTools) {
2861
+ return this.cachedTools;
2862
+ }
2836
2863
  const request = {
2837
2864
  method: "tools/list",
2838
2865
  params: {}
2839
2866
  };
2840
- return await this.withRetry(
2867
+ const result = await this.withRetry(
2841
2868
  () => this.client.request(request, types_js.ListToolsResultSchema)
2842
2869
  );
2870
+ this.cachedTools = result;
2871
+ return result;
2843
2872
  }
2844
2873
  /**
2845
2874
  * Lists all available tools from the connected MCP server
@@ -3125,10 +3154,14 @@ var MCPClient = class {
3125
3154
  this.emitStateChange("DISCONNECTED");
3126
3155
  }
3127
3156
  /**
3128
- * Dispose of all event emitters
3129
- * Call this when the client is no longer needed
3157
+ * Disposes all event emitters and releases cached state.
3158
+ *
3159
+ * Clears `cachedTools` to free memory, and disposes the connection and
3160
+ * observability event emitters so downstream listeners are unsubscribed.
3161
+ * Call this when the client is permanently shut down (not just disconnected).
3130
3162
  */
3131
3163
  dispose() {
3164
+ this.cachedTools = null;
3132
3165
  this._onConnectionEvent.dispose();
3133
3166
  this._onObservabilityEvent.dispose();
3134
3167
  }
@@ -3187,21 +3220,47 @@ var ToolPolicyGateway = class {
3187
3220
  this.sessionId = sessionId;
3188
3221
  this.client = client;
3189
3222
  }
3223
+ /**
3224
+ * Returns whether the underlying MCP client transport is currently connected.
3225
+ */
3190
3226
  isConnected() {
3191
3227
  return this.client.isConnected();
3192
3228
  }
3229
+ /**
3230
+ * Returns the server ID from the underlying client, if available.
3231
+ */
3193
3232
  getServerId() {
3194
3233
  return this.client.getServerId?.();
3195
3234
  }
3235
+ /**
3236
+ * Returns the human-readable server name from the underlying client, if available.
3237
+ */
3196
3238
  getServerName() {
3197
3239
  return this.client.getServerName?.();
3198
3240
  }
3241
+ /**
3242
+ * Returns the server URL from the underlying client, if available.
3243
+ */
3199
3244
  getServerUrl() {
3200
3245
  return this.client.getServerUrl?.();
3201
3246
  }
3247
+ /**
3248
+ * Returns the session ID — prefers the value reported by the underlying
3249
+ * client, falling back to the one injected at construction time.
3250
+ */
3202
3251
  getSessionId() {
3203
3252
  return this.client.getSessionId?.() ?? this.sessionId;
3204
3253
  }
3254
+ /**
3255
+ * Returns the **policy-filtered** list of tools that the current session
3256
+ * is allowed to call.
3257
+ *
3258
+ * Internally calls `client.fetchTools()` (which is cache-backed) so no
3259
+ * extra network round-trip is incurred when called after `fetchTools()`.
3260
+ *
3261
+ * @returns A `ListToolsResult` containing only the permitted tools.
3262
+ * @throws {Error} When the session does not exist in the store.
3263
+ */
3205
3264
  async listTools() {
3206
3265
  const session = await this.getSession();
3207
3266
  const result = await this.client.fetchTools();
@@ -3211,20 +3270,61 @@ var ToolPolicyGateway = class {
3211
3270
  tools
3212
3271
  };
3213
3272
  }
3273
+ /**
3274
+ * Returns the **complete, unfiltered** list of tools from the remote server,
3275
+ * bypassing any tool-access policy.
3276
+ *
3277
+ * Used by the management UI to show all available tools (including blocked
3278
+ * ones) so users can toggle individual tool access in the dialog.
3279
+ *
3280
+ * @returns The raw `ListToolsResult` from the remote server.
3281
+ */
3214
3282
  async listAllTools() {
3215
3283
  return await this.client.fetchTools();
3216
3284
  }
3285
+ /**
3286
+ * Executes a tool call on the remote server after verifying that the tool
3287
+ * is permitted by the current session's policy.
3288
+ *
3289
+ * @param name - The exact tool name to invoke.
3290
+ * @param args - Key/value arguments to pass to the tool.
3291
+ * @returns The tool's `CallToolResult`.
3292
+ * @throws {Error} When the tool is blocked by the session's policy.
3293
+ * @throws {Error} When the session does not exist in the store.
3294
+ */
3217
3295
  async callTool(name, args) {
3218
3296
  const session = await this.getSession();
3219
3297
  this.assertAllowed(session, name);
3220
3298
  return await this.client.callTool(name, args);
3221
3299
  }
3300
+ /**
3301
+ * Filters a raw tools array down to only those permitted by the session's
3302
+ * `toolPolicy`.
3303
+ *
3304
+ * @param session - The session whose policy should be applied.
3305
+ * @param tools - The unfiltered list of tools from the remote server.
3306
+ * @returns A subset of `tools` that the policy allows.
3307
+ */
3222
3308
  filterTools(session, tools) {
3223
3309
  return filterToolsByPolicy(tools, session.toolPolicy, this.getPolicyServerId(session));
3224
3310
  }
3311
+ /**
3312
+ * Throws if `toolName` is blocked by the session's policy.
3313
+ * Call this before proxying a `callTool` request to the remote server.
3314
+ *
3315
+ * @param session - The session whose policy should be enforced.
3316
+ * @param toolName - The tool being invoked.
3317
+ * @throws {Error} When the tool is not permitted.
3318
+ */
3225
3319
  assertAllowed(session, toolName) {
3226
3320
  assertToolAllowed(session.toolPolicy, toolName, this.getPolicyServerId(session));
3227
3321
  }
3322
+ /**
3323
+ * Loads the session from the store and throws if it does not exist.
3324
+ *
3325
+ * @returns The fully-hydrated `Session` record.
3326
+ * @throws {Error} When the session cannot be found.
3327
+ */
3228
3328
  async getSession() {
3229
3329
  const session = await sessions.get(this.userId, this.sessionId);
3230
3330
  if (!session) {
@@ -3232,6 +3332,11 @@ var ToolPolicyGateway = class {
3232
3332
  }
3233
3333
  return session;
3234
3334
  }
3335
+ /**
3336
+ * Resolves the server ID to use when evaluating tool policy.
3337
+ * Prefers the value from the live client (most accurate) and falls back
3338
+ * to the server ID stored on the session record.
3339
+ */
3235
3340
  getPolicyServerId(session) {
3236
3341
  return this.client.getServerId?.() ?? session.serverId;
3237
3342
  }
@@ -3791,12 +3896,29 @@ var SSEConnectionManager = class {
3791
3896
  this.clients.set(sessionId, client);
3792
3897
  return client;
3793
3898
  }
3899
+ /**
3900
+ * Fetches all tools from the remote MCP server and emits a `tools_discovered` event.
3901
+ *
3902
+ * Two lists are always published together:
3903
+ * - `tools` — policy-filtered list that agents are allowed to call.
3904
+ * - `allTools` — the complete, unfiltered list used by the management UI so
3905
+ * that blocked tools still appear as checkboxes in the dialog.
3906
+ *
3907
+ * `fetchTools()` is called first (populates the in-memory cache), then
3908
+ * `gateway.listTools()` re-uses that cache internally — so only one remote
3909
+ * network round-trip is made regardless of how many callers follow.
3910
+ *
3911
+ * @param sessionId - The session whose tools should be discovered.
3912
+ * @returns The session record and the policy-filtered tool list.
3913
+ * @throws {Error} When the session does not exist in the store.
3914
+ */
3794
3915
  async listPolicyFilteredTools(sessionId) {
3795
3916
  const session = await sessions.get(this.userId, sessionId);
3796
3917
  if (!session) {
3797
3918
  throw new Error("Session not found");
3798
3919
  }
3799
3920
  const client = await this.getOrCreateClient(sessionId);
3921
+ const allTools = await client.fetchTools().catch(() => ({ tools: [] }));
3800
3922
  const gateway = createToolPolicyGateway(this.userId, sessionId, client);
3801
3923
  const result = await gateway.listTools();
3802
3924
  this.emitConnectionEvent({
@@ -3805,12 +3927,15 @@ var SSEConnectionManager = class {
3805
3927
  serverId: session.serverId ?? "unknown",
3806
3928
  toolCount: result.tools.length,
3807
3929
  tools: result.tools,
3930
+ allTools: allTools.tools,
3808
3931
  timestamp: Date.now()
3809
3932
  });
3810
3933
  return { session, result };
3811
3934
  }
3812
3935
  /**
3813
- * List tools from a session
3936
+ * Returns the policy-filtered tool list for a session (agent-facing).
3937
+ * Internally re-uses `listPolicyFilteredTools` which also emits a
3938
+ * `tools_discovered` SSE event to keep client state up to date.
3814
3939
  */
3815
3940
  async listTools(params) {
3816
3941
  const { sessionId } = params;
@@ -3847,7 +3972,15 @@ var SSEConnectionManager = class {
3847
3972
  };
3848
3973
  }
3849
3974
  /**
3850
- * Update per-session tool access policy.
3975
+ * Persists a new tool access policy for a session and broadcasts the updated
3976
+ * filtered tool list to all connected browser clients via a `tools_discovered` event.
3977
+ *
3978
+ * Both `tools` (policy-filtered) and `allTools` (complete list) are emitted
3979
+ * so the management UI can immediately reflect the new checkbox states without
3980
+ * an additional round-trip to the server.
3981
+ *
3982
+ * @param params - Session ID and the new `{ mode, toolIds }` policy to apply.
3983
+ * @throws {Error} When the session does not exist or the policy references unknown tool IDs.
3851
3984
  */
3852
3985
  async setToolPolicy(params) {
3853
3986
  const { sessionId } = params;
@@ -3867,6 +4000,7 @@ var SSEConnectionManager = class {
3867
4000
  serverId: session.serverId ?? "unknown",
3868
4001
  toolCount: filteredTools.length,
3869
4002
  tools: filteredTools,
4003
+ allTools: allTools.tools,
3870
4004
  timestamp: Date.now()
3871
4005
  });
3872
4006
  return {
@@ -3877,7 +4011,8 @@ var SSEConnectionManager = class {
3877
4011
  };
3878
4012
  }
3879
4013
  /**
3880
- * Call a tool on the MCP server
4014
+ * Proxies a tool invocation to the remote MCP server.
4015
+ * Resolves the client for the given session and delegates to the tool router.
3881
4016
  */
3882
4017
  async callTool(params) {
3883
4018
  const { sessionId, toolName, toolArgs } = params;