@mastra/mcp 0.10.5-alpha.0 → 0.10.5-alpha.2
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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +23 -0
- package/dist/_tsup-dts-rollup.d.cts +167 -126
- package/dist/_tsup-dts-rollup.d.ts +167 -126
- package/dist/index.cjs +624 -391
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +620 -387
- package/integration-tests/node_modules/.bin/vitest +2 -2
- package/integration-tests/package.json +2 -2
- package/package.json +7 -7
- package/src/__fixtures__/tools.ts +0 -9
- package/src/client/client.test.ts +310 -1
- package/src/client/client.ts +66 -3
- package/src/client/configuration.ts +22 -2
- package/src/client/elicitationActions.ts +26 -0
- package/src/client/index.ts +1 -1
- package/src/server/server.test.ts +433 -0
- package/src/server/server.ts +431 -429
- package/src/server/types.ts +25 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MastraBase } from '@mastra/core/base';
|
|
2
|
+
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
2
3
|
import { createTool as createTool$1 } from '@mastra/core/tools';
|
|
3
4
|
import { isZodType } from '@mastra/core/utils';
|
|
4
5
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
@@ -6,11 +7,10 @@ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
|
6
7
|
import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
7
8
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
8
9
|
import { DEFAULT_REQUEST_TIMEOUT_MSEC } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
9
|
-
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, ListPromptsRequestSchema, PromptSchema, GetPromptRequestSchema, ListResourcesResultSchema, ReadResourceResultSchema, ListResourceTemplatesResultSchema, ListPromptsResultSchema, GetPromptResultSchema, PromptListChangedNotificationSchema, ResourceUpdatedNotificationSchema, ResourceListChangedNotificationSchema, CallToolResultSchema, JSONRPCMessageSchema, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
10
|
+
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, ListPromptsRequestSchema, PromptSchema, GetPromptRequestSchema, ListResourcesResultSchema, ReadResourceResultSchema, ListResourceTemplatesResultSchema, ListPromptsResultSchema, GetPromptResultSchema, PromptListChangedNotificationSchema, ResourceUpdatedNotificationSchema, ResourceListChangedNotificationSchema, ElicitRequestSchema, CallToolResultSchema, JSONRPCMessageSchema, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
10
11
|
import { asyncExitHook, gracefulExit } from 'exit-hook';
|
|
11
12
|
import { z } from 'zod';
|
|
12
13
|
import { convertJsonSchemaToZod } from 'zod-from-json-schema';
|
|
13
|
-
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
14
14
|
import equal from 'fast-deep-equal';
|
|
15
15
|
import { v5 } from 'uuid';
|
|
16
16
|
import { randomUUID } from 'crypto';
|
|
@@ -21,9 +21,25 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
21
21
|
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
22
22
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
23
23
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
24
|
-
import { streamSSE } from 'hono/streaming';
|
|
25
24
|
|
|
26
25
|
// src/client/client.ts
|
|
26
|
+
|
|
27
|
+
// src/client/elicitationActions.ts
|
|
28
|
+
var ElicitationClientActions = class {
|
|
29
|
+
client;
|
|
30
|
+
logger;
|
|
31
|
+
constructor({ client, logger }) {
|
|
32
|
+
this.client = client;
|
|
33
|
+
this.logger = logger;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Set a handler for elicitation requests.
|
|
37
|
+
* @param handler The callback function to handle the elicitation request.
|
|
38
|
+
*/
|
|
39
|
+
onRequest(handler) {
|
|
40
|
+
this.client.setElicitationRequestHandler(handler);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
27
43
|
var PromptClientActions = class {
|
|
28
44
|
client;
|
|
29
45
|
logger;
|
|
@@ -206,6 +222,7 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
206
222
|
currentOperationContext = null;
|
|
207
223
|
resources;
|
|
208
224
|
prompts;
|
|
225
|
+
elicitation;
|
|
209
226
|
constructor({
|
|
210
227
|
name,
|
|
211
228
|
version = "1.0.0",
|
|
@@ -219,18 +236,20 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
219
236
|
this.logHandler = server.logger;
|
|
220
237
|
this.enableServerLogs = server.enableServerLogs ?? true;
|
|
221
238
|
this.serverConfig = server;
|
|
239
|
+
const clientCapabilities = { ...capabilities, elicitation: {} };
|
|
222
240
|
this.client = new Client(
|
|
223
241
|
{
|
|
224
242
|
name,
|
|
225
243
|
version
|
|
226
244
|
},
|
|
227
245
|
{
|
|
228
|
-
capabilities
|
|
246
|
+
capabilities: clientCapabilities
|
|
229
247
|
}
|
|
230
248
|
);
|
|
231
249
|
this.setupLogging();
|
|
232
250
|
this.resources = new ResourceClientActions({ client: this, logger: this.logger });
|
|
233
251
|
this.prompts = new PromptClientActions({ client: this, logger: this.logger });
|
|
252
|
+
this.elicitation = new ElicitationClientActions({ client: this, logger: this.logger });
|
|
234
253
|
}
|
|
235
254
|
/**
|
|
236
255
|
* Log a message at the specified level
|
|
@@ -472,6 +491,13 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
472
491
|
handler();
|
|
473
492
|
});
|
|
474
493
|
}
|
|
494
|
+
setElicitationRequestHandler(handler) {
|
|
495
|
+
this.log("debug", "Setting elicitation request handler");
|
|
496
|
+
this.client.setRequestHandler(ElicitRequestSchema, async (request) => {
|
|
497
|
+
this.log("debug", `Received elicitation request: ${request.params.message}`);
|
|
498
|
+
return handler(request.params);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
475
501
|
convertInputSchema(inputSchema) {
|
|
476
502
|
if (isZodType(inputSchema)) {
|
|
477
503
|
return inputSchema;
|
|
@@ -493,7 +519,42 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
493
519
|
error: errorDetails,
|
|
494
520
|
originalJsonSchema: inputSchema
|
|
495
521
|
});
|
|
496
|
-
throw new
|
|
522
|
+
throw new MastraError({
|
|
523
|
+
id: "MCP_TOOL_INPUT_SCHEMA_CONVERSION_FAILED",
|
|
524
|
+
domain: ErrorDomain.MCP,
|
|
525
|
+
category: ErrorCategory.USER,
|
|
526
|
+
details: { error: errorDetails ?? "Unknown error" }
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
convertOutputSchema(outputSchema) {
|
|
531
|
+
if (!outputSchema) return;
|
|
532
|
+
if (isZodType(outputSchema)) {
|
|
533
|
+
return outputSchema;
|
|
534
|
+
}
|
|
535
|
+
try {
|
|
536
|
+
return convertJsonSchemaToZod(outputSchema);
|
|
537
|
+
} catch (error) {
|
|
538
|
+
let errorDetails;
|
|
539
|
+
if (error instanceof Error) {
|
|
540
|
+
errorDetails = error.stack;
|
|
541
|
+
} else {
|
|
542
|
+
try {
|
|
543
|
+
errorDetails = JSON.stringify(error);
|
|
544
|
+
} catch {
|
|
545
|
+
errorDetails = String(error);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
this.log("error", "Failed to convert JSON schema to Zod schema using zodFromJsonSchema", {
|
|
549
|
+
error: errorDetails,
|
|
550
|
+
originalJsonSchema: outputSchema
|
|
551
|
+
});
|
|
552
|
+
throw new MastraError({
|
|
553
|
+
id: "MCP_TOOL_OUTPUT_SCHEMA_CONVERSION_FAILED",
|
|
554
|
+
domain: ErrorDomain.MCP,
|
|
555
|
+
category: ErrorCategory.USER,
|
|
556
|
+
details: { error: errorDetails ?? "Unknown error" }
|
|
557
|
+
});
|
|
497
558
|
}
|
|
498
559
|
}
|
|
499
560
|
async tools() {
|
|
@@ -507,6 +568,7 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
507
568
|
id: `${this.name}_${tool.name}`,
|
|
508
569
|
description: tool.description || "",
|
|
509
570
|
inputSchema: this.convertInputSchema(tool.inputSchema),
|
|
571
|
+
outputSchema: this.convertOutputSchema(tool.outputSchema),
|
|
510
572
|
execute: async ({ context, runtimeContext }) => {
|
|
511
573
|
const previousContext = this.currentOperationContext;
|
|
512
574
|
this.currentOperationContext = runtimeContext || null;
|
|
@@ -600,6 +662,26 @@ To fix this you have three different options:
|
|
|
600
662
|
this.addToInstanceCache();
|
|
601
663
|
return this;
|
|
602
664
|
}
|
|
665
|
+
get elicitation() {
|
|
666
|
+
this.addToInstanceCache();
|
|
667
|
+
return {
|
|
668
|
+
onRequest: async (serverName, handler) => {
|
|
669
|
+
try {
|
|
670
|
+
const internalClient = await this.getConnectedClientForServer(serverName);
|
|
671
|
+
return internalClient.elicitation.onRequest(handler);
|
|
672
|
+
} catch (err) {
|
|
673
|
+
throw new MastraError({
|
|
674
|
+
id: "MCP_CLIENT_ON_REQUEST_ELICITATION_FAILED",
|
|
675
|
+
domain: ErrorDomain.MCP,
|
|
676
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
677
|
+
details: {
|
|
678
|
+
serverName
|
|
679
|
+
}
|
|
680
|
+
}, err);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
}
|
|
603
685
|
get resources() {
|
|
604
686
|
this.addToInstanceCache();
|
|
605
687
|
return {
|
|
@@ -929,6 +1011,165 @@ var MCPConfiguration = class extends MCPClient {
|
|
|
929
1011
|
);
|
|
930
1012
|
}
|
|
931
1013
|
};
|
|
1014
|
+
|
|
1015
|
+
// ../../node_modules/.pnpm/hono@4.8.1/node_modules/hono/dist/utils/stream.js
|
|
1016
|
+
var StreamingApi = class {
|
|
1017
|
+
writer;
|
|
1018
|
+
encoder;
|
|
1019
|
+
writable;
|
|
1020
|
+
abortSubscribers = [];
|
|
1021
|
+
responseReadable;
|
|
1022
|
+
aborted = false;
|
|
1023
|
+
closed = false;
|
|
1024
|
+
constructor(writable, _readable) {
|
|
1025
|
+
this.writable = writable;
|
|
1026
|
+
this.writer = writable.getWriter();
|
|
1027
|
+
this.encoder = new TextEncoder();
|
|
1028
|
+
const reader = _readable.getReader();
|
|
1029
|
+
this.abortSubscribers.push(async () => {
|
|
1030
|
+
await reader.cancel();
|
|
1031
|
+
});
|
|
1032
|
+
this.responseReadable = new ReadableStream({
|
|
1033
|
+
async pull(controller) {
|
|
1034
|
+
const { done, value } = await reader.read();
|
|
1035
|
+
done ? controller.close() : controller.enqueue(value);
|
|
1036
|
+
},
|
|
1037
|
+
cancel: () => {
|
|
1038
|
+
this.abort();
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
async write(input) {
|
|
1043
|
+
try {
|
|
1044
|
+
if (typeof input === "string") {
|
|
1045
|
+
input = this.encoder.encode(input);
|
|
1046
|
+
}
|
|
1047
|
+
await this.writer.write(input);
|
|
1048
|
+
} catch {
|
|
1049
|
+
}
|
|
1050
|
+
return this;
|
|
1051
|
+
}
|
|
1052
|
+
async writeln(input) {
|
|
1053
|
+
await this.write(input + "\n");
|
|
1054
|
+
return this;
|
|
1055
|
+
}
|
|
1056
|
+
sleep(ms) {
|
|
1057
|
+
return new Promise((res) => setTimeout(res, ms));
|
|
1058
|
+
}
|
|
1059
|
+
async close() {
|
|
1060
|
+
try {
|
|
1061
|
+
await this.writer.close();
|
|
1062
|
+
} catch {
|
|
1063
|
+
}
|
|
1064
|
+
this.closed = true;
|
|
1065
|
+
}
|
|
1066
|
+
async pipe(body) {
|
|
1067
|
+
this.writer.releaseLock();
|
|
1068
|
+
await body.pipeTo(this.writable, { preventClose: true });
|
|
1069
|
+
this.writer = this.writable.getWriter();
|
|
1070
|
+
}
|
|
1071
|
+
onAbort(listener) {
|
|
1072
|
+
this.abortSubscribers.push(listener);
|
|
1073
|
+
}
|
|
1074
|
+
abort() {
|
|
1075
|
+
if (!this.aborted) {
|
|
1076
|
+
this.aborted = true;
|
|
1077
|
+
this.abortSubscribers.forEach((subscriber) => subscriber());
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
|
|
1082
|
+
// ../../node_modules/.pnpm/hono@4.8.1/node_modules/hono/dist/helper/streaming/utils.js
|
|
1083
|
+
var isOldBunVersion = () => {
|
|
1084
|
+
const version = typeof Bun !== "undefined" ? Bun.version : void 0;
|
|
1085
|
+
if (version === void 0) {
|
|
1086
|
+
return false;
|
|
1087
|
+
}
|
|
1088
|
+
const result = version.startsWith("1.1") || version.startsWith("1.0") || version.startsWith("0.");
|
|
1089
|
+
isOldBunVersion = () => result;
|
|
1090
|
+
return result;
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
// ../../node_modules/.pnpm/hono@4.8.1/node_modules/hono/dist/utils/html.js
|
|
1094
|
+
var HtmlEscapedCallbackPhase = {
|
|
1095
|
+
Stringify: 1};
|
|
1096
|
+
var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
|
|
1097
|
+
if (typeof str === "object" && !(str instanceof String)) {
|
|
1098
|
+
if (!(str instanceof Promise)) {
|
|
1099
|
+
str = str.toString();
|
|
1100
|
+
}
|
|
1101
|
+
if (str instanceof Promise) {
|
|
1102
|
+
str = await str;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
const callbacks = str.callbacks;
|
|
1106
|
+
if (!callbacks?.length) {
|
|
1107
|
+
return Promise.resolve(str);
|
|
1108
|
+
}
|
|
1109
|
+
if (buffer) {
|
|
1110
|
+
buffer[0] += str;
|
|
1111
|
+
} else {
|
|
1112
|
+
buffer = [str];
|
|
1113
|
+
}
|
|
1114
|
+
const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context }))).then(
|
|
1115
|
+
(res) => Promise.all(
|
|
1116
|
+
res.filter(Boolean).map((str2) => resolveCallback(str2, phase, false, context, buffer))
|
|
1117
|
+
).then(() => buffer[0])
|
|
1118
|
+
);
|
|
1119
|
+
{
|
|
1120
|
+
return resStr;
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
// ../../node_modules/.pnpm/hono@4.8.1/node_modules/hono/dist/helper/streaming/sse.js
|
|
1125
|
+
var SSEStreamingApi = class extends StreamingApi {
|
|
1126
|
+
constructor(writable, readable) {
|
|
1127
|
+
super(writable, readable);
|
|
1128
|
+
}
|
|
1129
|
+
async writeSSE(message) {
|
|
1130
|
+
const data = await resolveCallback(message.data, HtmlEscapedCallbackPhase.Stringify, false, {});
|
|
1131
|
+
const dataLines = data.split("\n").map((line) => {
|
|
1132
|
+
return `data: ${line}`;
|
|
1133
|
+
}).join("\n");
|
|
1134
|
+
const sseData = [
|
|
1135
|
+
message.event && `event: ${message.event}`,
|
|
1136
|
+
dataLines,
|
|
1137
|
+
message.id && `id: ${message.id}`,
|
|
1138
|
+
message.retry && `retry: ${message.retry}`
|
|
1139
|
+
].filter(Boolean).join("\n") + "\n\n";
|
|
1140
|
+
await this.write(sseData);
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
var run = async (stream2, cb, onError) => {
|
|
1144
|
+
try {
|
|
1145
|
+
await cb(stream2);
|
|
1146
|
+
} catch (e) {
|
|
1147
|
+
{
|
|
1148
|
+
console.error(e);
|
|
1149
|
+
}
|
|
1150
|
+
} finally {
|
|
1151
|
+
stream2.close();
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
var contextStash = /* @__PURE__ */ new WeakMap();
|
|
1155
|
+
var streamSSE = (c, cb, onError) => {
|
|
1156
|
+
const { readable, writable } = new TransformStream();
|
|
1157
|
+
const stream2 = new SSEStreamingApi(writable, readable);
|
|
1158
|
+
if (isOldBunVersion()) {
|
|
1159
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1160
|
+
if (!stream2.closed) {
|
|
1161
|
+
stream2.abort();
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
contextStash.set(stream2.responseReadable, c);
|
|
1166
|
+
c.header("Transfer-Encoding", "chunked");
|
|
1167
|
+
c.header("Content-Type", "text/event-stream");
|
|
1168
|
+
c.header("Cache-Control", "no-cache");
|
|
1169
|
+
c.header("Connection", "keep-alive");
|
|
1170
|
+
run(stream2, cb);
|
|
1171
|
+
return c.newResponse(stream2.responseReadable);
|
|
1172
|
+
};
|
|
932
1173
|
var MAXIMUM_MESSAGE_SIZE = 4 * 1024 * 1024;
|
|
933
1174
|
var SSETransport = class {
|
|
934
1175
|
messageUrl;
|
|
@@ -940,9 +1181,9 @@ var SSETransport = class {
|
|
|
940
1181
|
/**
|
|
941
1182
|
* Creates a new SSETransport, which will direct the MPC client to POST messages to messageUrl
|
|
942
1183
|
*/
|
|
943
|
-
constructor(messageUrl,
|
|
1184
|
+
constructor(messageUrl, stream2) {
|
|
944
1185
|
this.messageUrl = messageUrl;
|
|
945
|
-
this.stream =
|
|
1186
|
+
this.stream = stream2;
|
|
946
1187
|
this._sessionId = crypto.randomUUID();
|
|
947
1188
|
this.stream.onAbort(() => {
|
|
948
1189
|
void this.close();
|
|
@@ -959,6 +1200,10 @@ var SSETransport = class {
|
|
|
959
1200
|
if (this.stream.closed) {
|
|
960
1201
|
throw new Error("SSE transport already closed!");
|
|
961
1202
|
}
|
|
1203
|
+
await this.stream.writeSSE({
|
|
1204
|
+
event: "ping",
|
|
1205
|
+
data: ""
|
|
1206
|
+
});
|
|
962
1207
|
await this.stream.writeSSE({
|
|
963
1208
|
event: "endpoint",
|
|
964
1209
|
data: `${this.messageUrl}?sessionId=${this.sessionId}`
|
|
@@ -1132,15 +1377,8 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1132
1377
|
sseTransport;
|
|
1133
1378
|
sseHonoTransports;
|
|
1134
1379
|
streamableHTTPTransports = /* @__PURE__ */ new Map();
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
listResourcesHandlerIsRegistered = false;
|
|
1138
|
-
readResourceHandlerIsRegistered = false;
|
|
1139
|
-
listResourceTemplatesHandlerIsRegistered = false;
|
|
1140
|
-
subscribeResourceHandlerIsRegistered = false;
|
|
1141
|
-
unsubscribeResourceHandlerIsRegistered = false;
|
|
1142
|
-
listPromptsHandlerIsRegistered = false;
|
|
1143
|
-
getPromptHandlerIsRegistered = false;
|
|
1380
|
+
// Track server instances for each HTTP session
|
|
1381
|
+
httpServerInstances = /* @__PURE__ */ new Map();
|
|
1144
1382
|
definedResources;
|
|
1145
1383
|
definedResourceTemplates;
|
|
1146
1384
|
resourceOptions;
|
|
@@ -1149,6 +1387,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1149
1387
|
subscriptions = /* @__PURE__ */ new Set();
|
|
1150
1388
|
resources;
|
|
1151
1389
|
prompts;
|
|
1390
|
+
elicitation;
|
|
1152
1391
|
/**
|
|
1153
1392
|
* Get the current stdio transport.
|
|
1154
1393
|
*/
|
|
@@ -1183,7 +1422,8 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1183
1422
|
this.promptOptions = opts.prompts;
|
|
1184
1423
|
const capabilities = {
|
|
1185
1424
|
tools: {},
|
|
1186
|
-
logging: { enabled: true }
|
|
1425
|
+
logging: { enabled: true },
|
|
1426
|
+
elicitation: {}
|
|
1187
1427
|
};
|
|
1188
1428
|
if (opts.resources) {
|
|
1189
1429
|
capabilities.resources = { subscribe: true, listChanged: true };
|
|
@@ -1196,23 +1436,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1196
1436
|
`Initialized MCPServer '${this.name}' v${this.version} (ID: ${this.id}) with tools: ${Object.keys(this.convertedTools).join(", ")} and resources. Capabilities: ${JSON.stringify(capabilities)}`
|
|
1197
1437
|
);
|
|
1198
1438
|
this.sseHonoTransports = /* @__PURE__ */ new Map();
|
|
1199
|
-
this.
|
|
1200
|
-
this.registerCallToolHandler();
|
|
1201
|
-
if (opts.resources) {
|
|
1202
|
-
this.registerListResourcesHandler();
|
|
1203
|
-
this.registerReadResourceHandler({ getResourcesCallback: opts.resources.getResourceContent });
|
|
1204
|
-
this.registerSubscribeResourceHandler();
|
|
1205
|
-
this.registerUnsubscribeResourceHandler();
|
|
1206
|
-
if (opts.resources.resourceTemplates) {
|
|
1207
|
-
this.registerListResourceTemplatesHandler();
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
if (opts.prompts) {
|
|
1211
|
-
this.registerListPromptsHandler();
|
|
1212
|
-
this.registerGetPromptHandler({
|
|
1213
|
-
getPromptMessagesCallback: opts.prompts.getPromptMessages
|
|
1214
|
-
});
|
|
1215
|
-
}
|
|
1439
|
+
this.registerHandlersOnServer(this.server);
|
|
1216
1440
|
this.resources = new ServerResourceActions({
|
|
1217
1441
|
getSubscriptions: () => this.subscriptions,
|
|
1218
1442
|
getLogger: () => this.logger,
|
|
@@ -1231,6 +1455,342 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1231
1455
|
this.definedPrompts = void 0;
|
|
1232
1456
|
}
|
|
1233
1457
|
});
|
|
1458
|
+
this.elicitation = {
|
|
1459
|
+
sendRequest: async (request) => {
|
|
1460
|
+
return this.handleElicitationRequest(request);
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Handle an elicitation request by sending it to the connected client.
|
|
1466
|
+
* This method sends an elicitation/create request to the client and waits for the response.
|
|
1467
|
+
*
|
|
1468
|
+
* @param request - The elicitation request containing message and schema
|
|
1469
|
+
* @param serverInstance - Optional server instance to use; defaults to main server for backward compatibility
|
|
1470
|
+
* @returns Promise that resolves to the client's response
|
|
1471
|
+
*/
|
|
1472
|
+
async handleElicitationRequest(request, serverInstance) {
|
|
1473
|
+
this.logger.debug(`Sending elicitation request: ${request.message}`);
|
|
1474
|
+
const server = serverInstance || this.server;
|
|
1475
|
+
const response = await server.elicitInput(request);
|
|
1476
|
+
this.logger.debug(`Received elicitation response: ${JSON.stringify(response)}`);
|
|
1477
|
+
return response;
|
|
1478
|
+
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Creates a new Server instance configured with all handlers for HTTP sessions.
|
|
1481
|
+
* Each HTTP client connection gets its own Server instance to avoid routing conflicts.
|
|
1482
|
+
*/
|
|
1483
|
+
createServerInstance() {
|
|
1484
|
+
const capabilities = {
|
|
1485
|
+
tools: {},
|
|
1486
|
+
logging: { enabled: true },
|
|
1487
|
+
elicitation: {}
|
|
1488
|
+
};
|
|
1489
|
+
if (this.resourceOptions) {
|
|
1490
|
+
capabilities.resources = { subscribe: true, listChanged: true };
|
|
1491
|
+
}
|
|
1492
|
+
if (this.promptOptions) {
|
|
1493
|
+
capabilities.prompts = { listChanged: true };
|
|
1494
|
+
}
|
|
1495
|
+
const serverInstance = new Server({ name: this.name, version: this.version }, { capabilities });
|
|
1496
|
+
this.registerHandlersOnServer(serverInstance);
|
|
1497
|
+
return serverInstance;
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Registers all MCP handlers on a given server instance.
|
|
1501
|
+
* This allows us to create multiple server instances with identical functionality.
|
|
1502
|
+
*/
|
|
1503
|
+
registerHandlersOnServer(serverInstance) {
|
|
1504
|
+
serverInstance.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1505
|
+
this.logger.debug("Handling ListTools request");
|
|
1506
|
+
return {
|
|
1507
|
+
tools: Object.values(this.convertedTools).map((tool) => {
|
|
1508
|
+
const toolSpec = {
|
|
1509
|
+
name: tool.name,
|
|
1510
|
+
description: tool.description,
|
|
1511
|
+
inputSchema: tool.parameters.jsonSchema
|
|
1512
|
+
};
|
|
1513
|
+
if (tool.outputSchema) {
|
|
1514
|
+
toolSpec.outputSchema = tool.outputSchema.jsonSchema;
|
|
1515
|
+
}
|
|
1516
|
+
return toolSpec;
|
|
1517
|
+
})
|
|
1518
|
+
};
|
|
1519
|
+
});
|
|
1520
|
+
serverInstance.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1521
|
+
const startTime = Date.now();
|
|
1522
|
+
try {
|
|
1523
|
+
const tool = this.convertedTools[request.params.name];
|
|
1524
|
+
if (!tool) {
|
|
1525
|
+
this.logger.warn(`CallTool: Unknown tool '${request.params.name}' requested.`);
|
|
1526
|
+
return {
|
|
1527
|
+
content: [{ type: "text", text: `Unknown tool: ${request.params.name}` }],
|
|
1528
|
+
isError: true
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
const validation = tool.parameters.validate?.(request.params.arguments ?? {});
|
|
1532
|
+
if (validation && !validation.success) {
|
|
1533
|
+
this.logger.warn(`CallTool: Invalid tool arguments for '${request.params.name}'`, {
|
|
1534
|
+
errors: validation.error
|
|
1535
|
+
});
|
|
1536
|
+
return {
|
|
1537
|
+
content: [{ type: "text", text: `Invalid tool arguments: ${JSON.stringify(validation.error)}` }],
|
|
1538
|
+
isError: true
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
if (!tool.execute) {
|
|
1542
|
+
this.logger.warn(`CallTool: Tool '${request.params.name}' does not have an execute function.`);
|
|
1543
|
+
return {
|
|
1544
|
+
content: [{ type: "text", text: `Tool '${request.params.name}' does not have an execute function.` }],
|
|
1545
|
+
isError: true
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
const sessionElicitation = {
|
|
1549
|
+
sendRequest: async (request2) => {
|
|
1550
|
+
return this.handleElicitationRequest(request2, serverInstance);
|
|
1551
|
+
}
|
|
1552
|
+
};
|
|
1553
|
+
const result = await tool.execute(validation?.value, {
|
|
1554
|
+
messages: [],
|
|
1555
|
+
toolCallId: "",
|
|
1556
|
+
elicitation: sessionElicitation
|
|
1557
|
+
});
|
|
1558
|
+
this.logger.debug(`CallTool: Tool '${request.params.name}' executed successfully with result:`, result);
|
|
1559
|
+
const duration = Date.now() - startTime;
|
|
1560
|
+
this.logger.info(`Tool '${request.params.name}' executed successfully in ${duration}ms.`);
|
|
1561
|
+
const response = { isError: false, content: [] };
|
|
1562
|
+
if (tool.outputSchema) {
|
|
1563
|
+
if (!result.structuredContent) {
|
|
1564
|
+
throw new Error(`Tool ${request.params.name} has an output schema but no structured content was provided.`);
|
|
1565
|
+
}
|
|
1566
|
+
const outputValidation = tool.outputSchema.validate?.(result.structuredContent ?? {});
|
|
1567
|
+
if (outputValidation && !outputValidation.success) {
|
|
1568
|
+
this.logger.warn(`CallTool: Invalid structured content for '${request.params.name}'`, {
|
|
1569
|
+
errors: outputValidation.error
|
|
1570
|
+
});
|
|
1571
|
+
throw new Error(
|
|
1572
|
+
`Invalid structured content for tool ${request.params.name}: ${JSON.stringify(outputValidation.error)}`
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
response.structuredContent = result.structuredContent;
|
|
1576
|
+
}
|
|
1577
|
+
if (response.structuredContent) {
|
|
1578
|
+
response.content = [{ type: "text", text: JSON.stringify(response.structuredContent) }];
|
|
1579
|
+
} else {
|
|
1580
|
+
response.content = [
|
|
1581
|
+
{
|
|
1582
|
+
type: "text",
|
|
1583
|
+
text: typeof result === "string" ? result : JSON.stringify(result)
|
|
1584
|
+
}
|
|
1585
|
+
];
|
|
1586
|
+
}
|
|
1587
|
+
return response;
|
|
1588
|
+
} catch (error) {
|
|
1589
|
+
const duration = Date.now() - startTime;
|
|
1590
|
+
if (error instanceof z.ZodError) {
|
|
1591
|
+
this.logger.warn("Invalid tool arguments", {
|
|
1592
|
+
tool: request.params.name,
|
|
1593
|
+
errors: error.errors,
|
|
1594
|
+
duration: `${duration}ms`
|
|
1595
|
+
});
|
|
1596
|
+
return {
|
|
1597
|
+
content: [
|
|
1598
|
+
{
|
|
1599
|
+
type: "text",
|
|
1600
|
+
text: `Invalid arguments: ${error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ")}`
|
|
1601
|
+
}
|
|
1602
|
+
],
|
|
1603
|
+
isError: true
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
this.logger.error(`Tool execution failed: ${request.params.name}`, { error });
|
|
1607
|
+
return {
|
|
1608
|
+
content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
1609
|
+
isError: true
|
|
1610
|
+
};
|
|
1611
|
+
}
|
|
1612
|
+
});
|
|
1613
|
+
if (this.resourceOptions) {
|
|
1614
|
+
this.registerResourceHandlersOnServer(serverInstance);
|
|
1615
|
+
}
|
|
1616
|
+
if (this.promptOptions) {
|
|
1617
|
+
this.registerPromptHandlersOnServer(serverInstance);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
/**
|
|
1621
|
+
* Registers resource-related handlers on a server instance.
|
|
1622
|
+
*/
|
|
1623
|
+
registerResourceHandlersOnServer(serverInstance) {
|
|
1624
|
+
const capturedResourceOptions = this.resourceOptions;
|
|
1625
|
+
if (!capturedResourceOptions) return;
|
|
1626
|
+
if (capturedResourceOptions.listResources) {
|
|
1627
|
+
serverInstance.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
1628
|
+
this.logger.debug("Handling ListResources request");
|
|
1629
|
+
if (this.definedResources) {
|
|
1630
|
+
return { resources: this.definedResources };
|
|
1631
|
+
} else {
|
|
1632
|
+
try {
|
|
1633
|
+
const resources = await capturedResourceOptions.listResources();
|
|
1634
|
+
this.definedResources = resources;
|
|
1635
|
+
this.logger.debug(`Fetched and cached ${this.definedResources.length} resources.`);
|
|
1636
|
+
return { resources: this.definedResources };
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
this.logger.error("Error fetching resources via listResources():", { error });
|
|
1639
|
+
throw error;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
if (capturedResourceOptions.getResourceContent) {
|
|
1645
|
+
serverInstance.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
1646
|
+
const startTime = Date.now();
|
|
1647
|
+
const uri = request.params.uri;
|
|
1648
|
+
this.logger.debug(`Handling ReadResource request for URI: ${uri}`);
|
|
1649
|
+
if (!this.definedResources) {
|
|
1650
|
+
const resources = await this.resourceOptions?.listResources?.();
|
|
1651
|
+
if (!resources) throw new Error("Failed to load resources");
|
|
1652
|
+
this.definedResources = resources;
|
|
1653
|
+
}
|
|
1654
|
+
const resource = this.definedResources?.find((r) => r.uri === uri);
|
|
1655
|
+
if (!resource) {
|
|
1656
|
+
this.logger.warn(`ReadResource: Unknown resource URI '${uri}' requested.`);
|
|
1657
|
+
throw new Error(`Resource not found: ${uri}`);
|
|
1658
|
+
}
|
|
1659
|
+
try {
|
|
1660
|
+
const resourcesOrResourceContent = await capturedResourceOptions.getResourceContent({ uri });
|
|
1661
|
+
const resourcesContent = Array.isArray(resourcesOrResourceContent) ? resourcesOrResourceContent : [resourcesOrResourceContent];
|
|
1662
|
+
const contents = resourcesContent.map((resourceContent) => {
|
|
1663
|
+
const contentItem = {
|
|
1664
|
+
uri: resource.uri,
|
|
1665
|
+
mimeType: resource.mimeType
|
|
1666
|
+
};
|
|
1667
|
+
if ("text" in resourceContent) {
|
|
1668
|
+
contentItem.text = resourceContent.text;
|
|
1669
|
+
}
|
|
1670
|
+
if ("blob" in resourceContent) {
|
|
1671
|
+
contentItem.blob = resourceContent.blob;
|
|
1672
|
+
}
|
|
1673
|
+
return contentItem;
|
|
1674
|
+
});
|
|
1675
|
+
const duration = Date.now() - startTime;
|
|
1676
|
+
this.logger.info(`Resource '${uri}' read successfully in ${duration}ms.`);
|
|
1677
|
+
return {
|
|
1678
|
+
contents
|
|
1679
|
+
};
|
|
1680
|
+
} catch (error) {
|
|
1681
|
+
const duration = Date.now() - startTime;
|
|
1682
|
+
this.logger.error(`Failed to get content for resource URI '${uri}' in ${duration}ms`, { error });
|
|
1683
|
+
throw error;
|
|
1684
|
+
}
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
if (capturedResourceOptions.resourceTemplates) {
|
|
1688
|
+
serverInstance.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
1689
|
+
this.logger.debug("Handling ListResourceTemplates request");
|
|
1690
|
+
if (this.definedResourceTemplates) {
|
|
1691
|
+
return { resourceTemplates: this.definedResourceTemplates };
|
|
1692
|
+
} else {
|
|
1693
|
+
try {
|
|
1694
|
+
const templates = await capturedResourceOptions.resourceTemplates();
|
|
1695
|
+
this.definedResourceTemplates = templates;
|
|
1696
|
+
this.logger.debug(`Fetched and cached ${this.definedResourceTemplates.length} resource templates.`);
|
|
1697
|
+
return { resourceTemplates: this.definedResourceTemplates };
|
|
1698
|
+
} catch (error) {
|
|
1699
|
+
this.logger.error("Error fetching resource templates via resourceTemplates():", { error });
|
|
1700
|
+
throw error;
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
serverInstance.setRequestHandler(SubscribeRequestSchema, async (request) => {
|
|
1706
|
+
const uri = request.params.uri;
|
|
1707
|
+
this.logger.info(`Received resources/subscribe request for URI: ${uri}`);
|
|
1708
|
+
this.subscriptions.add(uri);
|
|
1709
|
+
return {};
|
|
1710
|
+
});
|
|
1711
|
+
serverInstance.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
|
|
1712
|
+
const uri = request.params.uri;
|
|
1713
|
+
this.logger.info(`Received resources/unsubscribe request for URI: ${uri}`);
|
|
1714
|
+
this.subscriptions.delete(uri);
|
|
1715
|
+
return {};
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* Registers prompt-related handlers on a server instance.
|
|
1720
|
+
*/
|
|
1721
|
+
registerPromptHandlersOnServer(serverInstance) {
|
|
1722
|
+
const capturedPromptOptions = this.promptOptions;
|
|
1723
|
+
if (!capturedPromptOptions) return;
|
|
1724
|
+
if (capturedPromptOptions.listPrompts) {
|
|
1725
|
+
serverInstance.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
1726
|
+
this.logger.debug("Handling ListPrompts request");
|
|
1727
|
+
if (this.definedPrompts) {
|
|
1728
|
+
return {
|
|
1729
|
+
prompts: this.definedPrompts?.map((p) => ({ ...p, version: p.version ?? void 0 }))
|
|
1730
|
+
};
|
|
1731
|
+
} else {
|
|
1732
|
+
try {
|
|
1733
|
+
const prompts = await capturedPromptOptions.listPrompts();
|
|
1734
|
+
for (const prompt of prompts) {
|
|
1735
|
+
PromptSchema.parse(prompt);
|
|
1736
|
+
}
|
|
1737
|
+
this.definedPrompts = prompts;
|
|
1738
|
+
this.logger.debug(`Fetched and cached ${this.definedPrompts.length} prompts.`);
|
|
1739
|
+
return {
|
|
1740
|
+
prompts: this.definedPrompts?.map((p) => ({ ...p, version: p.version ?? void 0 }))
|
|
1741
|
+
};
|
|
1742
|
+
} catch (error) {
|
|
1743
|
+
this.logger.error("Error fetching prompts via listPrompts():", {
|
|
1744
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1745
|
+
});
|
|
1746
|
+
throw error;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
});
|
|
1750
|
+
}
|
|
1751
|
+
if (capturedPromptOptions.getPromptMessages) {
|
|
1752
|
+
serverInstance.setRequestHandler(
|
|
1753
|
+
GetPromptRequestSchema,
|
|
1754
|
+
async (request) => {
|
|
1755
|
+
const startTime = Date.now();
|
|
1756
|
+
const { name, version, arguments: args } = request.params;
|
|
1757
|
+
if (!this.definedPrompts) {
|
|
1758
|
+
const prompts = await this.promptOptions?.listPrompts?.();
|
|
1759
|
+
if (!prompts) throw new Error("Failed to load prompts");
|
|
1760
|
+
this.definedPrompts = prompts;
|
|
1761
|
+
}
|
|
1762
|
+
let prompt;
|
|
1763
|
+
if (version) {
|
|
1764
|
+
prompt = this.definedPrompts?.find((p) => p.name === name && p.version === version);
|
|
1765
|
+
} else {
|
|
1766
|
+
prompt = this.definedPrompts?.find((p) => p.name === name);
|
|
1767
|
+
}
|
|
1768
|
+
if (!prompt) throw new Error(`Prompt "${name}"${version ? ` (version ${version})` : ""} not found`);
|
|
1769
|
+
if (prompt.arguments) {
|
|
1770
|
+
for (const arg of prompt.arguments) {
|
|
1771
|
+
if (arg.required && (args?.[arg.name] === void 0 || args?.[arg.name] === null)) {
|
|
1772
|
+
throw new Error(`Missing required argument: ${arg.name}`);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
try {
|
|
1777
|
+
let messages = [];
|
|
1778
|
+
if (capturedPromptOptions.getPromptMessages) {
|
|
1779
|
+
messages = await capturedPromptOptions.getPromptMessages({ name, version, args });
|
|
1780
|
+
}
|
|
1781
|
+
const duration = Date.now() - startTime;
|
|
1782
|
+
this.logger.info(
|
|
1783
|
+
`Prompt '${name}'${version ? ` (version ${version})` : ""} retrieved successfully in ${duration}ms.`
|
|
1784
|
+
);
|
|
1785
|
+
return { prompt, messages };
|
|
1786
|
+
} catch (error) {
|
|
1787
|
+
const duration = Date.now() - startTime;
|
|
1788
|
+
this.logger.error(`Failed to get content for prompt '${name}' in ${duration}ms`, { error });
|
|
1789
|
+
throw error;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
);
|
|
1793
|
+
}
|
|
1234
1794
|
}
|
|
1235
1795
|
convertAgentsToTools(agentsConfig, definedConvertedTools) {
|
|
1236
1796
|
const agentTools = {};
|
|
@@ -1330,8 +1890,8 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1330
1890
|
context
|
|
1331
1891
|
);
|
|
1332
1892
|
try {
|
|
1333
|
-
const
|
|
1334
|
-
const response = await
|
|
1893
|
+
const run2 = workflow.createRun({ runId: runtimeContext?.get("runId") });
|
|
1894
|
+
const response = await run2.start({ inputData: context, runtimeContext });
|
|
1335
1895
|
return response;
|
|
1336
1896
|
} catch (error) {
|
|
1337
1897
|
this.logger.error(
|
|
@@ -1354,6 +1914,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1354
1914
|
name: workflowToolName,
|
|
1355
1915
|
description: coreTool.description,
|
|
1356
1916
|
parameters: coreTool.parameters,
|
|
1917
|
+
outputSchema: coreTool.outputSchema,
|
|
1357
1918
|
execute: coreTool.execute,
|
|
1358
1919
|
toolType: "workflow"
|
|
1359
1920
|
};
|
|
@@ -1393,6 +1954,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1393
1954
|
name: toolName,
|
|
1394
1955
|
description: coreTool.description,
|
|
1395
1956
|
parameters: coreTool.parameters,
|
|
1957
|
+
outputSchema: coreTool.outputSchema,
|
|
1396
1958
|
execute: coreTool.execute
|
|
1397
1959
|
};
|
|
1398
1960
|
this.logger.info(`Registered explicit tool: '${toolName}'`);
|
|
@@ -1428,342 +1990,6 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1428
1990
|
);
|
|
1429
1991
|
return allConvertedTools;
|
|
1430
1992
|
}
|
|
1431
|
-
/**
|
|
1432
|
-
* Register the ListTools handler for listing all available tools.
|
|
1433
|
-
*/
|
|
1434
|
-
registerListToolsHandler() {
|
|
1435
|
-
if (this.listToolsHandlerIsRegistered) {
|
|
1436
|
-
return;
|
|
1437
|
-
}
|
|
1438
|
-
this.listToolsHandlerIsRegistered = true;
|
|
1439
|
-
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1440
|
-
this.logger.debug("Handling ListTools request");
|
|
1441
|
-
return {
|
|
1442
|
-
tools: Object.values(this.convertedTools).map((tool) => ({
|
|
1443
|
-
name: tool.name,
|
|
1444
|
-
description: tool.description,
|
|
1445
|
-
inputSchema: tool.parameters.jsonSchema
|
|
1446
|
-
}))
|
|
1447
|
-
};
|
|
1448
|
-
});
|
|
1449
|
-
}
|
|
1450
|
-
/**
|
|
1451
|
-
* Register the CallTool handler for executing a tool by name.
|
|
1452
|
-
*/
|
|
1453
|
-
registerCallToolHandler() {
|
|
1454
|
-
if (this.callToolHandlerIsRegistered) {
|
|
1455
|
-
return;
|
|
1456
|
-
}
|
|
1457
|
-
this.callToolHandlerIsRegistered = true;
|
|
1458
|
-
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1459
|
-
const startTime = Date.now();
|
|
1460
|
-
try {
|
|
1461
|
-
const tool = this.convertedTools[request.params.name];
|
|
1462
|
-
if (!tool) {
|
|
1463
|
-
this.logger.warn(`CallTool: Unknown tool '${request.params.name}' requested.`);
|
|
1464
|
-
return {
|
|
1465
|
-
content: [{ type: "text", text: `Unknown tool: ${request.params.name}` }],
|
|
1466
|
-
isError: true
|
|
1467
|
-
};
|
|
1468
|
-
}
|
|
1469
|
-
this.logger.debug(`CallTool: Invoking '${request.params.name}' with arguments:`, request.params.arguments);
|
|
1470
|
-
const validation = tool.parameters.validate?.(request.params.arguments ?? {});
|
|
1471
|
-
if (validation && !validation.success) {
|
|
1472
|
-
this.logger.warn(`CallTool: Invalid tool arguments for '${request.params.name}'`, {
|
|
1473
|
-
errors: validation.error
|
|
1474
|
-
});
|
|
1475
|
-
return {
|
|
1476
|
-
content: [{ type: "text", text: `Invalid tool arguments: ${JSON.stringify(validation.error)}` }],
|
|
1477
|
-
isError: true
|
|
1478
|
-
};
|
|
1479
|
-
}
|
|
1480
|
-
if (!tool.execute) {
|
|
1481
|
-
this.logger.warn(`CallTool: Tool '${request.params.name}' does not have an execute function.`);
|
|
1482
|
-
return {
|
|
1483
|
-
content: [{ type: "text", text: `Tool '${request.params.name}' does not have an execute function.` }],
|
|
1484
|
-
isError: true
|
|
1485
|
-
};
|
|
1486
|
-
}
|
|
1487
|
-
const result = await tool.execute(validation?.value, { messages: [], toolCallId: "" });
|
|
1488
|
-
const duration = Date.now() - startTime;
|
|
1489
|
-
this.logger.info(`Tool '${request.params.name}' executed successfully in ${duration}ms.`);
|
|
1490
|
-
return {
|
|
1491
|
-
content: [
|
|
1492
|
-
{
|
|
1493
|
-
type: "text",
|
|
1494
|
-
text: typeof result === "string" ? result : JSON.stringify(result)
|
|
1495
|
-
}
|
|
1496
|
-
],
|
|
1497
|
-
isError: false
|
|
1498
|
-
};
|
|
1499
|
-
} catch (error) {
|
|
1500
|
-
const duration = Date.now() - startTime;
|
|
1501
|
-
if (error instanceof z.ZodError) {
|
|
1502
|
-
this.logger.warn("Invalid tool arguments", {
|
|
1503
|
-
tool: request.params.name,
|
|
1504
|
-
errors: error.errors,
|
|
1505
|
-
duration: `${duration}ms`
|
|
1506
|
-
});
|
|
1507
|
-
return {
|
|
1508
|
-
content: [
|
|
1509
|
-
{
|
|
1510
|
-
type: "text",
|
|
1511
|
-
text: `Invalid arguments: ${error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ")}`
|
|
1512
|
-
}
|
|
1513
|
-
],
|
|
1514
|
-
isError: true
|
|
1515
|
-
};
|
|
1516
|
-
}
|
|
1517
|
-
this.logger.error(`Tool execution failed: ${request.params.name}`, { error });
|
|
1518
|
-
return {
|
|
1519
|
-
content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
1520
|
-
isError: true
|
|
1521
|
-
};
|
|
1522
|
-
}
|
|
1523
|
-
});
|
|
1524
|
-
}
|
|
1525
|
-
/**
|
|
1526
|
-
* Register the ListResources handler for listing all available resources.
|
|
1527
|
-
*/
|
|
1528
|
-
registerListResourcesHandler() {
|
|
1529
|
-
if (this.listResourcesHandlerIsRegistered) {
|
|
1530
|
-
return;
|
|
1531
|
-
}
|
|
1532
|
-
this.listResourcesHandlerIsRegistered = true;
|
|
1533
|
-
const capturedResourceOptions = this.resourceOptions;
|
|
1534
|
-
if (!capturedResourceOptions?.listResources) {
|
|
1535
|
-
this.logger.warn("ListResources capability not supported by server configuration.");
|
|
1536
|
-
return;
|
|
1537
|
-
}
|
|
1538
|
-
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
1539
|
-
this.logger.debug("Handling ListResources request");
|
|
1540
|
-
if (this.definedResources) {
|
|
1541
|
-
return { resources: this.definedResources };
|
|
1542
|
-
} else {
|
|
1543
|
-
try {
|
|
1544
|
-
const resources = await capturedResourceOptions.listResources();
|
|
1545
|
-
this.definedResources = resources;
|
|
1546
|
-
this.logger.debug(`Fetched and cached ${this.definedResources.length} resources.`);
|
|
1547
|
-
return { resources: this.definedResources };
|
|
1548
|
-
} catch (error) {
|
|
1549
|
-
this.logger.error("Error fetching resources via listResources():", { error });
|
|
1550
|
-
throw error;
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
});
|
|
1554
|
-
}
|
|
1555
|
-
/**
|
|
1556
|
-
* Register the ReadResource handler for reading a resource by URI.
|
|
1557
|
-
*/
|
|
1558
|
-
registerReadResourceHandler({
|
|
1559
|
-
getResourcesCallback
|
|
1560
|
-
}) {
|
|
1561
|
-
if (this.readResourceHandlerIsRegistered) {
|
|
1562
|
-
return;
|
|
1563
|
-
}
|
|
1564
|
-
this.readResourceHandlerIsRegistered = true;
|
|
1565
|
-
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
1566
|
-
const startTime = Date.now();
|
|
1567
|
-
const uri = request.params.uri;
|
|
1568
|
-
this.logger.debug(`Handling ReadResource request for URI: ${uri}`);
|
|
1569
|
-
if (!this.definedResources) {
|
|
1570
|
-
const resources = await this.resourceOptions?.listResources?.();
|
|
1571
|
-
if (!resources) throw new Error("Failed to load resources");
|
|
1572
|
-
this.definedResources = resources;
|
|
1573
|
-
}
|
|
1574
|
-
const resource = this.definedResources?.find((r) => r.uri === uri);
|
|
1575
|
-
if (!resource) {
|
|
1576
|
-
this.logger.warn(`ReadResource: Unknown resource URI '${uri}' requested.`);
|
|
1577
|
-
throw new Error(`Resource not found: ${uri}`);
|
|
1578
|
-
}
|
|
1579
|
-
try {
|
|
1580
|
-
const resourcesOrResourceContent = await getResourcesCallback({ uri });
|
|
1581
|
-
const resourcesContent = Array.isArray(resourcesOrResourceContent) ? resourcesOrResourceContent : [resourcesOrResourceContent];
|
|
1582
|
-
const contents = resourcesContent.map((resourceContent) => {
|
|
1583
|
-
const contentItem = {
|
|
1584
|
-
uri: resource.uri,
|
|
1585
|
-
mimeType: resource.mimeType
|
|
1586
|
-
};
|
|
1587
|
-
if ("text" in resourceContent) {
|
|
1588
|
-
contentItem.text = resourceContent.text;
|
|
1589
|
-
}
|
|
1590
|
-
if ("blob" in resourceContent) {
|
|
1591
|
-
contentItem.blob = resourceContent.blob;
|
|
1592
|
-
}
|
|
1593
|
-
return contentItem;
|
|
1594
|
-
});
|
|
1595
|
-
const duration = Date.now() - startTime;
|
|
1596
|
-
this.logger.info(`Resource '${uri}' read successfully in ${duration}ms.`);
|
|
1597
|
-
return {
|
|
1598
|
-
contents
|
|
1599
|
-
};
|
|
1600
|
-
} catch (error) {
|
|
1601
|
-
const duration = Date.now() - startTime;
|
|
1602
|
-
this.logger.error(`Failed to get content for resource URI '${uri}' in ${duration}ms`, { error });
|
|
1603
|
-
throw error;
|
|
1604
|
-
}
|
|
1605
|
-
});
|
|
1606
|
-
}
|
|
1607
|
-
/**
|
|
1608
|
-
* Register the ListResourceTemplates handler.
|
|
1609
|
-
*/
|
|
1610
|
-
registerListResourceTemplatesHandler() {
|
|
1611
|
-
if (this.listResourceTemplatesHandlerIsRegistered) {
|
|
1612
|
-
return;
|
|
1613
|
-
}
|
|
1614
|
-
if (!this.resourceOptions || typeof this.resourceOptions.resourceTemplates !== "function") {
|
|
1615
|
-
this.logger.warn(
|
|
1616
|
-
"ListResourceTemplates handler called, but resourceTemplates function is not available on resourceOptions or not a function."
|
|
1617
|
-
);
|
|
1618
|
-
this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
1619
|
-
this.logger.debug("Handling ListResourceTemplates request (no templates configured or resourceOptions issue)");
|
|
1620
|
-
return { resourceTemplates: [] };
|
|
1621
|
-
});
|
|
1622
|
-
this.listResourceTemplatesHandlerIsRegistered = true;
|
|
1623
|
-
return;
|
|
1624
|
-
}
|
|
1625
|
-
const resourceTemplatesFn = this.resourceOptions.resourceTemplates;
|
|
1626
|
-
this.listResourceTemplatesHandlerIsRegistered = true;
|
|
1627
|
-
this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
1628
|
-
this.logger.debug("Handling ListResourceTemplates request");
|
|
1629
|
-
if (this.definedResourceTemplates) {
|
|
1630
|
-
return { resourceTemplates: this.definedResourceTemplates };
|
|
1631
|
-
} else {
|
|
1632
|
-
try {
|
|
1633
|
-
const templates = await resourceTemplatesFn();
|
|
1634
|
-
this.definedResourceTemplates = templates;
|
|
1635
|
-
this.logger.debug(`Fetched and cached ${this.definedResourceTemplates.length} resource templates.`);
|
|
1636
|
-
return { resourceTemplates: this.definedResourceTemplates };
|
|
1637
|
-
} catch (error) {
|
|
1638
|
-
this.logger.error("Error fetching resource templates via resourceTemplates():", { error });
|
|
1639
|
-
throw error;
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
});
|
|
1643
|
-
}
|
|
1644
|
-
/**
|
|
1645
|
-
* Register the SubscribeResource handler.
|
|
1646
|
-
*/
|
|
1647
|
-
registerSubscribeResourceHandler() {
|
|
1648
|
-
if (this.subscribeResourceHandlerIsRegistered) {
|
|
1649
|
-
return;
|
|
1650
|
-
}
|
|
1651
|
-
if (!SubscribeRequestSchema) {
|
|
1652
|
-
this.logger.warn("SubscribeRequestSchema not available, cannot register SubscribeResource handler.");
|
|
1653
|
-
return;
|
|
1654
|
-
}
|
|
1655
|
-
this.subscribeResourceHandlerIsRegistered = true;
|
|
1656
|
-
this.server.setRequestHandler(SubscribeRequestSchema, async (request) => {
|
|
1657
|
-
const uri = request.params.uri;
|
|
1658
|
-
this.logger.info(`Received resources/subscribe request for URI: ${uri}`);
|
|
1659
|
-
this.subscriptions.add(uri);
|
|
1660
|
-
return {};
|
|
1661
|
-
});
|
|
1662
|
-
}
|
|
1663
|
-
/**
|
|
1664
|
-
* Register the UnsubscribeResource handler.
|
|
1665
|
-
*/
|
|
1666
|
-
registerUnsubscribeResourceHandler() {
|
|
1667
|
-
if (this.unsubscribeResourceHandlerIsRegistered) {
|
|
1668
|
-
return;
|
|
1669
|
-
}
|
|
1670
|
-
this.unsubscribeResourceHandlerIsRegistered = true;
|
|
1671
|
-
this.server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
|
|
1672
|
-
const uri = request.params.uri;
|
|
1673
|
-
this.logger.info(`Received resources/unsubscribe request for URI: ${uri}`);
|
|
1674
|
-
this.subscriptions.delete(uri);
|
|
1675
|
-
return {};
|
|
1676
|
-
});
|
|
1677
|
-
}
|
|
1678
|
-
/**
|
|
1679
|
-
* Register the ListPrompts handler.
|
|
1680
|
-
*/
|
|
1681
|
-
registerListPromptsHandler() {
|
|
1682
|
-
if (this.listPromptsHandlerIsRegistered) {
|
|
1683
|
-
return;
|
|
1684
|
-
}
|
|
1685
|
-
this.listPromptsHandlerIsRegistered = true;
|
|
1686
|
-
const capturedPromptOptions = this.promptOptions;
|
|
1687
|
-
if (!capturedPromptOptions?.listPrompts) {
|
|
1688
|
-
this.logger.warn("ListPrompts capability not supported by server configuration.");
|
|
1689
|
-
return;
|
|
1690
|
-
}
|
|
1691
|
-
this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
1692
|
-
this.logger.debug("Handling ListPrompts request");
|
|
1693
|
-
if (this.definedPrompts) {
|
|
1694
|
-
return {
|
|
1695
|
-
prompts: this.definedPrompts?.map((p) => ({ ...p, version: p.version ?? void 0 }))
|
|
1696
|
-
};
|
|
1697
|
-
} else {
|
|
1698
|
-
try {
|
|
1699
|
-
const prompts = await capturedPromptOptions.listPrompts();
|
|
1700
|
-
for (const prompt of prompts) {
|
|
1701
|
-
PromptSchema.parse(prompt);
|
|
1702
|
-
}
|
|
1703
|
-
this.definedPrompts = prompts;
|
|
1704
|
-
this.logger.debug(`Fetched and cached ${this.definedPrompts.length} prompts.`);
|
|
1705
|
-
return {
|
|
1706
|
-
prompts: this.definedPrompts?.map((p) => ({ ...p, version: p.version ?? void 0 }))
|
|
1707
|
-
};
|
|
1708
|
-
} catch (error) {
|
|
1709
|
-
this.logger.error("Error fetching prompts via listPrompts():", {
|
|
1710
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1711
|
-
});
|
|
1712
|
-
throw error;
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
|
-
});
|
|
1716
|
-
}
|
|
1717
|
-
/**
|
|
1718
|
-
* Register the GetPrompt handler.
|
|
1719
|
-
*/
|
|
1720
|
-
registerGetPromptHandler({
|
|
1721
|
-
getPromptMessagesCallback
|
|
1722
|
-
}) {
|
|
1723
|
-
if (this.getPromptHandlerIsRegistered) return;
|
|
1724
|
-
this.getPromptHandlerIsRegistered = true;
|
|
1725
|
-
this.server.setRequestHandler(
|
|
1726
|
-
GetPromptRequestSchema,
|
|
1727
|
-
async (request) => {
|
|
1728
|
-
const startTime = Date.now();
|
|
1729
|
-
const { name, version, arguments: args } = request.params;
|
|
1730
|
-
if (!this.definedPrompts) {
|
|
1731
|
-
const prompts = await this.promptOptions?.listPrompts?.();
|
|
1732
|
-
if (!prompts) throw new Error("Failed to load prompts");
|
|
1733
|
-
this.definedPrompts = prompts;
|
|
1734
|
-
}
|
|
1735
|
-
let prompt;
|
|
1736
|
-
if (version) {
|
|
1737
|
-
prompt = this.definedPrompts?.find((p) => p.name === name && p.version === version);
|
|
1738
|
-
} else {
|
|
1739
|
-
prompt = this.definedPrompts?.find((p) => p.name === name);
|
|
1740
|
-
}
|
|
1741
|
-
if (!prompt) throw new Error(`Prompt "${name}"${version ? ` (version ${version})` : ""} not found`);
|
|
1742
|
-
if (prompt.arguments) {
|
|
1743
|
-
for (const arg of prompt.arguments) {
|
|
1744
|
-
if (arg.required && (args?.[arg.name] === void 0 || args?.[arg.name] === null)) {
|
|
1745
|
-
throw new Error(`Missing required argument: ${arg.name}`);
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
try {
|
|
1750
|
-
let messages = [];
|
|
1751
|
-
if (getPromptMessagesCallback) {
|
|
1752
|
-
messages = await getPromptMessagesCallback({ name, version, args });
|
|
1753
|
-
}
|
|
1754
|
-
const duration = Date.now() - startTime;
|
|
1755
|
-
this.logger.info(
|
|
1756
|
-
`Prompt '${name}'${version ? ` (version ${version})` : ""} retrieved successfully in ${duration}ms.`
|
|
1757
|
-
);
|
|
1758
|
-
return { prompt, messages };
|
|
1759
|
-
} catch (error) {
|
|
1760
|
-
const duration = Date.now() - startTime;
|
|
1761
|
-
this.logger.error(`Failed to get content for prompt '${name}' in ${duration}ms`, { error });
|
|
1762
|
-
throw error;
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
);
|
|
1766
|
-
}
|
|
1767
1993
|
/**
|
|
1768
1994
|
* Start the MCP server using stdio transport (for Windsurf integration).
|
|
1769
1995
|
*/
|
|
@@ -1849,10 +2075,10 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1849
2075
|
async startHonoSSE({ url, ssePath, messagePath, context }) {
|
|
1850
2076
|
try {
|
|
1851
2077
|
if (url.pathname === ssePath) {
|
|
1852
|
-
return streamSSE(context, async (
|
|
2078
|
+
return streamSSE(context, async (stream2) => {
|
|
1853
2079
|
await this.connectHonoSSE({
|
|
1854
2080
|
messagePath,
|
|
1855
|
-
stream
|
|
2081
|
+
stream: stream2
|
|
1856
2082
|
});
|
|
1857
2083
|
});
|
|
1858
2084
|
} else if (url.pathname === messagePath) {
|
|
@@ -1976,11 +2202,17 @@ var MCPServer = class extends MCPServerBase {
|
|
|
1976
2202
|
`startHTTP: Streamable HTTP transport closed for session ${closedSessionId}, removing from map.`
|
|
1977
2203
|
);
|
|
1978
2204
|
this.streamableHTTPTransports.delete(closedSessionId);
|
|
2205
|
+
if (this.httpServerInstances.has(closedSessionId)) {
|
|
2206
|
+
this.httpServerInstances.delete(closedSessionId);
|
|
2207
|
+
this.logger.debug(`startHTTP: Cleaned up server instance for closed session ${closedSessionId}`);
|
|
2208
|
+
}
|
|
1979
2209
|
}
|
|
1980
2210
|
};
|
|
1981
|
-
|
|
2211
|
+
const sessionServerInstance = this.createServerInstance();
|
|
2212
|
+
await sessionServerInstance.connect(transport);
|
|
1982
2213
|
if (transport.sessionId) {
|
|
1983
2214
|
this.streamableHTTPTransports.set(transport.sessionId, transport);
|
|
2215
|
+
this.httpServerInstances.set(transport.sessionId, sessionServerInstance);
|
|
1984
2216
|
this.logger.debug(
|
|
1985
2217
|
`startHTTP: Streamable HTTP session initialized and stored with ID: ${transport.sessionId}`
|
|
1986
2218
|
);
|
|
@@ -2078,13 +2310,13 @@ var MCPServer = class extends MCPServerBase {
|
|
|
2078
2310
|
throw mastraError;
|
|
2079
2311
|
}
|
|
2080
2312
|
}
|
|
2081
|
-
async connectHonoSSE({ messagePath, stream }) {
|
|
2313
|
+
async connectHonoSSE({ messagePath, stream: stream2 }) {
|
|
2082
2314
|
this.logger.debug("Received SSE connection");
|
|
2083
|
-
const sseTransport = new SSETransport(messagePath,
|
|
2315
|
+
const sseTransport = new SSETransport(messagePath, stream2);
|
|
2084
2316
|
const sessionId = sseTransport.sessionId;
|
|
2085
2317
|
this.logger.debug("SSE Transport created with sessionId:", { sessionId });
|
|
2086
2318
|
this.sseHonoTransports.set(sessionId, sseTransport);
|
|
2087
|
-
|
|
2319
|
+
stream2.onAbort(() => {
|
|
2088
2320
|
this.logger.debug("SSE Transport aborted with sessionId:", { sessionId });
|
|
2089
2321
|
this.sseHonoTransports.delete(sessionId);
|
|
2090
2322
|
});
|
|
@@ -2098,8 +2330,8 @@ var MCPServer = class extends MCPServerBase {
|
|
|
2098
2330
|
while (true) {
|
|
2099
2331
|
const sessionIds = Array.from(this.sseHonoTransports.keys() || []);
|
|
2100
2332
|
this.logger.debug("Active Hono SSE sessions:", { sessionIds });
|
|
2101
|
-
await
|
|
2102
|
-
await
|
|
2333
|
+
await stream2.write(":keep-alive\n\n");
|
|
2334
|
+
await stream2.sleep(6e4);
|
|
2103
2335
|
}
|
|
2104
2336
|
} catch (e) {
|
|
2105
2337
|
const mastraError = new MastraError(
|
|
@@ -2122,13 +2354,6 @@ var MCPServer = class extends MCPServerBase {
|
|
|
2122
2354
|
* Close the MCP server and all its connections
|
|
2123
2355
|
*/
|
|
2124
2356
|
async close() {
|
|
2125
|
-
this.callToolHandlerIsRegistered = false;
|
|
2126
|
-
this.listToolsHandlerIsRegistered = false;
|
|
2127
|
-
this.listResourcesHandlerIsRegistered = false;
|
|
2128
|
-
this.readResourceHandlerIsRegistered = false;
|
|
2129
|
-
this.listResourceTemplatesHandlerIsRegistered = false;
|
|
2130
|
-
this.subscribeResourceHandlerIsRegistered = false;
|
|
2131
|
-
this.unsubscribeResourceHandlerIsRegistered = false;
|
|
2132
2357
|
try {
|
|
2133
2358
|
if (this.stdioTransport) {
|
|
2134
2359
|
await this.stdioTransport.close?.();
|
|
@@ -2150,6 +2375,12 @@ var MCPServer = class extends MCPServerBase {
|
|
|
2150
2375
|
}
|
|
2151
2376
|
this.streamableHTTPTransports.clear();
|
|
2152
2377
|
}
|
|
2378
|
+
if (this.httpServerInstances) {
|
|
2379
|
+
for (const serverInstance of this.httpServerInstances.values()) {
|
|
2380
|
+
await serverInstance.close?.();
|
|
2381
|
+
}
|
|
2382
|
+
this.httpServerInstances.clear();
|
|
2383
|
+
}
|
|
2153
2384
|
await this.server.close();
|
|
2154
2385
|
this.logger.info("MCP server closed.");
|
|
2155
2386
|
} catch (error) {
|
|
@@ -2208,6 +2439,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
2208
2439
|
name: tool.name,
|
|
2209
2440
|
description: tool.description,
|
|
2210
2441
|
inputSchema: tool.parameters?.jsonSchema || tool.parameters,
|
|
2442
|
+
outputSchema: tool.outputSchema?.jsonSchema || tool.outputSchema,
|
|
2211
2443
|
toolType: tool.toolType
|
|
2212
2444
|
}))
|
|
2213
2445
|
};
|
|
@@ -2228,6 +2460,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
2228
2460
|
name: tool.name,
|
|
2229
2461
|
description: tool.description,
|
|
2230
2462
|
inputSchema: tool.parameters?.jsonSchema || tool.parameters,
|
|
2463
|
+
outputSchema: tool.outputSchema?.jsonSchema || tool.outputSchema,
|
|
2231
2464
|
toolType: tool.toolType
|
|
2232
2465
|
};
|
|
2233
2466
|
}
|