@tambo-ai/react 0.54.1 → 0.55.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/README.md +49 -14
- package/dist/hooks/__tests__/use-tambo-threads.test.js +1 -9
- package/dist/hooks/__tests__/use-tambo-threads.test.js.map +1 -1
- package/dist/mcp/__tests__/mcp-client.test.js +21 -21
- package/dist/mcp/__tests__/mcp-client.test.js.map +1 -1
- package/dist/mcp/mcp-client.d.ts +61 -210
- package/dist/mcp/mcp-client.d.ts.map +1 -1
- package/dist/mcp/mcp-client.js +43 -17
- package/dist/mcp/mcp-client.js.map +1 -1
- package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/dist/mcp/tambo-mcp-provider.js +2 -1
- package/dist/mcp/tambo-mcp-provider.js.map +1 -1
- package/dist/providers/hooks/use-tambo-session-token.d.ts +2 -2
- package/dist/providers/hooks/use-tambo-session-token.js +2 -2
- package/dist/providers/hooks/use-tambo-session-token.js.map +1 -1
- package/dist/providers/tambo-context-helpers-provider.d.ts +7 -2
- package/dist/providers/tambo-context-helpers-provider.d.ts.map +1 -1
- package/dist/providers/tambo-context-helpers-provider.js +12 -7
- package/dist/providers/tambo-context-helpers-provider.js.map +1 -1
- package/dist/providers/tambo-prop-stream-provider/index.d.ts +1 -1
- package/dist/providers/tambo-prop-stream-provider/index.js +1 -1
- package/dist/providers/tambo-prop-stream-provider/index.js.map +1 -1
- package/dist/providers/tambo-provider.d.ts +1 -1
- package/dist/providers/tambo-provider.js +1 -1
- package/dist/providers/tambo-provider.js.map +1 -1
- package/esm/hooks/__tests__/use-tambo-threads.test.js +1 -9
- package/esm/hooks/__tests__/use-tambo-threads.test.js.map +1 -1
- package/esm/mcp/__tests__/mcp-client.test.js +21 -21
- package/esm/mcp/__tests__/mcp-client.test.js.map +1 -1
- package/esm/mcp/mcp-client.d.ts +61 -210
- package/esm/mcp/mcp-client.d.ts.map +1 -1
- package/esm/mcp/mcp-client.js +43 -17
- package/esm/mcp/mcp-client.js.map +1 -1
- package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/esm/mcp/tambo-mcp-provider.js +2 -1
- package/esm/mcp/tambo-mcp-provider.js.map +1 -1
- package/esm/providers/hooks/use-tambo-session-token.d.ts +2 -2
- package/esm/providers/hooks/use-tambo-session-token.js +2 -2
- package/esm/providers/hooks/use-tambo-session-token.js.map +1 -1
- package/esm/providers/tambo-context-helpers-provider.d.ts +7 -2
- package/esm/providers/tambo-context-helpers-provider.d.ts.map +1 -1
- package/esm/providers/tambo-context-helpers-provider.js +12 -7
- package/esm/providers/tambo-context-helpers-provider.js.map +1 -1
- package/esm/providers/tambo-prop-stream-provider/index.d.ts +1 -1
- package/esm/providers/tambo-prop-stream-provider/index.js +1 -1
- package/esm/providers/tambo-prop-stream-provider/index.js.map +1 -1
- package/esm/providers/tambo-provider.d.ts +1 -1
- package/esm/providers/tambo-provider.js +1 -1
- package/esm/providers/tambo-provider.js.map +1 -1
- package/package.json +11 -11
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 `
|
|
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={
|
|
156
|
+
<div key={i}>{part.text}</div>
|
|
133
157
|
) : (
|
|
134
|
-
<div key={
|
|
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
|
|
209
|
-
<div key={
|
|
210
|
-
<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:
|
|
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}>
|
|
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":"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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 \"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"]}
|
|
@@ -55,7 +55,7 @@ describe("MCPClient", () => {
|
|
|
55
55
|
it("should create and connect an MCPClient with HTTP transport by default", async () => {
|
|
56
56
|
const endpoint = "https://api.example.com/mcp";
|
|
57
57
|
const headers = { Authorization: "Bearer token" };
|
|
58
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, headers);
|
|
58
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, headers, undefined, undefined);
|
|
59
59
|
expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(new URL(endpoint), { sessionId: undefined, requestInit: { headers } });
|
|
60
60
|
expect(MockedClient).toHaveBeenCalledWith({
|
|
61
61
|
name: "tambo-mcp-client",
|
|
@@ -66,7 +66,7 @@ describe("MCPClient", () => {
|
|
|
66
66
|
});
|
|
67
67
|
it("should create and connect an MCPClient with SSE transport", async () => {
|
|
68
68
|
const endpoint = "https://api.example.com/mcp";
|
|
69
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.SSE);
|
|
69
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.SSE, undefined, undefined, undefined);
|
|
70
70
|
expect(MockedSSEClientTransport).toHaveBeenCalledWith(new URL(endpoint), {
|
|
71
71
|
requestInit: { headers: {} },
|
|
72
72
|
});
|
|
@@ -75,14 +75,14 @@ describe("MCPClient", () => {
|
|
|
75
75
|
});
|
|
76
76
|
it("should create client with default headers when none provided", async () => {
|
|
77
77
|
const endpoint = "https://api.example.com/mcp";
|
|
78
|
-
await mcp_client_1.MCPClient.create(endpoint);
|
|
78
|
+
await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
79
79
|
expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(new URL(endpoint), { sessionId: undefined, requestInit: { headers: {} } });
|
|
80
80
|
});
|
|
81
81
|
});
|
|
82
82
|
describe("reconnect", () => {
|
|
83
83
|
it("should create new transport and client instances and call connect when reconnect() is called (default behavior)", async () => {
|
|
84
84
|
const endpoint = "https://api.example.com/mcp";
|
|
85
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
85
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
86
86
|
// Clear previous calls to focus on reconnect behavior
|
|
87
87
|
jest.clearAllMocks();
|
|
88
88
|
// Create new mock instances to verify new instances are created
|
|
@@ -114,7 +114,7 @@ describe("MCPClient", () => {
|
|
|
114
114
|
});
|
|
115
115
|
it("should reconnect without session ID for SSE transport", async () => {
|
|
116
116
|
const endpoint = "https://api.example.com/mcp";
|
|
117
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.SSE);
|
|
117
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.SSE, undefined, undefined, undefined);
|
|
118
118
|
// Clear previous calls
|
|
119
119
|
jest.clearAllMocks();
|
|
120
120
|
await client.reconnect();
|
|
@@ -126,7 +126,7 @@ describe("MCPClient", () => {
|
|
|
126
126
|
});
|
|
127
127
|
it("should handle close errors when reportErrorOnClose is true", async () => {
|
|
128
128
|
const endpoint = "https://api.example.com/mcp";
|
|
129
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
129
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
130
130
|
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
|
|
131
131
|
// Make close throw an error
|
|
132
132
|
mockClientInstance.close.mockRejectedValue(new Error("Close failed"));
|
|
@@ -136,7 +136,7 @@ describe("MCPClient", () => {
|
|
|
136
136
|
});
|
|
137
137
|
it("should not log close errors when reportErrorOnClose is false", async () => {
|
|
138
138
|
const endpoint = "https://api.example.com/mcp";
|
|
139
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
139
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
140
140
|
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
|
|
141
141
|
// Make close throw an error
|
|
142
142
|
mockClientInstance.close.mockRejectedValue(new Error("Close failed"));
|
|
@@ -146,7 +146,7 @@ describe("MCPClient", () => {
|
|
|
146
146
|
});
|
|
147
147
|
it("should create new session when newSession is true", async () => {
|
|
148
148
|
const endpoint = "https://api.example.com/mcp";
|
|
149
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
149
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
150
150
|
// Clear previous calls to focus on reconnect behavior
|
|
151
151
|
jest.clearAllMocks();
|
|
152
152
|
// Create new mock instances to verify new instances are created
|
|
@@ -178,7 +178,7 @@ describe("MCPClient", () => {
|
|
|
178
178
|
});
|
|
179
179
|
it("should reuse existing session when newSession is false (default)", async () => {
|
|
180
180
|
const endpoint = "https://api.example.com/mcp";
|
|
181
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
181
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
182
182
|
// Clear previous calls to focus on reconnect behavior
|
|
183
183
|
jest.clearAllMocks();
|
|
184
184
|
// Create new mock instances to verify new instances are created
|
|
@@ -212,7 +212,7 @@ describe("MCPClient", () => {
|
|
|
212
212
|
describe("onclose", () => {
|
|
213
213
|
it("should reconnect MCPClient when client is closed by external means (no backoff on manual preemption)", async () => {
|
|
214
214
|
const endpoint = "https://api.example.com/mcp";
|
|
215
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
215
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
216
216
|
jest.useFakeTimers();
|
|
217
217
|
const consoleSpy = jest.spyOn(console, "warn").mockImplementation();
|
|
218
218
|
// Create new mock instances to verify reconnection creates new instances
|
|
@@ -260,7 +260,7 @@ describe("MCPClient", () => {
|
|
|
260
260
|
describe("reconnect re-entrancy and single-flight", () => {
|
|
261
261
|
it("prevents re-entrant onclose during deliberate close and coalesces concurrent calls", async () => {
|
|
262
262
|
const endpoint = "https://api.example.com/mcp";
|
|
263
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
263
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
264
264
|
// Simulate an implementation where closing the client would call its own onclose handler
|
|
265
265
|
const closeImpl = jest.fn(async () => {
|
|
266
266
|
if (typeof mockClientInstance.onclose === "function") {
|
|
@@ -302,7 +302,7 @@ describe("MCPClient", () => {
|
|
|
302
302
|
const setTimeoutSpy = jest.spyOn(global, "setTimeout");
|
|
303
303
|
const randSpy = jest.spyOn(Math, "random").mockReturnValue(0.0); // extreme low jitter
|
|
304
304
|
const endpoint = "https://api.example.com/mcp";
|
|
305
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
305
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
306
306
|
// Prepare one attempt that will succeed to avoid rescheduling
|
|
307
307
|
MockedClient.mockImplementation(() => ({
|
|
308
308
|
connect: jest.fn().mockResolvedValue(undefined),
|
|
@@ -340,7 +340,7 @@ describe("MCPClient", () => {
|
|
|
340
340
|
describe("listTools", () => {
|
|
341
341
|
it("should list all tools with pagination", async () => {
|
|
342
342
|
const endpoint = "https://api.example.com/mcp";
|
|
343
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
343
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
344
344
|
const mockTools = [
|
|
345
345
|
{
|
|
346
346
|
name: "tool1",
|
|
@@ -395,7 +395,7 @@ describe("MCPClient", () => {
|
|
|
395
395
|
});
|
|
396
396
|
it("should handle single page of tools", async () => {
|
|
397
397
|
const endpoint = "https://api.example.com/mcp";
|
|
398
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
398
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
399
399
|
const mockTools = [
|
|
400
400
|
{
|
|
401
401
|
name: "tool1",
|
|
@@ -426,7 +426,7 @@ describe("MCPClient", () => {
|
|
|
426
426
|
});
|
|
427
427
|
it("should throw error for invalid input schema", async () => {
|
|
428
428
|
const endpoint = "https://api.example.com/mcp";
|
|
429
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
429
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
430
430
|
const mockTools = [
|
|
431
431
|
{
|
|
432
432
|
name: "invalid-tool",
|
|
@@ -445,7 +445,7 @@ describe("MCPClient", () => {
|
|
|
445
445
|
describe("callTool", () => {
|
|
446
446
|
it("should call a tool with arguments", async () => {
|
|
447
447
|
const endpoint = "https://api.example.com/mcp";
|
|
448
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
448
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
449
449
|
const mockResult = { success: true, data: "test result" };
|
|
450
450
|
mockClientInstance.callTool.mockResolvedValue(mockResult);
|
|
451
451
|
const result = await client.callTool("testTool", {
|
|
@@ -460,7 +460,7 @@ describe("MCPClient", () => {
|
|
|
460
460
|
});
|
|
461
461
|
it("should handle tool call errors", async () => {
|
|
462
462
|
const endpoint = "https://api.example.com/mcp";
|
|
463
|
-
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP);
|
|
463
|
+
const client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
464
464
|
const error = new Error("Tool call failed");
|
|
465
465
|
mockClientInstance.callTool.mockRejectedValue(error);
|
|
466
466
|
await expect(client.callTool("testTool", {})).rejects.toThrow("Tool call failed");
|
|
@@ -470,13 +470,13 @@ describe("MCPClient", () => {
|
|
|
470
470
|
it("should initialize HTTP transport with session ID", async () => {
|
|
471
471
|
const endpoint = "https://api.example.com/mcp";
|
|
472
472
|
const headers = { Authorization: "Bearer token" };
|
|
473
|
-
await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, headers);
|
|
473
|
+
await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, headers, undefined, undefined);
|
|
474
474
|
expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(new URL(endpoint), { sessionId: undefined, requestInit: { headers } });
|
|
475
475
|
});
|
|
476
476
|
it("should initialize SSE transport without session ID", async () => {
|
|
477
477
|
const endpoint = "https://api.example.com/mcp";
|
|
478
478
|
const headers = { Authorization: "Bearer token" };
|
|
479
|
-
await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.SSE, headers);
|
|
479
|
+
await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.SSE, headers, undefined, undefined);
|
|
480
480
|
expect(MockedSSEClientTransport).toHaveBeenCalledWith(new URL(endpoint), {
|
|
481
481
|
requestInit: { headers },
|
|
482
482
|
});
|
|
@@ -485,7 +485,7 @@ describe("MCPClient", () => {
|
|
|
485
485
|
describe("client initialization", () => {
|
|
486
486
|
it("should initialize client with correct name and version", async () => {
|
|
487
487
|
const endpoint = "https://api.example.com/mcp";
|
|
488
|
-
await mcp_client_1.MCPClient.create(endpoint);
|
|
488
|
+
await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
489
489
|
expect(MockedClient).toHaveBeenCalledWith({
|
|
490
490
|
name: "tambo-mcp-client",
|
|
491
491
|
version: "1.0.0",
|
|
@@ -493,7 +493,7 @@ describe("MCPClient", () => {
|
|
|
493
493
|
});
|
|
494
494
|
it("should set onclose handler", async () => {
|
|
495
495
|
const endpoint = "https://api.example.com/mcp";
|
|
496
|
-
const _client = await mcp_client_1.MCPClient.create(endpoint);
|
|
496
|
+
const _client = await mcp_client_1.MCPClient.create(endpoint, mcp_client_1.MCPTransport.HTTP, undefined, undefined, undefined);
|
|
497
497
|
expect(mockClientInstance.onclose).toBeDefined();
|
|
498
498
|
expect(typeof mockClientInstance.onclose).toBe("function");
|
|
499
499
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-client.test.js","sourceRoot":"","sources":["../../../src/mcp/__tests__/mcp-client.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { MCPClient, MCPTransport } from \"../mcp-client\";\n\n// Mock the MCP SDK modules\njest.mock(\"@modelcontextprotocol/sdk/client/index.js\", () => ({\n Client: jest.fn().mockImplementation(() => ({\n connect: jest.fn(),\n close: jest.fn(),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n })),\n}));\n\njest.mock(\"@modelcontextprotocol/sdk/client/sse.js\", () => ({\n SSEClientTransport: jest.fn().mockImplementation(() => ({\n // SSE transport doesn't have sessionId\n })),\n}));\n\njest.mock(\"@modelcontextprotocol/sdk/client/streamableHttp.js\", () => ({\n StreamableHTTPClientTransport: jest\n .fn()\n .mockImplementation((url, options) => ({\n sessionId: options?.sessionId,\n })),\n}));\n\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { SSEClientTransport } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\n\n// Type the mocked modules\nconst MockedClient = Client as jest.MockedClass<typeof Client>;\nconst MockedSSEClientTransport = SSEClientTransport as jest.MockedClass<\n typeof SSEClientTransport\n>;\nconst MockedStreamableHTTPClientTransport =\n StreamableHTTPClientTransport as jest.MockedClass<\n typeof StreamableHTTPClientTransport\n >;\n\ndescribe(\"MCPClient\", () => {\n let mockClientInstance: any;\n let mockTransportInstance: any;\n\n beforeEach(() => {\n jest.clearAllMocks();\n\n // Create mock instances\n mockClientInstance = {\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n };\n\n mockTransportInstance = {\n sessionId: \"test-session-id\",\n };\n\n // Setup mocks\n MockedClient.mockImplementation(() => mockClientInstance);\n MockedStreamableHTTPClientTransport.mockImplementation(\n () => mockTransportInstance,\n );\n MockedSSEClientTransport.mockImplementation(\n () => ({}) as SSEClientTransport,\n );\n });\n\n describe(\"create\", () => {\n it(\"should create and connect an MCPClient with HTTP transport by default\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const headers = { Authorization: \"Bearer token\" };\n\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n headers,\n );\n\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: undefined, requestInit: { headers } },\n );\n expect(MockedClient).toHaveBeenCalledWith({\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n });\n expect(mockClientInstance.connect).toHaveBeenCalledWith(\n mockTransportInstance,\n );\n expect(client).toBeInstanceOf(MCPClient);\n });\n\n it(\"should create and connect an MCPClient with SSE transport\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n\n const client = await MCPClient.create(endpoint, MCPTransport.SSE);\n\n expect(MockedSSEClientTransport).toHaveBeenCalledWith(new URL(endpoint), {\n requestInit: { headers: {} },\n });\n expect(mockClientInstance.connect).toHaveBeenCalledWith({});\n expect(client).toBeInstanceOf(MCPClient);\n });\n\n it(\"should create client with default headers when none provided\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n\n await MCPClient.create(endpoint);\n\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: undefined, requestInit: { headers: {} } },\n );\n });\n });\n\n describe(\"reconnect\", () => {\n it(\"should create new transport and client instances and call connect when reconnect() is called (default behavior)\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n\n // Clear previous calls to focus on reconnect behavior\n jest.clearAllMocks();\n\n // Create new mock instances to verify new instances are created\n const newMockClientInstance = {\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n };\n\n const newMockTransportInstance = {\n sessionId: \"new-session-id\",\n };\n\n // Mock the constructors to return new instances\n MockedClient.mockImplementation(() => newMockClientInstance as any);\n MockedStreamableHTTPClientTransport.mockImplementation(\n () => newMockTransportInstance as any,\n );\n\n await client.reconnect(); // Uses default parameters\n\n // Verify old client was closed\n expect(mockClientInstance.close).toHaveBeenCalled();\n\n // Verify new transport was created with preserved session ID (default behavior)\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: \"test-session-id\", requestInit: { headers: {} } },\n );\n\n // Verify new client was created\n expect(MockedClient).toHaveBeenCalledWith({\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n });\n\n // Verify new client's connect was called with new transport\n expect(newMockClientInstance.connect).toHaveBeenCalledWith(\n newMockTransportInstance,\n );\n });\n\n it(\"should reconnect without session ID for SSE transport\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.SSE);\n\n // Clear previous calls\n jest.clearAllMocks();\n\n await client.reconnect();\n\n expect(mockClientInstance.close).toHaveBeenCalled();\n expect(MockedSSEClientTransport).toHaveBeenCalledWith(new URL(endpoint), {\n requestInit: { headers: {} },\n });\n expect(mockClientInstance.connect).toHaveBeenCalledWith({});\n });\n\n it(\"should handle close errors when reportErrorOnClose is true\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n const consoleSpy = jest.spyOn(console, \"error\").mockImplementation();\n\n // Make close throw an error\n mockClientInstance.close.mockRejectedValue(new Error(\"Close failed\"));\n\n await client.reconnect(false, true);\n\n expect(consoleSpy).toHaveBeenCalledWith(\n \"Error closing Tambo MCP Client:\",\n expect.any(Error),\n );\n consoleSpy.mockRestore();\n });\n\n it(\"should not log close errors when reportErrorOnClose is false\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n const consoleSpy = jest.spyOn(console, \"error\").mockImplementation();\n\n // Make close throw an error\n mockClientInstance.close.mockRejectedValue(new Error(\"Close failed\"));\n\n await client.reconnect(false, false);\n\n expect(consoleSpy).not.toHaveBeenCalled();\n consoleSpy.mockRestore();\n });\n\n it(\"should create new session when newSession is true\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n\n // Clear previous calls to focus on reconnect behavior\n jest.clearAllMocks();\n\n // Create new mock instances to verify new instances are created\n const newMockClientInstance = {\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n };\n\n const newMockTransportInstance = {\n sessionId: \"new-session-id\",\n };\n\n // Mock the constructors to return new instances\n MockedClient.mockImplementation(() => newMockClientInstance as any);\n MockedStreamableHTTPClientTransport.mockImplementation(\n () => newMockTransportInstance as any,\n );\n\n await client.reconnect(true, true); // newSession = true, reportErrorOnClose = true\n\n // Verify old client was closed\n expect(mockClientInstance.close).toHaveBeenCalled();\n\n // Verify new transport was created with undefined session ID (new session)\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: undefined, requestInit: { headers: {} } },\n );\n\n // Verify new client was created\n expect(MockedClient).toHaveBeenCalledWith({\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n });\n\n // Verify new client's connect was called with new transport\n expect(newMockClientInstance.connect).toHaveBeenCalledWith(\n newMockTransportInstance,\n );\n });\n\n it(\"should reuse existing session when newSession is false (default)\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n\n // Clear previous calls to focus on reconnect behavior\n jest.clearAllMocks();\n\n // Create new mock instances to verify new instances are created\n const newMockClientInstance = {\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n };\n\n const newMockTransportInstance = {\n sessionId: \"reused-session-id\",\n };\n\n // Mock the constructors to return new instances\n MockedClient.mockImplementation(() => newMockClientInstance as any);\n MockedStreamableHTTPClientTransport.mockImplementation(\n () => newMockTransportInstance as any,\n );\n\n await client.reconnect(false, true); // newSession = false, reportErrorOnClose = true\n\n // Verify old client was closed\n expect(mockClientInstance.close).toHaveBeenCalled();\n\n // Verify new transport was created with preserved session ID\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: \"test-session-id\", requestInit: { headers: {} } },\n );\n\n // Verify new client was created\n expect(MockedClient).toHaveBeenCalledWith({\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n });\n\n // Verify new client's connect was called with new transport\n expect(newMockClientInstance.connect).toHaveBeenCalledWith(\n newMockTransportInstance,\n );\n });\n });\n\n describe(\"onclose\", () => {\n it(\"should reconnect MCPClient when client is closed by external means (no backoff on manual preemption)\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n jest.useFakeTimers();\n const consoleSpy = jest.spyOn(console, \"warn\").mockImplementation();\n\n // Create new mock instances to verify reconnection creates new instances\n const newMockClientInstance = {\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n };\n\n const newMockTransportInstance = {\n sessionId: \"reconnected-session-id\",\n };\n\n // Mock the constructors to return new instances for reconnection\n MockedClient.mockImplementation(() => newMockClientInstance as any);\n MockedStreamableHTTPClientTransport.mockImplementation(\n () => newMockTransportInstance as any,\n );\n\n // Reset counts after initial creation\n jest.clearAllMocks();\n\n // Trigger automatic onclose (schedules a delayed reconnect)\n (client as any).onclose();\n // Manual reconnect should preempt the scheduled automatic attempt\n const reconnectPromise = client.reconnect();\n // No timers should be pending after manual preemption\n await reconnectPromise;\n\n // Verify warning message is logged\n expect(consoleSpy).toHaveBeenCalled();\n\n // Verify old client was closed\n expect(mockClientInstance.close).toHaveBeenCalled();\n\n // Verify new transport was created with preserved session ID\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: \"test-session-id\", requestInit: { headers: {} } },\n );\n\n // Verify new client was created\n expect(MockedClient).toHaveBeenCalledWith({\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n });\n\n // Verify new client's connect was called with new transport\n expect(newMockClientInstance.connect).toHaveBeenCalledWith(\n newMockTransportInstance,\n );\n\n // Ensure only a single reconnect attempt occurred\n expect(MockedClient).toHaveBeenCalledTimes(1);\n expect(newMockClientInstance.connect).toHaveBeenCalledTimes(1);\n\n consoleSpy.mockRestore();\n jest.useRealTimers();\n });\n });\n\n describe(\"reconnect re-entrancy and single-flight\", () => {\n it(\"prevents re-entrant onclose during deliberate close and coalesces concurrent calls\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n\n // Simulate an implementation where closing the client would call its own onclose handler\n const closeImpl = jest.fn(async () => {\n if (typeof mockClientInstance.onclose === \"function\") {\n // would cause recursion if not detached\n (mockClientInstance.onclose as unknown as () => void)();\n }\n return;\n });\n mockClientInstance.close = closeImpl;\n\n // Prepare new instances for the reconnect\n const newMockClientInstance = {\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n };\n MockedClient.mockImplementation(() => newMockClientInstance as any);\n\n // Reset counts after initial creation\n jest.clearAllMocks();\n\n // Trigger auto onclose and manual reconnect nearly simultaneously\n (client as any).onclose();\n await client.reconnect();\n\n // Should have detached onclose before calling close, avoiding recursion\n expect(closeImpl).toHaveBeenCalledTimes(1);\n expect(mockClientInstance.onclose).toBeUndefined();\n\n // Single-flight: only one new client/connect\n expect(MockedClient).toHaveBeenCalledTimes(1);\n expect(newMockClientInstance.connect).toHaveBeenCalledTimes(1);\n });\n });\n\n describe(\"backoff + jitter (automatic reconnect)\", () => {\n it(\"applies jitter and resets to initial delay after a successful reconnect (manual preempt)\", async () => {\n jest.useFakeTimers();\n const base = MCPClient.BACKOFF_INITIAL_MS;\n const ratio = MCPClient.BACKOFF_JITTER_RATIO;\n const min = Math.round(base * (1 - ratio));\n const max = Math.round(base * (1 + ratio));\n const setTimeoutSpy = jest.spyOn(global, \"setTimeout\");\n const randSpy = jest.spyOn(Math, \"random\").mockReturnValue(0.0); // extreme low jitter\n\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n\n // Prepare one attempt that will succeed to avoid rescheduling\n MockedClient.mockImplementation(\n () =>\n ({\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n }) as any,\n );\n\n // Trigger once to capture the delay\n (client as any).onclose();\n expect(setTimeoutSpy).toHaveBeenCalled();\n const numericDelays = setTimeoutSpy.mock.calls\n .map((c) => c[1])\n .filter((v) => typeof v === \"number\") as number[];\n expect(numericDelays.length).toBeGreaterThan(0);\n const d = numericDelays[0]!;\n expect(d).toBeGreaterThanOrEqual(min);\n expect(d).toBeLessThanOrEqual(max);\n\n // Manual reconnect succeeds and should reset backoff attempts\n await client.reconnect();\n\n // Trigger onclose again and ensure we start from initial range again\n (client as any).onclose();\n const numericDelays2 = setTimeoutSpy.mock.calls\n .map((c) => c[1])\n .filter((v) => typeof v === \"number\") as number[];\n expect(numericDelays2.length).toBeGreaterThanOrEqual(2);\n const dAgain = numericDelays2[1]!;\n expect(dAgain).toBeGreaterThanOrEqual(min);\n expect(dAgain).toBeLessThanOrEqual(max);\n\n jest.useRealTimers();\n setTimeoutSpy.mockRestore();\n randSpy.mockRestore();\n });\n });\n\n describe(\"listTools\", () => {\n it(\"should list all tools with pagination\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n\n const mockTools = [\n {\n name: \"tool1\",\n description: \"First tool\",\n inputSchema: {\n type: \"object\",\n properties: { arg1: { type: \"string\" } },\n },\n },\n {\n name: \"tool2\",\n description: \"Second tool\",\n inputSchema: {\n type: \"object\",\n properties: { arg2: { type: \"number\" } },\n },\n },\n ];\n\n const mockResponse1 = {\n tools: [mockTools[0]],\n nextCursor: \"cursor1\",\n };\n\n const mockResponse2 = {\n tools: [mockTools[1]],\n nextCursor: undefined,\n };\n\n mockClientInstance.listTools\n .mockResolvedValueOnce(mockResponse1)\n .mockResolvedValueOnce(mockResponse2);\n\n const result = await client.listTools();\n\n expect(mockClientInstance.listTools).toHaveBeenCalledTimes(2);\n expect(mockClientInstance.listTools).toHaveBeenNthCalledWith(\n 1,\n { cursor: undefined },\n {},\n );\n expect(mockClientInstance.listTools).toHaveBeenNthCalledWith(\n 2,\n { cursor: \"cursor1\" },\n {},\n );\n expect(result).toEqual([\n {\n name: \"tool1\",\n description: \"First tool\",\n inputSchema: {\n type: \"object\",\n properties: { arg1: { type: \"string\" } },\n },\n },\n {\n name: \"tool2\",\n description: \"Second tool\",\n inputSchema: {\n type: \"object\",\n properties: { arg2: { type: \"number\" } },\n },\n },\n ]);\n });\n\n it(\"should handle single page of tools\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n\n const mockTools = [\n {\n name: \"tool1\",\n description: \"Only tool\",\n inputSchema: {\n type: \"object\",\n properties: { arg1: { type: \"string\" } },\n },\n },\n ];\n\n const mockResponse = {\n tools: mockTools,\n nextCursor: undefined,\n };\n\n mockClientInstance.listTools.mockResolvedValue(mockResponse);\n\n const result = await client.listTools();\n\n expect(mockClientInstance.listTools).toHaveBeenCalledTimes(1);\n expect(result).toEqual([\n {\n name: \"tool1\",\n description: \"Only tool\",\n inputSchema: {\n type: \"object\",\n properties: { arg1: { type: \"string\" } },\n },\n },\n ]);\n });\n\n it(\"should throw error for invalid input schema\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n\n const mockTools = [\n {\n name: \"invalid-tool\",\n description: \"Tool with invalid schema\",\n inputSchema: { type: \"string\" }, // Invalid - should be object\n },\n ];\n\n const mockResponse = {\n tools: mockTools,\n nextCursor: undefined,\n };\n\n mockClientInstance.listTools.mockResolvedValue(mockResponse);\n\n await expect(client.listTools()).rejects.toThrow(\n \"Input schema for tool invalid-tool is not an object\",\n );\n });\n });\n\n describe(\"callTool\", () => {\n it(\"should call a tool with arguments\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n\n const mockResult = { success: true, data: \"test result\" };\n mockClientInstance.callTool.mockResolvedValue(mockResult);\n\n const result = await client.callTool(\"testTool\", {\n arg1: \"value1\",\n arg2: 42,\n });\n\n expect(mockClientInstance.callTool).toHaveBeenCalledWith({\n name: \"testTool\",\n arguments: { arg1: \"value1\", arg2: 42 },\n });\n expect(result).toBe(mockResult);\n });\n\n it(\"should handle tool call errors\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(endpoint, MCPTransport.HTTP);\n\n const error = new Error(\"Tool call failed\");\n mockClientInstance.callTool.mockRejectedValue(error);\n\n await expect(client.callTool(\"testTool\", {})).rejects.toThrow(\n \"Tool call failed\",\n );\n });\n });\n\n describe(\"transport initialization\", () => {\n it(\"should initialize HTTP transport with session ID\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const headers = { Authorization: \"Bearer token\" };\n\n await MCPClient.create(endpoint, MCPTransport.HTTP, headers);\n\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: undefined, requestInit: { headers } },\n );\n });\n\n it(\"should initialize SSE transport without session ID\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const headers = { Authorization: \"Bearer token\" };\n\n await MCPClient.create(endpoint, MCPTransport.SSE, headers);\n\n expect(MockedSSEClientTransport).toHaveBeenCalledWith(new URL(endpoint), {\n requestInit: { headers },\n });\n });\n });\n\n describe(\"client initialization\", () => {\n it(\"should initialize client with correct name and version\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n\n await MCPClient.create(endpoint);\n\n expect(MockedClient).toHaveBeenCalledWith({\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n });\n });\n\n it(\"should set onclose handler\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const _client = await MCPClient.create(endpoint);\n\n expect(mockClientInstance.onclose).toBeDefined();\n expect(typeof mockClientInstance.onclose).toBe(\"function\");\n });\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"mcp-client.test.js","sourceRoot":"","sources":["../../../src/mcp/__tests__/mcp-client.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { MCPClient, MCPTransport } from \"../mcp-client\";\n\n// Mock the MCP SDK modules\njest.mock(\"@modelcontextprotocol/sdk/client/index.js\", () => ({\n Client: jest.fn().mockImplementation(() => ({\n connect: jest.fn(),\n close: jest.fn(),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n })),\n}));\n\njest.mock(\"@modelcontextprotocol/sdk/client/sse.js\", () => ({\n SSEClientTransport: jest.fn().mockImplementation(() => ({\n // SSE transport doesn't have sessionId\n })),\n}));\n\njest.mock(\"@modelcontextprotocol/sdk/client/streamableHttp.js\", () => ({\n StreamableHTTPClientTransport: jest\n .fn()\n .mockImplementation((url, options) => ({\n sessionId: options?.sessionId,\n })),\n}));\n\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { SSEClientTransport } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\n\n// Type the mocked modules\nconst MockedClient = Client as jest.MockedClass<typeof Client>;\nconst MockedSSEClientTransport = SSEClientTransport as jest.MockedClass<\n typeof SSEClientTransport\n>;\nconst MockedStreamableHTTPClientTransport =\n StreamableHTTPClientTransport as jest.MockedClass<\n typeof StreamableHTTPClientTransport\n >;\n\ndescribe(\"MCPClient\", () => {\n let mockClientInstance: any;\n let mockTransportInstance: any;\n\n beforeEach(() => {\n jest.clearAllMocks();\n\n // Create mock instances\n mockClientInstance = {\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n };\n\n mockTransportInstance = {\n sessionId: \"test-session-id\",\n };\n\n // Setup mocks\n MockedClient.mockImplementation(() => mockClientInstance);\n MockedStreamableHTTPClientTransport.mockImplementation(\n () => mockTransportInstance,\n );\n MockedSSEClientTransport.mockImplementation(\n () => ({}) as SSEClientTransport,\n );\n });\n\n describe(\"create\", () => {\n it(\"should create and connect an MCPClient with HTTP transport by default\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const headers = { Authorization: \"Bearer token\" };\n\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n headers,\n undefined,\n undefined,\n );\n\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: undefined, requestInit: { headers } },\n );\n expect(MockedClient).toHaveBeenCalledWith({\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n });\n expect(mockClientInstance.connect).toHaveBeenCalledWith(\n mockTransportInstance,\n );\n expect(client).toBeInstanceOf(MCPClient);\n });\n\n it(\"should create and connect an MCPClient with SSE transport\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.SSE,\n undefined,\n undefined,\n undefined,\n );\n\n expect(MockedSSEClientTransport).toHaveBeenCalledWith(new URL(endpoint), {\n requestInit: { headers: {} },\n });\n expect(mockClientInstance.connect).toHaveBeenCalledWith({});\n expect(client).toBeInstanceOf(MCPClient);\n });\n\n it(\"should create client with default headers when none provided\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n\n await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: undefined, requestInit: { headers: {} } },\n );\n });\n });\n\n describe(\"reconnect\", () => {\n it(\"should create new transport and client instances and call connect when reconnect() is called (default behavior)\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n\n // Clear previous calls to focus on reconnect behavior\n jest.clearAllMocks();\n\n // Create new mock instances to verify new instances are created\n const newMockClientInstance = {\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n };\n\n const newMockTransportInstance = {\n sessionId: \"new-session-id\",\n };\n\n // Mock the constructors to return new instances\n MockedClient.mockImplementation(() => newMockClientInstance as any);\n MockedStreamableHTTPClientTransport.mockImplementation(\n () => newMockTransportInstance as any,\n );\n\n await client.reconnect(); // Uses default parameters\n\n // Verify old client was closed\n expect(mockClientInstance.close).toHaveBeenCalled();\n\n // Verify new transport was created with preserved session ID (default behavior)\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: \"test-session-id\", requestInit: { headers: {} } },\n );\n\n // Verify new client was created\n expect(MockedClient).toHaveBeenCalledWith({\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n });\n\n // Verify new client's connect was called with new transport\n expect(newMockClientInstance.connect).toHaveBeenCalledWith(\n newMockTransportInstance,\n );\n });\n\n it(\"should reconnect without session ID for SSE transport\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.SSE,\n undefined,\n undefined,\n undefined,\n );\n\n // Clear previous calls\n jest.clearAllMocks();\n\n await client.reconnect();\n\n expect(mockClientInstance.close).toHaveBeenCalled();\n expect(MockedSSEClientTransport).toHaveBeenCalledWith(new URL(endpoint), {\n requestInit: { headers: {} },\n });\n expect(mockClientInstance.connect).toHaveBeenCalledWith({});\n });\n\n it(\"should handle close errors when reportErrorOnClose is true\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n const consoleSpy = jest.spyOn(console, \"error\").mockImplementation();\n\n // Make close throw an error\n mockClientInstance.close.mockRejectedValue(new Error(\"Close failed\"));\n\n await client.reconnect(false, true);\n\n expect(consoleSpy).toHaveBeenCalledWith(\n \"Error closing Tambo MCP Client:\",\n expect.any(Error),\n );\n consoleSpy.mockRestore();\n });\n\n it(\"should not log close errors when reportErrorOnClose is false\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n const consoleSpy = jest.spyOn(console, \"error\").mockImplementation();\n\n // Make close throw an error\n mockClientInstance.close.mockRejectedValue(new Error(\"Close failed\"));\n\n await client.reconnect(false, false);\n\n expect(consoleSpy).not.toHaveBeenCalled();\n consoleSpy.mockRestore();\n });\n\n it(\"should create new session when newSession is true\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n\n // Clear previous calls to focus on reconnect behavior\n jest.clearAllMocks();\n\n // Create new mock instances to verify new instances are created\n const newMockClientInstance = {\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n };\n\n const newMockTransportInstance = {\n sessionId: \"new-session-id\",\n };\n\n // Mock the constructors to return new instances\n MockedClient.mockImplementation(() => newMockClientInstance as any);\n MockedStreamableHTTPClientTransport.mockImplementation(\n () => newMockTransportInstance as any,\n );\n\n await client.reconnect(true, true); // newSession = true, reportErrorOnClose = true\n\n // Verify old client was closed\n expect(mockClientInstance.close).toHaveBeenCalled();\n\n // Verify new transport was created with undefined session ID (new session)\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: undefined, requestInit: { headers: {} } },\n );\n\n // Verify new client was created\n expect(MockedClient).toHaveBeenCalledWith({\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n });\n\n // Verify new client's connect was called with new transport\n expect(newMockClientInstance.connect).toHaveBeenCalledWith(\n newMockTransportInstance,\n );\n });\n\n it(\"should reuse existing session when newSession is false (default)\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n\n // Clear previous calls to focus on reconnect behavior\n jest.clearAllMocks();\n\n // Create new mock instances to verify new instances are created\n const newMockClientInstance = {\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n };\n\n const newMockTransportInstance = {\n sessionId: \"reused-session-id\",\n };\n\n // Mock the constructors to return new instances\n MockedClient.mockImplementation(() => newMockClientInstance as any);\n MockedStreamableHTTPClientTransport.mockImplementation(\n () => newMockTransportInstance as any,\n );\n\n await client.reconnect(false, true); // newSession = false, reportErrorOnClose = true\n\n // Verify old client was closed\n expect(mockClientInstance.close).toHaveBeenCalled();\n\n // Verify new transport was created with preserved session ID\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: \"test-session-id\", requestInit: { headers: {} } },\n );\n\n // Verify new client was created\n expect(MockedClient).toHaveBeenCalledWith({\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n });\n\n // Verify new client's connect was called with new transport\n expect(newMockClientInstance.connect).toHaveBeenCalledWith(\n newMockTransportInstance,\n );\n });\n });\n\n describe(\"onclose\", () => {\n it(\"should reconnect MCPClient when client is closed by external means (no backoff on manual preemption)\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n jest.useFakeTimers();\n const consoleSpy = jest.spyOn(console, \"warn\").mockImplementation();\n\n // Create new mock instances to verify reconnection creates new instances\n const newMockClientInstance = {\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n };\n\n const newMockTransportInstance = {\n sessionId: \"reconnected-session-id\",\n };\n\n // Mock the constructors to return new instances for reconnection\n MockedClient.mockImplementation(() => newMockClientInstance as any);\n MockedStreamableHTTPClientTransport.mockImplementation(\n () => newMockTransportInstance as any,\n );\n\n // Reset counts after initial creation\n jest.clearAllMocks();\n\n // Trigger automatic onclose (schedules a delayed reconnect)\n (client as any).onclose();\n // Manual reconnect should preempt the scheduled automatic attempt\n const reconnectPromise = client.reconnect();\n // No timers should be pending after manual preemption\n await reconnectPromise;\n\n // Verify warning message is logged\n expect(consoleSpy).toHaveBeenCalled();\n\n // Verify old client was closed\n expect(mockClientInstance.close).toHaveBeenCalled();\n\n // Verify new transport was created with preserved session ID\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: \"test-session-id\", requestInit: { headers: {} } },\n );\n\n // Verify new client was created\n expect(MockedClient).toHaveBeenCalledWith({\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n });\n\n // Verify new client's connect was called with new transport\n expect(newMockClientInstance.connect).toHaveBeenCalledWith(\n newMockTransportInstance,\n );\n\n // Ensure only a single reconnect attempt occurred\n expect(MockedClient).toHaveBeenCalledTimes(1);\n expect(newMockClientInstance.connect).toHaveBeenCalledTimes(1);\n\n consoleSpy.mockRestore();\n jest.useRealTimers();\n });\n });\n\n describe(\"reconnect re-entrancy and single-flight\", () => {\n it(\"prevents re-entrant onclose during deliberate close and coalesces concurrent calls\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n\n // Simulate an implementation where closing the client would call its own onclose handler\n const closeImpl = jest.fn(async () => {\n if (typeof mockClientInstance.onclose === \"function\") {\n // would cause recursion if not detached\n (mockClientInstance.onclose as unknown as () => void)();\n }\n return;\n });\n mockClientInstance.close = closeImpl;\n\n // Prepare new instances for the reconnect\n const newMockClientInstance = {\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n };\n MockedClient.mockImplementation(() => newMockClientInstance as any);\n\n // Reset counts after initial creation\n jest.clearAllMocks();\n\n // Trigger auto onclose and manual reconnect nearly simultaneously\n (client as any).onclose();\n await client.reconnect();\n\n // Should have detached onclose before calling close, avoiding recursion\n expect(closeImpl).toHaveBeenCalledTimes(1);\n expect(mockClientInstance.onclose).toBeUndefined();\n\n // Single-flight: only one new client/connect\n expect(MockedClient).toHaveBeenCalledTimes(1);\n expect(newMockClientInstance.connect).toHaveBeenCalledTimes(1);\n });\n });\n\n describe(\"backoff + jitter (automatic reconnect)\", () => {\n it(\"applies jitter and resets to initial delay after a successful reconnect (manual preempt)\", async () => {\n jest.useFakeTimers();\n const base = MCPClient.BACKOFF_INITIAL_MS;\n const ratio = MCPClient.BACKOFF_JITTER_RATIO;\n const min = Math.round(base * (1 - ratio));\n const max = Math.round(base * (1 + ratio));\n const setTimeoutSpy = jest.spyOn(global, \"setTimeout\");\n const randSpy = jest.spyOn(Math, \"random\").mockReturnValue(0.0); // extreme low jitter\n\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n\n // Prepare one attempt that will succeed to avoid rescheduling\n MockedClient.mockImplementation(\n () =>\n ({\n connect: jest.fn().mockResolvedValue(undefined),\n close: jest.fn().mockResolvedValue(undefined),\n listTools: jest.fn(),\n callTool: jest.fn(),\n onclose: null,\n }) as any,\n );\n\n // Trigger once to capture the delay\n (client as any).onclose();\n expect(setTimeoutSpy).toHaveBeenCalled();\n const numericDelays = setTimeoutSpy.mock.calls\n .map((c) => c[1])\n .filter((v) => typeof v === \"number\") as number[];\n expect(numericDelays.length).toBeGreaterThan(0);\n const d = numericDelays[0]!;\n expect(d).toBeGreaterThanOrEqual(min);\n expect(d).toBeLessThanOrEqual(max);\n\n // Manual reconnect succeeds and should reset backoff attempts\n await client.reconnect();\n\n // Trigger onclose again and ensure we start from initial range again\n (client as any).onclose();\n const numericDelays2 = setTimeoutSpy.mock.calls\n .map((c) => c[1])\n .filter((v) => typeof v === \"number\") as number[];\n expect(numericDelays2.length).toBeGreaterThanOrEqual(2);\n const dAgain = numericDelays2[1]!;\n expect(dAgain).toBeGreaterThanOrEqual(min);\n expect(dAgain).toBeLessThanOrEqual(max);\n\n jest.useRealTimers();\n setTimeoutSpy.mockRestore();\n randSpy.mockRestore();\n });\n });\n\n describe(\"listTools\", () => {\n it(\"should list all tools with pagination\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n\n const mockTools = [\n {\n name: \"tool1\",\n description: \"First tool\",\n inputSchema: {\n type: \"object\",\n properties: { arg1: { type: \"string\" } },\n },\n },\n {\n name: \"tool2\",\n description: \"Second tool\",\n inputSchema: {\n type: \"object\",\n properties: { arg2: { type: \"number\" } },\n },\n },\n ];\n\n const mockResponse1 = {\n tools: [mockTools[0]],\n nextCursor: \"cursor1\",\n };\n\n const mockResponse2 = {\n tools: [mockTools[1]],\n nextCursor: undefined,\n };\n\n mockClientInstance.listTools\n .mockResolvedValueOnce(mockResponse1)\n .mockResolvedValueOnce(mockResponse2);\n\n const result = await client.listTools();\n\n expect(mockClientInstance.listTools).toHaveBeenCalledTimes(2);\n expect(mockClientInstance.listTools).toHaveBeenNthCalledWith(\n 1,\n { cursor: undefined },\n {},\n );\n expect(mockClientInstance.listTools).toHaveBeenNthCalledWith(\n 2,\n { cursor: \"cursor1\" },\n {},\n );\n expect(result).toEqual([\n {\n name: \"tool1\",\n description: \"First tool\",\n inputSchema: {\n type: \"object\",\n properties: { arg1: { type: \"string\" } },\n },\n },\n {\n name: \"tool2\",\n description: \"Second tool\",\n inputSchema: {\n type: \"object\",\n properties: { arg2: { type: \"number\" } },\n },\n },\n ]);\n });\n\n it(\"should handle single page of tools\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n\n const mockTools = [\n {\n name: \"tool1\",\n description: \"Only tool\",\n inputSchema: {\n type: \"object\",\n properties: { arg1: { type: \"string\" } },\n },\n },\n ];\n\n const mockResponse = {\n tools: mockTools,\n nextCursor: undefined,\n };\n\n mockClientInstance.listTools.mockResolvedValue(mockResponse);\n\n const result = await client.listTools();\n\n expect(mockClientInstance.listTools).toHaveBeenCalledTimes(1);\n expect(result).toEqual([\n {\n name: \"tool1\",\n description: \"Only tool\",\n inputSchema: {\n type: \"object\",\n properties: { arg1: { type: \"string\" } },\n },\n },\n ]);\n });\n\n it(\"should throw error for invalid input schema\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n\n const mockTools = [\n {\n name: \"invalid-tool\",\n description: \"Tool with invalid schema\",\n inputSchema: { type: \"string\" }, // Invalid - should be object\n },\n ];\n\n const mockResponse = {\n tools: mockTools,\n nextCursor: undefined,\n };\n\n mockClientInstance.listTools.mockResolvedValue(mockResponse);\n\n await expect(client.listTools()).rejects.toThrow(\n \"Input schema for tool invalid-tool is not an object\",\n );\n });\n });\n\n describe(\"callTool\", () => {\n it(\"should call a tool with arguments\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n\n const mockResult = { success: true, data: \"test result\" };\n mockClientInstance.callTool.mockResolvedValue(mockResult);\n\n const result = await client.callTool(\"testTool\", {\n arg1: \"value1\",\n arg2: 42,\n });\n\n expect(mockClientInstance.callTool).toHaveBeenCalledWith({\n name: \"testTool\",\n arguments: { arg1: \"value1\", arg2: 42 },\n });\n expect(result).toBe(mockResult);\n });\n\n it(\"should handle tool call errors\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n const error = new Error(\"Tool call failed\");\n mockClientInstance.callTool.mockRejectedValue(error);\n\n await expect(client.callTool(\"testTool\", {})).rejects.toThrow(\n \"Tool call failed\",\n );\n });\n });\n\n describe(\"transport initialization\", () => {\n it(\"should initialize HTTP transport with session ID\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const headers = { Authorization: \"Bearer token\" };\n\n await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n headers,\n undefined,\n undefined,\n );\n\n expect(MockedStreamableHTTPClientTransport).toHaveBeenCalledWith(\n new URL(endpoint),\n { sessionId: undefined, requestInit: { headers } },\n );\n });\n\n it(\"should initialize SSE transport without session ID\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const headers = { Authorization: \"Bearer token\" };\n\n await MCPClient.create(\n endpoint,\n MCPTransport.SSE,\n headers,\n undefined,\n undefined,\n );\n\n expect(MockedSSEClientTransport).toHaveBeenCalledWith(new URL(endpoint), {\n requestInit: { headers },\n });\n });\n });\n\n describe(\"client initialization\", () => {\n it(\"should initialize client with correct name and version\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n\n await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n\n expect(MockedClient).toHaveBeenCalledWith({\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n });\n });\n\n it(\"should set onclose handler\", async () => {\n const endpoint = \"https://api.example.com/mcp\";\n const _client = await MCPClient.create(\n endpoint,\n MCPTransport.HTTP,\n undefined,\n undefined,\n undefined,\n );\n\n expect(mockClientInstance.onclose).toBeDefined();\n expect(typeof mockClientInstance.onclose).toBe(\"function\");\n });\n });\n});\n"]}
|