@tambo-ai/react 0.54.1 → 0.56.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 (60) hide show
  1. package/README.md +49 -14
  2. package/dist/hooks/__tests__/use-tambo-threads.test.js +1 -9
  3. package/dist/hooks/__tests__/use-tambo-threads.test.js.map +1 -1
  4. package/dist/mcp/__tests__/mcp-client.test.js +73 -27
  5. package/dist/mcp/__tests__/mcp-client.test.js.map +1 -1
  6. package/dist/mcp/mcp-client.d.ts +93 -210
  7. package/dist/mcp/mcp-client.d.ts.map +1 -1
  8. package/dist/mcp/mcp-client.js +103 -18
  9. package/dist/mcp/mcp-client.js.map +1 -1
  10. package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
  11. package/dist/mcp/tambo-mcp-provider.js +2 -1
  12. package/dist/mcp/tambo-mcp-provider.js.map +1 -1
  13. package/dist/providers/__tests__/tambo-thread-provider.test.js +2 -6
  14. package/dist/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
  15. package/dist/providers/hooks/use-tambo-session-token.d.ts +2 -2
  16. package/dist/providers/hooks/use-tambo-session-token.js +2 -2
  17. package/dist/providers/hooks/use-tambo-session-token.js.map +1 -1
  18. package/dist/providers/tambo-context-helpers-provider.d.ts +7 -2
  19. package/dist/providers/tambo-context-helpers-provider.d.ts.map +1 -1
  20. package/dist/providers/tambo-context-helpers-provider.js +12 -7
  21. package/dist/providers/tambo-context-helpers-provider.js.map +1 -1
  22. package/dist/providers/tambo-prop-stream-provider/index.d.ts +1 -1
  23. package/dist/providers/tambo-prop-stream-provider/index.js +1 -1
  24. package/dist/providers/tambo-prop-stream-provider/index.js.map +1 -1
  25. package/dist/providers/tambo-provider.d.ts +1 -1
  26. package/dist/providers/tambo-provider.js +1 -1
  27. package/dist/providers/tambo-provider.js.map +1 -1
  28. package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
  29. package/dist/providers/tambo-thread-provider.js +2 -4
  30. package/dist/providers/tambo-thread-provider.js.map +1 -1
  31. package/esm/hooks/__tests__/use-tambo-threads.test.js +1 -9
  32. package/esm/hooks/__tests__/use-tambo-threads.test.js.map +1 -1
  33. package/esm/mcp/__tests__/mcp-client.test.js +73 -27
  34. package/esm/mcp/__tests__/mcp-client.test.js.map +1 -1
  35. package/esm/mcp/mcp-client.d.ts +93 -210
  36. package/esm/mcp/mcp-client.d.ts.map +1 -1
  37. package/esm/mcp/mcp-client.js +103 -18
  38. package/esm/mcp/mcp-client.js.map +1 -1
  39. package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
  40. package/esm/mcp/tambo-mcp-provider.js +2 -1
  41. package/esm/mcp/tambo-mcp-provider.js.map +1 -1
  42. package/esm/providers/__tests__/tambo-thread-provider.test.js +2 -6
  43. package/esm/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
  44. package/esm/providers/hooks/use-tambo-session-token.d.ts +2 -2
  45. package/esm/providers/hooks/use-tambo-session-token.js +2 -2
  46. package/esm/providers/hooks/use-tambo-session-token.js.map +1 -1
  47. package/esm/providers/tambo-context-helpers-provider.d.ts +7 -2
  48. package/esm/providers/tambo-context-helpers-provider.d.ts.map +1 -1
  49. package/esm/providers/tambo-context-helpers-provider.js +12 -7
  50. package/esm/providers/tambo-context-helpers-provider.js.map +1 -1
  51. package/esm/providers/tambo-prop-stream-provider/index.d.ts +1 -1
  52. package/esm/providers/tambo-prop-stream-provider/index.js +1 -1
  53. package/esm/providers/tambo-prop-stream-provider/index.js.map +1 -1
  54. package/esm/providers/tambo-provider.d.ts +1 -1
  55. package/esm/providers/tambo-provider.js +1 -1
  56. package/esm/providers/tambo-provider.js.map +1 -1
  57. package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
  58. package/esm/providers/tambo-thread-provider.js +2 -4
  59. package/esm/providers/tambo-thread-provider.js.map +1 -1
  60. package/package.json +16 -19
package/README.md CHANGED
@@ -100,14 +100,38 @@ import { TamboProvider } from "@tambo-ai/react";
100
100
 
101
101
  This is also handled automatically for you if you are using the `MessageThreadFull` component.
102
102
 
103
- For custom components, you can use the `useMessageContext` hook to get the current message.
103
+ For custom components rendered by Tambo, you can use the `useTamboCurrentMessage` hook inside the component to access the current message.
104
+
105
+ _Example (Tambo‑rendered custom component):_
106
+
107
+ ```tsx
108
+ import { useTamboCurrentMessage } from "@tambo-ai/react";
109
+
110
+ export function TamboRenderedMessage() {
111
+ const message = useTamboCurrentMessage();
112
+ return (
113
+ <div>
114
+ <div>Role: {message.role}</div>
115
+ <div>
116
+ {message.content.map((part, i) =>
117
+ part.type === "text" ? (
118
+ <div key={i}>{part.text}</div>
119
+ ) : (
120
+ <div key={i}>Non-text content: {part.type}</div>
121
+ ),
122
+ )}
123
+ </div>
124
+ {/* If Tambo provided rendered output, include it */}
125
+ <div>{message.renderedComponent}</div>
126
+ </div>
127
+ );
128
+ }
129
+ ```
130
+
131
+ If you're rendering your own message list (outside of a Tambo‑rendered component), you can instead pass each `message` as a prop, as shown below:
104
132
 
