@tambo-ai/react 0.58.1 → 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.
- package/dist/hooks/react-query-hooks.d.ts +14 -1
- package/dist/hooks/react-query-hooks.d.ts.map +1 -1
- package/dist/hooks/react-query-hooks.js +13 -0
- package/dist/hooks/react-query-hooks.js.map +1 -1
- package/dist/hooks/use-tambo-stream-status.js +1 -1
- package/dist/hooks/use-tambo-stream-status.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/__tests__/elicitation.test.d.ts +2 -0
- package/dist/mcp/__tests__/elicitation.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/elicitation.test.js +261 -0
- package/dist/mcp/__tests__/elicitation.test.js.map +1 -0
- package/dist/mcp/__tests__/mcp-client.test.js +0 -266
- package/dist/mcp/__tests__/mcp-client.test.js.map +1 -1
- package/dist/mcp/__tests__/mcp-hooks.test.d.ts +2 -0
- package/dist/mcp/__tests__/mcp-hooks.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/mcp-hooks.test.js +504 -0
- package/dist/mcp/__tests__/mcp-hooks.test.js.map +1 -0
- package/dist/mcp/__tests__/tambo-mcp-provider.test.js +361 -16
- package/dist/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -1
- package/dist/mcp/__tests__/use-mcp-servers.test.js +34 -9
- package/dist/mcp/__tests__/use-mcp-servers.test.js.map +1 -1
- package/dist/mcp/elicitation.d.ts +80 -0
- package/dist/mcp/elicitation.d.ts.map +1 -0
- package/dist/mcp/elicitation.js +55 -0
- package/dist/mcp/elicitation.js.map +1 -0
- package/dist/mcp/index.d.ts +4 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +5 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/mcp-client.d.ts +51 -86
- package/dist/mcp/mcp-client.d.ts.map +1 -1
- package/dist/mcp/mcp-client.js +22 -159
- package/dist/mcp/mcp-client.js.map +1 -1
- package/dist/mcp/mcp-hooks.d.ts +107 -0
- package/dist/mcp/mcp-hooks.d.ts.map +1 -0
- package/dist/mcp/mcp-hooks.js +103 -0
- package/dist/mcp/mcp-hooks.js.map +1 -0
- package/dist/mcp/tambo-mcp-provider.d.ts +86 -4
- package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/dist/mcp/tambo-mcp-provider.js +275 -106
- package/dist/mcp/tambo-mcp-provider.js.map +1 -1
- package/dist/providers/__tests__/tambo-thread-provider-initial-messages.test.js +3 -1
- package/dist/providers/__tests__/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/dist/providers/__tests__/tambo-thread-provider.test.js +25 -12
- package/dist/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
- package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/dist/providers/tambo-interactable-provider.js +11 -4
- package/dist/providers/tambo-interactable-provider.js.map +1 -1
- package/dist/providers/tambo-mcp-token-provider.d.ts +34 -0
- package/dist/providers/tambo-mcp-token-provider.d.ts.map +1 -0
- package/dist/providers/tambo-mcp-token-provider.js +69 -0
- package/dist/providers/tambo-mcp-token-provider.js.map +1 -0
- package/dist/providers/tambo-provider.d.ts.map +1 -1
- package/dist/providers/tambo-provider.js +7 -9
- package/dist/providers/tambo-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-provider.js +14 -0
- package/dist/providers/tambo-thread-provider.js.map +1 -1
- package/esm/hooks/react-query-hooks.d.ts +14 -1
- package/esm/hooks/react-query-hooks.d.ts.map +1 -1
- package/esm/hooks/react-query-hooks.js +13 -1
- package/esm/hooks/react-query-hooks.js.map +1 -1
- package/esm/hooks/use-tambo-stream-status.js +1 -1
- package/esm/hooks/use-tambo-stream-status.js.map +1 -1
- package/esm/index.js +2 -0
- package/esm/index.js.map +1 -1
- package/esm/mcp/__tests__/elicitation.test.d.ts +2 -0
- package/esm/mcp/__tests__/elicitation.test.d.ts.map +1 -0
- package/esm/mcp/__tests__/elicitation.test.js +259 -0
- package/esm/mcp/__tests__/elicitation.test.js.map +1 -0
- package/esm/mcp/__tests__/mcp-client.test.js +0 -266
- package/esm/mcp/__tests__/mcp-client.test.js.map +1 -1
- package/esm/mcp/__tests__/mcp-hooks.test.d.ts +2 -0
- package/esm/mcp/__tests__/mcp-hooks.test.d.ts.map +1 -0
- package/esm/mcp/__tests__/mcp-hooks.test.js +469 -0
- package/esm/mcp/__tests__/mcp-hooks.test.js.map +1 -0
- package/esm/mcp/__tests__/tambo-mcp-provider.test.js +361 -16
- package/esm/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -1
- package/esm/mcp/__tests__/use-mcp-servers.test.js +34 -9
- package/esm/mcp/__tests__/use-mcp-servers.test.js.map +1 -1
- package/esm/mcp/elicitation.d.ts +80 -0
- package/esm/mcp/elicitation.d.ts.map +1 -0
- package/esm/mcp/elicitation.js +52 -0
- package/esm/mcp/elicitation.js.map +1 -0
- package/esm/mcp/index.d.ts +4 -1
- package/esm/mcp/index.d.ts.map +1 -1
- package/esm/mcp/index.js +2 -1
- package/esm/mcp/index.js.map +1 -1
- package/esm/mcp/mcp-client.d.ts +51 -86
- package/esm/mcp/mcp-client.d.ts.map +1 -1
- package/esm/mcp/mcp-client.js +22 -159
- package/esm/mcp/mcp-client.js.map +1 -1
- package/esm/mcp/mcp-hooks.d.ts +107 -0
- package/esm/mcp/mcp-hooks.d.ts.map +1 -0
- package/esm/mcp/mcp-hooks.js +99 -0
- package/esm/mcp/mcp-hooks.js.map +1 -0
- package/esm/mcp/tambo-mcp-provider.d.ts +86 -4
- package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/esm/mcp/tambo-mcp-provider.js +275 -107
- package/esm/mcp/tambo-mcp-provider.js.map +1 -1
- package/esm/providers/__tests__/tambo-thread-provider-initial-messages.test.js +3 -1
- package/esm/providers/__tests__/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/esm/providers/__tests__/tambo-thread-provider.test.js +25 -12
- package/esm/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
- package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/esm/providers/tambo-interactable-provider.js +11 -4
- package/esm/providers/tambo-interactable-provider.js.map +1 -1
- package/esm/providers/tambo-mcp-token-provider.d.ts +34 -0
- package/esm/providers/tambo-mcp-token-provider.d.ts.map +1 -0
- package/esm/providers/tambo-mcp-token-provider.js +31 -0
- package/esm/providers/tambo-mcp-token-provider.js.map +1 -0
- package/esm/providers/tambo-provider.d.ts.map +1 -1
- package/esm/providers/tambo-provider.js +7 -9
- package/esm/providers/tambo-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-provider.js +14 -0
- package/esm/providers/tambo-thread-provider.js.map +1 -1
- package/package.json +8 -8
|
@@ -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"]}
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import { render, waitFor } from "@testing-library/react";
|
|
2
2
|
import React, { useEffect } from "react";
|
|
3
|
+
import { useTamboClient } from "../../providers/tambo-client-provider";
|
|
4
|
+
import { TamboMcpTokenProvider } from "../../providers/tambo-mcp-token-provider";
|
|
3
5
|
import { useTamboRegistry } from "../../providers/tambo-registry-provider";
|
|
4
6
|
import { MCPClient, MCPTransport } from "../mcp-client";
|
|
5
7
|
import { extractErrorMessage, TamboMcpProvider, useTamboMcpServers, } from "../tambo-mcp-provider";
|
|
8
|
+
// Helper to create mock RequestHandlerExtra for testing
|
|
9
|
+
function createMockExtra() {
|
|
10
|
+
return {
|
|
11
|
+
signal: new AbortController().signal,
|
|
12
|
+
requestId: "test-request-id",
|
|
13
|
+
sendNotification: (async () => { }),
|
|
14
|
+
sendRequest: (async () => ({ _meta: {} })),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
6
17
|
// Mock the MCP client to avoid ES module issues
|
|
7
18
|
jest.mock("../mcp-client", () => ({
|
|
8
19
|
MCPClient: jest.fn(),
|
|
@@ -15,6 +26,10 @@ jest.mock("../mcp-client", () => ({
|
|
|
15
26
|
jest.mock("../../providers/tambo-registry-provider", () => ({
|
|
16
27
|
useTamboRegistry: jest.fn(),
|
|
17
28
|
}));
|
|
29
|
+
// Mock the client provider to avoid dependency issues
|
|
30
|
+
jest.mock("../../providers/tambo-client-provider", () => ({
|
|
31
|
+
useTamboClient: jest.fn(),
|
|
32
|
+
}));
|
|
18
33
|
describe("extractErrorMessage", () => {
|
|
19
34
|
describe("Array content handling", () => {
|
|
20
35
|
it("should extract text from array content with multiple text items", () => {
|
|
@@ -120,10 +135,15 @@ describe("TamboMcpProvider server list changes", () => {
|
|
|
120
135
|
useTamboRegistry.mockReturnValue({
|
|
121
136
|
registerTool: jest.fn(),
|
|
122
137
|
});
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
.
|
|
126
|
-
|
|
138
|
+
// Mock client with baseURL
|
|
139
|
+
useTamboClient.mockReturnValue({
|
|
140
|
+
baseURL: "https://api.tambo.co",
|
|
141
|
+
});
|
|
142
|
+
// Ensure MCPClient.create exists and returns a fake client with listTools and close
|
|
143
|
+
MCPClient.create = jest.fn().mockResolvedValue({
|
|
144
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
145
|
+
close: jest.fn(),
|
|
146
|
+
});
|
|
127
147
|
});
|
|
128
148
|
const Capture = ({ onUpdate, }) => {
|
|
129
149
|
const servers = useTamboMcpServers();
|
|
@@ -134,15 +154,17 @@ describe("TamboMcpProvider server list changes", () => {
|
|
|
134
154
|
};
|
|
135
155
|
it("adds a new server when the list grows", async () => {
|
|
136
156
|
let latest = [];
|
|
137
|
-
const { rerender, getByTestId } = render(React.createElement(
|
|
138
|
-
React.createElement(
|
|
157
|
+
const { rerender, getByTestId } = render(React.createElement(TamboMcpTokenProvider, null,
|
|
158
|
+
React.createElement(TamboMcpProvider, { mcpServers: ["https://a.example"] },
|
|
159
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
139
160
|
// Wait for initial connection
|
|
140
161
|
await waitFor(() => {
|
|
141
162
|
expect(latest.length).toBe(1);
|
|
142
163
|
});
|
|
143
164
|
// Add new server
|
|
144
|
-
rerender(React.createElement(
|
|
145
|
-
React.createElement(
|
|
165
|
+
rerender(React.createElement(TamboMcpTokenProvider, null,
|
|
166
|
+
React.createElement(TamboMcpProvider, { mcpServers: ["https://a.example", "https://b.example"] },
|
|
167
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
146
168
|
await waitFor(() => {
|
|
147
169
|
expect(latest.length).toBe(2);
|
|
148
170
|
const urls = getByTestId("urls").textContent || "";
|
|
@@ -152,14 +174,16 @@ describe("TamboMcpProvider server list changes", () => {
|
|
|
152
174
|
});
|
|
153
175
|
it("removes a server when the list shrinks", async () => {
|
|
154
176
|
let latest = [];
|
|
155
|
-
const { rerender, getByTestId } = render(React.createElement(
|
|
156
|
-
React.createElement(
|
|
177
|
+
const { rerender, getByTestId } = render(React.createElement(TamboMcpTokenProvider, null,
|
|
178
|
+
React.createElement(TamboMcpProvider, { mcpServers: ["https://a.example", "https://b.example"] },
|
|
179
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
157
180
|
await waitFor(() => {
|
|
158
181
|
expect(latest.length).toBe(2);
|
|
159
182
|
});
|
|
160
183
|
// Remove one server
|
|
161
|
-
rerender(React.createElement(
|
|
162
|
-
React.createElement(
|
|
184
|
+
rerender(React.createElement(TamboMcpTokenProvider, null,
|
|
185
|
+
React.createElement(TamboMcpProvider, { mcpServers: ["https://a.example"] },
|
|
186
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
163
187
|
await waitFor(() => {
|
|
164
188
|
expect(latest.length).toBe(1);
|
|
165
189
|
const urls = getByTestId("urls").textContent || "";
|
|
@@ -173,8 +197,9 @@ describe("TamboMcpProvider server list changes", () => {
|
|
|
173
197
|
{ url: "https://a.example", transport: MCPTransport.SSE },
|
|
174
198
|
{ url: "https://b.example", transport: MCPTransport.SSE },
|
|
175
199
|
];
|
|
176
|
-
const { rerender } = render(React.createElement(
|
|
177
|
-
React.createElement(
|
|
200
|
+
const { rerender } = render(React.createElement(TamboMcpTokenProvider, null,
|
|
201
|
+
React.createElement(TamboMcpProvider, { mcpServers: initial },
|
|
202
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
178
203
|
await waitFor(() => {
|
|
179
204
|
expect(latest.length).toBe(2);
|
|
180
205
|
});
|
|
@@ -183,13 +208,333 @@ describe("TamboMcpProvider server list changes", () => {
|
|
|
183
208
|
{ url: "https://a.example", transport: MCPTransport.SSE },
|
|
184
209
|
{ url: "https://b.example", transport: MCPTransport.SSE },
|
|
185
210
|
];
|
|
186
|
-
rerender(React.createElement(
|
|
187
|
-
React.createElement(
|
|
211
|
+
rerender(React.createElement(TamboMcpTokenProvider, null,
|
|
212
|
+
React.createElement(TamboMcpProvider, { mcpServers: same },
|
|
213
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
188
214
|
await waitFor(() => {
|
|
189
215
|
expect(latest.length).toBe(2);
|
|
190
216
|
const urls = latest.map((s) => s.url).sort();
|
|
191
217
|
expect(urls).toEqual(["https://a.example", "https://b.example"].sort());
|
|
192
218
|
});
|
|
193
219
|
});
|
|
220
|
+
it("reuses client when same server is passed with new array instance", async () => {
|
|
221
|
+
const createSpy = jest.fn().mockResolvedValue({
|
|
222
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
223
|
+
close: jest.fn(),
|
|
224
|
+
});
|
|
225
|
+
MCPClient.create = createSpy;
|
|
226
|
+
let latest = [];
|
|
227
|
+
const initial = [{ url: "https://a.example", transport: MCPTransport.SSE }];
|
|
228
|
+
const { rerender } = render(React.createElement(TamboMcpTokenProvider, null,
|
|
229
|
+
React.createElement(TamboMcpProvider, { mcpServers: initial },
|
|
230
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
231
|
+
await waitFor(() => {
|
|
232
|
+
expect(latest.length).toBe(1);
|
|
233
|
+
});
|
|
234
|
+
// Verify client was created once
|
|
235
|
+
expect(createSpy).toHaveBeenCalledTimes(1);
|
|
236
|
+
const firstClient = latest[0].client;
|
|
237
|
+
// Pass a new array with same server
|
|
238
|
+
const same = [{ url: "https://a.example", transport: MCPTransport.SSE }];
|
|
239
|
+
rerender(React.createElement(TamboMcpTokenProvider, null,
|
|
240
|
+
React.createElement(TamboMcpProvider, { mcpServers: same },
|
|
241
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
242
|
+
await waitFor(() => {
|
|
243
|
+
expect(latest.length).toBe(1);
|
|
244
|
+
});
|
|
245
|
+
// Client should NOT have been created again
|
|
246
|
+
expect(createSpy).toHaveBeenCalledTimes(1);
|
|
247
|
+
// Should be the exact same client instance
|
|
248
|
+
expect(latest[0].client).toBe(firstClient);
|
|
249
|
+
});
|
|
250
|
+
it("calls close() on removed server clients", async () => {
|
|
251
|
+
const closeSpy = jest.fn();
|
|
252
|
+
const createSpy = jest.fn().mockResolvedValue({
|
|
253
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
254
|
+
close: closeSpy,
|
|
255
|
+
});
|
|
256
|
+
MCPClient.create = createSpy;
|
|
257
|
+
let latest = [];
|
|
258
|
+
const { rerender } = render(React.createElement(TamboMcpTokenProvider, null,
|
|
259
|
+
React.createElement(TamboMcpProvider, { mcpServers: ["https://a.example", "https://b.example"] },
|
|
260
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
261
|
+
await waitFor(() => {
|
|
262
|
+
expect(latest.length).toBe(2);
|
|
263
|
+
});
|
|
264
|
+
expect(closeSpy).not.toHaveBeenCalled();
|
|
265
|
+
// Remove one server
|
|
266
|
+
rerender(React.createElement(TamboMcpTokenProvider, null,
|
|
267
|
+
React.createElement(TamboMcpProvider, { mcpServers: ["https://a.example"] },
|
|
268
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
269
|
+
await waitFor(() => {
|
|
270
|
+
expect(latest.length).toBe(1);
|
|
271
|
+
});
|
|
272
|
+
// close() should have been called once for the removed server
|
|
273
|
+
expect(closeSpy).toHaveBeenCalledTimes(1);
|
|
274
|
+
});
|
|
275
|
+
it("calls close() on all clients when provider unmounts", async () => {
|
|
276
|
+
const closeSpy = jest.fn();
|
|
277
|
+
const createSpy = jest.fn().mockResolvedValue({
|
|
278
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
279
|
+
close: closeSpy,
|
|
280
|
+
});
|
|
281
|
+
MCPClient.create = createSpy;
|
|
282
|
+
let latest = [];
|
|
283
|
+
const { unmount } = render(React.createElement(TamboMcpTokenProvider, null,
|
|
284
|
+
React.createElement(TamboMcpProvider, { mcpServers: ["https://a.example", "https://b.example"] },
|
|
285
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
286
|
+
await waitFor(() => {
|
|
287
|
+
expect(latest.length).toBe(2);
|
|
288
|
+
});
|
|
289
|
+
expect(closeSpy).not.toHaveBeenCalled();
|
|
290
|
+
// Unmount the provider
|
|
291
|
+
unmount();
|
|
292
|
+
// close() should have been called for both clients
|
|
293
|
+
expect(closeSpy).toHaveBeenCalledTimes(2);
|
|
294
|
+
});
|
|
295
|
+
it("creates new client when customHeaders change", async () => {
|
|
296
|
+
const closeSpy = jest.fn();
|
|
297
|
+
let clientIdCounter = 0;
|
|
298
|
+
const createSpy = jest.fn().mockImplementation(async () => {
|
|
299
|
+
const id = ++clientIdCounter;
|
|
300
|
+
return {
|
|
301
|
+
id, // Add an ID so we can track which client is which
|
|
302
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
303
|
+
close: closeSpy,
|
|
304
|
+
};
|
|
305
|
+
});
|
|
306
|
+
MCPClient.create = createSpy;
|
|
307
|
+
let latest = [];
|
|
308
|
+
const { rerender } = render(React.createElement(TamboMcpTokenProvider, null,
|
|
309
|
+
React.createElement(TamboMcpProvider, { mcpServers: [
|
|
310
|
+
{
|
|
311
|
+
url: "https://a.example",
|
|
312
|
+
transport: MCPTransport.SSE,
|
|
313
|
+
customHeaders: { Authorization: "Bearer token1" },
|
|
314
|
+
},
|
|
315
|
+
] },
|
|
316
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
317
|
+
await waitFor(() => {
|
|
318
|
+
expect(latest.length).toBe(1);
|
|
319
|
+
expect(createSpy).toHaveBeenCalledTimes(1);
|
|
320
|
+
});
|
|
321
|
+
const firstClientId = latest[0].client?.id;
|
|
322
|
+
expect(firstClientId).toBe(1);
|
|
323
|
+
// Change the customHeaders
|
|
324
|
+
rerender(React.createElement(TamboMcpTokenProvider, null,
|
|
325
|
+
React.createElement(TamboMcpProvider, { mcpServers: [
|
|
326
|
+
{
|
|
327
|
+
url: "https://a.example",
|
|
328
|
+
transport: MCPTransport.SSE,
|
|
329
|
+
customHeaders: { Authorization: "Bearer token2" },
|
|
330
|
+
},
|
|
331
|
+
] },
|
|
332
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
333
|
+
// Wait for old client to be closed and new client to be created
|
|
334
|
+
await waitFor(() => {
|
|
335
|
+
expect(closeSpy).toHaveBeenCalledTimes(1);
|
|
336
|
+
expect(createSpy).toHaveBeenCalledTimes(2);
|
|
337
|
+
expect(latest.length).toBe(1);
|
|
338
|
+
const newClientId = latest[0].client?.id;
|
|
339
|
+
expect(newClientId).toBe(2); // Should be the new client
|
|
340
|
+
}, { timeout: 3000 });
|
|
341
|
+
});
|
|
342
|
+
// Note: Token changes for the internal Tambo server are covered by the
|
|
343
|
+
// "creates new client when customHeaders change" test above, since token
|
|
344
|
+
// changes result in different Authorization headers, which trigger client recreation.
|
|
345
|
+
});
|
|
346
|
+
describe("TamboMcpProvider handler support", () => {
|
|
347
|
+
let mockClient;
|
|
348
|
+
let createSpy;
|
|
349
|
+
beforeEach(() => {
|
|
350
|
+
// Mock registry so tool registration is a no-op
|
|
351
|
+
useTamboRegistry.mockReturnValue({
|
|
352
|
+
registerTool: jest.fn(),
|
|
353
|
+
});
|
|
354
|
+
// Mock client with baseURL
|
|
355
|
+
useTamboClient.mockReturnValue({
|
|
356
|
+
baseURL: "https://api.tambo.co",
|
|
357
|
+
});
|
|
358
|
+
// Create a mock client with update methods
|
|
359
|
+
mockClient = {
|
|
360
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
361
|
+
updateElicitationHandler: jest.fn(),
|
|
362
|
+
updateSamplingHandler: jest.fn(),
|
|
363
|
+
close: jest.fn(),
|
|
364
|
+
};
|
|
365
|
+
// Mock MCPClient.create to return our mock client
|
|
366
|
+
createSpy = jest.fn().mockResolvedValue(mockClient);
|
|
367
|
+
MCPClient.create = createSpy;
|
|
368
|
+
});
|
|
369
|
+
const Capture = ({ onUpdate, }) => {
|
|
370
|
+
const servers = useTamboMcpServers();
|
|
371
|
+
useEffect(() => {
|
|
372
|
+
onUpdate(servers);
|
|
373
|
+
}, [servers, onUpdate]);
|
|
374
|
+
return null;
|
|
375
|
+
};
|
|
376
|
+
it("should pass provider-level elicitation handler to MCPClient.create", async () => {
|
|
377
|
+
const mockElicitationHandler = jest.fn().mockResolvedValue({
|
|
378
|
+
content: [{ type: "text", text: "test" }],
|
|
379
|
+
});
|
|
380
|
+
const handlers = {
|
|
381
|
+
elicitation: mockElicitationHandler,
|
|
382
|
+
};
|
|
383
|
+
let latest = [];
|
|
384
|
+
render(React.createElement(TamboMcpTokenProvider, null,
|
|
385
|
+
React.createElement(TamboMcpProvider, { mcpServers: ["https://test.example"], handlers: handlers },
|
|
386
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
387
|
+
await waitFor(() => {
|
|
388
|
+
expect(latest.length).toBe(1);
|
|
389
|
+
});
|
|
390
|
+
// Verify MCPClient.create was called
|
|
391
|
+
expect(createSpy).toHaveBeenCalledWith("https://test.example", MCPTransport.SSE, undefined, undefined, undefined, expect.objectContaining({
|
|
392
|
+
elicitation: expect.any(Function),
|
|
393
|
+
}));
|
|
394
|
+
// Get the actual handler that was passed
|
|
395
|
+
const passedHandlers = createSpy.mock.calls[0][5];
|
|
396
|
+
expect(passedHandlers.elicitation).toBeDefined();
|
|
397
|
+
// Call the wrapped handler and verify it receives serverInfo
|
|
398
|
+
const mockRequest = {
|
|
399
|
+
method: "sampling/createMessage",
|
|
400
|
+
params: {},
|
|
401
|
+
};
|
|
402
|
+
const mockExtra = createMockExtra();
|
|
403
|
+
await passedHandlers.elicitation(mockRequest, mockExtra);
|
|
404
|
+
expect(mockElicitationHandler).toHaveBeenCalledWith(mockRequest, mockExtra, expect.objectContaining({
|
|
405
|
+
url: "https://test.example",
|
|
406
|
+
transport: MCPTransport.SSE,
|
|
407
|
+
}));
|
|
408
|
+
});
|
|
409
|
+
it("should pass provider-level sampling handler to MCPClient.create", async () => {
|
|
410
|
+
const mockSamplingHandler = jest.fn().mockResolvedValue({
|
|
411
|
+
model: "test-model",
|
|
412
|
+
stopReason: "endTurn",
|
|
413
|
+
role: "assistant",
|
|
414
|
+
content: { type: "text", text: "response" },
|
|
415
|
+
});
|
|
416
|
+
const handlers = {
|
|
417
|
+
sampling: mockSamplingHandler,
|
|
418
|
+
};
|
|
419
|
+
let latest = [];
|
|
420
|
+
render(React.createElement(TamboMcpTokenProvider, null,
|
|
421
|
+
React.createElement(TamboMcpProvider, { mcpServers: ["https://test.example"], handlers: handlers },
|
|
422
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
423
|
+
await waitFor(() => {
|
|
424
|
+
expect(latest.length).toBe(1);
|
|
425
|
+
});
|
|
426
|
+
// Verify MCPClient.create was called with sampling handler
|
|
427
|
+
expect(createSpy).toHaveBeenCalledWith("https://test.example", MCPTransport.SSE, undefined, undefined, undefined, expect.objectContaining({
|
|
428
|
+
sampling: expect.any(Function),
|
|
429
|
+
}));
|
|
430
|
+
// Get the actual handler and verify it receives serverInfo
|
|
431
|
+
const passedHandlers = createSpy.mock.calls[0][5];
|
|
432
|
+
const mockRequest = {
|
|
433
|
+
method: "sampling/createMessage",
|
|
434
|
+
params: {
|
|
435
|
+
messages: [],
|
|
436
|
+
modelPreferences: {},
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
const mockExtra = createMockExtra();
|
|
440
|
+
await passedHandlers.sampling(mockRequest, mockExtra);
|
|
441
|
+
expect(mockSamplingHandler).toHaveBeenCalledWith(mockRequest, mockExtra, expect.objectContaining({
|
|
442
|
+
url: "https://test.example",
|
|
443
|
+
transport: MCPTransport.SSE,
|
|
444
|
+
}));
|
|
445
|
+
});
|
|
446
|
+
it("should allow per-server handlers to override provider-level handlers", async () => {
|
|
447
|
+
const providerElicitationHandler = jest.fn().mockResolvedValue({
|
|
448
|
+
content: [{ type: "text", text: "provider" }],
|
|
449
|
+
});
|
|
450
|
+
const serverElicitationHandler = jest.fn().mockResolvedValue({
|
|
451
|
+
content: [{ type: "text", text: "server" }],
|
|
452
|
+
});
|
|
453
|
+
const handlers = {
|
|
454
|
+
elicitation: providerElicitationHandler,
|
|
455
|
+
};
|
|
456
|
+
let latest = [];
|
|
457
|
+
render(React.createElement(TamboMcpTokenProvider, null,
|
|
458
|
+
React.createElement(TamboMcpProvider, { mcpServers: [
|
|
459
|
+
{
|
|
460
|
+
url: "https://test.example",
|
|
461
|
+
handlers: {
|
|
462
|
+
elicitation: serverElicitationHandler,
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
], handlers: handlers },
|
|
466
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
467
|
+
await waitFor(() => {
|
|
468
|
+
expect(latest.length).toBe(1);
|
|
469
|
+
});
|
|
470
|
+
// Verify the per-server handler was used, not the provider handler
|
|
471
|
+
const passedHandlers = createSpy.mock.calls[0][5];
|
|
472
|
+
expect(passedHandlers.elicitation).toBe(serverElicitationHandler);
|
|
473
|
+
});
|
|
474
|
+
it("should pass different serverInfo to handlers for different servers", async () => {
|
|
475
|
+
const mockElicitationHandler = jest.fn().mockResolvedValue({
|
|
476
|
+
content: [{ type: "text", text: "test" }],
|
|
477
|
+
});
|
|
478
|
+
const handlers = {
|
|
479
|
+
elicitation: mockElicitationHandler,
|
|
480
|
+
};
|
|
481
|
+
let latest = [];
|
|
482
|
+
render(React.createElement(TamboMcpTokenProvider, null,
|
|
483
|
+
React.createElement(TamboMcpProvider, { mcpServers: [
|
|
484
|
+
{ url: "https://server-a.example", name: "Server A" },
|
|
485
|
+
{ url: "https://server-b.example", name: "Server B" },
|
|
486
|
+
], handlers: handlers },
|
|
487
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
488
|
+
await waitFor(() => {
|
|
489
|
+
expect(latest.length).toBe(2);
|
|
490
|
+
});
|
|
491
|
+
// Verify both servers got handlers
|
|
492
|
+
expect(createSpy).toHaveBeenCalledTimes(2);
|
|
493
|
+
// Get handlers for both servers
|
|
494
|
+
const serverAHandlers = createSpy.mock.calls[0][5];
|
|
495
|
+
const serverBHandlers = createSpy.mock.calls[1][5];
|
|
496
|
+
const mockRequest = {
|
|
497
|
+
method: "sampling/createMessage",
|
|
498
|
+
params: {},
|
|
499
|
+
};
|
|
500
|
+
const mockExtra = createMockExtra();
|
|
501
|
+
// Call handler for server A
|
|
502
|
+
await serverAHandlers.elicitation(mockRequest, mockExtra);
|
|
503
|
+
expect(mockElicitationHandler).toHaveBeenCalledWith(mockRequest, mockExtra, expect.objectContaining({
|
|
504
|
+
url: "https://server-a.example",
|
|
505
|
+
name: "Server A",
|
|
506
|
+
}));
|
|
507
|
+
mockElicitationHandler.mockClear();
|
|
508
|
+
// Call handler for server B
|
|
509
|
+
await serverBHandlers.elicitation(mockRequest, mockExtra);
|
|
510
|
+
expect(mockElicitationHandler).toHaveBeenCalledWith(mockRequest, mockExtra, expect.objectContaining({
|
|
511
|
+
url: "https://server-b.example",
|
|
512
|
+
name: "Server B",
|
|
513
|
+
}));
|
|
514
|
+
});
|
|
515
|
+
it("should update handlers when provider handlers change", async () => {
|
|
516
|
+
const initialHandler = jest.fn().mockResolvedValue({
|
|
517
|
+
content: [{ type: "text", text: "initial" }],
|
|
518
|
+
});
|
|
519
|
+
const updatedHandler = jest.fn().mockResolvedValue({
|
|
520
|
+
content: [{ type: "text", text: "updated" }],
|
|
521
|
+
});
|
|
522
|
+
let latest = [];
|
|
523
|
+
const { rerender } = render(React.createElement(TamboMcpTokenProvider, null,
|
|
524
|
+
React.createElement(TamboMcpProvider, { mcpServers: ["https://test.example"], handlers: { elicitation: initialHandler } },
|
|
525
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
526
|
+
await waitFor(() => {
|
|
527
|
+
expect(latest.length).toBe(1);
|
|
528
|
+
});
|
|
529
|
+
// Update the handlers
|
|
530
|
+
rerender(React.createElement(TamboMcpTokenProvider, null,
|
|
531
|
+
React.createElement(TamboMcpProvider, { mcpServers: ["https://test.example"], handlers: { elicitation: updatedHandler } },
|
|
532
|
+
React.createElement(Capture, { onUpdate: (s) => (latest = s) }))));
|
|
533
|
+
await waitFor(() => {
|
|
534
|
+
expect(mockClient.updateElicitationHandler).toHaveBeenCalled();
|
|
535
|
+
});
|
|
536
|
+
// Verify the handler was updated
|
|
537
|
+
expect(mockClient.updateElicitationHandler).toHaveBeenCalledWith(expect.any(Function));
|
|
538
|
+
});
|
|
194
539
|
});
|
|
195
540
|
//# sourceMappingURL=tambo-mcp-provider.test.js.map
|