@mcpjam/inspector 1.0.6 → 1.0.7
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/server/index.js
CHANGED
|
@@ -47,815 +47,7 @@ import { serveStatic } from "@hono/node-server/serve-static";
|
|
|
47
47
|
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
48
48
|
import { join as join2, dirname, resolve } from "path";
|
|
49
49
|
import { fileURLToPath } from "url";
|
|
50
|
-
|
|
51
|
-
// ../sdk/mcp-client-manager/index.ts
|
|
52
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
53
|
-
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
54
|
-
import {
|
|
55
|
-
getDefaultEnvironment,
|
|
56
|
-
StdioClientTransport
|
|
57
|
-
} from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
58
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
59
|
-
import { DEFAULT_REQUEST_TIMEOUT_MSEC } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
60
|
-
import {
|
|
61
|
-
CallToolResultSchema as CallToolResultSchema2,
|
|
62
|
-
ElicitRequestSchema,
|
|
63
|
-
ResourceListChangedNotificationSchema,
|
|
64
|
-
ResourceUpdatedNotificationSchema,
|
|
65
|
-
PromptListChangedNotificationSchema
|
|
66
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
67
|
-
|
|
68
|
-
// ../sdk/mcp-client-manager/tool-converters.ts
|
|
69
|
-
import {
|
|
70
|
-
CallToolResultSchema
|
|
71
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
72
|
-
import {
|
|
73
|
-
dynamicTool,
|
|
74
|
-
jsonSchema,
|
|
75
|
-
tool as defineTool
|
|
76
|
-
} from "ai";
|
|
77
|
-
var ensureJsonSchemaObject = (schema) => {
|
|
78
|
-
if (schema && typeof schema === "object") {
|
|
79
|
-
const record = schema;
|
|
80
|
-
const base = record.jsonSchema ? ensureJsonSchemaObject(record.jsonSchema) : record;
|
|
81
|
-
if (!("type" in base) || base.type === void 0) {
|
|
82
|
-
base.type = "object";
|
|
83
|
-
}
|
|
84
|
-
if (base.type === "object") {
|
|
85
|
-
base.properties = base.properties ?? {};
|
|
86
|
-
if (base.additionalProperties === void 0) {
|
|
87
|
-
base.additionalProperties = false;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return base;
|
|
91
|
-
}
|
|
92
|
-
return {
|
|
93
|
-
type: "object",
|
|
94
|
-
properties: {},
|
|
95
|
-
additionalProperties: false
|
|
96
|
-
};
|
|
97
|
-
};
|
|
98
|
-
async function convertMCPToolsToVercelTools(listToolsResult, {
|
|
99
|
-
schemas = "automatic",
|
|
100
|
-
callTool
|
|
101
|
-
}) {
|
|
102
|
-
const tools2 = {};
|
|
103
|
-
for (const toolDescription of listToolsResult.tools) {
|
|
104
|
-
const { name, description, inputSchema } = toolDescription;
|
|
105
|
-
const execute = async (args, options) => {
|
|
106
|
-
options?.abortSignal?.throwIfAborted?.();
|
|
107
|
-
const result = await callTool({ name, args, options });
|
|
108
|
-
return CallToolResultSchema.parse(result);
|
|
109
|
-
};
|
|
110
|
-
let vercelTool;
|
|
111
|
-
if (schemas === "automatic") {
|
|
112
|
-
const normalizedInputSchema = ensureJsonSchemaObject(inputSchema);
|
|
113
|
-
vercelTool = dynamicTool({
|
|
114
|
-
description,
|
|
115
|
-
inputSchema: jsonSchema({
|
|
116
|
-
type: "object",
|
|
117
|
-
properties: normalizedInputSchema.properties ?? {},
|
|
118
|
-
additionalProperties: normalizedInputSchema.additionalProperties ?? false
|
|
119
|
-
}),
|
|
120
|
-
execute
|
|
121
|
-
});
|
|
122
|
-
} else {
|
|
123
|
-
const overrides = schemas;
|
|
124
|
-
if (!(name in overrides)) {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
vercelTool = defineTool({
|
|
128
|
-
description,
|
|
129
|
-
inputSchema: overrides[name].inputSchema,
|
|
130
|
-
execute
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
tools2[name] = vercelTool;
|
|
134
|
-
}
|
|
135
|
-
return tools2;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// ../sdk/mcp-client-manager/index.ts
|
|
139
|
-
var MCPClientManager = class {
|
|
140
|
-
clientStates = /* @__PURE__ */ new Map();
|
|
141
|
-
notificationHandlers = /* @__PURE__ */ new Map();
|
|
142
|
-
elicitationHandlers = /* @__PURE__ */ new Map();
|
|
143
|
-
toolsMetadataCache = /* @__PURE__ */ new Map();
|
|
144
|
-
defaultClientVersion;
|
|
145
|
-
defaultCapabilities;
|
|
146
|
-
defaultTimeout;
|
|
147
|
-
// Default JSON-RPC logging controls
|
|
148
|
-
defaultLogJsonRpc = false;
|
|
149
|
-
defaultRpcLogger;
|
|
150
|
-
// Global elicitation callback support (used by streaming chat endpoint)
|
|
151
|
-
elicitationCallback;
|
|
152
|
-
pendingElicitations = /* @__PURE__ */ new Map();
|
|
153
|
-
constructor(servers2 = {}, options = {}) {
|
|
154
|
-
this.defaultClientVersion = options.defaultClientVersion ?? "1.0.0";
|
|
155
|
-
this.defaultCapabilities = { ...options.defaultCapabilities ?? {} };
|
|
156
|
-
this.defaultTimeout = options.defaultTimeout ?? DEFAULT_REQUEST_TIMEOUT_MSEC;
|
|
157
|
-
this.defaultLogJsonRpc = options.defaultLogJsonRpc ?? false;
|
|
158
|
-
this.defaultRpcLogger = options.rpcLogger;
|
|
159
|
-
for (const [id, config] of Object.entries(servers2)) {
|
|
160
|
-
void this.connectToServer(id, config);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
listServers() {
|
|
164
|
-
return Array.from(this.clientStates.keys());
|
|
165
|
-
}
|
|
166
|
-
hasServer(serverId) {
|
|
167
|
-
return this.clientStates.has(serverId);
|
|
168
|
-
}
|
|
169
|
-
getServerSummaries() {
|
|
170
|
-
return Array.from(this.clientStates.entries()).map(([serverId, state]) => ({
|
|
171
|
-
id: serverId,
|
|
172
|
-
status: this.resolveConnectionStatus(state),
|
|
173
|
-
config: state.config
|
|
174
|
-
}));
|
|
175
|
-
}
|
|
176
|
-
getConnectionStatus(serverId) {
|
|
177
|
-
return this.resolveConnectionStatus(this.clientStates.get(serverId));
|
|
178
|
-
}
|
|
179
|
-
getServerConfig(serverId) {
|
|
180
|
-
return this.clientStates.get(serverId)?.config;
|
|
181
|
-
}
|
|
182
|
-
async connectToServer(serverId, config) {
|
|
183
|
-
if (this.clientStates.has(serverId)) {
|
|
184
|
-
throw new Error(`MCP server "${serverId}" is already connected.`);
|
|
185
|
-
}
|
|
186
|
-
const timeout = this.getTimeout(config);
|
|
187
|
-
const state = this.clientStates.get(serverId) ?? {
|
|
188
|
-
config,
|
|
189
|
-
timeout
|
|
190
|
-
};
|
|
191
|
-
state.config = config;
|
|
192
|
-
state.timeout = timeout;
|
|
193
|
-
if (state.client) {
|
|
194
|
-
this.clientStates.set(serverId, state);
|
|
195
|
-
return state.client;
|
|
196
|
-
}
|
|
197
|
-
if (state.promise) {
|
|
198
|
-
this.clientStates.set(serverId, state);
|
|
199
|
-
return state.promise;
|
|
200
|
-
}
|
|
201
|
-
const connectionPromise = (async () => {
|
|
202
|
-
const client = new Client(
|
|
203
|
-
{
|
|
204
|
-
name: serverId,
|
|
205
|
-
version: config.version ?? this.defaultClientVersion
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
capabilities: this.buildCapabilities(config)
|
|
209
|
-
}
|
|
210
|
-
);
|
|
211
|
-
this.applyNotificationHandlers(serverId, client);
|
|
212
|
-
this.applyElicitationHandler(serverId, client);
|
|
213
|
-
if (config.onError) {
|
|
214
|
-
client.onerror = (error) => {
|
|
215
|
-
config.onError?.(error);
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
client.onclose = () => {
|
|
219
|
-
this.resetState(serverId);
|
|
220
|
-
};
|
|
221
|
-
let transport;
|
|
222
|
-
if (this.isStdioConfig(config)) {
|
|
223
|
-
transport = await this.connectViaStdio(
|
|
224
|
-
serverId,
|
|
225
|
-
client,
|
|
226
|
-
config,
|
|
227
|
-
timeout
|
|
228
|
-
);
|
|
229
|
-
} else {
|
|
230
|
-
transport = await this.connectViaHttp(
|
|
231
|
-
serverId,
|
|
232
|
-
client,
|
|
233
|
-
config,
|
|
234
|
-
timeout
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
state.client = client;
|
|
238
|
-
state.transport = transport;
|
|
239
|
-
state.promise = void 0;
|
|
240
|
-
this.clientStates.set(serverId, state);
|
|
241
|
-
return client;
|
|
242
|
-
})().catch((error) => {
|
|
243
|
-
state.promise = void 0;
|
|
244
|
-
state.client = void 0;
|
|
245
|
-
state.transport = void 0;
|
|
246
|
-
this.clientStates.set(serverId, state);
|
|
247
|
-
throw error;
|
|
248
|
-
});
|
|
249
|
-
state.promise = connectionPromise;
|
|
250
|
-
this.clientStates.set(serverId, state);
|
|
251
|
-
return connectionPromise;
|
|
252
|
-
}
|
|
253
|
-
async disconnectServer(serverId) {
|
|
254
|
-
const client = this.getClientById(serverId);
|
|
255
|
-
try {
|
|
256
|
-
await client.close();
|
|
257
|
-
} finally {
|
|
258
|
-
if (client.transport) {
|
|
259
|
-
await this.safeCloseTransport(client.transport);
|
|
260
|
-
}
|
|
261
|
-
this.resetState(serverId);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
removeServer(serverId) {
|
|
265
|
-
this.resetState(serverId);
|
|
266
|
-
this.notificationHandlers.delete(serverId);
|
|
267
|
-
this.elicitationHandlers.delete(serverId);
|
|
268
|
-
}
|
|
269
|
-
async disconnectAllServers() {
|
|
270
|
-
const serverIds = this.listServers();
|
|
271
|
-
await Promise.all(
|
|
272
|
-
serverIds.map((serverId) => this.disconnectServer(serverId))
|
|
273
|
-
);
|
|
274
|
-
for (const serverId of serverIds) {
|
|
275
|
-
this.resetState(serverId);
|
|
276
|
-
this.notificationHandlers.delete(serverId);
|
|
277
|
-
this.elicitationHandlers.delete(serverId);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
async listTools(serverId, params, options) {
|
|
281
|
-
await this.ensureConnected(serverId);
|
|
282
|
-
const client = this.getClientById(serverId);
|
|
283
|
-
try {
|
|
284
|
-
const result = await client.listTools(
|
|
285
|
-
params,
|
|
286
|
-
this.withTimeout(serverId, options)
|
|
287
|
-
);
|
|
288
|
-
const metadataMap = /* @__PURE__ */ new Map();
|
|
289
|
-
for (const tool2 of result.tools) {
|
|
290
|
-
if (tool2._meta) {
|
|
291
|
-
metadataMap.set(tool2.name, tool2._meta);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
this.toolsMetadataCache.set(serverId, metadataMap);
|
|
295
|
-
return result;
|
|
296
|
-
} catch (error) {
|
|
297
|
-
if (this.isMethodUnavailableError(error, "tools/list")) {
|
|
298
|
-
this.toolsMetadataCache.set(serverId, /* @__PURE__ */ new Map());
|
|
299
|
-
return { tools: [] };
|
|
300
|
-
}
|
|
301
|
-
throw error;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
async getTools(serverIds) {
|
|
305
|
-
const targetServerIds = serverIds && serverIds.length > 0 ? serverIds : this.listServers();
|
|
306
|
-
const toolLists = await Promise.all(
|
|
307
|
-
targetServerIds.map(async (serverId) => {
|
|
308
|
-
await this.ensureConnected(serverId);
|
|
309
|
-
const client = this.getClientById(serverId);
|
|
310
|
-
const result = await client.listTools(
|
|
311
|
-
void 0,
|
|
312
|
-
this.withTimeout(serverId)
|
|
313
|
-
);
|
|
314
|
-
const metadataMap = /* @__PURE__ */ new Map();
|
|
315
|
-
for (const tool2 of result.tools) {
|
|
316
|
-
if (tool2._meta) {
|
|
317
|
-
metadataMap.set(tool2.name, tool2._meta);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
this.toolsMetadataCache.set(serverId, metadataMap);
|
|
321
|
-
return result.tools;
|
|
322
|
-
})
|
|
323
|
-
);
|
|
324
|
-
return { tools: toolLists.flat() };
|
|
325
|
-
}
|
|
326
|
-
getAllToolsMetadata(serverId) {
|
|
327
|
-
const metadataMap = this.toolsMetadataCache.get(serverId);
|
|
328
|
-
return metadataMap ? Object.fromEntries(metadataMap) : {};
|
|
329
|
-
}
|
|
330
|
-
pingServer(serverId, options) {
|
|
331
|
-
const client = this.getClientById(serverId);
|
|
332
|
-
try {
|
|
333
|
-
client.ping(options);
|
|
334
|
-
} catch (error) {
|
|
335
|
-
throw new Error(
|
|
336
|
-
`Failed to ping MCP server "${serverId}": ${error instanceof Error ? error.message : "Unknown error"}`
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
async getToolsForAiSdk(serverIds, options = {}) {
|
|
341
|
-
const ids = Array.isArray(serverIds) ? serverIds : serverIds ? [serverIds] : this.listServers();
|
|
342
|
-
const loadForServer = async (id) => {
|
|
343
|
-
await this.ensureConnected(id);
|
|
344
|
-
const listToolsResult = await this.listTools(id);
|
|
345
|
-
return convertMCPToolsToVercelTools(listToolsResult, {
|
|
346
|
-
schemas: options.schemas,
|
|
347
|
-
callTool: async ({ name, args, options: callOptions }) => {
|
|
348
|
-
const requestOptions = callOptions?.abortSignal ? { signal: callOptions.abortSignal } : void 0;
|
|
349
|
-
const result = await this.executeTool(
|
|
350
|
-
id,
|
|
351
|
-
name,
|
|
352
|
-
args ?? {},
|
|
353
|
-
requestOptions
|
|
354
|
-
);
|
|
355
|
-
return CallToolResultSchema2.parse(result);
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
};
|
|
359
|
-
const perServerTools = await Promise.all(
|
|
360
|
-
ids.map(async (id) => {
|
|
361
|
-
try {
|
|
362
|
-
const tools2 = await loadForServer(id);
|
|
363
|
-
for (const [name, tool2] of Object.entries(tools2)) {
|
|
364
|
-
tool2._serverId = id;
|
|
365
|
-
}
|
|
366
|
-
return tools2;
|
|
367
|
-
} catch (error) {
|
|
368
|
-
if (this.isMethodUnavailableError(error, "tools/list")) {
|
|
369
|
-
return {};
|
|
370
|
-
}
|
|
371
|
-
throw error;
|
|
372
|
-
}
|
|
373
|
-
})
|
|
374
|
-
);
|
|
375
|
-
const flattened = {};
|
|
376
|
-
for (const toolset of perServerTools) {
|
|
377
|
-
for (const [name, tool2] of Object.entries(toolset)) {
|
|
378
|
-
flattened[name] = tool2;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
return flattened;
|
|
382
|
-
}
|
|
383
|
-
async executeTool(serverId, toolName, args = {}, options) {
|
|
384
|
-
await this.ensureConnected(serverId);
|
|
385
|
-
const client = this.getClientById(serverId);
|
|
386
|
-
return client.callTool(
|
|
387
|
-
{
|
|
388
|
-
name: toolName,
|
|
389
|
-
arguments: args
|
|
390
|
-
},
|
|
391
|
-
CallToolResultSchema2,
|
|
392
|
-
this.withTimeout(serverId, options)
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
async listResources(serverId, params, options) {
|
|
396
|
-
await this.ensureConnected(serverId);
|
|
397
|
-
const client = this.getClientById(serverId);
|
|
398
|
-
try {
|
|
399
|
-
return await client.listResources(
|
|
400
|
-
params,
|
|
401
|
-
this.withTimeout(serverId, options)
|
|
402
|
-
);
|
|
403
|
-
} catch (error) {
|
|
404
|
-
if (this.isMethodUnavailableError(error, "resources/list")) {
|
|
405
|
-
return {
|
|
406
|
-
resources: []
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
throw error;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
async readResource(serverId, params, options) {
|
|
413
|
-
await this.ensureConnected(serverId);
|
|
414
|
-
const client = this.getClientById(serverId);
|
|
415
|
-
return client.readResource(params, this.withTimeout(serverId, options));
|
|
416
|
-
}
|
|
417
|
-
async subscribeResource(serverId, params, options) {
|
|
418
|
-
await this.ensureConnected(serverId);
|
|
419
|
-
const client = this.getClientById(serverId);
|
|
420
|
-
return client.subscribeResource(
|
|
421
|
-
params,
|
|
422
|
-
this.withTimeout(serverId, options)
|
|
423
|
-
);
|
|
424
|
-
}
|
|
425
|
-
async unsubscribeResource(serverId, params, options) {
|
|
426
|
-
await this.ensureConnected(serverId);
|
|
427
|
-
const client = this.getClientById(serverId);
|
|
428
|
-
return client.unsubscribeResource(
|
|
429
|
-
params,
|
|
430
|
-
this.withTimeout(serverId, options)
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
async listResourceTemplates(serverId, params, options) {
|
|
434
|
-
await this.ensureConnected(serverId);
|
|
435
|
-
const client = this.getClientById(serverId);
|
|
436
|
-
return client.listResourceTemplates(
|
|
437
|
-
params,
|
|
438
|
-
this.withTimeout(serverId, options)
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
async listPrompts(serverId, params, options) {
|
|
442
|
-
await this.ensureConnected(serverId);
|
|
443
|
-
const client = this.getClientById(serverId);
|
|
444
|
-
try {
|
|
445
|
-
return await client.listPrompts(
|
|
446
|
-
params,
|
|
447
|
-
this.withTimeout(serverId, options)
|
|
448
|
-
);
|
|
449
|
-
} catch (error) {
|
|
450
|
-
if (this.isMethodUnavailableError(error, "prompts/list")) {
|
|
451
|
-
return {
|
|
452
|
-
prompts: []
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
throw error;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
async getPrompt(serverId, params, options) {
|
|
459
|
-
await this.ensureConnected(serverId);
|
|
460
|
-
const client = this.getClientById(serverId);
|
|
461
|
-
return client.getPrompt(params, this.withTimeout(serverId, options));
|
|
462
|
-
}
|
|
463
|
-
getSessionIdByServer(serverId) {
|
|
464
|
-
const state = this.clientStates.get(serverId);
|
|
465
|
-
if (!state?.transport) {
|
|
466
|
-
throw new Error(`Unknown MCP server "${serverId}".`);
|
|
467
|
-
}
|
|
468
|
-
if (state.transport instanceof StreamableHTTPClientTransport) {
|
|
469
|
-
return state.transport.sessionId;
|
|
470
|
-
}
|
|
471
|
-
throw new Error(
|
|
472
|
-
`Server "${serverId}" must be Streamable HTTP to get the session ID.`
|
|
473
|
-
);
|
|
474
|
-
}
|
|
475
|
-
addNotificationHandler(serverId, schema, handler) {
|
|
476
|
-
const serverHandlers = this.notificationHandlers.get(serverId) ?? /* @__PURE__ */ new Map();
|
|
477
|
-
const handlersForSchema = serverHandlers.get(schema) ?? /* @__PURE__ */ new Set();
|
|
478
|
-
handlersForSchema.add(handler);
|
|
479
|
-
serverHandlers.set(schema, handlersForSchema);
|
|
480
|
-
this.notificationHandlers.set(serverId, serverHandlers);
|
|
481
|
-
const client = this.clientStates.get(serverId)?.client;
|
|
482
|
-
if (client) {
|
|
483
|
-
client.setNotificationHandler(
|
|
484
|
-
schema,
|
|
485
|
-
this.createNotificationDispatcher(serverId, schema)
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
onResourceListChanged(serverId, handler) {
|
|
490
|
-
this.addNotificationHandler(
|
|
491
|
-
serverId,
|
|
492
|
-
ResourceListChangedNotificationSchema,
|
|
493
|
-
handler
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
onResourceUpdated(serverId, handler) {
|
|
497
|
-
this.addNotificationHandler(
|
|
498
|
-
serverId,
|
|
499
|
-
ResourceUpdatedNotificationSchema,
|
|
500
|
-
handler
|
|
501
|
-
);
|
|
502
|
-
}
|
|
503
|
-
onPromptListChanged(serverId, handler) {
|
|
504
|
-
this.addNotificationHandler(
|
|
505
|
-
serverId,
|
|
506
|
-
PromptListChangedNotificationSchema,
|
|
507
|
-
handler
|
|
508
|
-
);
|
|
509
|
-
}
|
|
510
|
-
getClient(serverId) {
|
|
511
|
-
return this.clientStates.get(serverId)?.client;
|
|
512
|
-
}
|
|
513
|
-
setElicitationHandler(serverId, handler) {
|
|
514
|
-
if (!this.clientStates.has(serverId)) {
|
|
515
|
-
throw new Error(`Unknown MCP server "${serverId}".`);
|
|
516
|
-
}
|
|
517
|
-
this.elicitationHandlers.set(serverId, handler);
|
|
518
|
-
const client = this.clientStates.get(serverId)?.client;
|
|
519
|
-
if (client) {
|
|
520
|
-
this.applyElicitationHandler(serverId, client);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
clearElicitationHandler(serverId) {
|
|
524
|
-
this.elicitationHandlers.delete(serverId);
|
|
525
|
-
const client = this.clientStates.get(serverId)?.client;
|
|
526
|
-
if (client) {
|
|
527
|
-
client.removeRequestHandler("elicitation/create");
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
// Global elicitation callback API (no serverId required)
|
|
531
|
-
setElicitationCallback(callback) {
|
|
532
|
-
this.elicitationCallback = callback;
|
|
533
|
-
for (const [serverId, state] of this.clientStates.entries()) {
|
|
534
|
-
const client = state.client;
|
|
535
|
-
if (!client) continue;
|
|
536
|
-
if (this.elicitationHandlers.has(serverId)) {
|
|
537
|
-
this.applyElicitationHandler(serverId, client);
|
|
538
|
-
} else {
|
|
539
|
-
this.applyElicitationHandler(serverId, client);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
clearElicitationCallback() {
|
|
544
|
-
this.elicitationCallback = void 0;
|
|
545
|
-
for (const [serverId, state] of this.clientStates.entries()) {
|
|
546
|
-
const client = state.client;
|
|
547
|
-
if (!client) continue;
|
|
548
|
-
if (this.elicitationHandlers.has(serverId)) {
|
|
549
|
-
this.applyElicitationHandler(serverId, client);
|
|
550
|
-
} else {
|
|
551
|
-
client.removeRequestHandler("elicitation/create");
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
// Expose the pending elicitation map so callers can add resolvers
|
|
556
|
-
getPendingElicitations() {
|
|
557
|
-
return this.pendingElicitations;
|
|
558
|
-
}
|
|
559
|
-
// Helper to resolve a pending elicitation from outside
|
|
560
|
-
respondToElicitation(requestId, response) {
|
|
561
|
-
const pending = this.pendingElicitations.get(requestId);
|
|
562
|
-
if (!pending) return false;
|
|
563
|
-
try {
|
|
564
|
-
pending.resolve(response);
|
|
565
|
-
return true;
|
|
566
|
-
} finally {
|
|
567
|
-
this.pendingElicitations.delete(requestId);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
async connectViaStdio(serverId, client, config, timeout) {
|
|
571
|
-
const underlying = new StdioClientTransport({
|
|
572
|
-
command: config.command,
|
|
573
|
-
args: config.args,
|
|
574
|
-
env: { ...getDefaultEnvironment(), ...config.env ?? {} }
|
|
575
|
-
});
|
|
576
|
-
const wrapped = this.wrapTransportForLogging(serverId, config, underlying);
|
|
577
|
-
await client.connect(wrapped, { timeout });
|
|
578
|
-
return underlying;
|
|
579
|
-
}
|
|
580
|
-
async connectViaHttp(serverId, client, config, timeout) {
|
|
581
|
-
const preferSSE = config.preferSSE ?? config.url.pathname.endsWith("/sse");
|
|
582
|
-
let streamableError;
|
|
583
|
-
if (!preferSSE) {
|
|
584
|
-
const streamableTransport = new StreamableHTTPClientTransport(
|
|
585
|
-
config.url,
|
|
586
|
-
{
|
|
587
|
-
requestInit: config.requestInit,
|
|
588
|
-
reconnectionOptions: config.reconnectionOptions,
|
|
589
|
-
authProvider: config.authProvider,
|
|
590
|
-
sessionId: config.sessionId
|
|
591
|
-
}
|
|
592
|
-
);
|
|
593
|
-
try {
|
|
594
|
-
const wrapped = this.wrapTransportForLogging(
|
|
595
|
-
serverId,
|
|
596
|
-
config,
|
|
597
|
-
streamableTransport
|
|
598
|
-
);
|
|
599
|
-
await client.connect(wrapped, {
|
|
600
|
-
timeout: Math.min(timeout, 3e3)
|
|
601
|
-
});
|
|
602
|
-
return streamableTransport;
|
|
603
|
-
} catch (error) {
|
|
604
|
-
streamableError = error;
|
|
605
|
-
await this.safeCloseTransport(streamableTransport);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
const sseTransport = new SSEClientTransport(config.url, {
|
|
609
|
-
requestInit: config.requestInit,
|
|
610
|
-
eventSourceInit: config.eventSourceInit,
|
|
611
|
-
authProvider: config.authProvider
|
|
612
|
-
});
|
|
613
|
-
try {
|
|
614
|
-
const wrapped = this.wrapTransportForLogging(
|
|
615
|
-
serverId,
|
|
616
|
-
config,
|
|
617
|
-
sseTransport
|
|
618
|
-
);
|
|
619
|
-
await client.connect(wrapped, { timeout });
|
|
620
|
-
return sseTransport;
|
|
621
|
-
} catch (error) {
|
|
622
|
-
await this.safeCloseTransport(sseTransport);
|
|
623
|
-
const streamableMessage = streamableError ? ` Streamable HTTP error: ${this.formatError(streamableError)}.` : "";
|
|
624
|
-
throw new Error(
|
|
625
|
-
`Failed to connect to MCP server "${serverId}" using HTTP transports.${streamableMessage} SSE error: ${this.formatError(error)}.`
|
|
626
|
-
);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
async safeCloseTransport(transport) {
|
|
630
|
-
try {
|
|
631
|
-
await transport.close();
|
|
632
|
-
} catch {
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
applyNotificationHandlers(serverId, client) {
|
|
636
|
-
const serverHandlers = this.notificationHandlers.get(serverId);
|
|
637
|
-
if (!serverHandlers) {
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
640
|
-
for (const [schema] of serverHandlers) {
|
|
641
|
-
client.setNotificationHandler(
|
|
642
|
-
schema,
|
|
643
|
-
this.createNotificationDispatcher(serverId, schema)
|
|
644
|
-
);
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
createNotificationDispatcher(serverId, schema) {
|
|
648
|
-
return (notification) => {
|
|
649
|
-
const serverHandlers = this.notificationHandlers.get(serverId);
|
|
650
|
-
const handlersForSchema = serverHandlers?.get(schema);
|
|
651
|
-
if (!handlersForSchema || handlersForSchema.size === 0) {
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
for (const handler of handlersForSchema) {
|
|
655
|
-
try {
|
|
656
|
-
handler(notification);
|
|
657
|
-
} catch {
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
};
|
|
661
|
-
}
|
|
662
|
-
applyElicitationHandler(serverId, client) {
|
|
663
|
-
const serverSpecific = this.elicitationHandlers.get(serverId);
|
|
664
|
-
if (serverSpecific) {
|
|
665
|
-
client.setRequestHandler(
|
|
666
|
-
ElicitRequestSchema,
|
|
667
|
-
async (request) => serverSpecific(request.params)
|
|
668
|
-
);
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
if (this.elicitationCallback) {
|
|
672
|
-
client.setRequestHandler(ElicitRequestSchema, async (request) => {
|
|
673
|
-
const reqId = `elicit_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
674
|
-
return await this.elicitationCallback({
|
|
675
|
-
requestId: reqId,
|
|
676
|
-
message: request.params?.message,
|
|
677
|
-
schema: request.params?.requestedSchema ?? request.params?.schema
|
|
678
|
-
});
|
|
679
|
-
});
|
|
680
|
-
return;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
async ensureConnected(serverId) {
|
|
684
|
-
const state = this.clientStates.get(serverId);
|
|
685
|
-
if (state?.client) {
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
688
|
-
if (!state) {
|
|
689
|
-
throw new Error(`Unknown MCP server "${serverId}".`);
|
|
690
|
-
}
|
|
691
|
-
if (state.promise) {
|
|
692
|
-
await state.promise;
|
|
693
|
-
return;
|
|
694
|
-
}
|
|
695
|
-
await this.connectToServer(serverId, state.config);
|
|
696
|
-
}
|
|
697
|
-
resetState(serverId) {
|
|
698
|
-
this.clientStates.delete(serverId);
|
|
699
|
-
this.toolsMetadataCache.delete(serverId);
|
|
700
|
-
}
|
|
701
|
-
resolveConnectionStatus(state) {
|
|
702
|
-
if (!state) {
|
|
703
|
-
return "disconnected";
|
|
704
|
-
}
|
|
705
|
-
if (state.client) {
|
|
706
|
-
return "connected";
|
|
707
|
-
}
|
|
708
|
-
if (state.promise) {
|
|
709
|
-
return "connecting";
|
|
710
|
-
}
|
|
711
|
-
return "disconnected";
|
|
712
|
-
}
|
|
713
|
-
withTimeout(serverId, options) {
|
|
714
|
-
const state = this.clientStates.get(serverId);
|
|
715
|
-
const timeout = state?.timeout ?? (state ? this.getTimeout(state.config) : this.defaultTimeout);
|
|
716
|
-
if (!options) {
|
|
717
|
-
return { timeout };
|
|
718
|
-
}
|
|
719
|
-
if (options.timeout === void 0) {
|
|
720
|
-
return { ...options, timeout };
|
|
721
|
-
}
|
|
722
|
-
return options;
|
|
723
|
-
}
|
|
724
|
-
buildCapabilities(config) {
|
|
725
|
-
const capabilities = {
|
|
726
|
-
...this.defaultCapabilities,
|
|
727
|
-
...config.capabilities ?? {}
|
|
728
|
-
};
|
|
729
|
-
if (!capabilities.elicitation) {
|
|
730
|
-
capabilities.elicitation = {};
|
|
731
|
-
}
|
|
732
|
-
return capabilities;
|
|
733
|
-
}
|
|
734
|
-
formatError(error) {
|
|
735
|
-
if (error instanceof Error) {
|
|
736
|
-
return error.message;
|
|
737
|
-
}
|
|
738
|
-
try {
|
|
739
|
-
return JSON.stringify(error);
|
|
740
|
-
} catch {
|
|
741
|
-
return String(error);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
// Returns a transport that logs JSON-RPC traffic if enabled for this server
|
|
745
|
-
wrapTransportForLogging(serverId, config, transport) {
|
|
746
|
-
const logger2 = this.resolveRpcLogger(serverId, config);
|
|
747
|
-
if (!logger2) return transport;
|
|
748
|
-
const log = logger2;
|
|
749
|
-
const self = this;
|
|
750
|
-
class LoggingTransport {
|
|
751
|
-
constructor(inner) {
|
|
752
|
-
this.inner = inner;
|
|
753
|
-
this.inner.onmessage = (message, extra) => {
|
|
754
|
-
try {
|
|
755
|
-
log({ direction: "receive", message, serverId });
|
|
756
|
-
} catch {
|
|
757
|
-
}
|
|
758
|
-
this.onmessage?.(message, extra);
|
|
759
|
-
};
|
|
760
|
-
this.inner.onclose = () => {
|
|
761
|
-
this.onclose?.();
|
|
762
|
-
};
|
|
763
|
-
this.inner.onerror = (error) => {
|
|
764
|
-
this.onerror?.(error);
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
onclose;
|
|
768
|
-
onerror;
|
|
769
|
-
onmessage;
|
|
770
|
-
async start() {
|
|
771
|
-
if (typeof this.inner.start === "function") {
|
|
772
|
-
await this.inner.start();
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
async send(message, options) {
|
|
776
|
-
try {
|
|
777
|
-
log({ direction: "send", message, serverId });
|
|
778
|
-
} catch {
|
|
779
|
-
}
|
|
780
|
-
await this.inner.send(message, options);
|
|
781
|
-
}
|
|
782
|
-
async close() {
|
|
783
|
-
await this.inner.close();
|
|
784
|
-
}
|
|
785
|
-
get sessionId() {
|
|
786
|
-
return this.inner.sessionId;
|
|
787
|
-
}
|
|
788
|
-
setProtocolVersion(version2) {
|
|
789
|
-
if (typeof this.inner.setProtocolVersion === "function") {
|
|
790
|
-
this.inner.setProtocolVersion(version2);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
return new LoggingTransport(transport);
|
|
795
|
-
}
|
|
796
|
-
resolveRpcLogger(serverId, config) {
|
|
797
|
-
if (config.rpcLogger) return config.rpcLogger;
|
|
798
|
-
if (config.logJsonRpc || this.defaultLogJsonRpc) {
|
|
799
|
-
return ({ direction, message, serverId: id }) => {
|
|
800
|
-
let printable;
|
|
801
|
-
try {
|
|
802
|
-
printable = typeof message === "string" ? message : JSON.stringify(message);
|
|
803
|
-
} catch {
|
|
804
|
-
printable = String(message);
|
|
805
|
-
}
|
|
806
|
-
console.debug(`[MCP:${id}] ${direction.toUpperCase()} ${printable}`);
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
if (this.defaultRpcLogger) return this.defaultRpcLogger;
|
|
810
|
-
return void 0;
|
|
811
|
-
}
|
|
812
|
-
isMethodUnavailableError(error, method) {
|
|
813
|
-
if (!(error instanceof Error)) {
|
|
814
|
-
return false;
|
|
815
|
-
}
|
|
816
|
-
const message = error.message.toLowerCase();
|
|
817
|
-
const methodTokens = /* @__PURE__ */ new Set();
|
|
818
|
-
const pushToken = (token) => {
|
|
819
|
-
if (token) {
|
|
820
|
-
methodTokens.add(token.toLowerCase());
|
|
821
|
-
}
|
|
822
|
-
};
|
|
823
|
-
pushToken(method);
|
|
824
|
-
for (const part of method.split(/[\/:._-]/)) {
|
|
825
|
-
pushToken(part);
|
|
826
|
-
}
|
|
827
|
-
const indicators = [
|
|
828
|
-
"method not found",
|
|
829
|
-
"not implemented",
|
|
830
|
-
"unsupported",
|
|
831
|
-
"does not support",
|
|
832
|
-
"unimplemented"
|
|
833
|
-
];
|
|
834
|
-
const indicatorMatch = indicators.some(
|
|
835
|
-
(indicator) => message.includes(indicator)
|
|
836
|
-
);
|
|
837
|
-
if (!indicatorMatch) {
|
|
838
|
-
return false;
|
|
839
|
-
}
|
|
840
|
-
if (Array.from(methodTokens).some((token) => message.includes(token))) {
|
|
841
|
-
return true;
|
|
842
|
-
}
|
|
843
|
-
return true;
|
|
844
|
-
}
|
|
845
|
-
getTimeout(config) {
|
|
846
|
-
return config.timeout ?? this.defaultTimeout;
|
|
847
|
-
}
|
|
848
|
-
isStdioConfig(config) {
|
|
849
|
-
return "command" in config;
|
|
850
|
-
}
|
|
851
|
-
getClientById(serverId) {
|
|
852
|
-
const state = this.clientStates.get(serverId);
|
|
853
|
-
if (!state?.client) {
|
|
854
|
-
throw new Error(`MCP server "${serverId}" is not connected.`);
|
|
855
|
-
}
|
|
856
|
-
return state.client;
|
|
857
|
-
}
|
|
858
|
-
};
|
|
50
|
+
import { MCPClientManager } from "@/sdk";
|
|
859
51
|
|
|
860
52
|
// routes/mcp/index.ts
|
|
861
53
|
import { Hono as Hono12 } from "hono";
|
|
@@ -2810,7 +2002,7 @@ import { MCPClient } from "@mastra/mcp";
|
|
|
2810
2002
|
import { streamText as streamText2 } from "ai";
|
|
2811
2003
|
|
|
2812
2004
|
// ../node_modules/convex/dist/esm/index.js
|
|
2813
|
-
var version = "1.
|
|
2005
|
+
var version = "1.28.0";
|
|
2814
2006
|
|
|
2815
2007
|
// ../node_modules/convex/dist/esm/values/base64.js
|
|
2816
2008
|
var lookup = [];
|