105
133
  ```tsx
106
- import {
107
- useTambo,
108
- useMessageContext,
109
- type TamboThreadMessage,
110
- } from "@tambo-ai/react";
134
+ import { useTambo, type TamboThreadMessage } from "@tambo-ai/react";
111
135
 
112
136
  function ChatHistory() {
113
137
  const { thread } = useTambo();
@@ -127,11 +151,11 @@ function CustomMessage({ message }: { message: TamboThreadMessage }) {
127
151
  {/* Render the message content */}
128
152
  <div>Role: {message.role}</div>
129
153
  <div>
130
- {message.content.map((part) =>
154
+ {message.content.map((part, i) =>
131
155
  part.type === "text" ? (
132
- <div key={part.id}>{part.text}</div>
156
+ <div key={i}>{part.text}</div>
133
157
  ) : (
134
- <div key={part.id}>Non-text content: {part.type}</div>
158
+ <div key={i}>Non-text content: {part.type}</div>
135
159
  ),
136
160
  )}
137
161
  </div>
@@ -205,9 +229,17 @@ function ChatInterface() {
205
229
  <div>
206
230
  {/* Display messages */}
207
231
  <div>
208
- {thread.messages.map((message, index) => (
209
- <div key={index} className={`message ${message.role}`}>
210
- <div>{message.content}</div>
232
+ {thread.messages.map((message) => (
233
+ <div key={message.id} className={`message ${message.role}`}>
234
+ <div>
235
+ {message.content.map((part, i) =>
236
+ part.type === "text" ? (
237
+ <div key={i}>{part.text}</div>
238
+ ) : (
239
+ <div key={i}>Non-text content: {part.type}</div>
240
+ ),
241
+ )}
242
+ </div>
211
243
  {message.renderedComponent}
212
244
  </div>
213
245
  ))}
@@ -360,11 +392,12 @@ const tools: TamboTool[] = [
360
392
  ```tsx
361
393
  import { TamboProvider } from "@tambo-ai/react";
362
394
  import { TamboMcpProvider } from "@tambo-ai/react/mcp";
395
+ import { MCPTransport } from "@tambo-ai/react/mcp";
363
396
 
364
397
  const mcpServers = [
365
398
  {
366
399
  url: "https://mcp-server-1.com",
367
- transport: "http",
400
+ transport: MCPTransport.HTTP,
368
401
  name: "mcp-server-1",
369
402
  },
370
403
  ];
@@ -374,7 +407,9 @@ const mcpServers = [
374
407
  apiKey={process.env.NEXT_PUBLIC_TAMBO_API_KEY!}
375
408
  components={components}
376
409
  >
377
- <TamboMcpProvider mcpServers={mcpServers}>{children}</TamboMcpProvider>
410
+ <TamboMcpProvider mcpServers={mcpServers}>
411
+ <YourApp />
412
+ </TamboMcpProvider>
378
413
  </TamboProvider>;
379
414
  ```
380
415
 
@@ -162,23 +162,15 @@ describe("useTamboThreadList", () => {
162
162
  "isRefetching": false,
163
163
  "isStale": true,
164
164
  "isSuccess": false,
165
- "promise": Promise {
166
- "reason": [Error: experimental_prefetchInRender feature flag is not enabled],
167
- "status": "rejected",
168
- },
165
+ "promise": Promise {},
169
166
  "refetch": [Function],
170
167
  "status": "pending",
171
168
  }
172
169
  `);
173
- expect(result.current.isLoading).toBe(true);
174
- expect(result.current.data).toBeNull();
175
170
  resolvePromise(mockThreads);
176
171
  await (0, react_1.waitFor)(() => {
177
172
  expect(result.current.isLoading).toBe(false);
178
173
  });
179
- await (0, react_1.waitFor)(() => {
180
- expect(result.current.isLoading).toBe(false);
181
- });
182
174
  });
