@mcp-ts/sdk 2.5.1 → 2.5.3

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 (58) 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-BRZO_JyI.d.mts} +1 -1
  26. package/dist/{index-BEdyuz1M.d.ts → index-C3YGagi2.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 +163 -15
  30. package/dist/index.js.map +1 -1
  31. package/dist/index.mjs +163 -15
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/{multi-session-client-CclWRxTD.d.mts → multi-session-client-CuacvZaQ.d.ts} +36 -10
  34. package/dist/{multi-session-client-DzvZD6Tt.d.ts → multi-session-client-DqzT8Oo7.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 +163 -15
  38. package/dist/server/index.js.map +1 -1
  39. package/dist/server/index.mjs +163 -15
  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-2qUyZ-cP.d.ts} +1 -1
  46. package/dist/{tool-router-C9ECn3FX.d.mts → tool-router-B0qpui-V.d.mts} +1 -1
  47. package/dist/{types-CbFbPkfQ.d.mts → types-BkJ_rgzo.d.mts} +3 -0
  48. package/dist/{types-CbFbPkfQ.d.ts → types-BkJ_rgzo.d.ts} +3 -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 +41 -5
  53. package/src/server/mcp/oauth-client.ts +44 -20
  54. package/src/server/mcp/storage-oauth-provider.ts +13 -0
  55. package/src/server/mcp/tool-policy-gateway.ts +98 -2
  56. package/src/server/storage/tool-policy.ts +102 -0
  57. package/src/shared/events.ts +1 -0
  58. package/src/shared/types.ts +2 -0
@@ -1940,6 +1940,15 @@ var StorageOAuthClientProvider = class {
1940
1940
  if (data.clientId && !this._clientId) {
1941
1941
  this._clientId = data.clientId;
1942
1942
  }
1943
+ if (this._clientId && !data.clientId) {
1944
+ await this.patchCredentials({
1945
+ clientId: this._clientId,
1946
+ clientInformation: {
1947
+ client_id: this._clientId,
1948
+ ...this.clientSecret ? { client_secret: this.clientSecret } : {}
1949
+ }
1950
+ });
1951
+ }
1943
1952
  if (data.clientInformation) {
1944
1953
  return data.clientInformation;
1945
1954
  }
@@ -2174,6 +2183,15 @@ var MCPClient = class {
2174
2183
  __publicField(this, "_onObservabilityEvent", new Emitter());
2175
2184
  __publicField(this, "onObservabilityEvent", this._onObservabilityEvent.event);
2176
2185
  __publicField(this, "currentState", "DISCONNECTED");
2186
+ /**
2187
+ * In-memory cache for the remote server's full tools list.
2188
+ *
2189
+ * Populated on the first `fetchTools()` call and reused for the lifetime of
2190
+ * the connection. Cleared to `null` at the start of `connect()` so that a
2191
+ * reconnect always retrieves a fresh list, and also in `dispose()` to release
2192
+ * the memory when the client is no longer needed.
2193
+ */
2194
+ __publicField(this, "cachedTools", null);
2177
2195
  this.serverUrl = options.serverUrl;
2178
2196
  this.serverName = options.serverName;
2179
2197
  this.callbackUrl = options.callbackUrl;
@@ -2494,13 +2512,20 @@ var MCPClient = class {
2494
2512
  throw lastError || new Error("No transports available");
2495
2513
  }
2496
2514
  /**
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
2515
+ * Connects to the MCP server.
2516
+ *
2517
+ * Automatically validates and refreshes OAuth tokens if needed.
2518
+ * Saves the session to Redis on first successful connection.
2519
+ *
2520
+ * The in-memory tools cache (`cachedTools`) is cleared at the start of every
2521
+ * call so that a reconnection always fetches a fresh tool list from the remote
2522
+ * server — even if the same `MCPClient` instance is reused.
2523
+ *
2524
+ * @throws {UnauthorizedError} When OAuth authorization is required.
2525
+ * @throws {Error} When connection fails for other reasons.
2502
2526
  */
2503
2527
  async connect() {
2528
+ this.cachedTools = null;
2504
2529
  if (this.client.transport) {
2505
2530
  this.transport = null;
2506
2531
  try {
@@ -2683,17 +2708,30 @@ var MCPClient = class {
2683
2708
  }
2684
2709
  }
2685
2710
  /**
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.
2711
+ * Lists all available tools from the connected MCP server without emitting
2712
+ * discovery events. The result is cached in memory for the lifetime of the
2713
+ * connection — subsequent callers (e.g. `gateway.listTools()` running right
2714
+ * after `fetchTools()`) pay zero extra network cost.
2715
+ *
2716
+ * Gateways use this to apply a tool-access policy before publishing the
2717
+ * filtered list to agents or UI state.
2718
+ *
2719
+ * @returns The full `ListToolsResult` from the remote server.
2720
+ * @throws {Error} When the client is not connected or the request times out.
2688
2721
  */
2689
2722
  async fetchTools() {
2723
+ if (this.cachedTools) {
2724
+ return this.cachedTools;
2725
+ }
2690
2726
  const request = {
2691
2727
  method: "tools/list",
2692
2728
  params: {}
2693
2729
  };
2694
- return await this.withRetry(
2730
+ const result = await this.withRetry(
2695
2731
  () => this.client.request(request, ListToolsResultSchema)
2696
2732
  );
2733
+ this.cachedTools = result;
2734
+ return result;
2697
2735
  }
2698
2736
  /**
2699
2737
  * Lists all available tools from the connected MCP server
@@ -2979,10 +3017,14 @@ var MCPClient = class {
2979
3017
  this.emitStateChange("DISCONNECTED");
2980
3018
  }
2981
3019
  /**
2982
- * Dispose of all event emitters
2983
- * Call this when the client is no longer needed
3020
+ * Disposes all event emitters and releases cached state.
3021
+ *
3022
+ * Clears `cachedTools` to free memory, and disposes the connection and
3023
+ * observability event emitters so downstream listeners are unsubscribed.
3024
+ * Call this when the client is permanently shut down (not just disconnected).
2984
3025
  */
2985
3026
  dispose() {
3027
+ this.cachedTools = null;
2986
3028
  this._onConnectionEvent.dispose();
2987
3029
  this._onObservabilityEvent.dispose();
2988
3030
  }
@@ -3037,21 +3079,47 @@ var ToolPolicyGateway = class {
3037
3079
  this.sessionId = sessionId;
3038
3080
  this.client = client;
3039
3081
  }
3082
+ /**
3083
+ * Returns whether the underlying MCP client transport is currently connected.
3084
+ */
3040
3085
  isConnected() {
3041
3086
  return this.client.isConnected();
3042
3087
  }
3088
+ /**
3089
+ * Returns the server ID from the underlying client, if available.
3090
+ */
3043
3091
  getServerId() {
3044
3092
  return this.client.getServerId?.();
3045
3093
  }
3094
+ /**
3095
+ * Returns the human-readable server name from the underlying client, if available.
3096
+ */
3046
3097
  getServerName() {
3047
3098
  return this.client.getServerName?.();
3048
3099
  }
3100
+ /**
3101
+ * Returns the server URL from the underlying client, if available.
3102
+ */
3049
3103
  getServerUrl() {
3050
3104
  return this.client.getServerUrl?.();
3051
3105
  }
3106
+ /**
3107
+ * Returns the session ID — prefers the value reported by the underlying
3108
+ * client, falling back to the one injected at construction time.
3109
+ */
3052
3110
  getSessionId() {
3053
3111
  return this.client.getSessionId?.() ?? this.sessionId;
3054
3112
  }
3113
+ /**
3114
+ * Returns the **policy-filtered** list of tools that the current session
3115
+ * is allowed to call.
3116
+ *
3117
+ * Internally calls `client.fetchTools()` (which is cache-backed) so no
3118
+ * extra network round-trip is incurred when called after `fetchTools()`.
3119
+ *
3120
+ * @returns A `ListToolsResult` containing only the permitted tools.
3121
+ * @throws {Error} When the session does not exist in the store.
3122
+ */
3055
3123
  async listTools() {
3056
3124
  const session = await this.getSession();
3057
3125
  const result = await this.client.fetchTools();
@@ -3061,20 +3129,61 @@ var ToolPolicyGateway = class {
3061
3129
  tools
3062
3130
  };
3063
3131
  }
3132
+ /**
3133
+ * Returns the **complete, unfiltered** list of tools from the remote server,
3134
+ * bypassing any tool-access policy.
3135
+ *
3136
+ * Used by the management UI to show all available tools (including blocked
3137
+ * ones) so users can toggle individual tool access in the dialog.
3138
+ *
3139
+ * @returns The raw `ListToolsResult` from the remote server.
3140
+ */
3064
3141
  async listAllTools() {
3065
3142
  return await this.client.fetchTools();
3066
3143
  }
3144
+ /**
3145
+ * Executes a tool call on the remote server after verifying that the tool
3146
+ * is permitted by the current session's policy.
3147
+ *
3148
+ * @param name - The exact tool name to invoke.
3149
+ * @param args - Key/value arguments to pass to the tool.
3150
+ * @returns The tool's `CallToolResult`.
3151
+ * @throws {Error} When the tool is blocked by the session's policy.
3152
+ * @throws {Error} When the session does not exist in the store.
3153
+ */
3067
3154
  async callTool(name, args) {
3068
3155
  const session = await this.getSession();
3069
3156
  this.assertAllowed(session, name);
3070
3157
  return await this.client.callTool(name, args);
3071
3158
  }
3159
+ /**
3160
+ * Filters a raw tools array down to only those permitted by the session's
3161
+ * `toolPolicy`.
3162
+ *
3163
+ * @param session - The session whose policy should be applied.
3164
+ * @param tools - The unfiltered list of tools from the remote server.
3165
+ * @returns A subset of `tools` that the policy allows.
3166
+ */
3072
3167
  filterTools(session, tools) {
3073
3168
  return filterToolsByPolicy(tools, session.toolPolicy, this.getPolicyServerId(session));
3074
3169
  }
3170
+ /**
3171
+ * Throws if `toolName` is blocked by the session's policy.
3172
+ * Call this before proxying a `callTool` request to the remote server.
3173
+ *
3174
+ * @param session - The session whose policy should be enforced.
3175
+ * @param toolName - The tool being invoked.
3176
+ * @throws {Error} When the tool is not permitted.
3177
+ */
3075
3178
  assertAllowed(session, toolName) {
3076
3179
  assertToolAllowed(session.toolPolicy, toolName, this.getPolicyServerId(session));
3077
3180
  }
3181
+ /**
3182
+ * Loads the session from the store and throws if it does not exist.
3183
+ *
3184
+ * @returns The fully-hydrated `Session` record.
3185
+ * @throws {Error} When the session cannot be found.
3186
+ */
3078
3187
  async getSession() {
3079
3188
  const session = await sessions.get(this.userId, this.sessionId);
3080
3189
  if (!session) {
@@ -3082,6 +3191,11 @@ var ToolPolicyGateway = class {
3082
3191
  }
3083
3192
  return session;
3084
3193
  }
3194
+ /**
3195
+ * Resolves the server ID to use when evaluating tool policy.
3196
+ * Prefers the value from the live client (most accurate) and falls back
3197
+ * to the server ID stored on the session record.
3198
+ */
3085
3199
  getPolicyServerId(session) {
3086
3200
  return this.client.getServerId?.() ?? session.serverId;
3087
3201
  }
@@ -3466,7 +3580,7 @@ var SSEConnectionManager = class {
3466
3580
  * Connect to an MCP server
3467
3581
  */
3468
3582
  async connect(params) {
3469
- const { serverName, serverUrl, callbackUrl, transportType } = params;
3583
+ const { serverName, serverUrl, callbackUrl, transportType, clientId, clientSecret } = params;
3470
3584
  const headers = normalizeHeaders(params.headers);
3471
3585
  const serverId = params.serverId && params.serverId.length <= 12 ? params.serverId : generateServerId();
3472
3586
  const existingSessions = await sessions.list(this.userId);
@@ -3495,6 +3609,8 @@ var SSEConnectionManager = class {
3495
3609
  callbackUrl,
3496
3610
  transportType,
3497
3611
  headers,
3612
+ clientId,
3613
+ clientSecret,
3498
3614
  ...clientMetadata
3499
3615
  // Spread client metadata (clientName, clientUri, logoUri, policyUri)
3500
3616
  });
@@ -3536,7 +3652,7 @@ var SSEConnectionManager = class {
3536
3652
  * and creates a fresh connection while reusing the existing session credentials in a single RPC call.
3537
3653
  */
3538
3654
  async reconnect(params) {
3539
- const { serverId: rawServerId, serverName, serverUrl, callbackUrl, transportType } = params;
3655
+ const { serverId: rawServerId, serverName, serverUrl, callbackUrl, transportType, clientId, clientSecret } = params;
3540
3656
  const headers = normalizeHeaders(params.headers);
3541
3657
  const serverId = rawServerId && rawServerId.length <= 12 ? rawServerId : generateServerId();
3542
3658
  const existingSessions = await sessions.list(this.userId);
@@ -3562,6 +3678,8 @@ var SSEConnectionManager = class {
3562
3678
  callbackUrl,
3563
3679
  transportType,
3564
3680
  headers,
3681
+ clientId,
3682
+ clientSecret,
3565
3683
  ...clientMetadata
3566
3684
  });
3567
3685
  this.clients.set(sessionId, client);
@@ -3637,12 +3755,29 @@ var SSEConnectionManager = class {
3637
3755
  this.clients.set(sessionId, client);
3638
3756
  return client;
3639
3757
  }
3758
+ /**
3759
+ * Fetches all tools from the remote MCP server and emits a `tools_discovered` event.
3760
+ *
3761
+ * Two lists are always published together:
3762
+ * - `tools` — policy-filtered list that agents are allowed to call.
3763
+ * - `allTools` — the complete, unfiltered list used by the management UI so
3764
+ * that blocked tools still appear as checkboxes in the dialog.
3765
+ *
3766
+ * `fetchTools()` is called first (populates the in-memory cache), then
3767
+ * `gateway.listTools()` re-uses that cache internally — so only one remote
3768
+ * network round-trip is made regardless of how many callers follow.
3769
+ *
3770
+ * @param sessionId - The session whose tools should be discovered.
3771
+ * @returns The session record and the policy-filtered tool list.
3772
+ * @throws {Error} When the session does not exist in the store.
3773
+ */
3640
3774
  async listPolicyFilteredTools(sessionId) {
3641
3775
  const session = await sessions.get(this.userId, sessionId);
3642
3776
  if (!session) {
3643
3777
  throw new Error("Session not found");
3644
3778
  }
3645
3779
  const client = await this.getOrCreateClient(sessionId);
3780
+ const allTools = await client.fetchTools().catch(() => ({ tools: [] }));
3646
3781
  const gateway = createToolPolicyGateway(this.userId, sessionId, client);
3647
3782
  const result = await gateway.listTools();
3648
3783
  this.emitConnectionEvent({
@@ -3651,12 +3786,15 @@ var SSEConnectionManager = class {
3651
3786
  serverId: session.serverId ?? "unknown",
3652
3787
  toolCount: result.tools.length,
3653
3788
  tools: result.tools,
3789
+ allTools: allTools.tools,
3654
3790
  timestamp: Date.now()
3655
3791
  });
3656
3792
  return { session, result };
3657
3793
  }
3658
3794
  /**
3659
- * List tools from a session
3795
+ * Returns the policy-filtered tool list for a session (agent-facing).
3796
+ * Internally re-uses `listPolicyFilteredTools` which also emits a
3797
+ * `tools_discovered` SSE event to keep client state up to date.
3660
3798
  */
3661
3799
  async listTools(params) {
3662
3800
  const { sessionId } = params;
@@ -3693,7 +3831,15 @@ var SSEConnectionManager = class {
3693
3831
  };
3694
3832
  }
3695
3833
  /**
3696
- * Update per-session tool access policy.
3834
+ * Persists a new tool access policy for a session and broadcasts the updated
3835
+ * filtered tool list to all connected browser clients via a `tools_discovered` event.
3836
+ *
3837
+ * Both `tools` (policy-filtered) and `allTools` (complete list) are emitted
3838
+ * so the management UI can immediately reflect the new checkbox states without
3839
+ * an additional round-trip to the server.
3840
+ *
3841
+ * @param params - Session ID and the new `{ mode, toolIds }` policy to apply.
3842
+ * @throws {Error} When the session does not exist or the policy references unknown tool IDs.
3697
3843
  */
3698
3844
  async setToolPolicy(params) {
3699
3845
  const { sessionId } = params;
@@ -3713,6 +3859,7 @@ var SSEConnectionManager = class {
3713
3859
  serverId: session.serverId ?? "unknown",
3714
3860
  toolCount: filteredTools.length,
3715
3861
  tools: filteredTools,
3862
+ allTools: allTools.tools,
3716
3863
  timestamp: Date.now()
3717
3864
  });
3718
3865
  return {
@@ -3723,7 +3870,8 @@ var SSEConnectionManager = class {
3723
3870
  };
3724
3871
  }
3725
3872
  /**
3726
- * Call a tool on the MCP server
3873
+ * Proxies a tool invocation to the remote MCP server.
3874
+ * Resolves the client for the given session and delegates to the tool router.
3727
3875
  */
3728
3876
  async callTool(params) {
3729
3877
  const { sessionId, toolName, toolArgs } = params;