@tambo-ai/react 0.58.1 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hooks/react-query-hooks.d.ts +14 -1
- package/dist/hooks/react-query-hooks.d.ts.map +1 -1
- package/dist/hooks/react-query-hooks.js +13 -0
- package/dist/hooks/react-query-hooks.js.map +1 -1
- package/dist/hooks/use-tambo-stream-status.js +1 -1
- package/dist/hooks/use-tambo-stream-status.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/__tests__/elicitation.test.d.ts +2 -0
- package/dist/mcp/__tests__/elicitation.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/elicitation.test.js +261 -0
- package/dist/mcp/__tests__/elicitation.test.js.map +1 -0
- package/dist/mcp/__tests__/mcp-client.test.js +0 -266
- package/dist/mcp/__tests__/mcp-client.test.js.map +1 -1
- package/dist/mcp/__tests__/mcp-hooks.test.d.ts +2 -0
- package/dist/mcp/__tests__/mcp-hooks.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/mcp-hooks.test.js +504 -0
- package/dist/mcp/__tests__/mcp-hooks.test.js.map +1 -0
- package/dist/mcp/__tests__/tambo-mcp-provider.test.js +361 -16
- package/dist/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -1
- package/dist/mcp/__tests__/use-mcp-servers.test.js +34 -9
- package/dist/mcp/__tests__/use-mcp-servers.test.js.map +1 -1
- package/dist/mcp/elicitation.d.ts +80 -0
- package/dist/mcp/elicitation.d.ts.map +1 -0
- package/dist/mcp/elicitation.js +55 -0
- package/dist/mcp/elicitation.js.map +1 -0
- package/dist/mcp/index.d.ts +4 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +5 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/mcp-client.d.ts +51 -86
- package/dist/mcp/mcp-client.d.ts.map +1 -1
- package/dist/mcp/mcp-client.js +22 -159
- package/dist/mcp/mcp-client.js.map +1 -1
- package/dist/mcp/mcp-hooks.d.ts +107 -0
- package/dist/mcp/mcp-hooks.d.ts.map +1 -0
- package/dist/mcp/mcp-hooks.js +103 -0
- package/dist/mcp/mcp-hooks.js.map +1 -0
- package/dist/mcp/tambo-mcp-provider.d.ts +86 -4
- package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/dist/mcp/tambo-mcp-provider.js +275 -106
- package/dist/mcp/tambo-mcp-provider.js.map +1 -1
- package/dist/providers/__tests__/tambo-thread-provider-initial-messages.test.js +3 -1
- package/dist/providers/__tests__/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/dist/providers/__tests__/tambo-thread-provider.test.js +25 -12
- package/dist/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
- package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/dist/providers/tambo-interactable-provider.js +11 -4
- package/dist/providers/tambo-interactable-provider.js.map +1 -1
- package/dist/providers/tambo-mcp-token-provider.d.ts +34 -0
- package/dist/providers/tambo-mcp-token-provider.d.ts.map +1 -0
- package/dist/providers/tambo-mcp-token-provider.js +69 -0
- package/dist/providers/tambo-mcp-token-provider.js.map +1 -0
- package/dist/providers/tambo-provider.d.ts.map +1 -1
- package/dist/providers/tambo-provider.js +7 -9
- package/dist/providers/tambo-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-provider.js +14 -0
- package/dist/providers/tambo-thread-provider.js.map +1 -1
- package/esm/hooks/react-query-hooks.d.ts +14 -1
- package/esm/hooks/react-query-hooks.d.ts.map +1 -1
- package/esm/hooks/react-query-hooks.js +13 -1
- package/esm/hooks/react-query-hooks.js.map +1 -1
- package/esm/hooks/use-tambo-stream-status.js +1 -1
- package/esm/hooks/use-tambo-stream-status.js.map +1 -1
- package/esm/index.js +2 -0
- package/esm/index.js.map +1 -1
- package/esm/mcp/__tests__/elicitation.test.d.ts +2 -0
- package/esm/mcp/__tests__/elicitation.test.d.ts.map +1 -0
- package/esm/mcp/__tests__/elicitation.test.js +259 -0
- package/esm/mcp/__tests__/elicitation.test.js.map +1 -0
- package/esm/mcp/__tests__/mcp-client.test.js +0 -266
- package/esm/mcp/__tests__/mcp-client.test.js.map +1 -1
- package/esm/mcp/__tests__/mcp-hooks.test.d.ts +2 -0
- package/esm/mcp/__tests__/mcp-hooks.test.d.ts.map +1 -0
- package/esm/mcp/__tests__/mcp-hooks.test.js +469 -0
- package/esm/mcp/__tests__/mcp-hooks.test.js.map +1 -0
- package/esm/mcp/__tests__/tambo-mcp-provider.test.js +361 -16
- package/esm/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -1
- package/esm/mcp/__tests__/use-mcp-servers.test.js +34 -9
- package/esm/mcp/__tests__/use-mcp-servers.test.js.map +1 -1
- package/esm/mcp/elicitation.d.ts +80 -0
- package/esm/mcp/elicitation.d.ts.map +1 -0
- package/esm/mcp/elicitation.js +52 -0
- package/esm/mcp/elicitation.js.map +1 -0
- package/esm/mcp/index.d.ts +4 -1
- package/esm/mcp/index.d.ts.map +1 -1
- package/esm/mcp/index.js +2 -1
- package/esm/mcp/index.js.map +1 -1
- package/esm/mcp/mcp-client.d.ts +51 -86
- package/esm/mcp/mcp-client.d.ts.map +1 -1
- package/esm/mcp/mcp-client.js +22 -159
- package/esm/mcp/mcp-client.js.map +1 -1
- package/esm/mcp/mcp-hooks.d.ts +107 -0
- package/esm/mcp/mcp-hooks.d.ts.map +1 -0
- package/esm/mcp/mcp-hooks.js +99 -0
- package/esm/mcp/mcp-hooks.js.map +1 -0
- package/esm/mcp/tambo-mcp-provider.d.ts +86 -4
- package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/esm/mcp/tambo-mcp-provider.js +275 -107
- package/esm/mcp/tambo-mcp-provider.js.map +1 -1
- package/esm/providers/__tests__/tambo-thread-provider-initial-messages.test.js +3 -1
- package/esm/providers/__tests__/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/esm/providers/__tests__/tambo-thread-provider.test.js +25 -12
- package/esm/providers/__tests__/tambo-thread-provider.test.js.map +1 -1
- package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/esm/providers/tambo-interactable-provider.js +11 -4
- package/esm/providers/tambo-interactable-provider.js.map +1 -1
- package/esm/providers/tambo-mcp-token-provider.d.ts +34 -0
- package/esm/providers/tambo-mcp-token-provider.d.ts.map +1 -0
- package/esm/providers/tambo-mcp-token-provider.js +31 -0
- package/esm/providers/tambo-mcp-token-provider.js.map +1 -0
- package/esm/providers/tambo-provider.d.ts.map +1 -1
- package/esm/providers/tambo-provider.js +7 -9
- package/esm/providers/tambo-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-provider.js +14 -0
- package/esm/providers/tambo-thread-provider.js.map +1 -1
- package/package.json +8 -8
package/dist/mcp/mcp-client.js
CHANGED
|
@@ -21,6 +21,11 @@ var MCPTransport;
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
class MCPClient {
|
|
24
|
+
/**
|
|
25
|
+
* The underlying MCP client
|
|
26
|
+
*
|
|
27
|
+
* Be careful not to mutate the client directly, use the methods provided instead.
|
|
28
|
+
*/
|
|
24
29
|
client;
|
|
25
30
|
transport;
|
|
26
31
|
transportType;
|
|
@@ -29,42 +34,6 @@ class MCPClient {
|
|
|
29
34
|
headers;
|
|
30
35
|
authProvider;
|
|
31
36
|
handlers;
|
|
32
|
-
/**
|
|
33
|
-
* Tracks an in-flight reconnect so concurrent triggers coalesce
|
|
34
|
-
* (single-flight). When set, additional calls to `reconnect()` or
|
|
35
|
-
* the automatic `onclose` handler will await the same Promise instead of
|
|
36
|
-
* starting another reconnect sequence.
|
|
37
|
-
*/
|
|
38
|
-
reconnecting;
|
|
39
|
-
/**
|
|
40
|
-
* Timer id for a scheduled automatic reconnect (used by `onclose`).
|
|
41
|
-
* Present only while waiting for the backoff delay to elapse.
|
|
42
|
-
*/
|
|
43
|
-
reconnectTimer;
|
|
44
|
-
/**
|
|
45
|
-
* Count of consecutive automatic reconnect failures used to compute
|
|
46
|
-
* exponential backoff. Reset to 0 after a successful connection.
|
|
47
|
-
*/
|
|
48
|
-
backoffAttempts = 0;
|
|
49
|
-
/**
|
|
50
|
-
* Backoff policy (discoverable constants)
|
|
51
|
-
* - BACKOFF_INITIAL_MS: initial delay for the first automatic retry
|
|
52
|
-
* - BACKOFF_MULTIPLIER: exponential growth factor for each failed attempt
|
|
53
|
-
* - BACKOFF_MAX_MS: upper bound for the delay
|
|
54
|
-
* - BACKOFF_JITTER_RATIO: jitter range as a fraction of the base delay
|
|
55
|
-
*
|
|
56
|
-
* Jitter is applied symmetrically in [-ratio, +ratio]. For example, with a
|
|
57
|
-
* 500ms base delay and 0.2 ratio, the actual delay is in [400ms, 600ms].
|
|
58
|
-
*
|
|
59
|
-
* The backoff applies only to automatic reconnects started from the
|
|
60
|
-
* `onclose` handler. Explicit/manual calls to `reconnect()` run immediately
|
|
61
|
-
* (no backoff), and will preempt any scheduled automatic attempt.
|
|
62
|
-
*/
|
|
63
|
-
static BACKOFF_INITIAL_MS = 500;
|
|
64
|
-
static BACKOFF_MULTIPLIER = 2;
|
|
65
|
-
static BACKOFF_MAX_MS = 30_000;
|
|
66
|
-
static BACKOFF_JITTER_RATIO = 0.2;
|
|
67
|
-
elicitation = new EventTarget();
|
|
68
37
|
/**
|
|
69
38
|
* Private constructor to enforce using the static create method.
|
|
70
39
|
* @param endpoint - The URL of the MCP server to connect to
|
|
@@ -101,127 +70,6 @@ class MCPClient {
|
|
|
101
70
|
}
|
|
102
71
|
return mcpClient;
|
|
103
72
|
}
|
|
104
|
-
/**
|
|
105
|
-
* Reconnects to the MCP server, optionally retaining the same session ID.
|
|
106
|
-
*
|
|
107
|
-
* Single‑flight semantics:
|
|
108
|
-
* - If a reconnect is already in progress (triggered either manually or by
|
|
109
|
-
* the automatic `onclose` handler), additional calls will await the
|
|
110
|
-
* in-flight reconnect rather than start another one.
|
|
111
|
-
* - If an automatic reconnect has been scheduled but not yet started (i.e.,
|
|
112
|
-
* we are waiting in a backoff delay), calling `reconnect()` manually will
|
|
113
|
-
* cancel the scheduled attempt and perform an immediate reconnect.
|
|
114
|
-
*
|
|
115
|
-
* Backoff policy:
|
|
116
|
-
* - Backoff delays with jitter are applied only for automatic reconnects
|
|
117
|
-
* (via `onclose`). Manual calls to `reconnect()` do not use backoff.
|
|
118
|
-
* @param newSession - Whether to create a new session (true) or reuse existing session ID (false)
|
|
119
|
-
* @param reportErrorOnClose - Whether to report errors when closing the client
|
|
120
|
-
* Note that only StreamableHTTPClientTransport supports session IDs.
|
|
121
|
-
* @returns A promise that resolves when the reconnect is complete
|
|
122
|
-
*/
|
|
123
|
-
async reconnect(newSession = false, reportErrorOnClose = true) {
|
|
124
|
-
// If a reconnect is already running, coalesce into it.
|
|
125
|
-
if (this.reconnecting) {
|
|
126
|
-
return await this.reconnecting;
|
|
127
|
-
}
|
|
128
|
-
// Manual reconnect preempts any scheduled automatic attempt.
|
|
129
|
-
if (this.reconnectTimer) {
|
|
130
|
-
clearTimeout(this.reconnectTimer);
|
|
131
|
-
this.reconnectTimer = undefined;
|
|
132
|
-
}
|
|
133
|
-
const doReconnect = async () => {
|
|
134
|
-
const sessionId = newSession ? undefined : this.sessionId;
|
|
135
|
-
// Prevent re-entrant onclose during deliberate close by detaching
|
|
136
|
-
// the handler from the previous client instance.
|
|
137
|
-
const prevClient = this.client;
|
|
138
|
-
// Prevent re-entrant onclose callbacks from the previous client
|
|
139
|
-
prevClient.onclose = undefined;
|
|
140
|
-
try {
|
|
141
|
-
await prevClient.close();
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
if (reportErrorOnClose) {
|
|
145
|
-
console.error("Error closing Tambo MCP Client:", error);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
this.transport = this.initializeTransport(sessionId);
|
|
149
|
-
this.client = this.initializeClient();
|
|
150
|
-
await this.client.connect(this.transport);
|
|
151
|
-
// We may have gotten a session id from the server, so we need to set it
|
|
152
|
-
if ("sessionId" in this.transport) {
|
|
153
|
-
this.sessionId = this.transport.sessionId;
|
|
154
|
-
if (sessionId !== this.sessionId) {
|
|
155
|
-
// This is a pretty unusual thing to happen, but it might be possible?
|
|
156
|
-
console.warn("Session id mismatch", sessionId, this.sessionId);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
this.reconnecting = (async () => {
|
|
161
|
-
try {
|
|
162
|
-
await doReconnect();
|
|
163
|
-
// Successful manual reconnect: reset backoff.
|
|
164
|
-
this.backoffAttempts = 0;
|
|
165
|
-
}
|
|
166
|
-
finally {
|
|
167
|
-
this.reconnecting = undefined;
|
|
168
|
-
}
|
|
169
|
-
})();
|
|
170
|
-
return await this.reconnecting;
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Called by the underlying MCP SDK when the connection closes.
|
|
174
|
-
* Schedules an automatic reconnect with bounded exponential backoff and
|
|
175
|
-
* jitter. If a reconnect is already scheduled or running, this is a no-op.
|
|
176
|
-
*/
|
|
177
|
-
onclose() {
|
|
178
|
-
this.scheduleAutoReconnect();
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Compute the next backoff delay with symmetric jitter.
|
|
182
|
-
* @returns The next backoff delay in milliseconds
|
|
183
|
-
*/
|
|
184
|
-
computeBackoffDelayMs() {
|
|
185
|
-
const base = Math.min(MCPClient.BACKOFF_MAX_MS, MCPClient.BACKOFF_INITIAL_MS *
|
|
186
|
-
Math.pow(MCPClient.BACKOFF_MULTIPLIER, this.backoffAttempts));
|
|
187
|
-
const jitterRange = MCPClient.BACKOFF_JITTER_RATIO * base;
|
|
188
|
-
const jitter = (Math.random() * 2 - 1) * jitterRange; // [-range, +range]
|
|
189
|
-
const ms = Math.max(0, Math.round(base + jitter));
|
|
190
|
-
return ms;
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Schedule an automatic reconnect attempt if one is not already scheduled
|
|
194
|
-
* or running. Uses the backoff policy and self-reschedules on failure.
|
|
195
|
-
*/
|
|
196
|
-
scheduleAutoReconnect() {
|
|
197
|
-
if (this.reconnecting || this.reconnectTimer) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
const delayMs = this.computeBackoffDelayMs();
|
|
201
|
-
console.warn("Tambo MCP Client closed; attempting automatic reconnect in", `${delayMs}ms`);
|
|
202
|
-
this.reconnectTimer = setTimeout(async () => {
|
|
203
|
-
this.reconnectTimer = undefined;
|
|
204
|
-
// Start the actual reconnect (single-flight)
|
|
205
|
-
const inFlight = (this.reconnecting = this.reconnect(false, false));
|
|
206
|
-
try {
|
|
207
|
-
await inFlight;
|
|
208
|
-
// Success: reset attempts
|
|
209
|
-
this.backoffAttempts = 0;
|
|
210
|
-
}
|
|
211
|
-
catch (err) {
|
|
212
|
-
// Failure: increase attempts; scheduling occurs in finally below so the
|
|
213
|
-
// new timer isn't blocked by `this.reconnecting` being truthy.
|
|
214
|
-
this.backoffAttempts += 1;
|
|
215
|
-
console.warn("Automatic reconnect failed; will retry with backoff.", err);
|
|
216
|
-
}
|
|
217
|
-
finally {
|
|
218
|
-
this.reconnecting = undefined;
|
|
219
|
-
if (this.backoffAttempts > 0) {
|
|
220
|
-
this.scheduleAutoReconnect();
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}, delayMs);
|
|
224
|
-
}
|
|
225
73
|
initializeTransport(sessionId) {
|
|
226
74
|
if (this.transportType === MCPTransport.SSE) {
|
|
227
75
|
return new sse_js_1.SSEClientTransport(new URL(this.endpoint), {
|
|
@@ -255,7 +103,6 @@ class MCPClient {
|
|
|
255
103
|
...samplingCapability,
|
|
256
104
|
},
|
|
257
105
|
});
|
|
258
|
-
client.onclose = this.onclose.bind(this);
|
|
259
106
|
if (this.handlers.elicitation) {
|
|
260
107
|
client.setRequestHandler(types_js_1.ElicitRequestSchema, this.handlers.elicitation);
|
|
261
108
|
}
|
|
@@ -313,14 +160,19 @@ class MCPClient {
|
|
|
313
160
|
* @returns The result from the tool execution
|
|
314
161
|
* @throws {Error} Will throw an error if the tool call fails or if arguments are invalid
|
|
315
162
|
*/
|
|
316
|
-
async callTool(name, args) {
|
|
163
|
+
async callTool(name, args, _meta) {
|
|
317
164
|
const result = await this.client.callTool({
|
|
318
165
|
name,
|
|
319
166
|
arguments: args,
|
|
167
|
+
_meta,
|
|
320
168
|
});
|
|
321
169
|
return result;
|
|
322
170
|
}
|
|
323
171
|
updateElicitationHandler(handler) {
|
|
172
|
+
// Skip if handler hasn't changed
|
|
173
|
+
if (handler === this.handlers.elicitation) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
324
176
|
// Because we advertise the elicitation capability on initial connection, we can only update
|
|
325
177
|
// an existing handler, not add it if we haven't set it yet.
|
|
326
178
|
if (handler && !this.handlers.elicitation) {
|
|
@@ -338,6 +190,10 @@ class MCPClient {
|
|
|
338
190
|
this.client.setRequestHandler(types_js_1.ElicitRequestSchema, handler);
|
|
339
191
|
}
|
|
340
192
|
updateSamplingHandler(handler) {
|
|
193
|
+
// Skip if handler hasn't changed
|
|
194
|
+
if (handler === this.handlers.sampling) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
341
197
|
// Because we advertise the sampling capability on initial connection, we can only update
|
|
342
198
|
// an existing handler, not add it if we haven't set it yet.
|
|
343
199
|
if (handler && !this.handlers.sampling) {
|
|
@@ -354,6 +210,13 @@ class MCPClient {
|
|
|
354
210
|
}
|
|
355
211
|
this.client.setRequestHandler(types_js_1.CreateMessageRequestSchema, handler);
|
|
356
212
|
}
|
|
213
|
+
async close() {
|
|
214
|
+
// Not really sure which one of these to close first, but we'll close the
|
|
215
|
+
// transport first so that no requests can come in and hit closing/closed
|
|
216
|
+
// clients
|
|
217
|
+
await this.transport.close();
|
|
218
|
+
await this.client.close();
|
|
219
|
+
}
|
|
357
220
|
}
|
|
358
221
|
exports.MCPClient = MCPClient;
|
|
359
222
|
//# sourceMappingURL=mcp-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-client.js","sourceRoot":"","sources":["../../src/mcp/mcp-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { type OAuthClientProvider } from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { SSEClientTransport } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport {\n CreateMessageRequest,\n CreateMessageRequestSchema,\n CreateMessageResult,\n ElicitRequest,\n ElicitRequestSchema,\n ElicitResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { JSONSchema7 } from \"json-schema\";\n\nexport enum MCPTransport {\n SSE = \"sse\",\n HTTP = \"http\",\n}\n\n/**\n * Handlers for MCP requests - these are only used if the server supports the corresponding capabilities\n * @param elicitation - Handler for elicitation requests\n * @param sampling - Handler for sampling requests\n * @example\n * ```typescript\n * const mcp = await MCPClient.create(\n * 'https://api.example.com/mcp',\n * MCPTransport.HTTP,\n * {},\n * undefined,\n * undefined,\n * {\n * elicitation: (e: ElicitRequest) => Promise.resolve({...}),\n * },\n * });\n * ```\n */\ninterface MCPHandlers {\n elicitation?: (e: ElicitRequest) => Promise<ElicitResult>;\n sampling?: (e: CreateMessageRequest) => Promise<CreateMessageResult>;\n}\n\n/**\n * A client for interacting with MCP (Model Context Protocol) servers.\n * Provides a simple interface for listing and calling tools exposed by the server.\n * @example\n * ```typescript\n * const mcp = await MCPClient.create('https://api.example.com/mcp');\n * const tools = await mcp.listTools();\n * const result = await mcp.callTool('toolName', { arg1: 'value1' });\n * ```\n */\nexport class MCPClient {\n private client: Client;\n private transport: SSEClientTransport | StreamableHTTPClientTransport;\n private transportType: MCPTransport;\n public sessionId?: string;\n private endpoint: string;\n private headers: Record<string, string>;\n private authProvider?: OAuthClientProvider;\n private handlers: MCPHandlers;\n /**\n * Tracks an in-flight reconnect so concurrent triggers coalesce\n * (single-flight). When set, additional calls to `reconnect()` or\n * the automatic `onclose` handler will await the same Promise instead of\n * starting another reconnect sequence.\n */\n private reconnecting?: Promise<void>;\n /**\n * Timer id for a scheduled automatic reconnect (used by `onclose`).\n * Present only while waiting for the backoff delay to elapse.\n */\n private reconnectTimer?: ReturnType<typeof setTimeout>;\n /**\n * Count of consecutive automatic reconnect failures used to compute\n * exponential backoff. Reset to 0 after a successful connection.\n */\n private backoffAttempts = 0;\n\n /**\n * Backoff policy (discoverable constants)\n * - BACKOFF_INITIAL_MS: initial delay for the first automatic retry\n * - BACKOFF_MULTIPLIER: exponential growth factor for each failed attempt\n * - BACKOFF_MAX_MS: upper bound for the delay\n * - BACKOFF_JITTER_RATIO: jitter range as a fraction of the base delay\n *\n * Jitter is applied symmetrically in [-ratio, +ratio]. For example, with a\n * 500ms base delay and 0.2 ratio, the actual delay is in [400ms, 600ms].\n *\n * The backoff applies only to automatic reconnects started from the\n * `onclose` handler. Explicit/manual calls to `reconnect()` run immediately\n * (no backoff), and will preempt any scheduled automatic attempt.\n */\n static readonly BACKOFF_INITIAL_MS = 500;\n static readonly BACKOFF_MULTIPLIER = 2;\n static readonly BACKOFF_MAX_MS = 30_000;\n static readonly BACKOFF_JITTER_RATIO = 0.2;\n\n public elicitation: EventTarget = new EventTarget();\n /**\n * Private constructor to enforce using the static create method.\n * @param endpoint - The URL of the MCP server to connect to\n * @param transportType - The transport to use for the MCP client\n * @param headers - Optional custom headers to include in requests\n */\n private constructor(\n endpoint: string,\n transportType: MCPTransport,\n headers?: Record<string, string>,\n authProvider?: OAuthClientProvider,\n sessionId?: string,\n handlers: MCPHandlers = {},\n ) {\n this.endpoint = endpoint;\n this.headers = headers ?? {};\n this.authProvider = authProvider;\n this.transportType = transportType;\n this.handlers = handlers;\n this.transport = this.initializeTransport(sessionId);\n this.client = this.initializeClient();\n }\n\n /**\n * Creates and initializes a new MCPClient instance. This is the recommended\n * way to create an MCPClient as it handles both instantiation and connection\n * setup.\n * @param endpoint - The URL of the MCP server to connect to\n * @param transportType - The transport type to use for the MCP client. Defaults to HTTP.\n * @param headers - Optional custom headers to include in requests\n * @param authProvider - Optional auth provider to use for authentication\n * @param sessionId - Optional session id to use for the MCP client - if not\n * provided, a new session will be created\n * @returns A connected MCPClient instance ready for use\n * @throws {Error} Will throw an error if connection fails\n */\n static async create(\n endpoint: string,\n transportType: MCPTransport = MCPTransport.HTTP,\n headers: Record<string, string> | undefined,\n authProvider: OAuthClientProvider | undefined,\n sessionId: string | undefined,\n handlers: MCPHandlers = {},\n ): Promise<MCPClient> {\n const mcpClient = new MCPClient(\n endpoint,\n transportType,\n headers,\n authProvider,\n sessionId,\n handlers,\n );\n await mcpClient.client.connect(mcpClient.transport);\n if (\"sessionId\" in mcpClient.transport) {\n mcpClient.sessionId = mcpClient.transport.sessionId;\n }\n return mcpClient;\n }\n /**\n * Reconnects to the MCP server, optionally retaining the same session ID.\n *\n * Single‑flight semantics:\n * - If a reconnect is already in progress (triggered either manually or by\n * the automatic `onclose` handler), additional calls will await the\n * in-flight reconnect rather than start another one.\n * - If an automatic reconnect has been scheduled but not yet started (i.e.,\n * we are waiting in a backoff delay), calling `reconnect()` manually will\n * cancel the scheduled attempt and perform an immediate reconnect.\n *\n * Backoff policy:\n * - Backoff delays with jitter are applied only for automatic reconnects\n * (via `onclose`). Manual calls to `reconnect()` do not use backoff.\n * @param newSession - Whether to create a new session (true) or reuse existing session ID (false)\n * @param reportErrorOnClose - Whether to report errors when closing the client\n * Note that only StreamableHTTPClientTransport supports session IDs.\n * @returns A promise that resolves when the reconnect is complete\n */\n async reconnect(newSession = false, reportErrorOnClose = true) {\n // If a reconnect is already running, coalesce into it.\n if (this.reconnecting) {\n return await this.reconnecting;\n }\n\n // Manual reconnect preempts any scheduled automatic attempt.\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n }\n\n const doReconnect = async () => {\n const sessionId = newSession ? undefined : this.sessionId;\n\n // Prevent re-entrant onclose during deliberate close by detaching\n // the handler from the previous client instance.\n const prevClient = this.client;\n // Prevent re-entrant onclose callbacks from the previous client\n prevClient.onclose = undefined;\n\n try {\n await prevClient.close();\n } catch (error) {\n if (reportErrorOnClose) {\n console.error(\"Error closing Tambo MCP Client:\", error);\n }\n }\n\n this.transport = this.initializeTransport(sessionId);\n this.client = this.initializeClient();\n await this.client.connect(this.transport);\n // We may have gotten a session id from the server, so we need to set it\n if (\"sessionId\" in this.transport) {\n this.sessionId = this.transport.sessionId;\n if (sessionId !== this.sessionId) {\n // This is a pretty unusual thing to happen, but it might be possible?\n console.warn(\"Session id mismatch\", sessionId, this.sessionId);\n }\n }\n };\n\n this.reconnecting = (async () => {\n try {\n await doReconnect();\n // Successful manual reconnect: reset backoff.\n this.backoffAttempts = 0;\n } finally {\n this.reconnecting = undefined;\n }\n })();\n\n return await this.reconnecting;\n }\n\n /**\n * Called by the underlying MCP SDK when the connection closes.\n * Schedules an automatic reconnect with bounded exponential backoff and\n * jitter. If a reconnect is already scheduled or running, this is a no-op.\n */\n private onclose() {\n this.scheduleAutoReconnect();\n }\n\n /**\n * Compute the next backoff delay with symmetric jitter.\n * @returns The next backoff delay in milliseconds\n */\n private computeBackoffDelayMs(): number {\n const base = Math.min(\n MCPClient.BACKOFF_MAX_MS,\n MCPClient.BACKOFF_INITIAL_MS *\n Math.pow(MCPClient.BACKOFF_MULTIPLIER, this.backoffAttempts),\n );\n const jitterRange = MCPClient.BACKOFF_JITTER_RATIO * base;\n const jitter = (Math.random() * 2 - 1) * jitterRange; // [-range, +range]\n const ms = Math.max(0, Math.round(base + jitter));\n return ms;\n }\n\n /**\n * Schedule an automatic reconnect attempt if one is not already scheduled\n * or running. Uses the backoff policy and self-reschedules on failure.\n */\n private scheduleAutoReconnect() {\n if (this.reconnecting || this.reconnectTimer) {\n return;\n }\n\n const delayMs = this.computeBackoffDelayMs();\n console.warn(\n \"Tambo MCP Client closed; attempting automatic reconnect in\",\n `${delayMs}ms`,\n );\n\n this.reconnectTimer = setTimeout(async () => {\n this.reconnectTimer = undefined;\n // Start the actual reconnect (single-flight)\n const inFlight = (this.reconnecting = this.reconnect(false, false));\n try {\n await inFlight;\n // Success: reset attempts\n this.backoffAttempts = 0;\n } catch (err) {\n // Failure: increase attempts; scheduling occurs in finally below so the\n // new timer isn't blocked by `this.reconnecting` being truthy.\n this.backoffAttempts += 1;\n console.warn(\n \"Automatic reconnect failed; will retry with backoff.\",\n err,\n );\n } finally {\n this.reconnecting = undefined;\n if (this.backoffAttempts > 0) {\n this.scheduleAutoReconnect();\n }\n }\n }, delayMs);\n }\n\n private initializeTransport(sessionId: string | undefined) {\n if (this.transportType === MCPTransport.SSE) {\n return new SSEClientTransport(new URL(this.endpoint), {\n authProvider: this.authProvider,\n requestInit: { headers: this.headers },\n });\n } else {\n return new StreamableHTTPClientTransport(new URL(this.endpoint), {\n authProvider: this.authProvider,\n requestInit: { headers: this.headers },\n sessionId,\n });\n }\n }\n\n /**\n * Initializes the MCP client with the appropriate capabilities and handlers\n * @returns The initialized MCP client\n */\n private initializeClient() {\n const elicitationCapability = this.handlers.elicitation\n ? { elicitation: {} }\n : {};\n const samplingCapability = this.handlers.sampling ? { sampling: {} } : {};\n const client = new Client(\n {\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n },\n {\n capabilities: {\n ...elicitationCapability,\n ...samplingCapability,\n },\n },\n );\n client.onclose = this.onclose.bind(this);\n if (this.handlers.elicitation) {\n client.setRequestHandler(ElicitRequestSchema, this.handlers.elicitation);\n }\n if (this.handlers.sampling) {\n client.setRequestHandler(\n CreateMessageRequestSchema,\n this.handlers.sampling,\n );\n }\n return client;\n }\n\n /**\n * Retrieves a complete list of all available tools from the MCP server.\n * Handles pagination automatically by following cursors until all tools are fetched.\n * @returns A complete list of all available tools and their descriptions\n * @throws {Error} Will throw an error if any server request fails during pagination\n */\n async listTools(): Promise<MCPToolSpec[]> {\n const allTools: MCPToolSpec[] = [];\n let hasMore = true;\n let cursor: string | undefined = undefined;\n\n while (hasMore) {\n const response = await this.client.listTools({ cursor }, {});\n allTools.push(\n ...response.tools.map((tool): MCPToolSpec => {\n // make sure the right type is used\n const inputSchemaType: string = tool.inputSchema.type;\n if (inputSchemaType !== \"object\") {\n throw new Error(\n `Input schema for tool ${tool.name} is not an object`,\n );\n }\n\n return {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema as JSONSchema7,\n };\n }),\n );\n\n if (response.nextCursor) {\n cursor = response.nextCursor;\n } else {\n hasMore = false;\n }\n }\n\n return allTools;\n }\n\n getServerCapabilities() {\n return this.client.getServerCapabilities();\n }\n\n getServerVersion() {\n return this.client.getServerVersion();\n }\n\n getInstructions() {\n return this.client.getInstructions();\n }\n\n /**\n * Calls a specific tool on the MCP server with the provided arguments.\n * @param name - The name of the tool to call\n * @param args - Arguments to pass to the tool, must match the tool's expected schema\n * @returns The result from the tool execution\n * @throws {Error} Will throw an error if the tool call fails or if arguments are invalid\n */\n async callTool(\n name: string,\n args: Record<string, unknown>,\n ): Promise<MCPToolCallResult> {\n const result = await this.client.callTool({\n name,\n arguments: args,\n });\n return result;\n }\n\n updateElicitationHandler(\n handler: ((e: ElicitRequest) => Promise<ElicitResult>) | undefined,\n ) {\n // Because we advertise the elicitation capability on initial connection, we can only update\n // an existing handler, not add it if we haven't set it yet.\n if (handler && !this.handlers.elicitation) {\n throw new Error(\"Elicitation handler must be set on create\");\n }\n this.handlers = {\n ...this.handlers,\n elicitation: handler,\n };\n if (!handler) {\n const method = ElicitRequestSchema.shape.method.value;\n this.client.removeRequestHandler(method);\n return;\n }\n this.client.setRequestHandler(ElicitRequestSchema, handler);\n }\n\n updateSamplingHandler(\n handler:\n | ((e: CreateMessageRequest) => Promise<CreateMessageResult>)\n | undefined,\n ) {\n // Because we advertise the sampling capability on initial connection, we can only update\n // an existing handler, not add it if we haven't set it yet.\n if (handler && !this.handlers.sampling) {\n throw new Error(\"Sampling handler must be set on create\");\n }\n this.handlers = {\n ...this.handlers,\n sampling: handler,\n };\n if (!handler) {\n const method = CreateMessageRequestSchema.shape.method.value;\n this.client.removeRequestHandler(method);\n return;\n }\n this.client.setRequestHandler(CreateMessageRequestSchema, handler);\n }\n}\n\n/**\n * The result of a tool call.\n * This is the same as the result of a tool call in the OpenAI SDK, but is reified here\n */\nexport type MCPToolCallResult = Awaited<\n ReturnType<typeof Client.prototype.callTool>\n>;\n\n// Example usage:\n/*\nconst mcp = await MCPClient.create('https://api.example.com/mcp', MCPTransport.HTTP);\nconst tools = await mcp.listTools();\nconst result = await mcp.callTool('toolName', { arg1: 'value1' });\n*/\n\nexport interface MCPToolSpec {\n name: string;\n description?: string;\n inputSchema?: JSONSchema7;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mcp-client.js","sourceRoot":"","sources":["../../src/mcp/mcp-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { type OAuthClientProvider } from \"@modelcontextprotocol/sdk/client/auth.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { SSEClientTransport } from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport {\n ClientNotification,\n ClientRequest,\n CreateMessageRequest,\n CreateMessageRequestSchema,\n CreateMessageResult,\n ElicitRequest,\n ElicitRequestSchema,\n ElicitResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { JSONSchema7 } from \"json-schema\";\n\nexport enum MCPTransport {\n SSE = \"sse\",\n HTTP = \"http\",\n}\n\n/**\n * Handler for MCP elicitation requests.\n * Receives the elicit request and a RequestHandlerExtra containing an AbortSignal that fires when the request is cancelled.\n * @param request - The elicitation request from the server\n * @param extra - Additional context including AbortSignal for cancellation\n * @returns Promise resolving to the elicitation result\n * @example\n * ```typescript\n * const handler: MCPElicitationHandler = async (request, extra) => {\n * // Listen for cancellation\n * extra.signal.addEventListener('abort', () => {\n * console.log('Request cancelled');\n * });\n *\n * // Return user's response\n * return {\n * action: 'accept',\n * content: { name: 'John' }\n * };\n * };\n * ```\n */\nexport type MCPElicitationHandler = (\n e: ElicitRequest,\n extra: RequestHandlerExtra<ClientRequest, ClientNotification>,\n) => Promise<ElicitResult>;\n\n/**\n * Handler for MCP sampling requests (create_message).\n * Receives the sampling request and a RequestHandlerExtra containing an AbortSignal that fires when the request is cancelled.\n * @param request - The sampling/create_message request from the server\n * @param extra - Additional context including AbortSignal for cancellation\n * @returns Promise resolving to the sampling result\n */\nexport type MCPSamplingHandler = (\n e: CreateMessageRequest,\n extra: RequestHandlerExtra<ClientRequest, ClientNotification>,\n) => Promise<CreateMessageResult>;\n\n/**\n * Handlers for MCP requests - these are only used if the server supports the corresponding capabilities\n * @param elicitation - Handler for elicitation requests (receives request and RequestHandlerExtra with AbortSignal)\n * @param sampling - Handler for sampling requests (receives request and RequestHandlerExtra with AbortSignal)\n * @example\n * ```typescript\n * const mcp = await MCPClient.create(\n * 'https://api.example.com/mcp',\n * MCPTransport.HTTP,\n * {},\n * undefined,\n * undefined,\n * {\n * elicitation: (e, extra) => Promise.resolve({...}),\n * },\n * );\n * ```\n */\nexport interface MCPHandlers {\n elicitation: MCPElicitationHandler;\n sampling: MCPSamplingHandler;\n}\n\n/**\n * A client for interacting with MCP (Model Context Protocol) servers.\n * Provides a simple interface for listing and calling tools exposed by the server.\n * @example\n * ```typescript\n * const mcp = await MCPClient.create('https://api.example.com/mcp');\n * const tools = await mcp.listTools();\n * const result = await mcp.callTool('toolName', { arg1: 'value1' });\n * ```\n */\nexport class MCPClient {\n /**\n * The underlying MCP client\n *\n * Be careful not to mutate the client directly, use the methods provided instead.\n */\n client: Client;\n private transport: SSEClientTransport | StreamableHTTPClientTransport;\n private transportType: MCPTransport;\n public sessionId?: string;\n private endpoint: string;\n private headers: Record<string, string>;\n private authProvider?: OAuthClientProvider;\n private handlers: Partial<MCPHandlers>;\n\n /**\n * Private constructor to enforce using the static create method.\n * @param endpoint - The URL of the MCP server to connect to\n * @param transportType - The transport to use for the MCP client\n * @param headers - Optional custom headers to include in requests\n */\n private constructor(\n endpoint: string,\n transportType: MCPTransport,\n headers?: Record<string, string>,\n authProvider?: OAuthClientProvider,\n sessionId?: string,\n handlers: Partial<MCPHandlers> = {},\n ) {\n this.endpoint = endpoint;\n this.headers = headers ?? {};\n this.authProvider = authProvider;\n this.transportType = transportType;\n this.handlers = handlers;\n this.transport = this.initializeTransport(sessionId);\n this.client = this.initializeClient();\n }\n\n /**\n * Creates and initializes a new MCPClient instance. This is the recommended\n * way to create an MCPClient as it handles both instantiation and connection\n * setup.\n * @param endpoint - The URL of the MCP server to connect to\n * @param transportType - The transport type to use for the MCP client. Defaults to HTTP.\n * @param headers - Optional custom headers to include in requests\n * @param authProvider - Optional auth provider to use for authentication\n * @param sessionId - Optional session id to use for the MCP client - if not\n * provided, a new session will be created\n * @returns A connected MCPClient instance ready for use\n * @throws {Error} Will throw an error if connection fails\n */\n static async create(\n endpoint: string,\n transportType: MCPTransport = MCPTransport.HTTP,\n headers: Record<string, string> | undefined,\n authProvider: OAuthClientProvider | undefined,\n sessionId: string | undefined,\n handlers: Partial<MCPHandlers> = {},\n ): Promise<MCPClient> {\n const mcpClient = new MCPClient(\n endpoint,\n transportType,\n headers,\n authProvider,\n sessionId,\n handlers,\n );\n await mcpClient.client.connect(mcpClient.transport);\n if (\"sessionId\" in mcpClient.transport) {\n mcpClient.sessionId = mcpClient.transport.sessionId;\n }\n return mcpClient;\n }\n\n private initializeTransport(sessionId: string | undefined) {\n if (this.transportType === MCPTransport.SSE) {\n return new SSEClientTransport(new URL(this.endpoint), {\n authProvider: this.authProvider,\n requestInit: { headers: this.headers },\n });\n } else {\n return new StreamableHTTPClientTransport(new URL(this.endpoint), {\n authProvider: this.authProvider,\n requestInit: { headers: this.headers },\n sessionId,\n });\n }\n }\n\n /**\n * Initializes the MCP client with the appropriate capabilities and handlers\n * @returns The initialized MCP client\n */\n private initializeClient() {\n const elicitationCapability = this.handlers.elicitation\n ? { elicitation: {} }\n : {};\n const samplingCapability = this.handlers.sampling ? { sampling: {} } : {};\n const client = new Client(\n {\n name: \"tambo-mcp-client\",\n version: \"1.0.0\",\n },\n {\n capabilities: {\n ...elicitationCapability,\n ...samplingCapability,\n },\n },\n );\n\n if (this.handlers.elicitation) {\n client.setRequestHandler(ElicitRequestSchema, this.handlers.elicitation);\n }\n if (this.handlers.sampling) {\n client.setRequestHandler(\n CreateMessageRequestSchema,\n this.handlers.sampling,\n );\n }\n return client;\n }\n\n /**\n * Retrieves a complete list of all available tools from the MCP server.\n * Handles pagination automatically by following cursors until all tools are fetched.\n * @returns A complete list of all available tools and their descriptions\n * @throws {Error} Will throw an error if any server request fails during pagination\n */\n async listTools(): Promise<MCPToolSpec[]> {\n const allTools: MCPToolSpec[] = [];\n let hasMore = true;\n let cursor: string | undefined = undefined;\n\n while (hasMore) {\n const response = await this.client.listTools({ cursor }, {});\n allTools.push(\n ...response.tools.map((tool): MCPToolSpec => {\n // make sure the right type is used\n const inputSchemaType: string = tool.inputSchema.type;\n if (inputSchemaType !== \"object\") {\n throw new Error(\n `Input schema for tool ${tool.name} is not an object`,\n );\n }\n\n return {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema as JSONSchema7,\n };\n }),\n );\n\n if (response.nextCursor) {\n cursor = response.nextCursor;\n } else {\n hasMore = false;\n }\n }\n\n return allTools;\n }\n\n getServerCapabilities() {\n return this.client.getServerCapabilities();\n }\n\n getServerVersion() {\n return this.client.getServerVersion();\n }\n\n getInstructions() {\n return this.client.getInstructions();\n }\n\n /**\n * Calls a specific tool on the MCP server with the provided arguments.\n * @param name - The name of the tool to call\n * @param args - Arguments to pass to the tool, must match the tool's expected schema\n * @returns The result from the tool execution\n * @throws {Error} Will throw an error if the tool call fails or if arguments are invalid\n */\n async callTool(\n name: string,\n args: Record<string, unknown>,\n _meta?: Record<string, unknown>,\n ): Promise<MCPToolCallResult> {\n const result = await this.client.callTool({\n name,\n arguments: args,\n _meta,\n });\n return result;\n }\n\n updateElicitationHandler(handler: MCPElicitationHandler | undefined) {\n // Skip if handler hasn't changed\n if (handler === this.handlers.elicitation) {\n return;\n }\n\n // Because we advertise the elicitation capability on initial connection, we can only update\n // an existing handler, not add it if we haven't set it yet.\n if (handler && !this.handlers.elicitation) {\n throw new Error(\"Elicitation handler must be set on create\");\n }\n this.handlers = {\n ...this.handlers,\n elicitation: handler,\n };\n if (!handler) {\n const method = ElicitRequestSchema.shape.method.value;\n this.client.removeRequestHandler(method);\n return;\n }\n this.client.setRequestHandler(ElicitRequestSchema, handler);\n }\n\n updateSamplingHandler(handler: MCPSamplingHandler | undefined) {\n // Skip if handler hasn't changed\n if (handler === this.handlers.sampling) {\n return;\n }\n\n // Because we advertise the sampling capability on initial connection, we can only update\n // an existing handler, not add it if we haven't set it yet.\n if (handler && !this.handlers.sampling) {\n throw new Error(\"Sampling handler must be set on create\");\n }\n this.handlers = {\n ...this.handlers,\n sampling: handler,\n };\n if (!handler) {\n const method = CreateMessageRequestSchema.shape.method.value;\n this.client.removeRequestHandler(method);\n return;\n }\n this.client.setRequestHandler(CreateMessageRequestSchema, handler);\n }\n\n async close() {\n // Not really sure which one of these to close first, but we'll close the\n // transport first so that no requests can come in and hit closing/closed\n // clients\n await this.transport.close();\n await this.client.close();\n }\n}\n\n/**\n * The result of a tool call.\n * This is the same as the result of a tool call in the OpenAI SDK, but is reified here\n */\nexport type MCPToolCallResult = Awaited<\n ReturnType<typeof Client.prototype.callTool>\n>;\n\n// Example usage:\n/*\nconst mcp = await MCPClient.create('https://api.example.com/mcp', MCPTransport.HTTP);\nconst tools = await mcp.listTools();\nconst result = await mcp.callTool('toolName', { arg1: 'value1' });\n*/\n\nexport interface MCPToolSpec {\n name: string;\n description?: string;\n inputSchema?: JSONSchema7;\n}\n"]}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { type ListPromptsResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { UseQueryResult } from "@tanstack/react-query";
|
|
3
|
+
import { type ConnectedMcpServer } from "./tambo-mcp-provider";
|
|
4
|
+
export type ListPromptItem = ListPromptsResult["prompts"][number];
|
|
5
|
+
export interface ListPromptEntry {
|
|
6
|
+
server: ConnectedMcpServer;
|
|
7
|
+
prompt: ListPromptItem;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Hook to get the prompts for all the registered MCP servers.
|
|
11
|
+
* @returns The prompts for the MCP servers, including the server that the prompt was found on.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useTamboMcpPromptList(): {
|
|
14
|
+
data: ListPromptEntry[];
|
|
15
|
+
error: Error | null;
|
|
16
|
+
errors: Error[];
|
|
17
|
+
isPending: boolean;
|
|
18
|
+
isSuccess: boolean;
|
|
19
|
+
isError: boolean;
|
|
20
|
+
isPaused: boolean;
|
|
21
|
+
isRefetching: boolean;
|
|
22
|
+
isFetching: boolean;
|
|
23
|
+
isLoading: boolean;
|
|
24
|
+
refetch: () => Promise<void>;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Hook to get the prompt for the specified name.
|
|
28
|
+
* @param promptName - The name of the prompt to get. If the prompt won't return anything
|
|
29
|
+
* @param args - The arguments to pass to the prompt.
|
|
30
|
+
* @returns The prompt for the specified name.
|
|
31
|
+
*/
|
|
32
|
+
export declare function useTamboMcpPrompt(promptName: string | undefined, args?: Record<string, string>): UseQueryResult<{
|
|
33
|
+
[x: string]: unknown;
|
|
34
|
+
messages: {
|
|
35
|
+
[x: string]: unknown;
|
|
36
|
+
content: {
|
|
37
|
+
[x: string]: unknown;
|
|
38
|
+
text: string;
|
|
39
|
+
type: "text";
|
|
40
|
+
_meta?: {
|
|
41
|
+
[x: string]: unknown;
|
|
42
|
+
} | undefined;
|
|
43
|
+
} | {
|
|
44
|
+
[x: string]: unknown;
|
|
45
|
+
data: string;
|
|
46
|
+
type: "image";
|
|
47
|
+
mimeType: string;
|
|
48
|
+
_meta?: {
|
|
49
|
+
[x: string]: unknown;
|
|
50
|
+
} | undefined;
|
|
51
|
+
} | {
|
|
52
|
+
[x: string]: unknown;
|
|
53
|
+
data: string;
|
|
54
|
+
type: "audio";
|
|
55
|
+
mimeType: string;
|
|
56
|
+
_meta?: {
|
|
57
|
+
[x: string]: unknown;
|
|
58
|
+
} | undefined;
|
|
59
|
+
} | {
|
|
60
|
+
[x: string]: unknown;
|
|
61
|
+
name: string;
|
|
62
|
+
type: "resource_link";
|
|
63
|
+
uri: string;
|
|
64
|
+
title?: string | undefined;
|
|
65
|
+
description?: string | undefined;
|
|
66
|
+
_meta?: {
|
|
67
|
+
[x: string]: unknown;
|
|
68
|
+
} | undefined;
|
|
69
|
+
mimeType?: string | undefined;
|
|
70
|
+
icons?: {
|
|
71
|
+
[x: string]: unknown;
|
|
72
|
+
src: string;
|
|
73
|
+
mimeType?: string | undefined;
|
|
74
|
+
sizes?: string[] | undefined;
|
|
75
|
+
}[] | undefined;
|
|
76
|
+
} | {
|
|
77
|
+
[x: string]: unknown;
|
|
78
|
+
type: "resource";
|
|
79
|
+
resource: {
|
|
80
|
+
[x: string]: unknown;
|
|
81
|
+
text: string;
|
|
82
|
+
uri: string;
|
|
83
|
+
_meta?: {
|
|
84
|
+
[x: string]: unknown;
|
|
85
|
+
} | undefined;
|
|
86
|
+
mimeType?: string | undefined;
|
|
87
|
+
} | {
|
|
88
|
+
[x: string]: unknown;
|
|
89
|
+
uri: string;
|
|
90
|
+
blob: string;
|
|
91
|
+
_meta?: {
|
|
92
|
+
[x: string]: unknown;
|
|
93
|
+
} | undefined;
|
|
94
|
+
mimeType?: string | undefined;
|
|
95
|
+
};
|
|
96
|
+
_meta?: {
|
|
97
|
+
[x: string]: unknown;
|
|
98
|
+
} | undefined;
|
|
99
|
+
};
|
|
100
|
+
role: "user" | "assistant";
|
|
101
|
+
}[];
|
|
102
|
+
description?: string | undefined;
|
|
103
|
+
_meta?: {
|
|
104
|
+
[x: string]: unknown;
|
|
105
|
+
} | undefined;
|
|
106
|
+
} | null, Error>;
|
|
107
|
+
//# sourceMappingURL=mcp-hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-hooks.d.ts","sourceRoot":"","sources":["../../src/mcp/mcp-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EACL,KAAK,kBAAkB,EAGxB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,MAAM,cAAc,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAClE,MAAM,WAAW,eAAe;IAE9B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,MAAM,EAAE,cAAc,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB;;WA8B5B,KAAK,GAAG,IAAI;YACX,KAAK,EAAE;eACJ,OAAO;eACP,OAAO;aACT,OAAO;cACN,OAAO;kBACH,OAAO;gBACT,OAAO;eACR,OAAO;aACT,MAAM,OAAO,CAAC,IAAI,CAAC;EAb7B;AAwDD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiClC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useTamboMcpPromptList = useTamboMcpPromptList;
|
|
4
|
+
exports.useTamboMcpPrompt = useTamboMcpPrompt;
|
|
5
|
+
const hooks_1 = require("../hooks");
|
|
6
|
+
const tambo_mcp_provider_1 = require("./tambo-mcp-provider");
|
|
7
|
+
/**
|
|
8
|
+
* Hook to get the prompts for all the registered MCP servers.
|
|
9
|
+
* @returns The prompts for the MCP servers, including the server that the prompt was found on.
|
|
10
|
+
*/
|
|
11
|
+
function useTamboMcpPromptList() {
|
|
12
|
+
const mcpServers = (0, tambo_mcp_provider_1.useTamboMcpServers)();
|
|
13
|
+
const queries = (0, hooks_1.useTamboQueries)({
|
|
14
|
+
queries: mcpServers.map((mcpServer) => ({
|
|
15
|
+
queryKey: ["mcp-prompts", mcpServer.key],
|
|
16
|
+
// Only run for connected servers that have a client
|
|
17
|
+
enabled: isConnectedMcpServer(mcpServer),
|
|
18
|
+
queryFn: async () => {
|
|
19
|
+
// Fast path: if this server doesn't have a client, skip work
|
|
20
|
+
if (!isConnectedMcpServer(mcpServer))
|
|
21
|
+
return [];
|
|
22
|
+
const result = await mcpServer.client.client.listPrompts();
|
|
23
|
+
const prompts = result?.prompts ?? [];
|
|
24
|
+
const promptsEntries = prompts.map((prompt) => ({
|
|
25
|
+
server: mcpServer,
|
|
26
|
+
prompt,
|
|
27
|
+
}));
|
|
28
|
+
return promptsEntries;
|
|
29
|
+
},
|
|
30
|
+
})),
|
|
31
|
+
combine: (results) => {
|
|
32
|
+
return combineArrayResults(results);
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
return queries;
|
|
36
|
+
}
|
|
37
|
+
// TODO: find a more general place for this
|
|
38
|
+
function combineArrayResults(results) {
|
|
39
|
+
const errors = results
|
|
40
|
+
.filter((result) => result.isError)
|
|
41
|
+
.map((result) => result.error);
|
|
42
|
+
// Treat queries that are idle (disabled) as non-blocking for aggregate status
|
|
43
|
+
const enabledish = results.filter((r) => r.fetchStatus !== "idle" || r.isSuccess || r.isError);
|
|
44
|
+
return {
|
|
45
|
+
// Prefer flatMap to avoid extra intermediate arrays
|
|
46
|
+
data: results.flatMap((result) => result.isSuccess && Array.isArray(result.data) ? result.data : []),
|
|
47
|
+
// Preserve a single error for compatibility and expose the full list for diagnostics
|
|
48
|
+
error: errors[0] ?? null,
|
|
49
|
+
errors,
|
|
50
|
+
isPending: enabledish.some((result) => result.isPending),
|
|
51
|
+
isSuccess: enabledish.length > 0 && enabledish.every((result) => result.isSuccess),
|
|
52
|
+
isError: errors.length > 0,
|
|
53
|
+
isPaused: enabledish.some((result) => result.isPaused),
|
|
54
|
+
isRefetching: enabledish.some((result) => result.isRefetching),
|
|
55
|
+
isFetching: enabledish.some((result) => result.isFetching),
|
|
56
|
+
isLoading: enabledish.some((result) => result.isLoading),
|
|
57
|
+
// Aggregate refetch to trigger all underlying queries
|
|
58
|
+
refetch: async () => {
|
|
59
|
+
await Promise.all(results.map(async (r) => {
|
|
60
|
+
await r.refetch();
|
|
61
|
+
}));
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// Type guard for narrowing to connected servers
|
|
66
|
+
function isConnectedMcpServer(server) {
|
|
67
|
+
return "client" in server && server.client != null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Hook to get the prompt for the specified name.
|
|
71
|
+
* @param promptName - The name of the prompt to get. If the prompt won't return anything
|
|
72
|
+
* @param args - The arguments to pass to the prompt.
|
|
73
|
+
* @returns The prompt for the specified name.
|
|
74
|
+
*/
|
|
75
|
+
function useTamboMcpPrompt(promptName, args = {}) {
|
|
76
|
+
// figure out which server has the prompt
|
|
77
|
+
const { data: promptEntries } = useTamboMcpPromptList();
|
|
78
|
+
const promptEntry = promptEntries?.find((prompt) => prompt.prompt.name === promptName);
|
|
79
|
+
// Use the stable server key (and the server instance itself) instead of brittle
|
|
80
|
+
// name/url/transport matching.
|
|
81
|
+
const mcpServer = promptEntry?.server;
|
|
82
|
+
// Canonicalize args to avoid unstable cache keys from object identity/order
|
|
83
|
+
const sortedArgsEntries = Object.keys(args)
|
|
84
|
+
.sort()
|
|
85
|
+
.map((k) => [k, args[k]]);
|
|
86
|
+
return (0, hooks_1.useTamboQuery)({
|
|
87
|
+
// Include server identity and sorted args to prevent stale cache hits
|
|
88
|
+
queryKey: ["mcp-prompt", promptName, mcpServer?.key, sortedArgsEntries],
|
|
89
|
+
// Only run when we have a prompt name and a connected server
|
|
90
|
+
enabled: Boolean(promptName && mcpServer && isConnectedMcpServer(mcpServer)),
|
|
91
|
+
queryFn: async () => {
|
|
92
|
+
if (!promptName || !mcpServer || !isConnectedMcpServer(mcpServer)) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const result = await mcpServer.client.client.getPrompt({
|
|
96
|
+
name: promptName,
|
|
97
|
+
arguments: args,
|
|
98
|
+
});
|
|
99
|
+
return result ?? null; // return null because react-query doesn't like undefined results
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=mcp-hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-hooks.js","sourceRoot":"","sources":["../../src/mcp/mcp-hooks.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n GetPromptResult,\n type ListPromptsResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { UseQueryResult } from \"@tanstack/react-query\";\nimport { useTamboQueries, useTamboQuery } from \"../hooks\";\nimport {\n type ConnectedMcpServer,\n type McpServer,\n useTamboMcpServers,\n} from \"./tambo-mcp-provider\";\n\nexport type ListPromptItem = ListPromptsResult[\"prompts\"][number];\nexport interface ListPromptEntry {\n // Only connected servers produce prompt entries, so expose the connected type\n server: ConnectedMcpServer;\n prompt: ListPromptItem;\n}\n\n/**\n * Hook to get the prompts for all the registered MCP servers.\n * @returns The prompts for the MCP servers, including the server that the prompt was found on.\n */\nexport function useTamboMcpPromptList() {\n const mcpServers = useTamboMcpServers();\n const queries = useTamboQueries({\n queries: mcpServers.map((mcpServer) => ({\n queryKey: [\"mcp-prompts\", mcpServer.key],\n // Only run for connected servers that have a client\n enabled: isConnectedMcpServer(mcpServer),\n queryFn: async (): Promise<ListPromptEntry[]> => {\n // Fast path: if this server doesn't have a client, skip work\n if (!isConnectedMcpServer(mcpServer)) return [];\n\n const result = await mcpServer.client.client.listPrompts();\n const prompts: ListPromptItem[] = result?.prompts ?? [];\n const promptsEntries = prompts.map((prompt) => ({\n server: mcpServer,\n prompt,\n }));\n return promptsEntries;\n },\n })),\n combine: (results) => {\n return combineArrayResults(results);\n },\n });\n\n return queries;\n}\n// TODO: find a more general place for this\nfunction combineArrayResults<T>(results: UseQueryResult<T[], Error>[]): {\n data: T[];\n error: Error | null;\n errors: Error[];\n isPending: boolean;\n isSuccess: boolean;\n isError: boolean;\n isPaused: boolean;\n isRefetching: boolean;\n isFetching: boolean;\n isLoading: boolean;\n refetch: () => Promise<void>;\n} {\n const errors = results\n .filter((result) => result.isError)\n .map((result) => result.error as Error);\n\n // Treat queries that are idle (disabled) as non-blocking for aggregate status\n const enabledish = results.filter(\n (r) => r.fetchStatus !== \"idle\" || r.isSuccess || r.isError,\n );\n\n return {\n // Prefer flatMap to avoid extra intermediate arrays\n data: results.flatMap((result) =>\n result.isSuccess && Array.isArray(result.data) ? result.data : [],\n ),\n // Preserve a single error for compatibility and expose the full list for diagnostics\n error: errors[0] ?? null,\n errors,\n isPending: enabledish.some((result) => result.isPending),\n isSuccess:\n enabledish.length > 0 && enabledish.every((result) => result.isSuccess),\n isError: errors.length > 0,\n isPaused: enabledish.some((result) => result.isPaused),\n isRefetching: enabledish.some((result) => result.isRefetching),\n isFetching: enabledish.some((result) => result.isFetching),\n isLoading: enabledish.some((result) => result.isLoading),\n // Aggregate refetch to trigger all underlying queries\n refetch: async () => {\n await Promise.all(\n results.map(async (r) => {\n await r.refetch();\n }),\n );\n },\n };\n}\n\n// Type guard for narrowing to connected servers\nfunction isConnectedMcpServer(server: McpServer): server is ConnectedMcpServer {\n return \"client\" in server && server.client != null;\n}\n\n/**\n * Hook to get the prompt for the specified name.\n * @param promptName - The name of the prompt to get. If the prompt won't return anything\n * @param args - The arguments to pass to the prompt.\n * @returns The prompt for the specified name.\n */\nexport function useTamboMcpPrompt(\n promptName: string | undefined,\n args: Record<string, string> = {},\n) {\n // figure out which server has the prompt\n const { data: promptEntries } = useTamboMcpPromptList();\n const promptEntry = promptEntries?.find(\n (prompt) => prompt.prompt.name === promptName,\n );\n // Use the stable server key (and the server instance itself) instead of brittle\n // name/url/transport matching.\n const mcpServer = promptEntry?.server;\n\n // Canonicalize args to avoid unstable cache keys from object identity/order\n const sortedArgsEntries = Object.keys(args)\n .sort()\n .map((k) => [k, args[k]] as const);\n return useTamboQuery({\n // Include server identity and sorted args to prevent stale cache hits\n queryKey: [\"mcp-prompt\", promptName, mcpServer?.key, sortedArgsEntries],\n // Only run when we have a prompt name and a connected server\n enabled: Boolean(\n promptName && mcpServer && isConnectedMcpServer(mcpServer),\n ),\n queryFn: async (): Promise<GetPromptResult | null> => {\n if (!promptName || !mcpServer || !isConnectedMcpServer(mcpServer)) {\n return null;\n }\n const result = await mcpServer.client.client.getPrompt({\n name: promptName,\n arguments: args,\n });\n return result ?? null; // return null because react-query doesn't like undefined results\n },\n });\n}\n"]}
|