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