183
175
  it("should handle error state", async () => {
184
176
  const mockError = new Error("Failed to fetch threads");
@@ -1 +1 @@
1
- {"version":3,"file":"use-tambo-threads.test.js","sourceRoot":"","sources":["../../../src/hooks/__tests__/use-tambo-threads.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { QueryClient } from \"@tanstack/react-query\";\nimport { renderHook, waitFor } from \"@testing-library/react\";\nimport { DeepPartial } from \"ts-essentials\";\nimport {\n useTamboClient,\n useTamboQueryClient,\n} from \"../../providers/tambo-client-provider\";\nimport { PartialTamboAI } from \"../../testing/types\";\nimport { useTamboThreadList } from \"../use-tambo-threads\";\n\njest.mock(\"../../providers/tambo-client-provider\", () => ({\n useTamboClient: jest.fn(),\n useTamboQueryClient: jest.fn(),\n}));\n\ndescribe(\"useTamboThreadList\", () => {\n const mockThreads = [\n { id: \"thread-1\", title: \"Thread 1\" },\n { id: \"thread-2\", title: \"Thread 2\" },\n ];\n\n const mockProjects = {\n getCurrent: jest.fn(),\n retrieve: jest.fn(),\n delete: jest.fn(),\n } satisfies Partial<TamboAI[\"beta\"][\"projects\"]>;\n\n const mockThreadsApi = {\n list: jest.fn(),\n messages: {\n list: jest.fn(),\n create: jest.fn(),\n delete: jest.fn(),\n updateComponentState: jest.fn(),\n },\n suggestions: {\n list: jest.fn(),\n generate: jest.fn(),\n },\n create: jest.fn(),\n retrieve: jest.fn(),\n update: jest.fn(),\n delete: jest.fn(),\n advance: jest.fn(),\n advanceByID: jest.fn(),\n } satisfies DeepPartial<TamboAI[\"beta\"][\"threads\"]>;\n\n const mockBeta = {\n projects: mockProjects,\n threads: mockThreadsApi,\n registry: {\n retrieve: jest.fn(),\n },\n } satisfies PartialTamboAI[\"beta\"];\n\n const mockTamboAI = {\n apiKey: \"\",\n beta: mockBeta,\n } satisfies PartialTamboAI as unknown as TamboAI;\n\n beforeEach(() => {\n jest.mocked(useTamboQueryClient).mockReturnValue(new QueryClient());\n });\n\n it(\"should fetch threads for current project when no projectId is provided\", async () => {\n const mockClient = jest.mocked(useTamboClient);\n mockClient.mockReturnValue({\n ...mockTamboAI,\n beta: {\n ...mockBeta,\n projects: {\n ...mockProjects,\n getCurrent: jest.fn().mockResolvedValue({ id: \"current-project\" }),\n },\n threads: {\n ...mockThreadsApi,\n list: jest.fn().mockResolvedValue(mockThreads),\n },\n },\n } satisfies PartialTamboAI as any);\n\n const { result } = renderHook(() => useTamboThreadList());\n\n await waitFor(() => {\n expect(result.current.data).toEqual(mockThreads);\n });\n });\n\n it(\"should fetch threads for specified projectId\", async () => {\n const mockList = jest.fn().mockResolvedValue(mockThreads);\n const mockClient = jest.mocked(useTamboClient);\n mockClient.mockReturnValue({\n ...mockTamboAI,\n beta: {\n ...mockBeta,\n threads: {\n ...mockThreadsApi,\n list: mockList,\n },\n },\n } satisfies PartialTamboAI as any);\n\n const { result } = renderHook(() =>\n useTamboThreadList({ projectId: \"custom-project\" }),\n );\n\n await waitFor(() => {\n expect(result.current.data).toEqual(mockThreads);\n });\n\n expect(mockList).toHaveBeenCalledWith(\"custom-project\", {});\n });\n\n it(\"should fetch threads with contextKey when provided\", async () => {\n const mockList = jest.fn().mockResolvedValue(mockThreads);\n const mockClient = jest.mocked(useTamboClient);\n mockClient.mockReturnValue({\n ...mockTamboAI,\n beta: {\n ...mockBeta,\n projects: {\n ...mockProjects,\n getCurrent: jest.fn().mockResolvedValue({ id: \"current-project\" }),\n },\n threads: {\n ...mockThreadsApi,\n list: mockList,\n },\n },\n } satisfies PartialTamboAI as any);\n\n const { result } = renderHook(() =>\n useTamboThreadList({ contextKey: \"test-context\" }),\n );\n\n await waitFor(() => {\n expect(result.current.data).toEqual(mockThreads);\n });\n\n expect(mockList).toHaveBeenCalledWith(\"current-project\", {\n contextKey: \"test-context\",\n });\n });\n\n it(\"should handle loading state\", async () => {\n let resolvePromise: (value: any) => void;\n const promise = new Promise((resolve) => {\n resolvePromise = resolve;\n });\n\n const mockClient = jest.mocked(useTamboClient);\n mockClient.mockReturnValue({\n ...mockTamboAI,\n beta: {\n ...mockBeta,\n projects: {\n ...mockProjects,\n getCurrent: jest.fn().mockResolvedValue({ id: \"current-project\" }),\n },\n threads: {\n ...mockThreadsApi,\n list: jest.fn().mockReturnValue(promise),\n },\n },\n } satisfies PartialTamboAI as any);\n\n const { result } = renderHook(() =>\n useTamboThreadList({}, { retry: false }),\n );\n\n expect(result.current).toMatchInlineSnapshot(`\n {\n \"data\": null,\n \"dataUpdatedAt\": 0,\n \"error\": null,\n \"errorUpdateCount\": 0,\n \"errorUpdatedAt\": 0,\n \"failureCount\": 0,\n \"failureReason\": null,\n \"fetchStatus\": \"fetching\",\n \"isEnabled\": true,\n \"isError\": false,\n \"isFetched\": false,\n \"isFetchedAfterMount\": false,\n \"isFetching\": true,\n \"isInitialLoading\": true,\n \"isLoading\": true,\n \"isLoadingError\": false,\n \"isPaused\": false,\n \"isPending\": true,\n \"isPlaceholderData\": false,\n \"isRefetchError\": false,\n \"isRefetching\": false,\n \"isStale\": true,\n \"isSuccess\": false,\n \"promise\": Promise {\n \"reason\": [Error: experimental_prefetchInRender feature flag is not enabled],\n \"status\": \"rejected\",\n },\n \"refetch\": [Function],\n \"status\": \"pending\",\n }\n `);\n expect(result.current.isLoading).toBe(true);\n expect(result.current.data).toBeNull();\n\n resolvePromise!(mockThreads);\n await waitFor(() => {\n expect(result.current.isLoading).toBe(false);\n });\n await waitFor(() => {\n expect(result.current.isLoading).toBe(false);\n });\n });\n\n it(\"should handle error state\", async () => {\n const mockError = new Error(\"Failed to fetch threads\");\n const mockClient = jest.mocked(useTamboClient);\n mockClient.mockReturnValue({\n ...mockTamboAI,\n beta: {\n ...mockBeta,\n projects: {\n ...mockProjects,\n getCurrent: jest.fn().mockResolvedValue({ id: \"current-project\" }),\n },\n threads: {\n ...mockThreadsApi,\n list: jest.fn().mockImplementation(async () => {\n // console.log(\"Mocking error\", mockCount++);\n throw mockError;\n }),\n },\n },\n } satisfies PartialTamboAI as any);\n\n const { result } = renderHook(() =>\n useTamboThreadList({}, { retry: false }),\n );\n\n await waitFor(() => {\n const { isLoading, error, isError } = result.current;\n expect(isLoading).toBe(false);\n expect(isError).toBe(true);\n expect(error).toBe(mockError);\n });\n });\n});\n"]}
1
+ {"version":3,"file":"use-tambo-threads.test.js","sourceRoot":"","sources":["../../../src/hooks/__tests__/use-tambo-threads.test.tsx"],"names":[],"mappings":";;AACA,uDAAoD;AACpD,kDAA6D;AAE7D,iFAG+C;AAE/C,4DAA0D;AAE1D,IAAI,CAAC,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE,CAAC,CAAC;IACxD,cAAc,EAAE,IAAI,CAAC,EAAE,EAAE;IACzB,mBAAmB,EAAE,IAAI,CAAC,EAAE,EAAE;CAC/B,CAAC,CAAC,CAAC;AAEJ,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,MAAM,WAAW,GAAG;QAClB,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE;QACrC,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE;KACtC,CAAC;IAEF,MAAM,YAAY,GAAG;QACnB,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE;QACrB,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE;QACnB,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE;KAC6B,CAAC;IAEjD,MAAM,cAAc,GAAG;QACrB,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;QACf,QAAQ,EAAE;YACR,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;YACf,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE;YACjB,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE;YACjB,oBAAoB,EAAE,IAAI,CAAC,EAAE,EAAE;SAChC;QACD,WAAW,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;YACf,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE;SACpB;QACD,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE;QACjB,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE;QACnB,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE;QACjB,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE;QACjB,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE;QAClB,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE;KAC2B,CAAC;IAEpD,MAAM,QAAQ,GAAG;QACf,QAAQ,EAAE,YAAY;QACtB,OAAO,EAAE,cAAc;QACvB,QAAQ,EAAE;YACR,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE;SACpB;KAC+B,CAAC;IAEnC,MAAM,WAAW,GAAG;QAClB,MAAM,EAAE,EAAE;QACV,IAAI,EAAE,QAAQ;KACgC,CAAC;IAEjD,UAAU,CAAC,GAAG,EAAE;QACd,IAAI,CAAC,MAAM,CAAC,2CAAmB,CAAC,CAAC,eAAe,CAAC,IAAI,yBAAW,EAAE,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,sCAAc,CAAC,CAAC;QAC/C,UAAU,CAAC,eAAe,CAAC;YACzB,GAAG,WAAW;YACd,IAAI,EAAE;gBACJ,GAAG,QAAQ;gBACX,QAAQ,EAAE;oBACR,GAAG,YAAY;oBACf,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,EAAE,iBAAiB,EAAE,CAAC;iBACnE;gBACD,OAAO,EAAE;oBACP,GAAG,cAAc;oBACjB,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,WAAW,CAAC;iBAC/C;aACF;SAC8B,CAAC,CAAC;QAEnC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CAAC,IAAA,sCAAkB,GAAE,CAAC,CAAC;QAE1D,MAAM,IAAA,eAAO,EAAC,GAAG,EAAE;YACjB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,sCAAc,CAAC,CAAC;QAC/C,UAAU,CAAC,eAAe,CAAC;YACzB,GAAG,WAAW;YACd,IAAI,EAAE;gBACJ,GAAG,QAAQ;gBACX,OAAO,EAAE;oBACP,GAAG,cAAc;oBACjB,IAAI,EAAE,QAAQ;iBACf;aACF;SAC8B,CAAC,CAAC;QAEnC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,sCAAkB,EAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CACpD,CAAC;QAEF,MAAM,IAAA,eAAO,EAAC,GAAG,EAAE;YACjB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,sCAAc,CAAC,CAAC;QAC/C,UAAU,CAAC,eAAe,CAAC;YACzB,GAAG,WAAW;YACd,IAAI,EAAE;gBACJ,GAAG,QAAQ;gBACX,QAAQ,EAAE;oBACR,GAAG,YAAY;oBACf,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,EAAE,iBAAiB,EAAE,CAAC;iBACnE;gBACD,OAAO,EAAE;oBACP,GAAG,cAAc;oBACjB,IAAI,EAAE,QAAQ;iBACf;aACF;SAC8B,CAAC,CAAC;QAEnC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,sCAAkB,EAAC,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC,CACnD,CAAC;QAEF,MAAM,IAAA,eAAO,EAAC,GAAG,EAAE;YACjB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,iBAAiB,EAAE;YACvD,UAAU,EAAE,cAAc;SAC3B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC3C,IAAI,cAAoC,CAAC;QACzC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YACtC,cAAc,GAAG,OAAO,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,sCAAc,CAAC,CAAC;QAC/C,UAAU,CAAC,eAAe,CAAC;YACzB,GAAG,WAAW;YACd,IAAI,EAAE;gBACJ,GAAG,QAAQ;gBACX,QAAQ,EAAE;oBACR,GAAG,YAAY;oBACf,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,EAAE,iBAAiB,EAAE,CAAC;iBACnE;gBACD,OAAO,EAAE;oBACP,GAAG,cAAc;oBACjB,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC;iBACzC;aACF;SAC8B,CAAC,CAAC;QAEnC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,sCAAkB,EAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CACzC,CAAC;QAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA6B5C,CAAC,CAAC;QAEH,cAAe,CAAC,WAAW,CAAC,CAAC;QAC7B,MAAM,IAAA,eAAO,EAAC,GAAG,EAAE;YACjB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,SAAS,GAAG,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACvD,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,sCAAc,CAAC,CAAC;QAC/C,UAAU,CAAC,eAAe,CAAC;YACzB,GAAG,WAAW;YACd,IAAI,EAAE;gBACJ,GAAG,QAAQ;gBACX,QAAQ,EAAE;oBACR,GAAG,YAAY;oBACf,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,EAAE,iBAAiB,EAAE,CAAC;iBACnE;gBACD,OAAO,EAAE;oBACP,GAAG,cAAc;oBACjB,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,KAAK,IAAI,EAAE;wBAC5C,6CAA6C;wBAC7C,MAAM,SAAS,CAAC;oBAClB,CAAC,CAAC;iBACH;aACF;SAC8B,CAAC,CAAC;QAEnC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,kBAAU,EAAC,GAAG,EAAE,CACjC,IAAA,sCAAkB,EAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CACzC,CAAC;QAEF,MAAM,IAAA,eAAO,EAAC,GAAG,EAAE;YACjB,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC;YACrD,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC9B,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3B,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { QueryClient } from \"@tanstack/react-query\";\nimport { renderHook, waitFor } from \"@testing-library/react\";\nimport { DeepPartial } from \"ts-essentials\";\nimport {\n useTamboClient,\n useTamboQueryClient,\n} from \"../../providers/tambo-client-provider\";\nimport { PartialTamboAI } from \"../../testing/types\";\nimport { useTamboThreadList } from \"../use-tambo-threads\";\n\njest.mock(\"../../providers/tambo-client-provider\", () => ({\n useTamboClient: jest.fn(),\n useTamboQueryClient: jest.fn(),\n}));\n\ndescribe(\"useTamboThreadList\", () => {\n const mockThreads = [\n { id: \"thread-1\", title: \"Thread 1\" },\n { id: \"thread-2\", title: \"Thread 2\" },\n ];\n\n const mockProjects = {\n getCurrent: jest.fn(),\n retrieve: jest.fn(),\n delete: jest.fn(),\n } satisfies Partial<TamboAI[\"beta\"][\"projects\"]>;\n\n const mockThreadsApi = {\n list: jest.fn(),\n messages: {\n list: jest.fn(),\n create: jest.fn(),\n delete: jest.fn(),\n updateComponentState: jest.fn(),\n },\n suggestions: {\n list: jest.fn(),\n generate: jest.fn(),\n },\n create: jest.fn(),\n retrieve: jest.fn(),\n update: jest.fn(),\n delete: jest.fn(),\n advance: jest.fn(),\n advanceByID: jest.fn(),\n } satisfies DeepPartial<TamboAI[\"beta\"][\"threads\"]>;\n\n const mockBeta = {\n projects: mockProjects,\n threads: mockThreadsApi,\n registry: {\n retrieve: jest.fn(),\n },\n } satisfies PartialTamboAI[\"beta\"];\n\n const mockTamboAI = {\n apiKey: \"\",\n beta: mockBeta,\n } satisfies PartialTamboAI as unknown as TamboAI;\n\n beforeEach(() => {\n jest.mocked(useTamboQueryClient).mockReturnValue(new QueryClient());\n });\n\n it(\"should fetch threads for current project when no projectId is provided\", async () => {\n const mockClient = jest.mocked(useTamboClient);\n mockClient.mockReturnValue({\n ...mockTamboAI,\n beta: {\n ...mockBeta,\n projects: {\n ...mockProjects,\n getCurrent: jest.fn().mockResolvedValue({ id: \"current-project\" }),\n },\n threads: {\n ...mockThreadsApi,\n list: jest.fn().mockResolvedValue(mockThreads),\n },\n },\n } satisfies PartialTamboAI as any);\n\n const { result } = renderHook(() => useTamboThreadList());\n\n await waitFor(() => {\n expect(result.current.data).toEqual(mockThreads);\n });\n });\n\n it(\"should fetch threads for specified projectId\", async () => {\n const mockList = jest.fn().mockResolvedValue(mockThreads);\n const mockClient = jest.mocked(useTamboClient);\n mockClient.mockReturnValue({\n ...mockTamboAI,\n beta: {\n ...mockBeta,\n threads: {\n ...mockThreadsApi,\n list: mockList,\n },\n },\n } satisfies PartialTamboAI as any);\n\n const { result } = renderHook(() =>\n useTamboThreadList({ projectId: \"custom-project\" }),\n );\n\n await waitFor(() => {\n expect(result.current.data).toEqual(mockThreads);\n });\n\n expect(mockList).toHaveBeenCalledWith(\"custom-project\", {});\n });\n\n it(\"should fetch threads with contextKey when provided\", async () => {\n const mockList = jest.fn().mockResolvedValue(mockThreads);\n const mockClient = jest.mocked(useTamboClient);\n mockClient.mockReturnValue({\n ...mockTamboAI,\n beta: {\n ...mockBeta,\n projects: {\n ...mockProjects,\n getCurrent: jest.fn().mockResolvedValue({ id: \"current-project\" }),\n },\n threads: {\n ...mockThreadsApi,\n list: mockList,\n },\n },\n } satisfies PartialTamboAI as any);\n\n const { result } = renderHook(() =>\n useTamboThreadList({ contextKey: \"test-context\" }),\n );\n\n await waitFor(() => {\n expect(result.current.data).toEqual(mockThreads);\n });\n\n expect(mockList).toHaveBeenCalledWith(\"current-project\", {\n contextKey: \"test-context\",\n });\n });\n\n it(\"should handle loading state\", async () => {\n let resolvePromise: (value: any) => void;\n const promise = new Promise((resolve) => {\n resolvePromise = resolve;\n });\n\n const mockClient = jest.mocked(useTamboClient);\n mockClient.mockReturnValue({\n ...mockTamboAI,\n beta: {\n ...mockBeta,\n projects: {\n ...mockProjects,\n getCurrent: jest.fn().mockResolvedValue({ id: \"current-project\" }),\n },\n threads: {\n ...mockThreadsApi,\n list: jest.fn().mockReturnValue(promise),\n },\n },\n } satisfies PartialTamboAI as any);\n\n const { result } = renderHook(() =>\n useTamboThreadList({}, { retry: false }),\n );\n\n expect(result.current).toMatchInlineSnapshot(`\n {\n \"data\": null,\n \"dataUpdatedAt\": 0,\n \"error\": null,\n \"errorUpdateCount\": 0,\n \"errorUpdatedAt\": 0,\n \"failureCount\": 0,\n \"failureReason\": null,\n \"fetchStatus\": \"fetching\",\n \"isEnabled\": true,\n \"isError\": false,\n \"isFetched\": false,\n \"isFetchedAfterMount\": false,\n \"isFetching\": true,\n \"isInitialLoading\": true,\n \"isLoading\": true,\n \"isLoadingError\": false,\n \"isPaused\": false,\n \"isPending\": true,\n \"isPlaceholderData\": false,\n \"isRefetchError\": false,\n \"isRefetching\": false,\n \"isStale\": true,\n \"isSuccess\": false,\n \"promise\": Promise {},\n \"refetch\": [Function],\n \"status\": \"pending\",\n }\n `);\n\n resolvePromise!(mockThreads);\n await waitFor(() => {\n expect(result.current.isLoading).toBe(false);\n });\n });\n\n it(\"should handle error state\", async () => {\n const mockError = new Error(\"Failed to fetch threads\");\n const mockClient = jest.mocked(useTamboClient);\n mockClient.mockReturnValue({\n ...mockTamboAI,\n beta: {\n ...mockBeta,\n projects: {\n ...mockProjects,\n getCurrent: jest.fn().mockResolvedValue({ id: \"current-project\" }),\n },\n threads: {\n ...mockThreadsApi,\n list: jest.fn().mockImplementation(async () => {\n // console.log(\"Mocking error\", mockCount++);\n throw mockError;\n }),\n },\n },\n } satisfies PartialTamboAI as any);\n\n const { result } = renderHook(() =>\n useTamboThreadList({}, { retry: false }),\n );\n\n await waitFor(() => {\n const { isLoading, error, isError } = result.current;\n expect(isLoading).toBe(false);\n expect(isError).toBe(true);\n expect(error).toBe(mockError);\n });\n });\n});\n"]}
@@ -8,6 +8,8 @@ jest.mock("@modelcontextprotocol/sdk/client/index.js", () => ({
8
8
  close: jest.fn(),
9
9
  listTools: jest.fn(),
10
10
  callTool: jest.fn(),
11
+ setRequestHandler: jest.fn(),
12
+ removeRequestHandler: jest.fn(),
11
13
  onclose: null,
12
14
  })),
13
15
  }));
