@tambo-ai/react 0.59.0 → 0.60.0

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 (83) hide show
  1. package/dist/hooks/react-query-hooks.d.ts +14 -1
  2. package/dist/hooks/react-query-hooks.d.ts.map +1 -1
  3. package/dist/hooks/react-query-hooks.js +13 -0
  4. package/dist/hooks/react-query-hooks.js.map +1 -1
  5. package/dist/index.js +2 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/mcp/__tests__/elicitation.test.d.ts +2 -0
  8. package/dist/mcp/__tests__/elicitation.test.d.ts.map +1 -0
  9. package/dist/mcp/__tests__/elicitation.test.js +261 -0
  10. package/dist/mcp/__tests__/elicitation.test.js.map +1 -0
  11. package/dist/mcp/__tests__/mcp-hooks.test.d.ts +2 -0
  12. package/dist/mcp/__tests__/mcp-hooks.test.d.ts.map +1 -0
  13. package/dist/mcp/__tests__/mcp-hooks.test.js +504 -0
  14. package/dist/mcp/__tests__/mcp-hooks.test.js.map +1 -0
  15. package/dist/mcp/__tests__/tambo-mcp-provider.test.js +151 -12
  16. package/dist/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -1
  17. package/dist/mcp/elicitation.d.ts +80 -0
  18. package/dist/mcp/elicitation.d.ts.map +1 -0
  19. package/dist/mcp/elicitation.js +55 -0
  20. package/dist/mcp/elicitation.js.map +1 -0
  21. package/dist/mcp/index.d.ts +3 -2
  22. package/dist/mcp/index.d.ts.map +1 -1
  23. package/dist/mcp/index.js +2 -1
  24. package/dist/mcp/index.js.map +1 -1
  25. package/dist/mcp/mcp-client.d.ts +41 -9
  26. package/dist/mcp/mcp-client.d.ts.map +1 -1
  27. package/dist/mcp/mcp-client.js.map +1 -1
  28. package/dist/mcp/mcp-hooks.d.ts +20 -6
  29. package/dist/mcp/mcp-hooks.d.ts.map +1 -1
  30. package/dist/mcp/mcp-hooks.js +71 -24
  31. package/dist/mcp/mcp-hooks.js.map +1 -1
  32. package/dist/mcp/tambo-mcp-provider.d.ts +60 -6
  33. package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
  34. package/dist/mcp/tambo-mcp-provider.js +231 -172
  35. package/dist/mcp/tambo-mcp-provider.js.map +1 -1
  36. package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
  37. package/dist/providers/tambo-interactable-provider.js +11 -4
  38. package/dist/providers/tambo-interactable-provider.js.map +1 -1
  39. package/dist/providers/tambo-provider.d.ts.map +1 -1
  40. package/dist/providers/tambo-provider.js +0 -4
  41. package/dist/providers/tambo-provider.js.map +1 -1
  42. package/esm/hooks/react-query-hooks.d.ts +14 -1
  43. package/esm/hooks/react-query-hooks.d.ts.map +1 -1
  44. package/esm/hooks/react-query-hooks.js +13 -1
  45. package/esm/hooks/react-query-hooks.js.map +1 -1
  46. package/esm/index.js +2 -0
  47. package/esm/index.js.map +1 -1
  48. package/esm/mcp/__tests__/elicitation.test.d.ts +2 -0
  49. package/esm/mcp/__tests__/elicitation.test.d.ts.map +1 -0
  50. package/esm/mcp/__tests__/elicitation.test.js +259 -0
  51. package/esm/mcp/__tests__/elicitation.test.js.map +1 -0
  52. package/esm/mcp/__tests__/mcp-hooks.test.d.ts +2 -0
  53. package/esm/mcp/__tests__/mcp-hooks.test.d.ts.map +1 -0
  54. package/esm/mcp/__tests__/mcp-hooks.test.js +469 -0
  55. package/esm/mcp/__tests__/mcp-hooks.test.js.map +1 -0
  56. package/esm/mcp/__tests__/tambo-mcp-provider.test.js +151 -12
  57. package/esm/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -1
  58. package/esm/mcp/elicitation.d.ts +80 -0
  59. package/esm/mcp/elicitation.d.ts.map +1 -0
  60. package/esm/mcp/elicitation.js +52 -0
  61. package/esm/mcp/elicitation.js.map +1 -0
  62. package/esm/mcp/index.d.ts +3 -2
  63. package/esm/mcp/index.d.ts.map +1 -1
  64. package/esm/mcp/index.js +1 -1
  65. package/esm/mcp/index.js.map +1 -1
  66. package/esm/mcp/mcp-client.d.ts +41 -9
  67. package/esm/mcp/mcp-client.d.ts.map +1 -1
  68. package/esm/mcp/mcp-client.js.map +1 -1
  69. package/esm/mcp/mcp-hooks.d.ts +20 -6
  70. package/esm/mcp/mcp-hooks.d.ts.map +1 -1
  71. package/esm/mcp/mcp-hooks.js +73 -26
  72. package/esm/mcp/mcp-hooks.js.map +1 -1
  73. package/esm/mcp/tambo-mcp-provider.d.ts +60 -6
  74. package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
  75. package/esm/mcp/tambo-mcp-provider.js +231 -173
  76. package/esm/mcp/tambo-mcp-provider.js.map +1 -1
  77. package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
  78. package/esm/providers/tambo-interactable-provider.js +11 -4
  79. package/esm/providers/tambo-interactable-provider.js.map +1 -1
  80. package/esm/providers/tambo-provider.d.ts.map +1 -1
  81. package/esm/providers/tambo-provider.js +0 -4
  82. package/esm/providers/tambo-provider.js.map +1 -1
  83. package/package.json +8 -8