@@ -41,6 +43,8 @@ describe("MCPClient", () => {
41
43
  close: jest.fn().mockResolvedValue(undefined),
42
44
  listTools: jest.fn(),
43
45
  callTool: jest.fn(),
46
+ setRequestHandler: jest.fn(),
47
+ removeRequestHandler: jest.fn(),
44
48
  onclose: null,
45
49
  };
46
50
  mockTransportInstance = {
@@ -55,18 +59,18 @@ describe("MCPClient", () => {
55
59
  it("should create and connect an MCPClient with HTTP transport by default", async () => {
56
60
  const endpoint = "https://api.example.com/mcp";
57
61
  const headers = { Authorization: "Bearer token" };
58
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, headers);
62
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, headers, undefined, undefined);
59
63
  expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(new URL(endpoint), { sessionId: undefined, requestInit: { headers } });
60
64
  expect(MockedClient).toHaveBeenCalledWith({
61
65
  name: "tambo-mcp-client",
62
66
  version: "1.0.0",
63
- });
67
+ }, { capabilities: {} });
64
68
  expect(mockClientInstance.connect).toHaveBeenCalledWith(mockTransportInstance);
65
69
  expect(client).toBeInstanceOf(mcp_client_1.MCPClient);
66
70
  });
67
71
  it("should create and connect an MCPClient with SSE transport", async () => {
68
72
  const endpoint = "https://api.example.com/mcp";
69
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.SSE);
73
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.SSE, undefined, undefined, undefined);
70
74
  expect(MockedSSEClientTransport).toHaveBeenCalledWith(new URL(endpoint), {
71
75
  requestInit: { headers: {} },
72
76
  });
@@ -75,14 +79,14 @@ describe("MCPClient", () => {
75
79
  });
76
80
  it("should create client with default headers when none provided", async () => {
77
81
  const endpoint = "https://api.example.com/mcp";
78
- await mcp_client_1.MCPClient.create(endpoint);
82
+ await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
79
83
  expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(new URL(endpoint), { sessionId: undefined, requestInit: { headers: {} } });
80
84
  });
81
85
  });
82
86
  describe("reconnect", () => {
83
87
  it("should create new transport and client instances and call connect when reconnect() is called (default behavior)", async () => {
84
88
  const endpoint = "https://api.example.com/mcp";
85
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
89
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
86
90
  // Clear previous calls to focus on reconnect behavior
87
91
  jest.clearAllMocks();
88
92
  // Create new mock instances to verify new instances are created
@@ -108,13 +112,13 @@ describe("MCPClient", () => {
108
112
  expect(MockedClient).toHaveBeenCalledWith({
109
113
  name: "tambo-mcp-client",
110
114
  version: "1.0.0",
111
- });
115
+ }, { capabilities: {} });
112
116
  // Verify new client's connect was called with new transport
113
117
  expect(newMockClientInstance.connect).toHaveBeenCalledWith(newMockTransportInstance);
114
118
  });
115
119
  it("should reconnect without session ID for SSE transport", async () => {
116
120
  const endpoint = "https://api.example.com/mcp";
117
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.SSE);
121
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.SSE, undefined, undefined, undefined);
118
122
  // Clear previous calls
119
123
  jest.clearAllMocks();
120
124
  await client.reconnect();
@@ -126,7 +130,7 @@ describe("MCPClient", () => {
126
130
  });
127
131
  it("should handle close errors when reportErrorOnClose is true", async () => {
128
132
  const endpoint = "https://api.example.com/mcp";
129
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
133
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
130
134
  const consoleSpy = jest.spyOn(console, "error").mockImplementation();
131
135
  // Make close throw an error
132
136
  mockClientInstance.close.mockRejectedValue(new Error("Close failed"));
@@ -136,7 +140,7 @@ describe("MCPClient", () => {
136
140
  });
137
141
  it("should not log close errors when reportErrorOnClose is false", async () => {
138
142
  const endpoint = "https://api.example.com/mcp";
139
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
143
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
140
144
  const consoleSpy = jest.spyOn(console, "error").mockImplementation();
141
145
  // Make close throw an error
142
146
  mockClientInstance.close.mockRejectedValue(new Error("Close failed"));
@@ -146,7 +150,7 @@ describe("MCPClient", () => {
146
150
  });
147
151
  it("should create new session when newSession is true", async () => {
148
152
  const endpoint = "https://api.example.com/mcp";
149
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
153
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
150
154
  // Clear previous calls to focus on reconnect behavior
151
155
  jest.clearAllMocks();
152
156
  // Create new mock instances to verify new instances are created
@@ -172,13 +176,13 @@ describe("MCPClient", () => {
172
176
  expect(MockedClient).toHaveBeenCalledWith({
173
177
  name: "tambo-mcp-client",
174
178
  version: "1.0.0",
175
- });
179
+ }, { capabilities: {} });
176
180
  // Verify new client's connect was called with new transport
177
181
  expect(newMockClientInstance.connect).toHaveBeenCalledWith(newMockTransportInstance);
178
182
  });
179
183
  it("should reuse existing session when newSession is false (default)", async () => {
180
184
  const endpoint = "https://api.example.com/mcp";
181
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
185
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
182
186
  // Clear previous calls to focus on reconnect behavior
183
187
  jest.clearAllMocks();
184
188
  // Create new mock instances to verify new instances are created
@@ -204,7 +208,7 @@ describe("MCPClient", () => {
204
208
  expect(MockedClient).toHaveBeenCalledWith({
205
209
  name: "tambo-mcp-client",
206
210
  version: "1.0.0",
207
- });
211
+ }, { capabilities: {} });
208
212
  // Verify new client's connect was called with new transport
209
213
  expect(newMockClientInstance.connect).toHaveBeenCalledWith(newMockTransportInstance);
210
214
  });
@@ -212,7 +216,7 @@ describe("MCPClient", () => {
212
216
  describe("onclose", () => {
213
217
  it("should reconnect MCPClient when client is closed by external means (no backoff on manual preemption)", async () => {
214
218
  const endpoint = "https://api.example.com/mcp";
215
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
219
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
216
220
  jest.useFakeTimers();
217
221
  const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
218
222
  // Create new mock instances to verify reconnection creates new instances
@@ -247,7 +251,7 @@ describe("MCPClient", () => {
247
251
  expect(MockedClient).toHaveBeenCalledWith({
248
252
  name: "tambo-mcp-client",
249
253
  version: "1.0.0",
250
- });
254
+ }, { capabilities: {} });
251
255
  // Verify new client's connect was called with new transport
252
256
  expect(newMockClientInstance.connect).toHaveBeenCalledWith(newMockTransportInstance);
253
257
  // Ensure only a single reconnect attempt occurred
@@ -260,7 +264,7 @@ describe("MCPClient", () => {
260
264
  describe("reconnect re-entrancy and single-flight", () => {
261
265
  it("prevents re-entrant onclose during deliberate close and coalesces concurrent calls", async () => {
262
266
  const endpoint = "https://api.example.com/mcp";
263
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
267
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
264
268
  // Simulate an implementation where closing the client would call its own onclose handler
265
269
  const closeImpl = jest.fn(async () => {
266
270
  if (typeof mockClientInstance.onclose === "function") {
@@ -302,7 +306,7 @@ describe("MCPClient", () => {
302
306
  const setTimeoutSpy = jest.spyOn(global, "setTimeout");
303
307
  const randSpy = jest.spyOn(Math, "random").mockReturnValue(0.0); // extreme low jitter
304
308
  const endpoint = "https://api.example.com/mcp";
305
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
309
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
306
310
  // Prepare one attempt that will succeed to avoid rescheduling
307
311
  MockedClient.mockImplementation(() => ({
308
312
  connect: jest.fn().mockResolvedValue(undefined),
@@ -340,7 +344,7 @@ describe("MCPClient", () => {
340
344
  describe("listTools", () => {
341
345
  it("should list all tools with pagination", async () => {
342
346
  const endpoint = "https://api.example.com/mcp";
343
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
347
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
344
348
  const mockTools = [
345
349
  {
346
350
  name: "tool1",
@@ -395,7 +399,7 @@ describe("MCPClient", () => {
395
399
  });
396
400
  it("should handle single page of tools", async () => {
397
401
  const endpoint = "https://api.example.com/mcp";
398
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
402
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
399
403
  const mockTools = [
400
404
  {
401
405
  name: "tool1",
@@ -426,7 +430,7 @@ describe("MCPClient", () => {
426
430
  });
427
431
  it("should throw error for invalid input schema", async () => {
428
432
  const endpoint = "https://api.example.com/mcp";
429
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
433
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
430
434
  const mockTools = [
431
435
  {
432
436
  name: "invalid-tool",
@@ -445,7 +449,7 @@ describe("MCPClient", () => {
445
449
  describe("callTool", () => {
446
450
  it("should call a tool with arguments", async () => {
447
451
  const endpoint = "https://api.example.com/mcp";
448
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
452
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
449
453
  const mockResult = { success: true, data: "test result" };
450
454
  mockClientInstance.callTool.mockResolvedValue(mockResult);
451
455
  const result = await client.callTool("testTool", {
@@ -460,7 +464,7 @@ describe("MCPClient", () => {
460
464
  });
461
465
  it("should handle tool call errors", async () => {
462
466
  const endpoint = "https://api.example.com/mcp";
463
- const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
467
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
464
468
  const error = new Error("Tool call failed");
465
469
  mockClientInstance.callTool.mockRejectedValue(error);
466
470
  await expect(client.callTool("testTool", {})).rejects.toThrow("Tool call failed");
@@ -470,13 +474,13 @@ describe("MCPClient", () => {
470
474
  it("should initialize HTTP transport with session ID", async () => {
471
475
  const endpoint = "https://api.example.com/mcp";
472
476
  const headers = { Authorization: "Bearer token" };
473
- await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, headers);
477
+ await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, headers, undefined, undefined);
474
478
  expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(new URL(endpoint), { sessionId: undefined, requestInit: { headers } });
475
479
  });
476
480
  it("should initialize SSE transport without session ID", async () => {
477
481
  const endpoint = "https://api.example.com/mcp";
478
482
  const headers = { Authorization: "Bearer token" };
479
- await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.SSE, headers);
483
+ await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.SSE, headers, undefined, undefined);
480
484
  expect(MockedSSEClientTransport).toHaveBeenCalledWith(new URL(endpoint), {
481
485
  requestInit: { headers },
482
486
  });
@@ -485,18 +489,60 @@ describe("MCPClient", () => {
485
489
  describe("client initialization", () => {
486
490
  it("should initialize client with correct name and version", async () => {
487
491
  const endpoint = "https://api.example.com/mcp";
488
- await mcp_client_1.MCPClient.create(endpoint);
492
+ await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
489
493
  expect(MockedClient).toHaveBeenCalledWith({
490
494
  name: "tambo-mcp-client",
491
495
  version: "1.0.0",
492
- });
496
+ }, { capabilities: {} });
493
497
  });
494
498
  it("should set onclose handler", async () => {
495
499
  const endpoint = "https://api.example.com/mcp";
496
- const _client = await mcp_client_1.MCPClient.create(endpoint);
500
+ const _client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
497
501
  expect(mockClientInstance.onclose).toBeDefined();
498
502
  expect(typeof mockClientInstance.onclose).toBe("function");
499
503
  });
500
504
  });
505
+ describe("handlers (elicitation/sampling)", () => {
506
+ it("sets handlers on create when provided", async () => {
507
+ const endpoint = "https://api.example.com/mcp";
508
+ const elicitation = jest.fn(async () => ({}));
509
+ const sampling = jest.fn(async () => ({}));
510
+ await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined, { elicitation, sampling });
511
+ expect(MockedClient).toHaveBeenLastCalledWith({
512
+ name: "tambo-mcp-client",
513
+ version: "1.0.0",
514
+ }, {
515
+ capabilities: {
516
+ elicitation: {},
517
+ sampling: {},
518
+ },
519
+ });
520
+ // Request handlers should be set for both
521
+ expect(mockClientInstance.setRequestHandler).toHaveBeenCalled();
522
+ expect(mockClientInstance.setRequestHandler.mock.calls.length).toBeGreaterThanOrEqual(2);
523
+ });
524
+ it("removes elicitation handler when set to undefined", async () => {
525
+ const endpoint = "https://api.example.com/mcp";
526
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined, {
527
+ elicitation: async () => ({}),
528
+ });
529
+ const removeSpy = mockClientInstance.removeRequestHandler;
530
+ // then remove
531
+ removeSpy.mockClear();
532
+ await client.updateElicitationHandler(undefined);
533
+ expect(removeSpy).toHaveBeenCalledWith(expect.any(String));
534
+ });
535
+ it("removes sampling handler when set to undefined", async () => {
536
+ const endpoint = "https://api.example.com/mcp";
537
+ const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined, {
538
+ sampling: async () => ({}),
539
+ });
540
+ const removeSpy = mockClientInstance.removeRequestHandler;
541
+ // then remove
542
+ removeSpy.mockClear();
543
+ await client.updateSamplingHandler(undefined);
544
+ expect(removeSpy).toHaveBeenCalledWith(expect.any(String));
545
+ });
546
+ });
501
547
  });
502
548
  //# sourceMappingURL=mcp-client.test.js.map