@@ -0,0 +1,504 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const react_1 = require("@testing-library/react");
37
+ const react_query_1 = require("@tanstack/react-query");
38
+ const react_2 = __importStar(require("react"));
39
+ const tambo_client_provider_1 = require("../../providers/tambo-client-provider");
40
+ const tambo_mcp_token_provider_1 = require("../../providers/tambo-mcp-token-provider");
41
+ const tambo_registry_provider_1 = require("../../providers/tambo-registry-provider");
42
+ const mcp_client_1 = require("../mcp-client");
43
+ const tambo_mcp_provider_1 = require("../tambo-mcp-provider");
44
+ const mcp_hooks_1 = require("../mcp-hooks");
45
+ // Mock the MCP client to avoid ES module issues
46
+ let createImpl = jest.fn();
47
+ jest.mock("../mcp-client", () => ({
48
+ MCPClient: { create: (...args) => createImpl(...args) },
49
+ MCPTransport: { SSE: "sse", HTTP: "http" },
50
+ }));
51
+ describe("useTamboMcpPromptList - individual server caching", () => {
52
+ let queryClient;
53
+ beforeEach(() => {
54
+ createImpl = jest.fn();
55
+ queryClient = new react_query_1.QueryClient({
56
+ defaultOptions: {
57
+ queries: {
58
+ retry: false, // Disable retries for tests
59
+ },
60
+ },
61
+ });
62
+ });
63
+ afterEach(() => {
64
+ queryClient.clear();
65
+ });
66
+ it("should fetch and combine prompts from multiple servers", async () => {
67
+ // Mock two servers with different prompts
68
+ const serverAPrompts = {
69
+ prompts: [
70
+ { name: "prompt-a1", description: "Prompt A1" },
71
+ { name: "prompt-a2", description: "Prompt A2" },
72
+ ],
73
+ };
74
+ const serverBPrompts = {
75
+ prompts: [
76
+ { name: "prompt-b1", description: "Prompt B1" },
77
+ { name: "prompt-b2", description: "Prompt B2" },
78
+ ],
79
+ };
80
+ const mockClientA = {
81
+ listTools: jest.fn().mockResolvedValue([]),
82
+ listPrompts: jest.fn().mockResolvedValue(serverAPrompts),
83
+ close: jest.fn(),
84
+ };
85
+ const mockClientB = {
86
+ listTools: jest.fn().mockResolvedValue([]),
87
+ listPrompts: jest.fn().mockResolvedValue(serverBPrompts),
88
+ close: jest.fn(),
89
+ };
90
+ const clientA = {
91
+ client: mockClientA,
92
+ listTools: jest.fn().mockResolvedValue([]),
93
+ close: jest.fn(),
94
+ };
95
+ const clientB = {
96
+ client: mockClientB,
97
+ listTools: jest.fn().mockResolvedValue([]),
98
+ close: jest.fn(),
99
+ };
100
+ // Mock MCPClient.create to return different clients for different URLs
101
+ createImpl.mockImplementation(async (url) => {
102
+ if (url === "https://server-a.example")
103
+ return clientA;
104
+ if (url === "https://server-b.example")
105
+ return clientB;
106
+ throw new Error(`Unexpected URL: ${url}`);
107
+ });
108
+ let capturedPrompts = [];
109
+ const Capture = () => {
110
+ const { data: prompts } = (0, mcp_hooks_1.useTamboMcpPromptList)();
111
+ (0, react_2.useEffect)(() => {
112
+ if (prompts) {
113
+ capturedPrompts = prompts;
114
+ }
115
+ }, [prompts]);
116
+ return null;
117
+ };
118
+ (0, react_1.render)(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
119
+ client: { baseURL: "https://api.tambo.co" },
120
+ queryClient,
121
+ isUpdatingToken: false,
122
+ } },
123
+ react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null,
124
+ react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
125
+ react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, { mcpServers: [
126
+ {
127
+ url: "https://server-a.example",
128
+ transport: mcp_client_1.MCPTransport.SSE,
129
+ },
130
+ {
131
+ url: "https://server-b.example",
132
+ transport: mcp_client_1.MCPTransport.SSE,
133
+ },
134
+ ] },
135
+ react_2.default.createElement(Capture, null))))));
136
+ // Wait for all prompts to be loaded
137
+ await (0, react_1.waitFor)(() => {
138
+ expect(capturedPrompts.length).toBe(4);
139
+ });
140
+ // Verify all prompts are present
141
+ const promptNames = capturedPrompts.map((p) => p.prompt.name);
142
+ expect(promptNames).toContain("prompt-a1");
143
+ expect(promptNames).toContain("prompt-a2");
144
+ expect(promptNames).toContain("prompt-b1");
145
+ expect(promptNames).toContain("prompt-b2");
146
+ // Verify each prompt has the correct server info
147
+ const promptA1 = capturedPrompts.find((p) => p.prompt.name === "prompt-a1");
148
+ expect(promptA1?.server.url).toBe("https://server-a.example");
149
+ const promptB1 = capturedPrompts.find((p) => p.prompt.name === "prompt-b1");
150
+ expect(promptB1?.server.url).toBe("https://server-b.example");
151
+ });
152
+ it("should remove prompts when a server is removed", async () => {
153
+ // Mock two servers with different prompts
154
+ const serverAPrompts = {
155
+ prompts: [
156
+ { name: "prompt-a1", description: "Prompt A1" },
157
+ { name: "prompt-a2", description: "Prompt A2" },
158
+ ],
159
+ };
160
+ const serverBPrompts = {
161
+ prompts: [
162
+ { name: "prompt-b1", description: "Prompt B1" },
163
+ { name: "prompt-b2", description: "Prompt B2" },
164
+ ],
165
+ };
166
+ const mockClientA = {
167
+ listTools: jest.fn().mockResolvedValue([]),
168
+ listPrompts: jest.fn().mockResolvedValue(serverAPrompts),
169
+ close: jest.fn(),
170
+ };
171
+ const mockClientB = {
172
+ listTools: jest.fn().mockResolvedValue([]),
173
+ listPrompts: jest.fn().mockResolvedValue(serverBPrompts),
174
+ close: jest.fn(),
175
+ };
176
+ const clientA = {
177
+ client: mockClientA,
178
+ listTools: jest.fn().mockResolvedValue([]),
179
+ close: jest.fn(),
180
+ };
181
+ const clientB = {
182
+ client: mockClientB,
183
+ listTools: jest.fn().mockResolvedValue([]),
184
+ close: jest.fn(),
185
+ };
186
+ createImpl.mockImplementation(async (url) => {
187
+ if (url === "https://server-a.example")
188
+ return clientA;
189
+ if (url === "https://server-b.example")
190
+ return clientB;
191
+ throw new Error(`Unexpected URL: ${url}`);
192
+ });
193
+ let capturedPrompts = [];
194
+ const Capture = () => {
195
+ const { data: prompts } = (0, mcp_hooks_1.useTamboMcpPromptList)();
196
+ (0, react_2.useEffect)(() => {
197
+ if (prompts) {
198
+ capturedPrompts = prompts;
199
+ }
200
+ }, [prompts]);
201
+ return null;
202
+ };
203
+ const { rerender } = (0, react_1.render)(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
204
+ client: { baseURL: "https://api.tambo.co" },
205
+ queryClient,
206
+ isUpdatingToken: false,
207
+ } },
208
+ react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null,
209
+ react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
210
+ react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, { mcpServers: [
211
+ {
212
+ url: "https://server-a.example",
213
+ transport: mcp_client_1.MCPTransport.SSE,
214
+ },
215
+ {
216
+ url: "https://server-b.example",
217
+ transport: mcp_client_1.MCPTransport.SSE,
218
+ },
219
+ ] },
220
+ react_2.default.createElement(Capture, null))))));
221
+ // Wait for initial prompts to be loaded
222
+ await (0, react_1.waitFor)(() => {
223
+ expect(capturedPrompts.length).toBe(4);
224
+ });
225
+ const initialPromptNames = capturedPrompts.map((p) => p.prompt.name);
226
+ expect(initialPromptNames).toContain("prompt-a1");
227
+ expect(initialPromptNames).toContain("prompt-a2");
228
+ expect(initialPromptNames).toContain("prompt-b1");
229
+ expect(initialPromptNames).toContain("prompt-b2");
230
+ // Remove server B
231
+ rerender(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
232
+ client: { baseURL: "https://api.tambo.co" },
233
+ queryClient,
234
+ isUpdatingToken: false,
235
+ } },
236
+ react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null,
237
+ react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
238
+ react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, { mcpServers: [
239
+ {
240
+ url: "https://server-a.example",
241
+ transport: mcp_client_1.MCPTransport.SSE,
242
+ },
243
+ ] },
244
+ react_2.default.createElement(Capture, null))))));
245
+ // Wait for prompts to be updated (server B prompts should disappear)
246
+ await (0, react_1.waitFor)(() => {
247
+ expect(capturedPrompts.length).toBe(2);
248
+ });
249
+ const updatedPromptNames = capturedPrompts.map((p) => p.prompt.name);
250
+ expect(updatedPromptNames).toContain("prompt-a1");
251
+ expect(updatedPromptNames).toContain("prompt-a2");
252
+ expect(updatedPromptNames).not.toContain("prompt-b1");
253
+ expect(updatedPromptNames).not.toContain("prompt-b2");
254
+ // Verify server B's client was closed
255
+ expect(clientB.close).toHaveBeenCalled();
256
+ });
257
+ it("should cache prompts individually per server using server key", async () => {
258
+ const serverAPrompts = {
259
+ prompts: [{ name: "prompt-a", description: "Prompt A" }],
260
+ };
261
+ const serverBPrompts = {
262
+ prompts: [{ name: "prompt-b", description: "Prompt B" }],
263
+ };
264
+ const mockClientA = {
265
+ listTools: jest.fn().mockResolvedValue([]),
266
+ listPrompts: jest.fn().mockResolvedValue(serverAPrompts),
267
+ close: jest.fn(),
268
+ };
269
+ const mockClientB = {
270
+ listTools: jest.fn().mockResolvedValue([]),
271
+ listPrompts: jest.fn().mockResolvedValue(serverBPrompts),
272
+ close: jest.fn(),
273
+ };
274
+ const clientA = {
275
+ client: mockClientA,
276
+ listTools: jest.fn().mockResolvedValue([]),
277
+ close: jest.fn(),
278
+ };
279
+ const clientB = {
280
+ client: mockClientB,
281
+ listTools: jest.fn().mockResolvedValue([]),
282
+ close: jest.fn(),
283
+ };
284
+ createImpl.mockImplementation(async (url) => {
285
+ if (url === "https://server-a.example")
286
+ return clientA;
287
+ if (url === "https://server-b.example")
288
+ return clientB;
289
+ throw new Error(`Unexpected URL: ${url}`);
290
+ });
291
+ let capturedPrompts = [];
292
+ let capturedServerKeys = [];
293
+ const Capture = () => {
294
+ const { data: prompts } = (0, mcp_hooks_1.useTamboMcpPromptList)();
295
+ const servers = (0, tambo_mcp_provider_1.useTamboMcpServers)();
296
+ (0, react_2.useEffect)(() => {
297
+ if (prompts) {
298
+ capturedPrompts = prompts;
299
+ }
300
+ capturedServerKeys = servers.map((s) => s.key);
301
+ }, [prompts, servers]);
302
+ return null;
303
+ };
304
+ (0, react_1.render)(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
305
+ client: { baseURL: "https://api.tambo.co" },
306
+ queryClient,
307
+ isUpdatingToken: false,
308
+ } },
309
+ react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null,
310
+ react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
311
+ react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, { mcpServers: [
312
+ {
313
+ url: "https://server-a.example",
314
+ transport: mcp_client_1.MCPTransport.SSE,
315
+ },
316
+ {
317
+ url: "https://server-b.example",
318
+ transport: mcp_client_1.MCPTransport.SSE,
319
+ },
320
+ ] },
321
+ react_2.default.createElement(Capture, null))))));
322
+ // Wait for prompts to be loaded
323
+ await (0, react_1.waitFor)(() => {
324
+ expect(capturedPrompts.length).toBe(2);
325
+ });
326
+ // Verify that listPrompts was called once per server
327
+ expect(mockClientA.listPrompts).toHaveBeenCalledTimes(1);
328
+ expect(mockClientB.listPrompts).toHaveBeenCalledTimes(1);
329
+ // Verify the query cache has separate entries for each server
330
+ const cacheKeys = queryClient
331
+ .getQueryCache()
332
+ .getAll()
333
+ .map((query) => query.queryKey);
334
+ // Should have separate cache entries for each server's prompts
335
+ const promptCacheKeys = cacheKeys.filter((key) => Array.isArray(key) && key[0] === "mcp-prompts");
336
+ expect(promptCacheKeys.length).toBe(2);
337
+ // Ensure provider keys are populated before asserting
338
+ await (0, react_1.waitFor)(() => expect(capturedServerKeys.length).toBe(2));
339
+ // Verify each connected server key is present exactly once in the cache keys
340
+ const promptKeySet = new Set(promptCacheKeys.map((k) => String(k[1])));
341
+ // The server keys are captured from the provider to avoid relying on string substrings
342
+ capturedServerKeys.forEach((key) => {
343
+ expect(promptKeySet.has(key)).toBe(true);
344
+ });
345
+ // And no duplicates
346
+ expect(promptKeySet.size).toBe(capturedServerKeys.length);
347
+ });
348
+ it("should handle server errors gracefully without affecting other servers", async () => {
349
+ const serverAPrompts = {
350
+ prompts: [{ name: "prompt-a", description: "Prompt A" }],
351
+ };
352
+ const mockClientA = {
353
+ listTools: jest.fn().mockResolvedValue([]),
354
+ listPrompts: jest.fn().mockResolvedValue(serverAPrompts),
355
+ close: jest.fn(),
356
+ };
357
+ const clientA = {
358
+ client: mockClientA,
359
+ listTools: jest.fn().mockResolvedValue([]),
360
+ close: jest.fn(),
361
+ };
362
+ // Server B will fail to connect
363
+ createImpl.mockImplementation(async (url) => {
364
+ if (url === "https://server-a.example")
365
+ return clientA;
366
+ if (url === "https://server-b.example") {
367
+ throw new Error("Connection failed");
368
+ }
369
+ throw new Error(`Unexpected URL: ${url}`);
370
+ });
371
+ let capturedPrompts = [];
372
+ let mcpServersCount = 0;
373
+ const Capture = () => {
374
+ const { data: prompts } = (0, mcp_hooks_1.useTamboMcpPromptList)();
375
+ const servers = (0, tambo_mcp_provider_1.useTamboMcpServers)();
376
+ (0, react_2.useEffect)(() => {
377
+ if (prompts) {
378
+ capturedPrompts = prompts;
379
+ }
380
+ mcpServersCount = servers.length;
381
+ }, [prompts, servers]);
382
+ return null;
383
+ };
384
+ (0, react_1.render)(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
385
+ client: { baseURL: "https://api.tambo.co" },
386
+ queryClient,
387
+ isUpdatingToken: false,
388
+ } },
389
+ react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null,
390
+ react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
391
+ react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, { mcpServers: [
392
+ {
393
+ url: "https://server-a.example",
394
+ transport: mcp_client_1.MCPTransport.SSE,
395
+ },
396
+ {
397
+ url: "https://server-b.example",
398
+ transport: mcp_client_1.MCPTransport.SSE,
399
+ },
400
+ ] },
401
+ react_2.default.createElement(Capture, null))))));
402
+ // Wait for server A prompts to be loaded
403
+ // Server B should fail but not block server A
404
+ await (0, react_1.waitFor)(() => {
405
+ expect(capturedPrompts.length).toBe(1);
406
+ expect(mcpServersCount).toBe(2); // Both servers should be in the list
407
+ });
408
+ // Verify only server A's prompts are present
409
+ const promptNames = capturedPrompts.map((p) => p.prompt.name);
410
+ expect(promptNames).toContain("prompt-a");
411
+ expect(promptNames).not.toContain("prompt-b");
412
+ });
413
+ it("should add prompts when a new server is added", async () => {
414
+ const serverAPrompts = {
415
+ prompts: [{ name: "prompt-a", description: "Prompt A" }],
416
+ };
417
+ const serverBPrompts = {
418
+ prompts: [{ name: "prompt-b", description: "Prompt B" }],
419
+ };
420
+ const mockClientA = {
421
+ listTools: jest.fn().mockResolvedValue([]),
422
+ listPrompts: jest.fn().mockResolvedValue(serverAPrompts),
423
+ close: jest.fn(),
424
+ };
425
+ const mockClientB = {
426
+ listTools: jest.fn().mockResolvedValue([]),
427
+ listPrompts: jest.fn().mockResolvedValue(serverBPrompts),
428
+ close: jest.fn(),
429
+ };
430
+ const clientA = {
431
+ client: mockClientA,
432
+ listTools: jest.fn().mockResolvedValue([]),
433
+ close: jest.fn(),
434
+ };
435
+ const clientB = {
436
+ client: mockClientB,
437
+ listTools: jest.fn().mockResolvedValue([]),
438
+ close: jest.fn(),
439
+ };
440
+ createImpl.mockImplementation(async (url) => {
441
+ if (url === "https://server-a.example")
442
+ return clientA;
443
+ if (url === "https://server-b.example")
444
+ return clientB;
445
+ throw new Error(`Unexpected URL: ${url}`);
446
+ });
447
+ let capturedPrompts = [];
448
+ const Capture = () => {
449
+ const { data: prompts } = (0, mcp_hooks_1.useTamboMcpPromptList)();
450
+ (0, react_2.useEffect)(() => {
451
+ if (prompts) {
452
+ capturedPrompts = prompts;
453
+ }
454
+ }, [prompts]);
455
+ return null;
456
+ };
457
+ const { rerender } = (0, react_1.render)(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
458
+ client: { baseURL: "https://api.tambo.co" },
459
+ queryClient,
460
+ isUpdatingToken: false,
461
+ } },
462
+ react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null,
463
+ react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
464
+ react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, { mcpServers: [
465
+ {
466
+ url: "https://server-a.example",
467
+ transport: mcp_client_1.MCPTransport.SSE,
468
+ },
469
+ ] },
470
+ react_2.default.createElement(Capture, null))))));
471
+ // Wait for initial prompts from server A
472
+ await (0, react_1.waitFor)(() => {
473
+ expect(capturedPrompts.length).toBe(1);
474
+ });
475
+ expect(capturedPrompts.map((p) => p.prompt.name)).toContain("prompt-a");
476
+ // Add server B
477
+ rerender(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
478
+ client: { baseURL: "https://api.tambo.co" },
479
+ queryClient,
480
+ isUpdatingToken: false,
481
+ } },
482
+ react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, null,
483
+ react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
484
+ react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, { mcpServers: [
485
+ {
486
+ url: "https://server-a.example",
487
+ transport: mcp_client_1.MCPTransport.SSE,
488
+ },
489
+ {
490
+ url: "https://server-b.example",
491
+ transport: mcp_client_1.MCPTransport.SSE,
492
+ },
493
+ ] },
494
+ react_2.default.createElement(Capture, null))))));
495
+ // Wait for server B prompts to be added
496
+ await (0, react_1.waitFor)(() => {
497
+ expect(capturedPrompts.length).toBe(2);
498
+ });
499
+ const promptNames = capturedPrompts.map((p) => p.prompt.name);
500
+ expect(promptNames).toContain("prompt-a");
501
+ expect(promptNames).toContain("prompt-b");
502
+ });
503
+ });
504
+ //# sourceMappingURL=mcp-hooks.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-hooks.test.js","sourceRoot":"","sources":["../../../src/mcp/__tests__/mcp-hooks.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import { render, waitFor } from \"@testing-library/react\";\nimport { QueryClient } from \"@tanstack/react-query\";\nimport React, { useEffect } from \"react\";\nimport { TamboClientContext } from \"../../providers/tambo-client-provider\";\nimport { TamboMcpTokenProvider } from \"../../providers/tambo-mcp-token-provider\";\nimport { TamboRegistryProvider } from \"../../providers/tambo-registry-provider\";\nimport { MCPTransport } from \"../mcp-client\";\nimport { TamboMcpProvider, useTamboMcpServers } from \"../tambo-mcp-provider\";\nimport { useTamboMcpPromptList, type ListPromptEntry } from \"../mcp-hooks\";\n\n// Mock the MCP client to avoid ES module issues\nlet createImpl: jest.Mock<any, any> = jest.fn();\njest.mock(\"../mcp-client\", () => ({\n MCPClient: { create: (...args: any[]) => createImpl(...args) },\n MCPTransport: { SSE: \"sse\", HTTP: \"http\" },\n}));\n\ndescribe(\"useTamboMcpPromptList - individual server caching\", () => {\n let queryClient: QueryClient;\n\n beforeEach(() => {\n createImpl = jest.fn();\n queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n retry: false, // Disable retries for tests\n },\n },\n });\n });\n\n afterEach(() => {\n queryClient.clear();\n });\n\n it(\"should fetch and combine prompts from multiple servers\", async () => {\n // Mock two servers with different prompts\n const serverAPrompts = {\n prompts: [\n { name: \"prompt-a1\", description: \"Prompt A1\" },\n { name: \"prompt-a2\", description: \"Prompt A2\" },\n ],\n };\n const serverBPrompts = {\n prompts: [\n { name: \"prompt-b1\", description: \"Prompt B1\" },\n { name: \"prompt-b2\", description: \"Prompt B2\" },\n ],\n };\n\n const mockClientA = {\n listTools: jest.fn().mockResolvedValue([]),\n listPrompts: jest.fn().mockResolvedValue(serverAPrompts),\n close: jest.fn(),\n };\n const mockClientB = {\n listTools: jest.fn().mockResolvedValue([]),\n listPrompts: jest.fn().mockResolvedValue(serverBPrompts),\n close: jest.fn(),\n };\n\n const clientA = {\n client: mockClientA,\n listTools: jest.fn().mockResolvedValue([]),\n close: jest.fn(),\n };\n const clientB = {\n client: mockClientB,\n listTools: jest.fn().mockResolvedValue([]),\n close: jest.fn(),\n };\n\n // Mock MCPClient.create to return different clients for different URLs\n createImpl.mockImplementation(async (url: string) => {\n if (url === \"https://server-a.example\") return clientA;\n if (url === \"https://server-b.example\") return clientB;\n throw new Error(`Unexpected URL: ${url}`);\n });\n\n let capturedPrompts: ListPromptEntry[] = [];\n const Capture: React.FC = () => {\n const { data: prompts } = useTamboMcpPromptList();\n useEffect(() => {\n if (prompts) {\n capturedPrompts = prompts;\n }\n }, [prompts]);\n return null;\n };\n\n render(\n <TamboClientContext.Provider\n value={{\n client: { baseURL: \"https://api.tambo.co\" } as any,\n queryClient,\n isUpdatingToken: false,\n }}\n >\n <TamboRegistryProvider>\n <TamboMcpTokenProvider>\n <TamboMcpProvider\n mcpServers={[\n {\n url: \"https://server-a.example\",\n transport: MCPTransport.SSE,\n },\n {\n url: \"https://server-b.example\",\n transport: MCPTransport.SSE,\n },\n ]}\n >\n <Capture />\n </TamboMcpProvider>\n </TamboMcpTokenProvider>\n </TamboRegistryProvider>\n </TamboClientContext.Provider>,\n );\n\n // Wait for all prompts to be loaded\n await waitFor(() => {\n expect(capturedPrompts.length).toBe(4);\n });\n\n // Verify all prompts are present\n const promptNames = capturedPrompts.map((p) => p.prompt.name);\n expect(promptNames).toContain(\"prompt-a1\");\n expect(promptNames).toContain(\"prompt-a2\");\n expect(promptNames).toContain(\"prompt-b1\");\n expect(promptNames).toContain(\"prompt-b2\");\n\n // Verify each prompt has the correct server info\n const promptA1 = capturedPrompts.find((p) => p.prompt.name === \"prompt-a1\");\n expect(promptA1?.server.url).toBe(\"https://server-a.example\");\n\n const promptB1 = capturedPrompts.find((p) => p.prompt.name === \"prompt-b1\");\n expect(promptB1?.server.url).toBe(\"https://server-b.example\");\n });\n\n it(\"should remove prompts when a server is removed\", async () => {\n // Mock two servers with different prompts\n const serverAPrompts = {\n prompts: [\n { name: \"prompt-a1\", description: \"Prompt A1\" },\n { name: \"prompt-a2\", description: \"Prompt A2\" },\n ],\n };\n const serverBPrompts = {\n prompts: [\n { name: \"prompt-b1\", description: \"Prompt B1\" },\n { name: \"prompt-b2\", description: \"Prompt B2\" },\n ],\n };\n\n const mockClientA = {\n listTools: jest.fn().mockResolvedValue([]),\n listPrompts: jest.fn().mockResolvedValue(serverAPrompts),\n close: jest.fn(),\n };\n const mockClientB = {\n listTools: jest.fn().mockResolvedValue([]),\n listPrompts: jest.fn().mockResolvedValue(serverBPrompts),\n close: jest.fn(),\n };\n\n const clientA = {\n client: mockClientA,\n listTools: jest.fn().mockResolvedValue([]),\n close: jest.fn(),\n };\n const clientB = {\n client: mockClientB,\n listTools: jest.fn().mockResolvedValue([]),\n close: jest.fn(),\n };\n\n createImpl.mockImplementation(async (url: string) => {\n if (url === \"https://server-a.example\") return clientA;\n if (url === \"https://server-b.example\") return clientB;\n throw new Error(`Unexpected URL: ${url}`);\n });\n\n let capturedPrompts: ListPromptEntry[] = [];\n const Capture: React.FC = () => {\n const { data: prompts } = useTamboMcpPromptList();\n useEffect(() => {\n if (prompts) {\n capturedPrompts = prompts;\n }\n }, [prompts]);\n return null;\n };\n\n const { rerender } = render(\n <TamboClientContext.Provider\n value={{\n client: { baseURL: \"https://api.tambo.co\" } as any,\n queryClient,\n isUpdatingToken: false,\n }}\n >\n <TamboRegistryProvider>\n <TamboMcpTokenProvider>\n <TamboMcpProvider\n mcpServers={[\n {\n url: \"https://server-a.example\",\n transport: MCPTransport.SSE,\n },\n {\n url: \"https://server-b.example\",\n transport: MCPTransport.SSE,\n },\n ]}\n >\n <Capture />\n </TamboMcpProvider>\n </TamboMcpTokenProvider>\n </TamboRegistryProvider>\n </TamboClientContext.Provider>,\n );\n\n // Wait for initial prompts to be loaded\n await waitFor(() => {\n expect(capturedPrompts.length).toBe(4);\n });\n\n const initialPromptNames = capturedPrompts.map((p) => p.prompt.name);\n expect(initialPromptNames).toContain(\"prompt-a1\");\n expect(initialPromptNames).toContain(\"prompt-a2\");\n expect(initialPromptNames).toContain(\"prompt-b1\");\n expect(initialPromptNames).toContain(\"prompt-b2\");\n\n // Remove server B\n rerender(\n <TamboClientContext.Provider\n value={{\n client: { baseURL: \"https://api.tambo.co\" } as any,\n queryClient,\n isUpdatingToken: false,\n }}\n >\n <TamboRegistryProvider>\n <TamboMcpTokenProvider>\n <TamboMcpProvider\n mcpServers={[\n {\n url: \"https://server-a.example\",\n transport: MCPTransport.SSE,\n },\n ]}\n >\n <Capture />\n </TamboMcpProvider>\n </TamboMcpTokenProvider>\n </TamboRegistryProvider>\n </TamboClientContext.Provider>,\n );\n\n // Wait for prompts to be updated (server B prompts should disappear)\n await waitFor(() => {\n expect(capturedPrompts.length).toBe(2);\n });\n\n const updatedPromptNames = capturedPrompts.map((p) => p.prompt.name);\n expect(updatedPromptNames).toContain(\"prompt-a1\");\n expect(updatedPromptNames).toContain(\"prompt-a2\");\n expect(updatedPromptNames).not.toContain(\"prompt-b1\");\n expect(updatedPromptNames).not.toContain(\"prompt-b2\");\n\n // Verify server B's client was closed\n expect(clientB.close).toHaveBeenCalled();\n });\n\n it(\"should cache prompts individually per server using server key\", async () => {\n const serverAPrompts = {\n prompts: [{ name: \"prompt-a\", description: \"Prompt A\" }],\n };\n const serverBPrompts = {\n prompts: [{ name: \"prompt-b\", description: \"Prompt B\" }],\n };\n\n const mockClientA = {\n listTools: jest.fn().mockResolvedValue([]),\n listPrompts: jest.fn().mockResolvedValue(serverAPrompts),\n close: jest.fn(),\n };\n const mockClientB = {\n listTools: jest.fn().mockResolvedValue([]),\n listPrompts: jest.fn().mockResolvedValue(serverBPrompts),\n close: jest.fn(),\n };\n\n const clientA = {\n client: mockClientA,\n listTools: jest.fn().mockResolvedValue([]),\n close: jest.fn(),\n };\n const clientB = {\n client: mockClientB,\n listTools: jest.fn().mockResolvedValue([]),\n close: jest.fn(),\n };\n\n createImpl.mockImplementation(async (url: string) => {\n if (url === \"https://server-a.example\") return clientA;\n if (url === \"https://server-b.example\") return clientB;\n throw new Error(`Unexpected URL: ${url}`);\n });\n\n let capturedPrompts: ListPromptEntry[] = [];\n let capturedServerKeys: string[] = [];\n const Capture: React.FC = () => {\n const { data: prompts } = useTamboMcpPromptList();\n const servers = useTamboMcpServers();\n useEffect(() => {\n if (prompts) {\n capturedPrompts = prompts;\n }\n capturedServerKeys = servers.map((s) => s.key);\n }, [prompts, servers]);\n return null;\n };\n\n render(\n <TamboClientContext.Provider\n value={{\n client: { baseURL: \"https://api.tambo.co\" } as any,\n queryClient,\n isUpdatingToken: false,\n }}\n >\n <TamboRegistryProvider>\n <TamboMcpTokenProvider>\n <TamboMcpProvider\n mcpServers={[\n {\n url: \"https://server-a.example\",\n transport: MCPTransport.SSE,\n },\n {\n url: \"https://server-b.example\",\n transport: MCPTransport.SSE,\n },\n ]}\n >\n <Capture />\n </TamboMcpProvider>\n </TamboMcpTokenProvider>\n </TamboRegistryProvider>\n </TamboClientContext.Provider>,\n );\n\n // Wait for prompts to be loaded\n await waitFor(() => {\n expect(capturedPrompts.length).toBe(2);\n });\n\n // Verify that listPrompts was called once per server\n expect(mockClientA.listPrompts).toHaveBeenCalledTimes(1);\n expect(mockClientB.listPrompts).toHaveBeenCalledTimes(1);\n\n // Verify the query cache has separate entries for each server\n const cacheKeys = queryClient\n .getQueryCache()\n .getAll()\n .map((query) => query.queryKey);\n\n // Should have separate cache entries for each server's prompts\n const promptCacheKeys = cacheKeys.filter(\n (key) => Array.isArray(key) && key[0] === \"mcp-prompts\",\n );\n expect(promptCacheKeys.length).toBe(2);\n // Ensure provider keys are populated before asserting\n await waitFor(() => expect(capturedServerKeys.length).toBe(2));\n\n // Verify each connected server key is present exactly once in the cache keys\n const promptKeySet = new Set(promptCacheKeys.map((k) => String(k[1])));\n // The server keys are captured from the provider to avoid relying on string substrings\n capturedServerKeys.forEach((key) => {\n expect(promptKeySet.has(key)).toBe(true);\n });\n // And no duplicates\n expect(promptKeySet.size).toBe(capturedServerKeys.length);\n });\n\n it(\"should handle server errors gracefully without affecting other servers\", async () => {\n const serverAPrompts = {\n prompts: [{ name: \"prompt-a\", description: \"Prompt A\" }],\n };\n\n const mockClientA = {\n listTools: jest.fn().mockResolvedValue([]),\n listPrompts: jest.fn().mockResolvedValue(serverAPrompts),\n close: jest.fn(),\n };\n\n const clientA = {\n client: mockClientA,\n listTools: jest.fn().mockResolvedValue([]),\n close: jest.fn(),\n };\n\n // Server B will fail to connect\n createImpl.mockImplementation(async (url: string) => {\n if (url === \"https://server-a.example\") return clientA;\n if (url === \"https://server-b.example\") {\n throw new Error(\"Connection failed\");\n }\n throw new Error(`Unexpected URL: ${url}`);\n });\n\n let capturedPrompts: ListPromptEntry[] = [];\n let mcpServersCount = 0;\n const Capture: React.FC = () => {\n const { data: prompts } = useTamboMcpPromptList();\n const servers = useTamboMcpServers();\n useEffect(() => {\n if (prompts) {\n capturedPrompts = prompts;\n }\n mcpServersCount = servers.length;\n }, [prompts, servers]);\n return null;\n };\n\n render(\n <TamboClientContext.Provider\n value={{\n client: { baseURL: \"https://api.tambo.co\" } as any,\n queryClient,\n isUpdatingToken: false,\n }}\n >\n <TamboRegistryProvider>\n <TamboMcpTokenProvider>\n <TamboMcpProvider\n mcpServers={[\n {\n url: \"https://server-a.example\",\n transport: MCPTransport.SSE,\n },\n {\n url: \"https://server-b.example\",\n transport: MCPTransport.SSE,\n },\n ]}\n >\n <Capture />\n </TamboMcpProvider>\n </TamboMcpTokenProvider>\n </TamboRegistryProvider>\n </TamboClientContext.Provider>,\n );\n\n // Wait for server A prompts to be loaded\n // Server B should fail but not block server A\n await waitFor(() => {\n expect(capturedPrompts.length).toBe(1);\n expect(mcpServersCount).toBe(2); // Both servers should be in the list\n });\n\n // Verify only server A's prompts are present\n const promptNames = capturedPrompts.map((p) => p.prompt.name);\n expect(promptNames).toContain(\"prompt-a\");\n expect(promptNames).not.toContain(\"prompt-b\");\n });\n\n it(\"should add prompts when a new server is added\", async () => {\n const serverAPrompts = {\n prompts: [{ name: \"prompt-a\", description: \"Prompt A\" }],\n };\n const serverBPrompts = {\n prompts: [{ name: \"prompt-b\", description: \"Prompt B\" }],\n };\n\n const mockClientA = {\n listTools: jest.fn().mockResolvedValue([]),\n listPrompts: jest.fn().mockResolvedValue(serverAPrompts),\n close: jest.fn(),\n };\n const mockClientB = {\n listTools: jest.fn().mockResolvedValue([]),\n listPrompts: jest.fn().mockResolvedValue(serverBPrompts),\n close: jest.fn(),\n };\n\n const clientA = {\n client: mockClientA,\n listTools: jest.fn().mockResolvedValue([]),\n close: jest.fn(),\n };\n const clientB = {\n client: mockClientB,\n listTools: jest.fn().mockResolvedValue([]),\n close: jest.fn(),\n };\n\n createImpl.mockImplementation(async (url: string) => {\n if (url === \"https://server-a.example\") return clientA;\n if (url === \"https://server-b.example\") return clientB;\n throw new Error(`Unexpected URL: ${url}`);\n });\n\n let capturedPrompts: ListPromptEntry[] = [];\n const Capture: React.FC = () => {\n const { data: prompts } = useTamboMcpPromptList();\n useEffect(() => {\n if (prompts) {\n capturedPrompts = prompts;\n }\n }, [prompts]);\n return null;\n };\n\n const { rerender } = render(\n <TamboClientContext.Provider\n value={{\n client: { baseURL: \"https://api.tambo.co\" } as any,\n queryClient,\n isUpdatingToken: false,\n }}\n >\n <TamboRegistryProvider>\n <TamboMcpTokenProvider>\n <TamboMcpProvider\n mcpServers={[\n {\n url: \"https://server-a.example\",\n transport: MCPTransport.SSE,\n },\n ]}\n >\n <Capture />\n </TamboMcpProvider>\n </TamboMcpTokenProvider>\n </TamboRegistryProvider>\n </TamboClientContext.Provider>,\n );\n\n // Wait for initial prompts from server A\n await waitFor(() => {\n expect(capturedPrompts.length).toBe(1);\n });\n\n expect(capturedPrompts.map((p) => p.prompt.name)).toContain(\"prompt-a\");\n\n // Add server B\n rerender(\n <TamboClientContext.Provider\n value={{\n client: { baseURL: \"https://api.tambo.co\" } as any,\n queryClient,\n isUpdatingToken: false,\n }}\n >\n <TamboRegistryProvider>\n <TamboMcpTokenProvider>\n <TamboMcpProvider\n mcpServers={[\n {\n url: \"https://server-a.example\",\n transport: MCPTransport.SSE,\n },\n {\n url: \"https://server-b.example\",\n transport: MCPTransport.SSE,\n },\n ]}\n >\n <Capture />\n </TamboMcpProvider>\n </TamboMcpTokenProvider>\n </TamboRegistryProvider>\n </TamboClientContext.Provider>,\n );\n\n // Wait for server B prompts to be added\n await waitFor(() => {\n expect(capturedPrompts.length).toBe(2);\n });\n\n const promptNames = capturedPrompts.map((p) => p.prompt.name);\n expect(promptNames).toContain(\"prompt-a\");\n expect(promptNames).toContain(\"prompt-b\");\n });\n});\n"]}