@elizaos/plugin-mcp 1.0.8 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/actions/callToolAction.d.ts +3 -0
- package/dist/actions/callToolAction.d.ts.map +1 -0
- package/dist/actions/readResourceAction.d.ts +3 -0
- package/dist/actions/readResourceAction.d.ts.map +1 -0
- package/dist/cjs/index.cjs +2214 -0
- package/dist/cjs/index.js.map +33 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1981 -563
- package/dist/index.js.map +33 -0
- package/dist/provider.d.ts +3 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/service.d.ts +41 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/templates/errorAnalysisPrompt.d.ts +2 -0
- package/dist/templates/errorAnalysisPrompt.d.ts.map +1 -0
- package/dist/templates/feedbackTemplate.d.ts +2 -0
- package/dist/templates/feedbackTemplate.d.ts.map +1 -0
- package/dist/templates/resourceAnalysisTemplate.d.ts +2 -0
- package/dist/templates/resourceAnalysisTemplate.d.ts.map +1 -0
- package/dist/templates/resourceSelectionTemplate.d.ts +2 -0
- package/dist/templates/resourceSelectionTemplate.d.ts.map +1 -0
- package/dist/templates/toolReasoningTemplate.d.ts +2 -0
- package/dist/templates/toolReasoningTemplate.d.ts.map +1 -0
- package/dist/templates/toolSelectionTemplate.d.ts +3 -0
- package/dist/templates/toolSelectionTemplate.d.ts.map +1 -0
- package/dist/tool-compatibility/index.d.ts +54 -0
- package/dist/tool-compatibility/index.d.ts.map +1 -0
- package/dist/tool-compatibility/providers/anthropic.d.ts +12 -0
- package/dist/tool-compatibility/providers/anthropic.d.ts.map +1 -0
- package/dist/tool-compatibility/providers/google.d.ts +12 -0
- package/dist/tool-compatibility/providers/google.d.ts.map +1 -0
- package/dist/tool-compatibility/providers/openai.d.ts +20 -0
- package/dist/tool-compatibility/providers/openai.d.ts.map +1 -0
- package/dist/types.d.ts +165 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/error.d.ts +14 -0
- package/dist/utils/error.d.ts.map +1 -0
- package/dist/utils/handler.d.ts +3 -0
- package/dist/utils/handler.d.ts.map +1 -0
- package/dist/utils/json.d.ts +9 -0
- package/dist/utils/json.d.ts.map +1 -0
- package/dist/utils/mcp.d.ts +5 -0
- package/dist/utils/mcp.d.ts.map +1 -0
- package/dist/utils/processing.d.ts +43 -0
- package/dist/utils/processing.d.ts.map +1 -0
- package/dist/utils/schemas.d.ts +69 -0
- package/dist/utils/schemas.d.ts.map +1 -0
- package/dist/utils/selection.d.ts +38 -0
- package/dist/utils/selection.d.ts.map +1 -0
- package/dist/utils/validation.d.ts +34 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/wrapper.d.ts +27 -0
- package/dist/utils/wrapper.d.ts.map +1 -0
- package/package.json +30 -7
- package/.github/workflows/npm-deploy.yml +0 -112
- package/.npmrc.template +0 -2
- package/biome.json +0 -34
- package/images/banner.jpg +0 -0
- package/images/logo.jpg +0 -0
|
@@ -0,0 +1,2214 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
19
|
+
var __toCommonJS = (from) => {
|
|
20
|
+
var entry = __moduleCache.get(from), desc;
|
|
21
|
+
if (entry)
|
|
22
|
+
return entry;
|
|
23
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
24
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
25
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
26
|
+
get: () => from[key],
|
|
27
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
28
|
+
}));
|
|
29
|
+
__moduleCache.set(from, entry);
|
|
30
|
+
return entry;
|
|
31
|
+
};
|
|
32
|
+
var __export = (target, all) => {
|
|
33
|
+
for (var name in all)
|
|
34
|
+
__defProp(target, name, {
|
|
35
|
+
get: all[name],
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
set: (newValue) => all[name] = () => newValue
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
42
|
+
|
|
43
|
+
// src/tool-compatibility/providers/openai.ts
|
|
44
|
+
var exports_openai = {};
|
|
45
|
+
__export(exports_openai, {
|
|
46
|
+
OpenAIReasoningMcpCompatibility: () => OpenAIReasoningMcpCompatibility,
|
|
47
|
+
OpenAIMcpCompatibility: () => OpenAIMcpCompatibility2
|
|
48
|
+
});
|
|
49
|
+
var OpenAIMcpCompatibility2, OpenAIReasoningMcpCompatibility;
|
|
50
|
+
var init_openai = __esm(() => {
|
|
51
|
+
OpenAIMcpCompatibility2 = class OpenAIMcpCompatibility2 extends McpToolCompatibility {
|
|
52
|
+
constructor(modelInfo2) {
|
|
53
|
+
super(modelInfo2);
|
|
54
|
+
}
|
|
55
|
+
shouldApply() {
|
|
56
|
+
return this.modelInfo.provider === "openai" && (!this.modelInfo.supportsStructuredOutputs || this.modelInfo.isReasoningModel === true);
|
|
57
|
+
}
|
|
58
|
+
getUnsupportedStringProperties() {
|
|
59
|
+
const baseUnsupported = ["format"];
|
|
60
|
+
if (this.modelInfo.isReasoningModel === true) {
|
|
61
|
+
return [...baseUnsupported, "pattern"];
|
|
62
|
+
}
|
|
63
|
+
if (this.modelInfo.modelId.includes("gpt-3.5") || this.modelInfo.modelId.includes("davinci")) {
|
|
64
|
+
return [...baseUnsupported, "pattern"];
|
|
65
|
+
}
|
|
66
|
+
return baseUnsupported;
|
|
67
|
+
}
|
|
68
|
+
getUnsupportedNumberProperties() {
|
|
69
|
+
if (this.modelInfo.isReasoningModel === true) {
|
|
70
|
+
return ["exclusiveMinimum", "exclusiveMaximum", "multipleOf"];
|
|
71
|
+
}
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
getUnsupportedArrayProperties() {
|
|
75
|
+
if (this.modelInfo.isReasoningModel === true) {
|
|
76
|
+
return ["uniqueItems"];
|
|
77
|
+
}
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
getUnsupportedObjectProperties() {
|
|
81
|
+
return ["minProperties", "maxProperties"];
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
OpenAIReasoningMcpCompatibility = class OpenAIReasoningMcpCompatibility extends McpToolCompatibility {
|
|
85
|
+
constructor(modelInfo2) {
|
|
86
|
+
super(modelInfo2);
|
|
87
|
+
}
|
|
88
|
+
shouldApply() {
|
|
89
|
+
return this.modelInfo.provider === "openai" && this.modelInfo.isReasoningModel === true;
|
|
90
|
+
}
|
|
91
|
+
getUnsupportedStringProperties() {
|
|
92
|
+
return ["format", "pattern", "minLength", "maxLength"];
|
|
93
|
+
}
|
|
94
|
+
getUnsupportedNumberProperties() {
|
|
95
|
+
return ["exclusiveMinimum", "exclusiveMaximum", "multipleOf"];
|
|
96
|
+
}
|
|
97
|
+
getUnsupportedArrayProperties() {
|
|
98
|
+
return ["uniqueItems", "minItems", "maxItems"];
|
|
99
|
+
}
|
|
100
|
+
getUnsupportedObjectProperties() {
|
|
101
|
+
return ["minProperties", "maxProperties", "additionalProperties"];
|
|
102
|
+
}
|
|
103
|
+
mergeDescription(originalDescription, constraints) {
|
|
104
|
+
const constraintText = this.formatConstraintsForReasoningModel(constraints);
|
|
105
|
+
if (originalDescription) {
|
|
106
|
+
return `${originalDescription}
|
|
107
|
+
|
|
108
|
+
IMPORTANT: ${constraintText}`;
|
|
109
|
+
}
|
|
110
|
+
return `IMPORTANT: ${constraintText}`;
|
|
111
|
+
}
|
|
112
|
+
formatConstraintsForReasoningModel(constraints) {
|
|
113
|
+
const rules = [];
|
|
114
|
+
if (constraints.minLength) {
|
|
115
|
+
rules.push(`minimum ${constraints.minLength} characters`);
|
|
116
|
+
}
|
|
117
|
+
if (constraints.maxLength) {
|
|
118
|
+
rules.push(`maximum ${constraints.maxLength} characters`);
|
|
119
|
+
}
|
|
120
|
+
if (constraints.minimum !== undefined) {
|
|
121
|
+
rules.push(`must be >= ${constraints.minimum}`);
|
|
122
|
+
}
|
|
123
|
+
if (constraints.maximum !== undefined) {
|
|
124
|
+
rules.push(`must be <= ${constraints.maximum}`);
|
|
125
|
+
}
|
|
126
|
+
if (constraints.format === "email") {
|
|
127
|
+
rules.push(`must be a valid email address`);
|
|
128
|
+
}
|
|
129
|
+
if (constraints.format === "uri" || constraints.format === "url") {
|
|
130
|
+
rules.push(`must be a valid URL`);
|
|
131
|
+
}
|
|
132
|
+
if (constraints.format === "uuid") {
|
|
133
|
+
rules.push(`must be a valid UUID`);
|
|
134
|
+
}
|
|
135
|
+
if (constraints.pattern) {
|
|
136
|
+
rules.push(`must match pattern: ${constraints.pattern}`);
|
|
137
|
+
}
|
|
138
|
+
if (constraints.enum) {
|
|
139
|
+
rules.push(`must be one of: ${constraints.enum.join(", ")}`);
|
|
140
|
+
}
|
|
141
|
+
if (constraints.minItems) {
|
|
142
|
+
rules.push(`array must have at least ${constraints.minItems} items`);
|
|
143
|
+
}
|
|
144
|
+
if (constraints.maxItems) {
|
|
145
|
+
rules.push(`array must have at most ${constraints.maxItems} items`);
|
|
146
|
+
}
|
|
147
|
+
return rules.length > 0 ? rules.join(", ") : JSON.stringify(constraints);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// src/tool-compatibility/providers/anthropic.ts
|
|
153
|
+
var exports_anthropic = {};
|
|
154
|
+
__export(exports_anthropic, {
|
|
155
|
+
AnthropicMcpCompatibility: () => AnthropicMcpCompatibility2
|
|
156
|
+
});
|
|
157
|
+
var AnthropicMcpCompatibility2;
|
|
158
|
+
var init_anthropic = __esm(() => {
|
|
159
|
+
AnthropicMcpCompatibility2 = class AnthropicMcpCompatibility2 extends McpToolCompatibility {
|
|
160
|
+
constructor(modelInfo2) {
|
|
161
|
+
super(modelInfo2);
|
|
162
|
+
}
|
|
163
|
+
shouldApply() {
|
|
164
|
+
return this.modelInfo.provider === "anthropic";
|
|
165
|
+
}
|
|
166
|
+
getUnsupportedStringProperties() {
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
getUnsupportedNumberProperties() {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
getUnsupportedArrayProperties() {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
getUnsupportedObjectProperties() {
|
|
176
|
+
return ["additionalProperties"];
|
|
177
|
+
}
|
|
178
|
+
mergeDescription(originalDescription, constraints) {
|
|
179
|
+
const constraintHints = this.formatConstraintsForAnthropic(constraints);
|
|
180
|
+
if (originalDescription && constraintHints) {
|
|
181
|
+
return `${originalDescription}. ${constraintHints}`;
|
|
182
|
+
} else if (constraintHints) {
|
|
183
|
+
return constraintHints;
|
|
184
|
+
}
|
|
185
|
+
return originalDescription || "";
|
|
186
|
+
}
|
|
187
|
+
formatConstraintsForAnthropic(constraints) {
|
|
188
|
+
const hints = [];
|
|
189
|
+
if (constraints.additionalProperties === false) {
|
|
190
|
+
hints.push("Only use the specified properties");
|
|
191
|
+
}
|
|
192
|
+
if (constraints.format === "date-time") {
|
|
193
|
+
hints.push("Use ISO 8601 date-time format");
|
|
194
|
+
}
|
|
195
|
+
if (constraints.pattern) {
|
|
196
|
+
hints.push(`Must match the pattern: ${constraints.pattern}`);
|
|
197
|
+
}
|
|
198
|
+
return hints.join(". ");
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// src/tool-compatibility/providers/google.ts
|
|
204
|
+
var exports_google = {};
|
|
205
|
+
__export(exports_google, {
|
|
206
|
+
GoogleMcpCompatibility: () => GoogleMcpCompatibility2
|
|
207
|
+
});
|
|
208
|
+
var GoogleMcpCompatibility2;
|
|
209
|
+
var init_google = __esm(() => {
|
|
210
|
+
GoogleMcpCompatibility2 = class GoogleMcpCompatibility2 extends McpToolCompatibility {
|
|
211
|
+
constructor(modelInfo2) {
|
|
212
|
+
super(modelInfo2);
|
|
213
|
+
}
|
|
214
|
+
shouldApply() {
|
|
215
|
+
return this.modelInfo.provider === "google";
|
|
216
|
+
}
|
|
217
|
+
getUnsupportedStringProperties() {
|
|
218
|
+
return ["minLength", "maxLength", "pattern", "format"];
|
|
219
|
+
}
|
|
220
|
+
getUnsupportedNumberProperties() {
|
|
221
|
+
return ["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf"];
|
|
222
|
+
}
|
|
223
|
+
getUnsupportedArrayProperties() {
|
|
224
|
+
return ["minItems", "maxItems", "uniqueItems"];
|
|
225
|
+
}
|
|
226
|
+
getUnsupportedObjectProperties() {
|
|
227
|
+
return ["minProperties", "maxProperties", "additionalProperties"];
|
|
228
|
+
}
|
|
229
|
+
mergeDescription(originalDescription, constraints) {
|
|
230
|
+
const constraintText = this.formatConstraintsForGoogle(constraints);
|
|
231
|
+
if (originalDescription && constraintText) {
|
|
232
|
+
return `${originalDescription}
|
|
233
|
+
|
|
234
|
+
Constraints: ${constraintText}`;
|
|
235
|
+
} else if (constraintText) {
|
|
236
|
+
return `Constraints: ${constraintText}`;
|
|
237
|
+
}
|
|
238
|
+
return originalDescription || "";
|
|
239
|
+
}
|
|
240
|
+
formatConstraintsForGoogle(constraints) {
|
|
241
|
+
const rules = [];
|
|
242
|
+
if (constraints.minLength) {
|
|
243
|
+
rules.push(`text must be at least ${constraints.minLength} characters long`);
|
|
244
|
+
}
|
|
245
|
+
if (constraints.maxLength) {
|
|
246
|
+
rules.push(`text must be no more than ${constraints.maxLength} characters long`);
|
|
247
|
+
}
|
|
248
|
+
if (constraints.minimum !== undefined) {
|
|
249
|
+
rules.push(`number must be at least ${constraints.minimum}`);
|
|
250
|
+
}
|
|
251
|
+
if (constraints.maximum !== undefined) {
|
|
252
|
+
rules.push(`number must be no more than ${constraints.maximum}`);
|
|
253
|
+
}
|
|
254
|
+
if (constraints.exclusiveMinimum !== undefined) {
|
|
255
|
+
rules.push(`number must be greater than ${constraints.exclusiveMinimum}`);
|
|
256
|
+
}
|
|
257
|
+
if (constraints.exclusiveMaximum !== undefined) {
|
|
258
|
+
rules.push(`number must be less than ${constraints.exclusiveMaximum}`);
|
|
259
|
+
}
|
|
260
|
+
if (constraints.multipleOf) {
|
|
261
|
+
rules.push(`number must be a multiple of ${constraints.multipleOf}`);
|
|
262
|
+
}
|
|
263
|
+
if (constraints.format === "email") {
|
|
264
|
+
rules.push(`must be a valid email address`);
|
|
265
|
+
}
|
|
266
|
+
if (constraints.format === "uri" || constraints.format === "url") {
|
|
267
|
+
rules.push(`must be a valid URL starting with http:// or https://`);
|
|
268
|
+
}
|
|
269
|
+
if (constraints.format === "uuid") {
|
|
270
|
+
rules.push(`must be a valid UUID in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`);
|
|
271
|
+
}
|
|
272
|
+
if (constraints.format === "date-time") {
|
|
273
|
+
rules.push(`must be a valid ISO 8601 date-time (e.g., 2023-12-25T10:30:00Z)`);
|
|
274
|
+
}
|
|
275
|
+
if (constraints.pattern) {
|
|
276
|
+
rules.push(`must match the regular expression pattern: ${constraints.pattern}`);
|
|
277
|
+
}
|
|
278
|
+
if (constraints.enum && Array.isArray(constraints.enum)) {
|
|
279
|
+
rules.push(`must be exactly one of these values: ${constraints.enum.join(", ")}`);
|
|
280
|
+
}
|
|
281
|
+
if (constraints.minItems) {
|
|
282
|
+
rules.push(`array must contain at least ${constraints.minItems} items`);
|
|
283
|
+
}
|
|
284
|
+
if (constraints.maxItems) {
|
|
285
|
+
rules.push(`array must contain no more than ${constraints.maxItems} items`);
|
|
286
|
+
}
|
|
287
|
+
if (constraints.uniqueItems === true) {
|
|
288
|
+
rules.push(`array items must all be unique (no duplicates)`);
|
|
289
|
+
}
|
|
290
|
+
if (constraints.minProperties) {
|
|
291
|
+
rules.push(`object must have at least ${constraints.minProperties} properties`);
|
|
292
|
+
}
|
|
293
|
+
if (constraints.maxProperties) {
|
|
294
|
+
rules.push(`object must have no more than ${constraints.maxProperties} properties`);
|
|
295
|
+
}
|
|
296
|
+
if (constraints.additionalProperties === false) {
|
|
297
|
+
rules.push(`object must only contain the specified properties, no additional properties allowed`);
|
|
298
|
+
}
|
|
299
|
+
return rules.join("; ");
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// src/tool-compatibility/index.ts
|
|
305
|
+
class McpToolCompatibility {
|
|
306
|
+
modelInfo;
|
|
307
|
+
constructor(modelInfo2) {
|
|
308
|
+
this.modelInfo = modelInfo2;
|
|
309
|
+
}
|
|
310
|
+
transformToolSchema(toolSchema) {
|
|
311
|
+
if (!this.shouldApply()) {
|
|
312
|
+
return toolSchema;
|
|
313
|
+
}
|
|
314
|
+
return this.processSchema(toolSchema);
|
|
315
|
+
}
|
|
316
|
+
processSchema(schema) {
|
|
317
|
+
const processed = { ...schema };
|
|
318
|
+
switch (processed.type) {
|
|
319
|
+
case "string":
|
|
320
|
+
return this.processStringSchema(processed);
|
|
321
|
+
case "number":
|
|
322
|
+
case "integer":
|
|
323
|
+
return this.processNumberSchema(processed);
|
|
324
|
+
case "array":
|
|
325
|
+
return this.processArraySchema(processed);
|
|
326
|
+
case "object":
|
|
327
|
+
return this.processObjectSchema(processed);
|
|
328
|
+
default:
|
|
329
|
+
return this.processGenericSchema(processed);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
processStringSchema(schema) {
|
|
333
|
+
const constraints = {};
|
|
334
|
+
const processed = { ...schema };
|
|
335
|
+
if (typeof schema.minLength === "number") {
|
|
336
|
+
constraints.minLength = schema.minLength;
|
|
337
|
+
}
|
|
338
|
+
if (typeof schema.maxLength === "number") {
|
|
339
|
+
constraints.maxLength = schema.maxLength;
|
|
340
|
+
}
|
|
341
|
+
if (typeof schema.pattern === "string") {
|
|
342
|
+
constraints.pattern = schema.pattern;
|
|
343
|
+
}
|
|
344
|
+
if (typeof schema.format === "string") {
|
|
345
|
+
constraints.format = schema.format;
|
|
346
|
+
}
|
|
347
|
+
if (Array.isArray(schema.enum)) {
|
|
348
|
+
constraints.enum = schema.enum;
|
|
349
|
+
}
|
|
350
|
+
const unsupportedProps = this.getUnsupportedStringProperties();
|
|
351
|
+
for (const prop of unsupportedProps) {
|
|
352
|
+
if (prop in processed) {
|
|
353
|
+
delete processed[prop];
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (Object.keys(constraints).length > 0) {
|
|
357
|
+
processed.description = this.mergeDescription(schema.description, constraints);
|
|
358
|
+
}
|
|
359
|
+
return processed;
|
|
360
|
+
}
|
|
361
|
+
processNumberSchema(schema) {
|
|
362
|
+
const constraints = {};
|
|
363
|
+
const processed = { ...schema };
|
|
364
|
+
if (typeof schema.minimum === "number") {
|
|
365
|
+
constraints.minimum = schema.minimum;
|
|
366
|
+
}
|
|
367
|
+
if (typeof schema.maximum === "number") {
|
|
368
|
+
constraints.maximum = schema.maximum;
|
|
369
|
+
}
|
|
370
|
+
if (typeof schema.exclusiveMinimum === "number") {
|
|
371
|
+
constraints.exclusiveMinimum = schema.exclusiveMinimum;
|
|
372
|
+
}
|
|
373
|
+
if (typeof schema.exclusiveMaximum === "number") {
|
|
374
|
+
constraints.exclusiveMaximum = schema.exclusiveMaximum;
|
|
375
|
+
}
|
|
376
|
+
if (typeof schema.multipleOf === "number") {
|
|
377
|
+
constraints.multipleOf = schema.multipleOf;
|
|
378
|
+
}
|
|
379
|
+
const unsupportedProps = this.getUnsupportedNumberProperties();
|
|
380
|
+
for (const prop of unsupportedProps) {
|
|
381
|
+
if (prop in processed) {
|
|
382
|
+
delete processed[prop];
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (Object.keys(constraints).length > 0) {
|
|
386
|
+
processed.description = this.mergeDescription(schema.description, constraints);
|
|
387
|
+
}
|
|
388
|
+
return processed;
|
|
389
|
+
}
|
|
390
|
+
processArraySchema(schema) {
|
|
391
|
+
const constraints = {};
|
|
392
|
+
const processed = { ...schema };
|
|
393
|
+
if (typeof schema.minItems === "number") {
|
|
394
|
+
constraints.minItems = schema.minItems;
|
|
395
|
+
}
|
|
396
|
+
if (typeof schema.maxItems === "number") {
|
|
397
|
+
constraints.maxItems = schema.maxItems;
|
|
398
|
+
}
|
|
399
|
+
if (typeof schema.uniqueItems === "boolean") {
|
|
400
|
+
constraints.uniqueItems = schema.uniqueItems;
|
|
401
|
+
}
|
|
402
|
+
if (schema.items && typeof schema.items === "object" && !Array.isArray(schema.items)) {
|
|
403
|
+
processed.items = this.processSchema(schema.items);
|
|
404
|
+
}
|
|
405
|
+
const unsupportedProps = this.getUnsupportedArrayProperties();
|
|
406
|
+
for (const prop of unsupportedProps) {
|
|
407
|
+
if (prop in processed) {
|
|
408
|
+
delete processed[prop];
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (Object.keys(constraints).length > 0) {
|
|
412
|
+
processed.description = this.mergeDescription(schema.description, constraints);
|
|
413
|
+
}
|
|
414
|
+
return processed;
|
|
415
|
+
}
|
|
416
|
+
processObjectSchema(schema) {
|
|
417
|
+
const constraints = {};
|
|
418
|
+
const processed = { ...schema };
|
|
419
|
+
if (typeof schema.minProperties === "number") {
|
|
420
|
+
constraints.minProperties = schema.minProperties;
|
|
421
|
+
}
|
|
422
|
+
if (typeof schema.maxProperties === "number") {
|
|
423
|
+
constraints.maxProperties = schema.maxProperties;
|
|
424
|
+
}
|
|
425
|
+
if (typeof schema.additionalProperties === "boolean") {
|
|
426
|
+
constraints.additionalProperties = schema.additionalProperties;
|
|
427
|
+
}
|
|
428
|
+
if (schema.properties && typeof schema.properties === "object") {
|
|
429
|
+
processed.properties = {};
|
|
430
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
431
|
+
if (typeof prop === "object" && !Array.isArray(prop)) {
|
|
432
|
+
processed.properties[key] = this.processSchema(prop);
|
|
433
|
+
} else {
|
|
434
|
+
processed.properties[key] = prop;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const unsupportedProps = this.getUnsupportedObjectProperties();
|
|
439
|
+
for (const prop of unsupportedProps) {
|
|
440
|
+
if (prop in processed) {
|
|
441
|
+
delete processed[prop];
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (Object.keys(constraints).length > 0) {
|
|
445
|
+
processed.description = this.mergeDescription(schema.description, constraints);
|
|
446
|
+
}
|
|
447
|
+
return processed;
|
|
448
|
+
}
|
|
449
|
+
processGenericSchema(schema) {
|
|
450
|
+
const processed = { ...schema };
|
|
451
|
+
if (Array.isArray(schema.oneOf)) {
|
|
452
|
+
processed.oneOf = schema.oneOf.map((s) => typeof s === "object" ? this.processSchema(s) : s);
|
|
453
|
+
}
|
|
454
|
+
if (Array.isArray(schema.anyOf)) {
|
|
455
|
+
processed.anyOf = schema.anyOf.map((s) => typeof s === "object" ? this.processSchema(s) : s);
|
|
456
|
+
}
|
|
457
|
+
if (Array.isArray(schema.allOf)) {
|
|
458
|
+
processed.allOf = schema.allOf.map((s) => typeof s === "object" ? this.processSchema(s) : s);
|
|
459
|
+
}
|
|
460
|
+
return processed;
|
|
461
|
+
}
|
|
462
|
+
mergeDescription(originalDescription, constraints) {
|
|
463
|
+
const constraintJson = JSON.stringify(constraints);
|
|
464
|
+
if (originalDescription) {
|
|
465
|
+
return `${originalDescription}
|
|
466
|
+
${constraintJson}`;
|
|
467
|
+
}
|
|
468
|
+
return constraintJson;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function detectModelProvider(runtime2) {
|
|
472
|
+
const modelString = runtime2?.modelProvider || runtime2?.model || "";
|
|
473
|
+
const modelId = String(modelString).toLowerCase();
|
|
474
|
+
let provider2 = "unknown";
|
|
475
|
+
let supportsStructuredOutputs = false;
|
|
476
|
+
let isReasoningModel = false;
|
|
477
|
+
if (modelId.includes("openai") || modelId.includes("gpt-") || modelId.includes("o1-") || modelId.includes("o3-")) {
|
|
478
|
+
provider2 = "openai";
|
|
479
|
+
supportsStructuredOutputs = modelId.includes("gpt-4") || modelId.includes("o1") || modelId.includes("o3");
|
|
480
|
+
isReasoningModel = modelId.includes("o1") || modelId.includes("o3");
|
|
481
|
+
} else if (modelId.includes("anthropic") || modelId.includes("claude")) {
|
|
482
|
+
provider2 = "anthropic";
|
|
483
|
+
supportsStructuredOutputs = true;
|
|
484
|
+
} else if (modelId.includes("google") || modelId.includes("gemini")) {
|
|
485
|
+
provider2 = "google";
|
|
486
|
+
supportsStructuredOutputs = true;
|
|
487
|
+
} else if (modelId.includes("openrouter")) {
|
|
488
|
+
provider2 = "openrouter";
|
|
489
|
+
supportsStructuredOutputs = false;
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
provider: provider2,
|
|
493
|
+
modelId,
|
|
494
|
+
supportsStructuredOutputs,
|
|
495
|
+
isReasoningModel
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
async function createMcpToolCompatibility(runtime2) {
|
|
499
|
+
const modelInfo2 = detectModelProvider(runtime2);
|
|
500
|
+
try {
|
|
501
|
+
switch (modelInfo2.provider) {
|
|
502
|
+
case "openai":
|
|
503
|
+
const { OpenAIMcpCompatibility: OpenAIMcpCompatibility3 } = await Promise.resolve().then(() => (init_openai(), exports_openai));
|
|
504
|
+
return new OpenAIMcpCompatibility3(modelInfo2);
|
|
505
|
+
case "anthropic":
|
|
506
|
+
const { AnthropicMcpCompatibility: AnthropicMcpCompatibility3 } = await Promise.resolve().then(() => (init_anthropic(), exports_anthropic));
|
|
507
|
+
return new AnthropicMcpCompatibility3(modelInfo2);
|
|
508
|
+
case "google":
|
|
509
|
+
const { GoogleMcpCompatibility: GoogleMcpCompatibility3 } = await Promise.resolve().then(() => (init_google(), exports_google));
|
|
510
|
+
return new GoogleMcpCompatibility3(modelInfo2);
|
|
511
|
+
default:
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
} catch (error) {
|
|
515
|
+
console.warn("Failed to load compatibility provider:", error);
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function createMcpToolCompatibilitySync(runtime) {
|
|
520
|
+
const modelInfo = detectModelProvider(runtime);
|
|
521
|
+
try {
|
|
522
|
+
switch (modelInfo.provider) {
|
|
523
|
+
case "openai":
|
|
524
|
+
const OpenAIModule = eval("require")("./providers/openai");
|
|
525
|
+
const { OpenAIMcpCompatibility } = OpenAIModule;
|
|
526
|
+
return new OpenAIMcpCompatibility(modelInfo);
|
|
527
|
+
case "anthropic":
|
|
528
|
+
const AnthropicModule = eval("require")("./providers/anthropic");
|
|
529
|
+
const { AnthropicMcpCompatibility } = AnthropicModule;
|
|
530
|
+
return new AnthropicMcpCompatibility(modelInfo);
|
|
531
|
+
case "google":
|
|
532
|
+
const GoogleModule = eval("require")("./providers/google");
|
|
533
|
+
const { GoogleMcpCompatibility } = GoogleModule;
|
|
534
|
+
return new GoogleMcpCompatibility(modelInfo);
|
|
535
|
+
default:
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
} catch (error) {
|
|
539
|
+
console.warn("Failed to load compatibility provider:", error);
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/index.ts
|
|
545
|
+
var exports_src = {};
|
|
546
|
+
__export(exports_src, {
|
|
547
|
+
detectModelProvider: () => detectModelProvider,
|
|
548
|
+
default: () => src_default,
|
|
549
|
+
createMcpToolCompatibilitySync: () => createMcpToolCompatibilitySync,
|
|
550
|
+
createMcpToolCompatibility: () => createMcpToolCompatibility,
|
|
551
|
+
ToolSelectionSchema: () => ToolSelectionSchema,
|
|
552
|
+
ResourceSelectionSchema: () => ResourceSelectionSchema,
|
|
553
|
+
McpToolCompatibility: () => McpToolCompatibility,
|
|
554
|
+
McpService: () => McpService,
|
|
555
|
+
MIN_MCP_TIMEOUT_SECONDS: () => MIN_MCP_TIMEOUT_SECONDS,
|
|
556
|
+
MCP_SERVICE_NAME: () => MCP_SERVICE_NAME,
|
|
557
|
+
MAX_RECONNECT_ATTEMPTS: () => MAX_RECONNECT_ATTEMPTS,
|
|
558
|
+
INITIAL_RETRY_DELAY: () => INITIAL_RETRY_DELAY,
|
|
559
|
+
DEFAULT_PING_CONFIG: () => DEFAULT_PING_CONFIG,
|
|
560
|
+
DEFAULT_MCP_TIMEOUT_SECONDS: () => DEFAULT_MCP_TIMEOUT_SECONDS,
|
|
561
|
+
DEFAULT_MAX_RETRIES: () => DEFAULT_MAX_RETRIES,
|
|
562
|
+
BACKOFF_MULTIPLIER: () => BACKOFF_MULTIPLIER
|
|
563
|
+
});
|
|
564
|
+
module.exports = __toCommonJS(exports_src);
|
|
565
|
+
var import_core9 = require("@elizaos/core");
|
|
566
|
+
|
|
567
|
+
// src/actions/callToolAction.ts
|
|
568
|
+
var import_core6 = require("@elizaos/core");
|
|
569
|
+
|
|
570
|
+
// src/types.ts
|
|
571
|
+
var MCP_SERVICE_NAME = "mcp";
|
|
572
|
+
var DEFAULT_MCP_TIMEOUT_SECONDS = 60000;
|
|
573
|
+
var MIN_MCP_TIMEOUT_SECONDS = 1;
|
|
574
|
+
var DEFAULT_MAX_RETRIES = 2;
|
|
575
|
+
var ToolSelectionSchema = {
|
|
576
|
+
type: "object",
|
|
577
|
+
required: ["serverName", "toolName", "arguments"],
|
|
578
|
+
properties: {
|
|
579
|
+
serverName: {
|
|
580
|
+
type: "string",
|
|
581
|
+
minLength: 1,
|
|
582
|
+
errorMessage: "serverName must not be empty"
|
|
583
|
+
},
|
|
584
|
+
toolName: {
|
|
585
|
+
type: "string",
|
|
586
|
+
minLength: 1,
|
|
587
|
+
errorMessage: "toolName must not be empty"
|
|
588
|
+
},
|
|
589
|
+
arguments: {
|
|
590
|
+
type: "object"
|
|
591
|
+
},
|
|
592
|
+
reasoning: {
|
|
593
|
+
type: "string"
|
|
594
|
+
},
|
|
595
|
+
noToolAvailable: {
|
|
596
|
+
type: "boolean"
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
var ResourceSelectionSchema = {
|
|
601
|
+
type: "object",
|
|
602
|
+
required: ["serverName", "uri"],
|
|
603
|
+
properties: {
|
|
604
|
+
serverName: {
|
|
605
|
+
type: "string",
|
|
606
|
+
minLength: 1,
|
|
607
|
+
errorMessage: "serverName must not be empty"
|
|
608
|
+
},
|
|
609
|
+
uri: {
|
|
610
|
+
type: "string",
|
|
611
|
+
minLength: 1,
|
|
612
|
+
errorMessage: "uri must not be empty"
|
|
613
|
+
},
|
|
614
|
+
reasoning: {
|
|
615
|
+
type: "string"
|
|
616
|
+
},
|
|
617
|
+
noResourceAvailable: {
|
|
618
|
+
type: "boolean"
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
var DEFAULT_PING_CONFIG = {
|
|
623
|
+
enabled: true,
|
|
624
|
+
intervalMs: 1e4,
|
|
625
|
+
timeoutMs: 5000,
|
|
626
|
+
failuresBeforeDisconnect: 3
|
|
627
|
+
};
|
|
628
|
+
var MAX_RECONNECT_ATTEMPTS = 5;
|
|
629
|
+
var BACKOFF_MULTIPLIER = 2;
|
|
630
|
+
var INITIAL_RETRY_DELAY = 2000;
|
|
631
|
+
|
|
632
|
+
// src/utils/error.ts
|
|
633
|
+
var import_core = require("@elizaos/core");
|
|
634
|
+
|
|
635
|
+
// src/templates/errorAnalysisPrompt.ts
|
|
636
|
+
var errorAnalysisPrompt = `
|
|
637
|
+
{{{mcpProvider.text}}}
|
|
638
|
+
|
|
639
|
+
{{{recentMessages}}}
|
|
640
|
+
|
|
641
|
+
# Prompt
|
|
642
|
+
|
|
643
|
+
You're an assistant helping a user, but there was an error accessing the resource you tried to use.
|
|
644
|
+
|
|
645
|
+
User request: "{{{userMessage}}}"
|
|
646
|
+
Error message: {{{error}}}
|
|
647
|
+
|
|
648
|
+
Create a helpful response that:
|
|
649
|
+
1. Acknowledges the issue in user-friendly terms
|
|
650
|
+
2. Offers alternative approaches to help if possible
|
|
651
|
+
3. Doesn't expose technical error details unless they're truly helpful
|
|
652
|
+
4. Maintains a helpful, conversational tone
|
|
653
|
+
|
|
654
|
+
Your response:
|
|
655
|
+
`;
|
|
656
|
+
|
|
657
|
+
// src/utils/error.ts
|
|
658
|
+
async function handleMcpError(state, mcpProvider, error, runtime2, message, type, callback) {
|
|
659
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
660
|
+
import_core.logger.error({ error, mcpType: type }, `Error executing MCP ${type}: ${errorMessage}`);
|
|
661
|
+
let responseText = `I'm sorry, I wasn't able to get the information you requested. There seems to be an issue with the ${type} right now. Is there something else I can help you with?`;
|
|
662
|
+
let thoughtText = `Error calling MCP ${type} and failed to generate a custom response. Providing a generic fallback response.`;
|
|
663
|
+
if (callback) {
|
|
664
|
+
const enhancedState = {
|
|
665
|
+
...state,
|
|
666
|
+
values: {
|
|
667
|
+
...state.values,
|
|
668
|
+
mcpProvider,
|
|
669
|
+
userMessage: message.content.text || "",
|
|
670
|
+
error: errorMessage
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
const prompt = import_core.composePromptFromState({
|
|
674
|
+
state: enhancedState,
|
|
675
|
+
template: errorAnalysisPrompt
|
|
676
|
+
});
|
|
677
|
+
try {
|
|
678
|
+
const errorResponse = await runtime2.useModel(import_core.ModelType.TEXT_SMALL, {
|
|
679
|
+
prompt
|
|
680
|
+
});
|
|
681
|
+
responseText = errorResponse;
|
|
682
|
+
thoughtText = `Error calling MCP ${type}: ${errorMessage}. Providing a helpful response to the user.`;
|
|
683
|
+
await callback({
|
|
684
|
+
thought: thoughtText,
|
|
685
|
+
text: responseText,
|
|
686
|
+
actions: ["REPLY"]
|
|
687
|
+
});
|
|
688
|
+
} catch (modelError) {
|
|
689
|
+
import_core.logger.error({ error: modelError instanceof Error ? modelError.message : String(modelError) }, "Failed to generate error response");
|
|
690
|
+
await callback({
|
|
691
|
+
thought: thoughtText,
|
|
692
|
+
text: responseText,
|
|
693
|
+
actions: ["REPLY"]
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return {
|
|
698
|
+
text: `Failed to execute MCP ${type}`,
|
|
699
|
+
values: {
|
|
700
|
+
success: false,
|
|
701
|
+
error: errorMessage,
|
|
702
|
+
errorType: type
|
|
703
|
+
},
|
|
704
|
+
data: {
|
|
705
|
+
actionName: type === "tool" ? "CALL_MCP_TOOL" : "READ_MCP_RESOURCE",
|
|
706
|
+
error: errorMessage,
|
|
707
|
+
mcpType: type
|
|
708
|
+
},
|
|
709
|
+
success: false,
|
|
710
|
+
error: error instanceof Error ? error : new Error(errorMessage)
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// src/utils/processing.ts
|
|
715
|
+
var import_core2 = require("@elizaos/core");
|
|
716
|
+
var import_core3 = require("@elizaos/core");
|
|
717
|
+
|
|
718
|
+
// src/templates/resourceAnalysisTemplate.ts
|
|
719
|
+
var resourceAnalysisTemplate = `
|
|
720
|
+
{{{mcpProvider.text}}}
|
|
721
|
+
|
|
722
|
+
{{{recentMessages}}}
|
|
723
|
+
|
|
724
|
+
# Prompt
|
|
725
|
+
|
|
726
|
+
You are a helpful assistant responding to a user's request. You've just accessed the resource "{{{uri}}}" to help answer this request.
|
|
727
|
+
|
|
728
|
+
Original user request: "{{{userMessage}}}"
|
|
729
|
+
|
|
730
|
+
Resource metadata:
|
|
731
|
+
{{{resourceMeta}}
|
|
732
|
+
|
|
733
|
+
Resource content:
|
|
734
|
+
{{{resourceContent}}
|
|
735
|
+
|
|
736
|
+
Instructions:
|
|
737
|
+
1. Analyze how well the resource's content addresses the user's specific question or need
|
|
738
|
+
2. Identify the most relevant information from the resource
|
|
739
|
+
3. Create a natural, conversational response that incorporates this information
|
|
740
|
+
4. If the resource content is insufficient, acknowledge its limitations and explain what you can determine
|
|
741
|
+
5. Do not start with phrases like "According to the resource" or "Here's what I found" - instead, integrate the information naturally
|
|
742
|
+
6. Maintain your helpful, intelligent assistant personality while presenting the information
|
|
743
|
+
|
|
744
|
+
Your response (written as if directly to the user):
|
|
745
|
+
`;
|
|
746
|
+
|
|
747
|
+
// src/templates/toolReasoningTemplate.ts
|
|
748
|
+
var toolReasoningTemplate = `
|
|
749
|
+
{{{mcpProvider.text}}}
|
|
750
|
+
|
|
751
|
+
{{{recentMessages}}}
|
|
752
|
+
|
|
753
|
+
# Prompt
|
|
754
|
+
|
|
755
|
+
You are a helpful assistant responding to a user's request. You've just used the "{{{toolName}}}" tool from the "{{{serverName}}}" server to help answer this request.
|
|
756
|
+
|
|
757
|
+
Original user request: "{{{userMessage}}}"
|
|
758
|
+
|
|
759
|
+
Tool response:
|
|
760
|
+
{{{toolOutput}}}
|
|
761
|
+
|
|
762
|
+
{{#if hasAttachments}}
|
|
763
|
+
The tool also returned images or other media that will be shared with the user.
|
|
764
|
+
{{/if}}
|
|
765
|
+
|
|
766
|
+
Instructions:
|
|
767
|
+
1. Analyze how well the tool's response addresses the user's specific question or need
|
|
768
|
+
2. Identify the most relevant information from the tool's output
|
|
769
|
+
3. Create a natural, conversational response that incorporates this information
|
|
770
|
+
4. If the tool's response is insufficient, acknowledge its limitations and explain what you can determine
|
|
771
|
+
5. Do not start with phrases like "I used the X tool" or "Here's what I found" - instead, integrate the information naturally
|
|
772
|
+
6. Maintain your helpful, intelligent assistant personality while presenting the information
|
|
773
|
+
|
|
774
|
+
Your response (written as if directly to the user):
|
|
775
|
+
`;
|
|
776
|
+
|
|
777
|
+
// src/utils/mcp.ts
|
|
778
|
+
async function createMcpMemory(runtime2, message, type, serverName, content, metadata) {
|
|
779
|
+
const memory = await runtime2.addEmbeddingToMemory({
|
|
780
|
+
entityId: message.entityId,
|
|
781
|
+
agentId: runtime2.agentId,
|
|
782
|
+
roomId: message.roomId,
|
|
783
|
+
content: {
|
|
784
|
+
text: `Used the "${type}" from "${serverName}" server.
|
|
785
|
+
Content: ${content}`,
|
|
786
|
+
metadata: {
|
|
787
|
+
...metadata,
|
|
788
|
+
serverName
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
await runtime2.createMemory(memory, type === "resource" ? "resources" : "tools", true);
|
|
793
|
+
}
|
|
794
|
+
function buildMcpProviderData(servers) {
|
|
795
|
+
const mcpData = {};
|
|
796
|
+
let textContent = "";
|
|
797
|
+
if (servers.length === 0) {
|
|
798
|
+
return {
|
|
799
|
+
values: { mcp: {} },
|
|
800
|
+
data: { mcp: {} },
|
|
801
|
+
text: "No MCP servers are currently connected."
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
for (const server of servers) {
|
|
805
|
+
mcpData[server.name] = {
|
|
806
|
+
status: server.status,
|
|
807
|
+
tools: {},
|
|
808
|
+
resources: {}
|
|
809
|
+
};
|
|
810
|
+
textContent += `## Server: ${server.name} (${server.status})
|
|
811
|
+
|
|
812
|
+
`;
|
|
813
|
+
if (server.tools && server.tools.length > 0) {
|
|
814
|
+
textContent += `### Tools:
|
|
815
|
+
|
|
816
|
+
`;
|
|
817
|
+
for (const tool of server.tools) {
|
|
818
|
+
mcpData[server.name].tools[tool.name] = {
|
|
819
|
+
description: tool.description || "No description available",
|
|
820
|
+
inputSchema: tool.inputSchema || {}
|
|
821
|
+
};
|
|
822
|
+
textContent += `- **${tool.name}**: ${tool.description || "No description available"}
|
|
823
|
+
`;
|
|
824
|
+
}
|
|
825
|
+
textContent += `
|
|
826
|
+
`;
|
|
827
|
+
}
|
|
828
|
+
if (server.resources && server.resources.length > 0) {
|
|
829
|
+
textContent += `### Resources:
|
|
830
|
+
|
|
831
|
+
`;
|
|
832
|
+
for (const resource of server.resources) {
|
|
833
|
+
mcpData[server.name].resources[resource.uri] = {
|
|
834
|
+
name: resource.name,
|
|
835
|
+
description: resource.description || "No description available",
|
|
836
|
+
mimeType: resource.mimeType
|
|
837
|
+
};
|
|
838
|
+
textContent += `- **${resource.name}** (${resource.uri}): ${resource.description || "No description available"}
|
|
839
|
+
`;
|
|
840
|
+
}
|
|
841
|
+
textContent += `
|
|
842
|
+
`;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return {
|
|
846
|
+
values: { mcp: mcpData },
|
|
847
|
+
data: { mcp: mcpData },
|
|
848
|
+
text: `# MCP Configuration
|
|
849
|
+
|
|
850
|
+
${textContent}`
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// src/utils/processing.ts
|
|
855
|
+
function getMimeTypeToContentType(mimeType) {
|
|
856
|
+
if (!mimeType)
|
|
857
|
+
return;
|
|
858
|
+
if (mimeType.startsWith("image/"))
|
|
859
|
+
return import_core2.ContentType.IMAGE;
|
|
860
|
+
if (mimeType.startsWith("video/"))
|
|
861
|
+
return import_core2.ContentType.VIDEO;
|
|
862
|
+
if (mimeType.startsWith("audio/"))
|
|
863
|
+
return import_core2.ContentType.AUDIO;
|
|
864
|
+
if (mimeType.includes("pdf") || mimeType.includes("document"))
|
|
865
|
+
return import_core2.ContentType.DOCUMENT;
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
function processResourceResult(result, uri) {
|
|
869
|
+
let resourceContent = "";
|
|
870
|
+
let resourceMeta = "";
|
|
871
|
+
for (const content of result.contents) {
|
|
872
|
+
if (content.text) {
|
|
873
|
+
resourceContent += content.text;
|
|
874
|
+
} else if (content.blob) {
|
|
875
|
+
resourceContent += `[Binary data - ${content.mimeType || "unknown type"}]`;
|
|
876
|
+
}
|
|
877
|
+
resourceMeta += `Resource: ${content.uri || uri}
|
|
878
|
+
`;
|
|
879
|
+
if (content.mimeType) {
|
|
880
|
+
resourceMeta += `Type: ${content.mimeType}
|
|
881
|
+
`;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return { resourceContent, resourceMeta };
|
|
885
|
+
}
|
|
886
|
+
function processToolResult(result, serverName, toolName, runtime2, messageEntityId) {
|
|
887
|
+
let toolOutput = "";
|
|
888
|
+
let hasAttachments = false;
|
|
889
|
+
const attachments = [];
|
|
890
|
+
for (const content of result.content) {
|
|
891
|
+
if (content.type === "text") {
|
|
892
|
+
toolOutput += content.text;
|
|
893
|
+
} else if (content.type === "image") {
|
|
894
|
+
hasAttachments = true;
|
|
895
|
+
attachments.push({
|
|
896
|
+
contentType: getMimeTypeToContentType(content.mimeType),
|
|
897
|
+
url: `data:${content.mimeType};base64,${content.data}`,
|
|
898
|
+
id: import_core2.createUniqueUuid(runtime2, messageEntityId),
|
|
899
|
+
title: "Generated image",
|
|
900
|
+
source: `${serverName}/${toolName}`,
|
|
901
|
+
description: "Tool-generated image",
|
|
902
|
+
text: "Generated image"
|
|
903
|
+
});
|
|
904
|
+
} else if (content.type === "resource") {
|
|
905
|
+
const resource = content.resource;
|
|
906
|
+
if (resource && "text" in resource) {
|
|
907
|
+
toolOutput += `
|
|
908
|
+
|
|
909
|
+
Resource (${resource.uri}):
|
|
910
|
+
${resource.text}`;
|
|
911
|
+
} else if (resource && "blob" in resource) {
|
|
912
|
+
toolOutput += `
|
|
913
|
+
|
|
914
|
+
Resource (${resource.uri}): [Binary data]`;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
return { toolOutput, hasAttachments, attachments };
|
|
919
|
+
}
|
|
920
|
+
async function handleResourceAnalysis(runtime2, message, uri, serverName, resourceContent, resourceMeta, callback) {
|
|
921
|
+
await createMcpMemory(runtime2, message, "resource", serverName, resourceContent, {
|
|
922
|
+
uri,
|
|
923
|
+
isResourceAccess: true
|
|
924
|
+
});
|
|
925
|
+
const analysisPrompt = createAnalysisPrompt(uri, message.content.text || "", resourceContent, resourceMeta);
|
|
926
|
+
const analyzedResponse = await runtime2.useModel(import_core2.ModelType.TEXT_SMALL, {
|
|
927
|
+
prompt: analysisPrompt
|
|
928
|
+
});
|
|
929
|
+
if (callback) {
|
|
930
|
+
await callback({
|
|
931
|
+
text: analyzedResponse,
|
|
932
|
+
thought: `I analyzed the content from the ${uri} resource on ${serverName} and crafted a thoughtful response that addresses the user's request while maintaining my conversational style.`,
|
|
933
|
+
actions: ["READ_MCP_RESOURCE"]
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
async function handleToolResponse(runtime2, message, serverName, toolName, toolArgs, toolOutput, hasAttachments, attachments, state, mcpProvider, callback) {
|
|
938
|
+
await createMcpMemory(runtime2, message, "tool", serverName, toolOutput, {
|
|
939
|
+
toolName,
|
|
940
|
+
arguments: toolArgs,
|
|
941
|
+
isToolCall: true
|
|
942
|
+
});
|
|
943
|
+
const reasoningPrompt = createReasoningPrompt(state, mcpProvider, toolName, serverName, message.content.text || "", toolOutput, hasAttachments);
|
|
944
|
+
import_core2.logger.info({ reasoningPrompt }, "reasoning prompt");
|
|
945
|
+
const reasonedResponse = await runtime2.useModel(import_core2.ModelType.TEXT_SMALL, {
|
|
946
|
+
prompt: reasoningPrompt
|
|
947
|
+
});
|
|
948
|
+
const agentId = message.agentId || runtime2.agentId;
|
|
949
|
+
const replyMemory = {
|
|
950
|
+
entityId: agentId,
|
|
951
|
+
roomId: message.roomId,
|
|
952
|
+
worldId: message.worldId,
|
|
953
|
+
content: {
|
|
954
|
+
text: reasonedResponse,
|
|
955
|
+
thought: `I analyzed the output from the ${toolName} tool on ${serverName} and crafted a thoughtful response that addresses the user's request while maintaining my conversational style.`,
|
|
956
|
+
actions: ["CALL_MCP_TOOL"],
|
|
957
|
+
attachments: hasAttachments && attachments.length > 0 ? attachments : undefined
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
await runtime2.createMemory(replyMemory, "messages");
|
|
961
|
+
if (callback) {
|
|
962
|
+
await callback({
|
|
963
|
+
text: reasonedResponse,
|
|
964
|
+
thought: `I analyzed the output from the ${toolName} tool on ${serverName} and crafted a thoughtful response that addresses the user's request while maintaining my conversational style.`,
|
|
965
|
+
actions: ["CALL_MCP_TOOL"],
|
|
966
|
+
attachments: hasAttachments && attachments.length > 0 ? attachments : undefined
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
async function sendInitialResponse(callback) {
|
|
971
|
+
if (callback) {
|
|
972
|
+
const responseContent = {
|
|
973
|
+
thought: "The user is asking for information that can be found in an MCP resource. I will retrieve and analyze the appropriate resource.",
|
|
974
|
+
text: "I'll retrieve that information for you. Let me access the resource...",
|
|
975
|
+
actions: ["READ_MCP_RESOURCE"]
|
|
976
|
+
};
|
|
977
|
+
await callback(responseContent);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
function createAnalysisPrompt(uri, userMessage, resourceContent, resourceMeta) {
|
|
981
|
+
const enhancedState = {
|
|
982
|
+
data: {},
|
|
983
|
+
text: "",
|
|
984
|
+
values: {
|
|
985
|
+
uri,
|
|
986
|
+
userMessage,
|
|
987
|
+
resourceContent,
|
|
988
|
+
resourceMeta
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
return import_core3.composePromptFromState({
|
|
992
|
+
state: enhancedState,
|
|
993
|
+
template: resourceAnalysisTemplate
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
function createReasoningPrompt(state, mcpProvider, toolName, serverName, userMessage, toolOutput, hasAttachments) {
|
|
997
|
+
const enhancedState = {
|
|
998
|
+
...state,
|
|
999
|
+
values: {
|
|
1000
|
+
...state.values,
|
|
1001
|
+
mcpProvider,
|
|
1002
|
+
toolName,
|
|
1003
|
+
serverName,
|
|
1004
|
+
userMessage,
|
|
1005
|
+
toolOutput,
|
|
1006
|
+
hasAttachments
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
return import_core3.composePromptFromState({
|
|
1010
|
+
state: enhancedState,
|
|
1011
|
+
template: toolReasoningTemplate
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// src/utils/selection.ts
|
|
1016
|
+
var import_core5 = require("@elizaos/core");
|
|
1017
|
+
|
|
1018
|
+
// src/utils/json.ts
|
|
1019
|
+
var import_ajv = __toESM(require("ajv"));
|
|
1020
|
+
var import_json5 = __toESM(require("json5"));
|
|
1021
|
+
function parseJSON(input) {
|
|
1022
|
+
let cleanedInput = input.replace(/^```(?:json)?\s*|\s*```$/g, "").trim();
|
|
1023
|
+
const firstBrace = cleanedInput.indexOf("{");
|
|
1024
|
+
const lastBrace = cleanedInput.lastIndexOf("}");
|
|
1025
|
+
if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {
|
|
1026
|
+
cleanedInput = cleanedInput.substring(firstBrace, lastBrace + 1);
|
|
1027
|
+
}
|
|
1028
|
+
return import_json5.default.parse(cleanedInput);
|
|
1029
|
+
}
|
|
1030
|
+
var ajv = new import_ajv.default({
|
|
1031
|
+
allErrors: true,
|
|
1032
|
+
strict: false
|
|
1033
|
+
});
|
|
1034
|
+
function validateJsonSchema(data, schema) {
|
|
1035
|
+
try {
|
|
1036
|
+
const validate = ajv.compile(schema);
|
|
1037
|
+
const valid = validate(data);
|
|
1038
|
+
if (!valid) {
|
|
1039
|
+
const errors = (validate.errors || []).map((err) => {
|
|
1040
|
+
const path = err.instancePath ? `${err.instancePath.replace(/^\//, "")}` : "value";
|
|
1041
|
+
return `${path}: ${err.message}`;
|
|
1042
|
+
});
|
|
1043
|
+
return { success: false, error: errors.join(", ") };
|
|
1044
|
+
}
|
|
1045
|
+
return { success: true, data };
|
|
1046
|
+
} catch (error) {
|
|
1047
|
+
return {
|
|
1048
|
+
success: false,
|
|
1049
|
+
error: `Schema validation error: ${error instanceof Error ? error.message : String(error)}`
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// src/utils/wrapper.ts
|
|
1055
|
+
var import_core4 = require("@elizaos/core");
|
|
1056
|
+
async function withModelRetry({
|
|
1057
|
+
runtime: runtime2,
|
|
1058
|
+
message,
|
|
1059
|
+
state,
|
|
1060
|
+
callback,
|
|
1061
|
+
input,
|
|
1062
|
+
validationFn,
|
|
1063
|
+
createFeedbackPromptFn,
|
|
1064
|
+
failureMsg,
|
|
1065
|
+
retryCount = 0
|
|
1066
|
+
}) {
|
|
1067
|
+
const maxRetries = getMaxRetries(runtime2);
|
|
1068
|
+
try {
|
|
1069
|
+
import_core4.logger.info(`[WITH-MODEL-RETRY] Raw selection input:
|
|
1070
|
+
${input}`);
|
|
1071
|
+
const parsedJson = typeof input === "string" ? parseJSON(input) : input;
|
|
1072
|
+
import_core4.logger.debug(`[WITH-MODEL-RETRY] Parsed selection input:
|
|
1073
|
+
${JSON.stringify(parsedJson, null, 2)}`);
|
|
1074
|
+
const validationResult = validationFn(parsedJson);
|
|
1075
|
+
if (validationResult.success === false) {
|
|
1076
|
+
throw new Error(validationResult.error);
|
|
1077
|
+
}
|
|
1078
|
+
return validationResult.data;
|
|
1079
|
+
} catch (parseError) {
|
|
1080
|
+
const errorMessage = parseError instanceof Error ? parseError.message : "Unknown parsing error";
|
|
1081
|
+
import_core4.logger.error({ errorMessage }, `[WITH-MODEL-RETRY] Failed to parse response: ${errorMessage}`);
|
|
1082
|
+
if (retryCount < maxRetries) {
|
|
1083
|
+
import_core4.logger.debug(`[WITH-MODEL-RETRY] Retrying (attempt ${retryCount + 1}/${maxRetries})`);
|
|
1084
|
+
const feedbackPrompt = createFeedbackPromptFn(input, errorMessage, state, message.content.text || "");
|
|
1085
|
+
const retrySelection = await runtime2.useModel(import_core4.ModelType.OBJECT_LARGE, {
|
|
1086
|
+
prompt: feedbackPrompt
|
|
1087
|
+
});
|
|
1088
|
+
return withModelRetry({
|
|
1089
|
+
runtime: runtime2,
|
|
1090
|
+
input: retrySelection,
|
|
1091
|
+
validationFn,
|
|
1092
|
+
message,
|
|
1093
|
+
state,
|
|
1094
|
+
createFeedbackPromptFn,
|
|
1095
|
+
callback,
|
|
1096
|
+
failureMsg,
|
|
1097
|
+
retryCount: retryCount + 1
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
if (callback && failureMsg) {
|
|
1101
|
+
await callback({
|
|
1102
|
+
text: failureMsg,
|
|
1103
|
+
thought: "Failed to parse response after multiple retries. Requesting clarification from user.",
|
|
1104
|
+
actions: ["REPLY"]
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
return null;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
function getMaxRetries(runtime2) {
|
|
1111
|
+
try {
|
|
1112
|
+
const settings = runtime2.getSetting("mcp");
|
|
1113
|
+
if (settings && "maxRetries" in settings && settings.maxRetries !== undefined) {
|
|
1114
|
+
const configValue = Number(settings.maxRetries);
|
|
1115
|
+
if (!Number.isNaN(configValue) && configValue >= 0) {
|
|
1116
|
+
import_core4.logger.debug(`[WITH-MODEL-RETRY] Using configured selection retries: ${configValue}`);
|
|
1117
|
+
return configValue;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
} catch (error) {
|
|
1121
|
+
import_core4.logger.debug({ error: error instanceof Error ? error.message : String(error) }, "[WITH-MODEL-RETRY] Error reading selection retries config");
|
|
1122
|
+
}
|
|
1123
|
+
return DEFAULT_MAX_RETRIES;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// src/templates/toolSelectionTemplate.ts
|
|
1127
|
+
var toolSelectionNameTemplate = `
|
|
1128
|
+
{{mcpProvider.text}}
|
|
1129
|
+
|
|
1130
|
+
{{recentMessages}}
|
|
1131
|
+
|
|
1132
|
+
# TASK: Select the Most Appropriate Tool and Server
|
|
1133
|
+
|
|
1134
|
+
You must select the most appropriate tool from the list above to fulfill the user's request. Your response must be a valid JSON object with the required properties.
|
|
1135
|
+
|
|
1136
|
+
## CRITICAL INSTRUCTIONS
|
|
1137
|
+
1. Provide both "serverName" and "toolName" from the options listed above.
|
|
1138
|
+
2. Each name must match EXACTLY as shown in the list:
|
|
1139
|
+
- Example (correct): "serverName": "github"
|
|
1140
|
+
- Example (incorrect): "serverName": "GitHub", "Github", or variations
|
|
1141
|
+
3. Extract ACTUAL parameter values from the conversation context.
|
|
1142
|
+
- Do not invent or use placeholders like "octocat" or "Hello-World" unless the user said so.
|
|
1143
|
+
4. Include a "reasoning" field explaining why the selected tool fits the request.
|
|
1144
|
+
5. If no tool is appropriate, respond with:
|
|
1145
|
+
{
|
|
1146
|
+
"noToolAvailable": true
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
!!! YOUR RESPONSE MUST BE A VALID JSON OBJECT ONLY !!!
|
|
1150
|
+
|
|
1151
|
+
CRITICAL: Your response must START with { and END with }. DO NOT include ANY text before or after the JSON.
|
|
1152
|
+
|
|
1153
|
+
## STRICT FORMAT REQUIREMENTS
|
|
1154
|
+
- The response MUST be a single valid JSON object.
|
|
1155
|
+
- DO NOT wrap the JSON in triple backticks (\`\`\`), code blocks, or include any explanatory text.
|
|
1156
|
+
- DO NOT include comments (// or /* */) anywhere.
|
|
1157
|
+
- DO NOT use placeholders (e.g., "replace with...", "example", "your...", etc.)
|
|
1158
|
+
- ALL strings must use double quotes.
|
|
1159
|
+
|
|
1160
|
+
## CRITICAL NOTES
|
|
1161
|
+
- All values must be fully grounded in user input or inferred contextually.
|
|
1162
|
+
- No missing fields unless they are explicitly optional in the schema.
|
|
1163
|
+
- All types must match the schema (strings, numbers, booleans).
|
|
1164
|
+
|
|
1165
|
+
## JSON OBJECT STRUCTURE
|
|
1166
|
+
Your response MUST contain ONLY these top-level keys:
|
|
1167
|
+
1. "serverName" — The name of the server (e.g., "github", "notion")
|
|
1168
|
+
2. "toolName" — The name of the tool (e.g., "get_file_contents", "search")
|
|
1169
|
+
3. "reasoning" — A string explaining how the values were inferred from the conversation.
|
|
1170
|
+
4. "noToolAvailable" — A boolean indicating if no tool is available (true/false)
|
|
1171
|
+
|
|
1172
|
+
## EXAMPLE RESPONSE
|
|
1173
|
+
{
|
|
1174
|
+
"serverName": "github",
|
|
1175
|
+
"toolName": "get_file_contents",
|
|
1176
|
+
"reasoning": "The user wants to retrieve the README from the facebook/react repository.",
|
|
1177
|
+
"noToolAvailable": false
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
## REMINDERS
|
|
1181
|
+
- Use "github" as serverName for GitHub tools.
|
|
1182
|
+
- Use "notion" as serverName for Notion tools.
|
|
1183
|
+
- For search and knowledge-based tasks, MCP tools are often appropriate.
|
|
1184
|
+
|
|
1185
|
+
REMEMBER: This output will be parsed directly as JSON. If the format is incorrect, the operation will fail.
|
|
1186
|
+
`;
|
|
1187
|
+
var toolSelectionArgumentTemplate = `
|
|
1188
|
+
{{recentMessages}}
|
|
1189
|
+
|
|
1190
|
+
# TASK: Generate a Strictly Valid JSON Object for Tool Execution
|
|
1191
|
+
|
|
1192
|
+
You have chosen the "{{toolSelectionName.toolName}}" tool from the "{{toolSelectionName.serverName}}" server to address the user's request.
|
|
1193
|
+
The reasoning behind this selection is: "{{toolSelectionName.reasoning}}"
|
|
1194
|
+
|
|
1195
|
+
## CRITICAL INSTRUCTIONS
|
|
1196
|
+
1. Ensure the "toolArguments" object strictly adheres to the structure and requirements defined in the schema.
|
|
1197
|
+
2. All parameter values must be extracted from the conversation context and must be concrete, usable values.
|
|
1198
|
+
3. Avoid placeholders or generic terms unless explicitly provided by the user.
|
|
1199
|
+
|
|
1200
|
+
!!! YOUR RESPONSE MUST BE A VALID JSON OBJECT ONLY !!!
|
|
1201
|
+
|
|
1202
|
+
## STRICT FORMAT REQUIREMENTS
|
|
1203
|
+
- The response MUST be a single valid JSON object.
|
|
1204
|
+
- DO NOT wrap the JSON in triple backticks (\`\`\`), code blocks, or include any explanatory text.
|
|
1205
|
+
- DO NOT include comments (// or /* */) anywhere.
|
|
1206
|
+
- DO NOT use placeholders (e.g., "replace with...", "example", "your...", etc.)
|
|
1207
|
+
- ALL strings must use double quotes
|
|
1208
|
+
|
|
1209
|
+
## CRITICAL NOTES
|
|
1210
|
+
- All values must be fully grounded in user input or inferred contextually.
|
|
1211
|
+
- No missing fields unless they are explicitly optional in the schema.
|
|
1212
|
+
- All types must match the schema (strings, numbers, booleans).
|
|
1213
|
+
|
|
1214
|
+
## JSON OBJECT STRUCTURE
|
|
1215
|
+
Your response MUST contain ONLY these two top-level keys:
|
|
1216
|
+
1. "toolArguments" — An object matching the input schema: {{toolInputSchema}}
|
|
1217
|
+
2. "reasoning" — A string explaining how the values were inferred from the conversation.
|
|
1218
|
+
|
|
1219
|
+
## EXAMPLE RESPONSE
|
|
1220
|
+
{
|
|
1221
|
+
"toolArguments": {
|
|
1222
|
+
"owner": "facebook",
|
|
1223
|
+
"repo": "react",
|
|
1224
|
+
"path": "README.md",
|
|
1225
|
+
"branch": "main"
|
|
1226
|
+
},
|
|
1227
|
+
"reasoning": "The user wants to see the README from the facebook/react repository based on our conversation."
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
REMEMBER: Your response will be parsed directly as JSON. If it fails to parse, the operation will fail completely.
|
|
1231
|
+
`;
|
|
1232
|
+
|
|
1233
|
+
// src/utils/schemas.ts
|
|
1234
|
+
var toolSelectionNameSchema = {
|
|
1235
|
+
type: "object",
|
|
1236
|
+
required: ["serverName", "toolName"],
|
|
1237
|
+
properties: {
|
|
1238
|
+
serverName: {
|
|
1239
|
+
type: "string",
|
|
1240
|
+
minLength: 1,
|
|
1241
|
+
errorMessage: "serverName must not be empty"
|
|
1242
|
+
},
|
|
1243
|
+
toolName: {
|
|
1244
|
+
type: "string",
|
|
1245
|
+
minLength: 1,
|
|
1246
|
+
errorMessage: "toolName must not be empty"
|
|
1247
|
+
},
|
|
1248
|
+
reasoning: {
|
|
1249
|
+
type: "string"
|
|
1250
|
+
},
|
|
1251
|
+
noToolAvailable: {
|
|
1252
|
+
type: "boolean"
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
var toolSelectionArgumentSchema = {
|
|
1257
|
+
type: "object",
|
|
1258
|
+
required: ["toolArguments"],
|
|
1259
|
+
properties: {
|
|
1260
|
+
toolArguments: {
|
|
1261
|
+
type: "object"
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
|
|
1266
|
+
// src/utils/validation.ts
|
|
1267
|
+
function validateToolSelectionName(parsed, state) {
|
|
1268
|
+
const basicResult = validateJsonSchema(parsed, toolSelectionNameSchema);
|
|
1269
|
+
if (basicResult.success === false) {
|
|
1270
|
+
return { success: false, error: basicResult.error };
|
|
1271
|
+
}
|
|
1272
|
+
const data = basicResult.data;
|
|
1273
|
+
const mcpData = state.values.mcp || {};
|
|
1274
|
+
const server = mcpData[data.serverName];
|
|
1275
|
+
if (!server || server.status !== "connected") {
|
|
1276
|
+
return {
|
|
1277
|
+
success: false,
|
|
1278
|
+
error: `Server "${data.serverName}" not found or not connected`
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
const toolInfo = server.tools?.[data.toolName];
|
|
1282
|
+
if (!toolInfo) {
|
|
1283
|
+
return {
|
|
1284
|
+
success: false,
|
|
1285
|
+
error: `Tool "${data.toolName}" not found on server "${data.serverName}"`
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
return { success: true, data };
|
|
1289
|
+
}
|
|
1290
|
+
function validateToolSelectionArgument(parsed, toolInputSchema) {
|
|
1291
|
+
const basicResult = validateJsonSchema(parsed, toolSelectionArgumentSchema);
|
|
1292
|
+
if (basicResult.success === false) {
|
|
1293
|
+
return { success: false, error: basicResult.error };
|
|
1294
|
+
}
|
|
1295
|
+
const data = basicResult.data;
|
|
1296
|
+
const validationResult = validateJsonSchema(data.toolArguments, toolInputSchema);
|
|
1297
|
+
if (validationResult.success === false) {
|
|
1298
|
+
return {
|
|
1299
|
+
success: false,
|
|
1300
|
+
error: `Invalid arguments: ${validationResult.error}`
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
return { success: true, data };
|
|
1304
|
+
}
|
|
1305
|
+
function validateResourceSelection(selection) {
|
|
1306
|
+
return validateJsonSchema(selection, ResourceSelectionSchema);
|
|
1307
|
+
}
|
|
1308
|
+
function createResourceSelectionFeedbackPrompt(originalResponse, errorMessage, composedState, userMessage) {
|
|
1309
|
+
let resourcesDescription = "";
|
|
1310
|
+
for (const [serverName, server] of Object.entries(composedState.values.mcp || {})) {
|
|
1311
|
+
if (server.status !== "connected")
|
|
1312
|
+
continue;
|
|
1313
|
+
for (const [uri, resource] of Object.entries(server.resources || {})) {
|
|
1314
|
+
resourcesDescription += `Resource: ${uri} (Server: ${serverName})
|
|
1315
|
+
`;
|
|
1316
|
+
resourcesDescription += `Name: ${resource.name || "No name available"}
|
|
1317
|
+
`;
|
|
1318
|
+
resourcesDescription += `Description: ${resource.description || "No description available"}
|
|
1319
|
+
|
|
1320
|
+
`;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
return createFeedbackPrompt(originalResponse, errorMessage, "resource", resourcesDescription, userMessage);
|
|
1324
|
+
}
|
|
1325
|
+
function createFeedbackPrompt(originalResponse, errorMessage, itemType, itemsDescription, userMessage) {
|
|
1326
|
+
return `Error parsing JSON: ${errorMessage}
|
|
1327
|
+
|
|
1328
|
+
Your original response:
|
|
1329
|
+
${originalResponse}
|
|
1330
|
+
|
|
1331
|
+
Please try again with valid JSON for ${itemType} selection.
|
|
1332
|
+
Available ${itemType}s:
|
|
1333
|
+
${itemsDescription}
|
|
1334
|
+
|
|
1335
|
+
User request: ${userMessage}`;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
// src/utils/selection.ts
|
|
1339
|
+
async function createToolSelectionName({
|
|
1340
|
+
runtime: runtime2,
|
|
1341
|
+
state,
|
|
1342
|
+
message,
|
|
1343
|
+
callback,
|
|
1344
|
+
mcpProvider
|
|
1345
|
+
}) {
|
|
1346
|
+
const toolSelectionPrompt = import_core5.composePromptFromState({
|
|
1347
|
+
state: { ...state, values: { ...state.values, mcpProvider } },
|
|
1348
|
+
template: toolSelectionNameTemplate
|
|
1349
|
+
});
|
|
1350
|
+
import_core5.logger.debug(`[SELECTION] Tool Selection Name Prompt:
|
|
1351
|
+
${toolSelectionPrompt}`);
|
|
1352
|
+
const toolSelectionName = await runtime2.useModel(import_core5.ModelType.TEXT_LARGE, {
|
|
1353
|
+
prompt: toolSelectionPrompt
|
|
1354
|
+
});
|
|
1355
|
+
import_core5.logger.debug(`[SELECTION] Tool Selection Name Response:
|
|
1356
|
+
${toolSelectionName}`);
|
|
1357
|
+
return await withModelRetry({
|
|
1358
|
+
runtime: runtime2,
|
|
1359
|
+
message,
|
|
1360
|
+
state,
|
|
1361
|
+
callback,
|
|
1362
|
+
input: toolSelectionName,
|
|
1363
|
+
validationFn: (parsed) => validateToolSelectionName(parsed, state),
|
|
1364
|
+
createFeedbackPromptFn: (originalResponse, errorMessage, state2, userMessage) => createToolSelectionFeedbackPrompt(originalResponse, errorMessage, state2, userMessage),
|
|
1365
|
+
failureMsg: "I'm having trouble figuring out the best way to help with your request."
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
async function createToolSelectionArgument({
|
|
1369
|
+
runtime: runtime2,
|
|
1370
|
+
state,
|
|
1371
|
+
message,
|
|
1372
|
+
callback,
|
|
1373
|
+
mcpProvider,
|
|
1374
|
+
toolSelectionName
|
|
1375
|
+
}) {
|
|
1376
|
+
if (!toolSelectionName) {
|
|
1377
|
+
import_core5.logger.warn("[SELECTION] Tool selection name is not provided. Cannot create tool selection argument.");
|
|
1378
|
+
return null;
|
|
1379
|
+
}
|
|
1380
|
+
const { serverName, toolName } = toolSelectionName;
|
|
1381
|
+
const toolInputSchema = mcpProvider.data.mcp[serverName].tools[toolName].inputSchema;
|
|
1382
|
+
import_core5.logger.trace(`[SELECTION] Tool Input Schema:
|
|
1383
|
+
${JSON.stringify({ toolInputSchema }, null, 2)}`);
|
|
1384
|
+
const toolSelectionArgumentPrompt = import_core5.composePromptFromState({
|
|
1385
|
+
state: {
|
|
1386
|
+
...state,
|
|
1387
|
+
values: {
|
|
1388
|
+
...state.values,
|
|
1389
|
+
toolSelectionName,
|
|
1390
|
+
toolInputSchema: JSON.stringify(toolInputSchema)
|
|
1391
|
+
}
|
|
1392
|
+
},
|
|
1393
|
+
template: toolSelectionArgumentTemplate
|
|
1394
|
+
});
|
|
1395
|
+
import_core5.logger.debug(`[SELECTION] Tool Selection Prompt:
|
|
1396
|
+
${toolSelectionArgumentPrompt}`);
|
|
1397
|
+
const toolSelectionArgument = await runtime2.useModel(import_core5.ModelType.TEXT_LARGE, {
|
|
1398
|
+
prompt: toolSelectionArgumentPrompt
|
|
1399
|
+
});
|
|
1400
|
+
import_core5.logger.debug(`[SELECTION] Tool Selection Argument Response:
|
|
1401
|
+
${toolSelectionArgument}`);
|
|
1402
|
+
return await withModelRetry({
|
|
1403
|
+
runtime: runtime2,
|
|
1404
|
+
message,
|
|
1405
|
+
state,
|
|
1406
|
+
callback,
|
|
1407
|
+
input: toolSelectionArgument,
|
|
1408
|
+
validationFn: (parsed) => validateToolSelectionArgument(parsed, state),
|
|
1409
|
+
createFeedbackPromptFn: (originalResponse, errorMessage, state2, userMessage) => createToolSelectionFeedbackPrompt(originalResponse, errorMessage, state2, userMessage),
|
|
1410
|
+
failureMsg: "I'm having trouble figuring out the best way to help with your request."
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
function createToolSelectionFeedbackPrompt(originalResponse, errorMessage, state, userMessage) {
|
|
1414
|
+
let toolsDescription = "";
|
|
1415
|
+
for (const [serverName, server] of Object.entries(state.values.mcp || {})) {
|
|
1416
|
+
if (server.status !== "connected")
|
|
1417
|
+
continue;
|
|
1418
|
+
for (const [toolName, tool] of Object.entries(server.tools || {})) {
|
|
1419
|
+
toolsDescription += `Tool: ${toolName} (Server: ${serverName})
|
|
1420
|
+
`;
|
|
1421
|
+
toolsDescription += `Description: ${tool.description || "No description available"}
|
|
1422
|
+
|
|
1423
|
+
`;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
const feedbackPrompt = createFeedbackPrompt2(originalResponse, errorMessage, "tool", toolsDescription, userMessage);
|
|
1427
|
+
import_core5.logger.debug(`[SELECTION] Tool Selection Feedback Prompt:
|
|
1428
|
+
${feedbackPrompt}`);
|
|
1429
|
+
return feedbackPrompt;
|
|
1430
|
+
}
|
|
1431
|
+
function createFeedbackPrompt2(originalResponse, errorMessage, itemType, itemsDescription, userMessage) {
|
|
1432
|
+
return `Error parsing JSON: ${errorMessage}
|
|
1433
|
+
|
|
1434
|
+
Your original response:
|
|
1435
|
+
${originalResponse}
|
|
1436
|
+
|
|
1437
|
+
Please try again with valid JSON for ${itemType} selection.
|
|
1438
|
+
Available ${itemType}s:
|
|
1439
|
+
${itemsDescription}
|
|
1440
|
+
|
|
1441
|
+
User request: ${userMessage}`;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
// src/utils/handler.ts
|
|
1445
|
+
async function handleNoToolAvailable(callback, toolSelection) {
|
|
1446
|
+
const responseText = "I don't have a specific tool that can help with that request. Let me try to assist you directly instead.";
|
|
1447
|
+
const thoughtText = "No appropriate MCP tool available for this request. Falling back to direct assistance.";
|
|
1448
|
+
if (callback && toolSelection?.noToolAvailable) {
|
|
1449
|
+
await callback({
|
|
1450
|
+
text: responseText,
|
|
1451
|
+
thought: thoughtText,
|
|
1452
|
+
actions: ["REPLY"]
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
return {
|
|
1456
|
+
text: responseText,
|
|
1457
|
+
values: {
|
|
1458
|
+
success: true,
|
|
1459
|
+
noToolAvailable: true,
|
|
1460
|
+
fallbackToDirectAssistance: true
|
|
1461
|
+
},
|
|
1462
|
+
data: {
|
|
1463
|
+
actionName: "CALL_MCP_TOOL",
|
|
1464
|
+
noToolAvailable: true,
|
|
1465
|
+
reason: toolSelection?.reasoning || "No appropriate tool available"
|
|
1466
|
+
},
|
|
1467
|
+
success: true
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
// src/actions/callToolAction.ts
|
|
1472
|
+
var callToolAction = {
|
|
1473
|
+
name: "CALL_MCP_TOOL",
|
|
1474
|
+
similes: [
|
|
1475
|
+
"CALL_TOOL",
|
|
1476
|
+
"CALL_MCP_TOOL",
|
|
1477
|
+
"USE_TOOL",
|
|
1478
|
+
"USE_MCP_TOOL",
|
|
1479
|
+
"EXECUTE_TOOL",
|
|
1480
|
+
"EXECUTE_MCP_TOOL",
|
|
1481
|
+
"RUN_TOOL",
|
|
1482
|
+
"RUN_MCP_TOOL",
|
|
1483
|
+
"INVOKE_TOOL",
|
|
1484
|
+
"INVOKE_MCP_TOOL"
|
|
1485
|
+
],
|
|
1486
|
+
description: "Calls a tool from an MCP server to perform a specific task",
|
|
1487
|
+
validate: async (runtime2, _message, _state) => {
|
|
1488
|
+
const mcpService = runtime2.getService(MCP_SERVICE_NAME);
|
|
1489
|
+
if (!mcpService)
|
|
1490
|
+
return false;
|
|
1491
|
+
const servers = mcpService.getServers();
|
|
1492
|
+
return servers.length > 0 && servers.some((server) => server.status === "connected" && server.tools && server.tools.length > 0);
|
|
1493
|
+
},
|
|
1494
|
+
handler: async (runtime2, message, _state, _options, callback) => {
|
|
1495
|
+
const composedState = await runtime2.composeState(message, ["RECENT_MESSAGES", "MCP"]);
|
|
1496
|
+
const mcpService = runtime2.getService(MCP_SERVICE_NAME);
|
|
1497
|
+
if (!mcpService) {
|
|
1498
|
+
throw new Error("MCP service not available");
|
|
1499
|
+
}
|
|
1500
|
+
const mcpProvider = mcpService.getProviderData();
|
|
1501
|
+
try {
|
|
1502
|
+
const toolSelectionName = await createToolSelectionName({
|
|
1503
|
+
runtime: runtime2,
|
|
1504
|
+
state: composedState,
|
|
1505
|
+
message,
|
|
1506
|
+
callback,
|
|
1507
|
+
mcpProvider
|
|
1508
|
+
});
|
|
1509
|
+
if (!toolSelectionName || toolSelectionName.noToolAvailable) {
|
|
1510
|
+
import_core6.logger.warn("[NO_TOOL_AVAILABLE] No appropriate tool available for the request");
|
|
1511
|
+
return await handleNoToolAvailable(callback, toolSelectionName);
|
|
1512
|
+
}
|
|
1513
|
+
const { serverName, toolName, reasoning } = toolSelectionName;
|
|
1514
|
+
import_core6.logger.info(`[CALLING] Calling tool "${serverName}/${toolName}" on server with reasoning: "${reasoning}"`);
|
|
1515
|
+
const toolSelectionArgument = await createToolSelectionArgument({
|
|
1516
|
+
runtime: runtime2,
|
|
1517
|
+
state: composedState,
|
|
1518
|
+
message,
|
|
1519
|
+
callback,
|
|
1520
|
+
mcpProvider,
|
|
1521
|
+
toolSelectionName
|
|
1522
|
+
});
|
|
1523
|
+
if (!toolSelectionArgument) {
|
|
1524
|
+
import_core6.logger.warn("[NO_TOOL_SELECTION_ARGUMENT] No appropriate tool selection argument available");
|
|
1525
|
+
return await handleNoToolAvailable(callback, toolSelectionName);
|
|
1526
|
+
}
|
|
1527
|
+
import_core6.logger.info(`[SELECTED] Tool Selection result:
|
|
1528
|
+
${JSON.stringify(toolSelectionArgument, null, 2)}`);
|
|
1529
|
+
const result = await mcpService.callTool(serverName, toolName, toolSelectionArgument.toolArguments);
|
|
1530
|
+
const { toolOutput, hasAttachments, attachments } = processToolResult(result, serverName, toolName, runtime2, message.entityId);
|
|
1531
|
+
await handleToolResponse(runtime2, message, serverName, toolName, toolSelectionArgument.toolArguments, toolOutput, hasAttachments, attachments, composedState, mcpProvider, callback);
|
|
1532
|
+
return {
|
|
1533
|
+
text: `Successfully called tool: ${serverName}/${toolName}`,
|
|
1534
|
+
values: {
|
|
1535
|
+
success: true,
|
|
1536
|
+
toolExecuted: true,
|
|
1537
|
+
serverName,
|
|
1538
|
+
toolName,
|
|
1539
|
+
hasAttachments
|
|
1540
|
+
},
|
|
1541
|
+
data: {
|
|
1542
|
+
actionName: "CALL_MCP_TOOL",
|
|
1543
|
+
serverName,
|
|
1544
|
+
toolName,
|
|
1545
|
+
toolArguments: toolSelectionArgument.toolArguments,
|
|
1546
|
+
reasoning: toolSelectionName.reasoning,
|
|
1547
|
+
output: toolOutput,
|
|
1548
|
+
attachments: attachments || []
|
|
1549
|
+
},
|
|
1550
|
+
success: true
|
|
1551
|
+
};
|
|
1552
|
+
} catch (error) {
|
|
1553
|
+
return await handleMcpError(composedState, mcpProvider, error, runtime2, message, "tool", callback);
|
|
1554
|
+
}
|
|
1555
|
+
},
|
|
1556
|
+
examples: [
|
|
1557
|
+
[
|
|
1558
|
+
{
|
|
1559
|
+
name: "{{user}}",
|
|
1560
|
+
content: {
|
|
1561
|
+
text: "Can you search for information about climate change?"
|
|
1562
|
+
}
|
|
1563
|
+
},
|
|
1564
|
+
{
|
|
1565
|
+
name: "{{assistant}}",
|
|
1566
|
+
content: {
|
|
1567
|
+
text: "I'll help you with that request. Let me access the right tool...",
|
|
1568
|
+
actions: ["CALL_MCP_TOOL"]
|
|
1569
|
+
}
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
name: "{{assistant}}",
|
|
1573
|
+
content: {
|
|
1574
|
+
text: `I found the following information about climate change:
|
|
1575
|
+
|
|
1576
|
+
Climate change refers to long-term shifts in temperatures and weather patterns. These shifts may be natural, but since the 1800s, human activities have been the main driver of climate change, primarily due to the burning of fossil fuels like coal, oil, and gas, which produces heat-trapping gases.`,
|
|
1577
|
+
actions: ["CALL_MCP_TOOL"]
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
]
|
|
1581
|
+
]
|
|
1582
|
+
};
|
|
1583
|
+
|
|
1584
|
+
// src/actions/readResourceAction.ts
|
|
1585
|
+
var import_core7 = require("@elizaos/core");
|
|
1586
|
+
|
|
1587
|
+
// src/templates/resourceSelectionTemplate.ts
|
|
1588
|
+
var resourceSelectionTemplate = `
|
|
1589
|
+
{{{mcpProvider.text}}}
|
|
1590
|
+
|
|
1591
|
+
{{{recentMessages}}}
|
|
1592
|
+
|
|
1593
|
+
# Prompt
|
|
1594
|
+
|
|
1595
|
+
You are an intelligent assistant helping select the right resource to address a user's request.
|
|
1596
|
+
|
|
1597
|
+
CRITICAL INSTRUCTIONS:
|
|
1598
|
+
1. You MUST specify both a valid serverName AND uri from the list above
|
|
1599
|
+
2. The serverName value should match EXACTLY the server name shown in parentheses (Server: X)
|
|
1600
|
+
CORRECT: "serverName": "github" (if the server is called "github")
|
|
1601
|
+
WRONG: "serverName": "GitHub" or "Github" or any other variation
|
|
1602
|
+
3. The uri value should match EXACTLY the resource uri listed
|
|
1603
|
+
CORRECT: "uri": "weather://San Francisco/current" (if that's the exact uri)
|
|
1604
|
+
WRONG: "uri": "weather://sanfrancisco/current" or any variation
|
|
1605
|
+
4. Identify the user's information need from the conversation context
|
|
1606
|
+
5. Select the most appropriate resource based on its description and the request
|
|
1607
|
+
6. If no resource seems appropriate, output {"noResourceAvailable": true}
|
|
1608
|
+
|
|
1609
|
+
!!! YOUR RESPONSE MUST BE A VALID JSON OBJECT ONLY !!!
|
|
1610
|
+
|
|
1611
|
+
STRICT FORMAT REQUIREMENTS:
|
|
1612
|
+
- NO code block formatting (NO backticks or \`\`\`)
|
|
1613
|
+
- NO comments (NO // or /* */)
|
|
1614
|
+
- NO placeholders like "replace with...", "example", "your...", "actual", etc.
|
|
1615
|
+
- Every parameter value must be a concrete, usable value (not instructions to replace)
|
|
1616
|
+
- Use proper JSON syntax with double quotes for strings
|
|
1617
|
+
- NO explanatory text before or after the JSON object
|
|
1618
|
+
|
|
1619
|
+
EXAMPLE RESPONSE:
|
|
1620
|
+
{
|
|
1621
|
+
"serverName": "weather-server",
|
|
1622
|
+
"uri": "weather://San Francisco/current",
|
|
1623
|
+
"reasoning": "Based on the conversation, the user is asking about current weather in San Francisco. This resource provides up-to-date weather information for that city."
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
REMEMBER: Your response will be parsed directly as JSON. If it fails to parse, the operation will fail completely!
|
|
1627
|
+
`;
|
|
1628
|
+
|
|
1629
|
+
// src/actions/readResourceAction.ts
|
|
1630
|
+
function createResourceSelectionPrompt(composedState, userMessage) {
|
|
1631
|
+
const mcpData = composedState.values.mcp || {};
|
|
1632
|
+
const serverNames = Object.keys(mcpData);
|
|
1633
|
+
let resourcesDescription = "";
|
|
1634
|
+
for (const serverName of serverNames) {
|
|
1635
|
+
const server = mcpData[serverName];
|
|
1636
|
+
if (server.status !== "connected")
|
|
1637
|
+
continue;
|
|
1638
|
+
const resourceUris = Object.keys(server.resources || {});
|
|
1639
|
+
for (const uri of resourceUris) {
|
|
1640
|
+
const resource = server.resources[uri];
|
|
1641
|
+
resourcesDescription += `Resource: ${uri} (Server: ${serverName})
|
|
1642
|
+
`;
|
|
1643
|
+
resourcesDescription += `Name: ${resource.name || "No name available"}
|
|
1644
|
+
`;
|
|
1645
|
+
resourcesDescription += `Description: ${resource.description || "No description available"}
|
|
1646
|
+
`;
|
|
1647
|
+
resourcesDescription += `MIME Type: ${resource.mimeType || "Not specified"}
|
|
1648
|
+
|
|
1649
|
+
`;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
const enhancedState = {
|
|
1653
|
+
...composedState,
|
|
1654
|
+
values: {
|
|
1655
|
+
...composedState.values,
|
|
1656
|
+
resourcesDescription,
|
|
1657
|
+
userMessage
|
|
1658
|
+
}
|
|
1659
|
+
};
|
|
1660
|
+
return import_core7.composePromptFromState({
|
|
1661
|
+
state: enhancedState,
|
|
1662
|
+
template: resourceSelectionTemplate
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
var readResourceAction = {
|
|
1666
|
+
name: "READ_MCP_RESOURCE",
|
|
1667
|
+
similes: [
|
|
1668
|
+
"READ_RESOURCE",
|
|
1669
|
+
"READ_MCP_RESOURCE",
|
|
1670
|
+
"GET_RESOURCE",
|
|
1671
|
+
"GET_MCP_RESOURCE",
|
|
1672
|
+
"FETCH_RESOURCE",
|
|
1673
|
+
"FETCH_MCP_RESOURCE",
|
|
1674
|
+
"ACCESS_RESOURCE",
|
|
1675
|
+
"ACCESS_MCP_RESOURCE"
|
|
1676
|
+
],
|
|
1677
|
+
description: "Reads a resource from an MCP server",
|
|
1678
|
+
validate: async (runtime2, _message, _state) => {
|
|
1679
|
+
const mcpService = runtime2.getService(MCP_SERVICE_NAME);
|
|
1680
|
+
if (!mcpService)
|
|
1681
|
+
return false;
|
|
1682
|
+
const servers = mcpService.getServers();
|
|
1683
|
+
return servers.length > 0 && servers.some((server) => server.status === "connected" && server.resources && server.resources.length > 0);
|
|
1684
|
+
},
|
|
1685
|
+
handler: async (runtime2, message, _state, _options, callback) => {
|
|
1686
|
+
const composedState = await runtime2.composeState(message, ["RECENT_MESSAGES", "MCP"]);
|
|
1687
|
+
const mcpService = runtime2.getService(MCP_SERVICE_NAME);
|
|
1688
|
+
if (!mcpService) {
|
|
1689
|
+
throw new Error("MCP service not available");
|
|
1690
|
+
}
|
|
1691
|
+
const mcpProvider = mcpService.getProviderData();
|
|
1692
|
+
try {
|
|
1693
|
+
await sendInitialResponse(callback);
|
|
1694
|
+
const resourceSelectionPrompt = createResourceSelectionPrompt(composedState, message.content.text || "");
|
|
1695
|
+
const resourceSelection = await runtime2.useModel(import_core7.ModelType.TEXT_SMALL, {
|
|
1696
|
+
prompt: resourceSelectionPrompt
|
|
1697
|
+
});
|
|
1698
|
+
const parsedSelection = await withModelRetry({
|
|
1699
|
+
runtime: runtime2,
|
|
1700
|
+
state: composedState,
|
|
1701
|
+
message,
|
|
1702
|
+
callback,
|
|
1703
|
+
input: resourceSelection,
|
|
1704
|
+
validationFn: (data) => validateResourceSelection(data),
|
|
1705
|
+
createFeedbackPromptFn: (originalResponse, errorMessage, state, userMessage) => createResourceSelectionFeedbackPrompt(originalResponse, errorMessage, state, userMessage),
|
|
1706
|
+
failureMsg: `I'm having trouble finding the resource you're looking for. Could you provide more details about what you need?`,
|
|
1707
|
+
retryCount: 0
|
|
1708
|
+
});
|
|
1709
|
+
if (!parsedSelection || parsedSelection.noResourceAvailable) {
|
|
1710
|
+
const responseText = "I don't have a specific resource that contains the information you're looking for. Let me try to assist you directly instead.";
|
|
1711
|
+
const thoughtText = "No appropriate MCP resource available for this request. Falling back to direct assistance.";
|
|
1712
|
+
if (callback && parsedSelection?.noResourceAvailable) {
|
|
1713
|
+
await callback({
|
|
1714
|
+
text: responseText,
|
|
1715
|
+
thought: thoughtText,
|
|
1716
|
+
actions: ["REPLY"]
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
return {
|
|
1720
|
+
text: responseText,
|
|
1721
|
+
values: {
|
|
1722
|
+
success: true,
|
|
1723
|
+
noResourceAvailable: true,
|
|
1724
|
+
fallbackToDirectAssistance: true
|
|
1725
|
+
},
|
|
1726
|
+
data: {
|
|
1727
|
+
actionName: "READ_MCP_RESOURCE",
|
|
1728
|
+
noResourceAvailable: true,
|
|
1729
|
+
reason: parsedSelection?.reasoning || "No appropriate resource available"
|
|
1730
|
+
},
|
|
1731
|
+
success: true
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
const { serverName, uri, reasoning } = parsedSelection;
|
|
1735
|
+
import_core7.logger.debug(`Selected resource "${uri}" on server "${serverName}" because: ${reasoning}`);
|
|
1736
|
+
const result = await mcpService.readResource(serverName, uri);
|
|
1737
|
+
import_core7.logger.debug(`Read resource ${uri} from server ${serverName}`);
|
|
1738
|
+
const { resourceContent, resourceMeta } = processResourceResult(result, uri);
|
|
1739
|
+
await handleResourceAnalysis(runtime2, message, uri, serverName, resourceContent, resourceMeta, callback);
|
|
1740
|
+
return {
|
|
1741
|
+
text: `Successfully read resource: ${uri}`,
|
|
1742
|
+
values: {
|
|
1743
|
+
success: true,
|
|
1744
|
+
resourceRead: true,
|
|
1745
|
+
serverName,
|
|
1746
|
+
uri
|
|
1747
|
+
},
|
|
1748
|
+
data: {
|
|
1749
|
+
actionName: "READ_MCP_RESOURCE",
|
|
1750
|
+
serverName,
|
|
1751
|
+
uri,
|
|
1752
|
+
reasoning,
|
|
1753
|
+
resourceMeta,
|
|
1754
|
+
contentLength: resourceContent?.length || 0
|
|
1755
|
+
},
|
|
1756
|
+
success: true
|
|
1757
|
+
};
|
|
1758
|
+
} catch (error) {
|
|
1759
|
+
return await handleMcpError(composedState, mcpProvider, error, runtime2, message, "resource", callback);
|
|
1760
|
+
}
|
|
1761
|
+
},
|
|
1762
|
+
examples: [
|
|
1763
|
+
[
|
|
1764
|
+
{
|
|
1765
|
+
name: "{{user}}",
|
|
1766
|
+
content: {
|
|
1767
|
+
text: "Can you get the documentation about installing ElizaOS?"
|
|
1768
|
+
}
|
|
1769
|
+
},
|
|
1770
|
+
{
|
|
1771
|
+
name: "{{assistant}}",
|
|
1772
|
+
content: {
|
|
1773
|
+
text: `I'll retrieve that information for you. Let me access the resource...`,
|
|
1774
|
+
actions: ["READ_MCP_RESOURCE"]
|
|
1775
|
+
}
|
|
1776
|
+
},
|
|
1777
|
+
{
|
|
1778
|
+
name: "{{assistant}}",
|
|
1779
|
+
content: {
|
|
1780
|
+
text: `ElizaOS installation is straightforward. You'll need Node.js 23+ and Git installed. For Windows users, WSL 2 is required. The quickest way to get started is by cloning the ElizaOS starter repository with \`git clone https://github.com/elizaos/eliza-starter.git\`, then run \`cd eliza-starter && cp .env.example .env && bun i && bun run build && bun start\`. This will set up a development environment with the core features enabled. After starting, you can access the web interface at http://localhost:3000 to interact with your agent.`,
|
|
1781
|
+
actions: ["READ_MCP_RESOURCE"]
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
]
|
|
1785
|
+
]
|
|
1786
|
+
};
|
|
1787
|
+
|
|
1788
|
+
// src/provider.ts
|
|
1789
|
+
var provider = {
|
|
1790
|
+
name: "MCP",
|
|
1791
|
+
description: "Information about connected MCP servers, tools, and resources",
|
|
1792
|
+
get: async (runtime2, _message, _state) => {
|
|
1793
|
+
const mcpService = runtime2.getService(MCP_SERVICE_NAME);
|
|
1794
|
+
if (!mcpService) {
|
|
1795
|
+
return {
|
|
1796
|
+
values: { mcp: {} },
|
|
1797
|
+
data: { mcp: {} },
|
|
1798
|
+
text: "No MCP servers are available."
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
return mcpService.getProviderData();
|
|
1802
|
+
}
|
|
1803
|
+
};
|
|
1804
|
+
|
|
1805
|
+
// src/service.ts
|
|
1806
|
+
var import_core8 = require("@elizaos/core");
|
|
1807
|
+
var import_client = require("@modelcontextprotocol/sdk/client/index.js");
|
|
1808
|
+
var import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
1809
|
+
var import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
1810
|
+
class McpService extends import_core8.Service {
|
|
1811
|
+
static serviceType = MCP_SERVICE_NAME;
|
|
1812
|
+
capabilityDescription = "Enables the agent to interact with MCP (Model Context Protocol) servers";
|
|
1813
|
+
connections = new Map;
|
|
1814
|
+
connectionStates = new Map;
|
|
1815
|
+
mcpProvider = {
|
|
1816
|
+
values: { mcp: {} },
|
|
1817
|
+
data: { mcp: {} },
|
|
1818
|
+
text: ""
|
|
1819
|
+
};
|
|
1820
|
+
pingConfig = DEFAULT_PING_CONFIG;
|
|
1821
|
+
toolCompatibility = null;
|
|
1822
|
+
compatibilityInitialized = false;
|
|
1823
|
+
constructor(runtime2) {
|
|
1824
|
+
super(runtime2);
|
|
1825
|
+
this.initializeMcpServers();
|
|
1826
|
+
}
|
|
1827
|
+
static async start(runtime2) {
|
|
1828
|
+
const service = new McpService(runtime2);
|
|
1829
|
+
return service;
|
|
1830
|
+
}
|
|
1831
|
+
async stop() {
|
|
1832
|
+
for (const [name] of this.connections) {
|
|
1833
|
+
await this.deleteConnection(name);
|
|
1834
|
+
}
|
|
1835
|
+
this.connections.clear();
|
|
1836
|
+
for (const state of this.connectionStates.values()) {
|
|
1837
|
+
if (state.pingInterval)
|
|
1838
|
+
clearInterval(state.pingInterval);
|
|
1839
|
+
if (state.reconnectTimeout)
|
|
1840
|
+
clearTimeout(state.reconnectTimeout);
|
|
1841
|
+
}
|
|
1842
|
+
this.connectionStates.clear();
|
|
1843
|
+
}
|
|
1844
|
+
async initializeMcpServers() {
|
|
1845
|
+
try {
|
|
1846
|
+
const mcpSettings = this.getMcpSettings();
|
|
1847
|
+
if (!mcpSettings || !mcpSettings.servers) {
|
|
1848
|
+
import_core8.logger.info("No MCP servers configured.");
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
await this.updateServerConnections(mcpSettings.servers);
|
|
1852
|
+
const servers = this.getServers();
|
|
1853
|
+
this.mcpProvider = buildMcpProviderData(servers);
|
|
1854
|
+
} catch (error) {
|
|
1855
|
+
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error) }, "Failed to initialize MCP servers");
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
getMcpSettings() {
|
|
1859
|
+
return this.runtime.getSetting("mcp");
|
|
1860
|
+
}
|
|
1861
|
+
async updateServerConnections(serverConfigs) {
|
|
1862
|
+
const currentNames = new Set(this.connections.keys());
|
|
1863
|
+
const newNames = new Set(Object.keys(serverConfigs));
|
|
1864
|
+
for (const name of currentNames) {
|
|
1865
|
+
if (!newNames.has(name)) {
|
|
1866
|
+
await this.deleteConnection(name);
|
|
1867
|
+
import_core8.logger.info(`Deleted MCP server: ${name}`);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
for (const [name, config] of Object.entries(serverConfigs)) {
|
|
1871
|
+
const currentConnection = this.connections.get(name);
|
|
1872
|
+
if (!currentConnection) {
|
|
1873
|
+
try {
|
|
1874
|
+
await this.initializeConnection(name, config);
|
|
1875
|
+
} catch (error) {
|
|
1876
|
+
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName: name }, `Failed to connect to new MCP server ${name}`);
|
|
1877
|
+
}
|
|
1878
|
+
} else if (JSON.stringify(config) !== currentConnection.server.config) {
|
|
1879
|
+
try {
|
|
1880
|
+
await this.deleteConnection(name);
|
|
1881
|
+
await this.initializeConnection(name, config);
|
|
1882
|
+
import_core8.logger.info(`Reconnected MCP server with updated config: ${name}`);
|
|
1883
|
+
} catch (error) {
|
|
1884
|
+
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName: name }, `Failed to reconnect MCP server ${name}`);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
async initializeConnection(name, config) {
|
|
1890
|
+
await this.deleteConnection(name);
|
|
1891
|
+
const state = {
|
|
1892
|
+
status: "connecting",
|
|
1893
|
+
reconnectAttempts: 0,
|
|
1894
|
+
consecutivePingFailures: 0
|
|
1895
|
+
};
|
|
1896
|
+
this.connectionStates.set(name, state);
|
|
1897
|
+
try {
|
|
1898
|
+
const client = new import_client.Client({ name: "ElizaOS", version: "1.0.0" }, { capabilities: {} });
|
|
1899
|
+
const transport = config.type === "stdio" ? await this.buildStdioClientTransport(name, config) : await this.buildHttpClientTransport(name, config);
|
|
1900
|
+
const connection = {
|
|
1901
|
+
server: {
|
|
1902
|
+
name,
|
|
1903
|
+
config: JSON.stringify(config),
|
|
1904
|
+
status: "connecting"
|
|
1905
|
+
},
|
|
1906
|
+
client,
|
|
1907
|
+
transport
|
|
1908
|
+
};
|
|
1909
|
+
this.connections.set(name, connection);
|
|
1910
|
+
this.setupTransportHandlers(name, connection, state);
|
|
1911
|
+
await client.connect(transport);
|
|
1912
|
+
connection.server = {
|
|
1913
|
+
status: "connected",
|
|
1914
|
+
name,
|
|
1915
|
+
config: JSON.stringify(config),
|
|
1916
|
+
error: "",
|
|
1917
|
+
tools: await this.fetchToolsList(name),
|
|
1918
|
+
resources: await this.fetchResourcesList(name),
|
|
1919
|
+
resourceTemplates: await this.fetchResourceTemplatesList(name)
|
|
1920
|
+
};
|
|
1921
|
+
state.status = "connected";
|
|
1922
|
+
state.lastConnected = new Date;
|
|
1923
|
+
state.reconnectAttempts = 0;
|
|
1924
|
+
state.consecutivePingFailures = 0;
|
|
1925
|
+
this.startPingMonitoring(name);
|
|
1926
|
+
import_core8.logger.info(`Successfully connected to MCP server: ${name}`);
|
|
1927
|
+
} catch (error) {
|
|
1928
|
+
state.status = "disconnected";
|
|
1929
|
+
state.lastError = error instanceof Error ? error : new Error(String(error));
|
|
1930
|
+
this.handleDisconnection(name, error);
|
|
1931
|
+
throw error;
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
setupTransportHandlers(name, connection, state) {
|
|
1935
|
+
connection.transport.onerror = async (error) => {
|
|
1936
|
+
import_core8.logger.error({ error, serverName: name }, `Transport error for "${name}"`);
|
|
1937
|
+
connection.server.status = "disconnected";
|
|
1938
|
+
this.appendErrorMessage(connection, error.message);
|
|
1939
|
+
this.handleDisconnection(name, error);
|
|
1940
|
+
};
|
|
1941
|
+
connection.transport.onclose = async () => {
|
|
1942
|
+
connection.server.status = "disconnected";
|
|
1943
|
+
this.handleDisconnection(name, new Error("Transport closed"));
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1946
|
+
startPingMonitoring(name) {
|
|
1947
|
+
const state = this.connectionStates.get(name);
|
|
1948
|
+
if (!state || !this.pingConfig.enabled)
|
|
1949
|
+
return;
|
|
1950
|
+
if (state.pingInterval)
|
|
1951
|
+
clearInterval(state.pingInterval);
|
|
1952
|
+
state.pingInterval = setInterval(() => {
|
|
1953
|
+
this.sendPing(name).catch((err) => {
|
|
1954
|
+
import_core8.logger.warn({ error: err instanceof Error ? err.message : String(err), serverName: name }, `Ping failed for ${name}`);
|
|
1955
|
+
this.handlePingFailure(name, err);
|
|
1956
|
+
});
|
|
1957
|
+
}, this.pingConfig.intervalMs);
|
|
1958
|
+
}
|
|
1959
|
+
async sendPing(name) {
|
|
1960
|
+
const connection = this.connections.get(name);
|
|
1961
|
+
if (!connection)
|
|
1962
|
+
throw new Error(`No connection for ping: ${name}`);
|
|
1963
|
+
await Promise.race([
|
|
1964
|
+
connection.client.listTools(),
|
|
1965
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Ping timeout")), this.pingConfig.timeoutMs))
|
|
1966
|
+
]);
|
|
1967
|
+
const state = this.connectionStates.get(name);
|
|
1968
|
+
if (state)
|
|
1969
|
+
state.consecutivePingFailures = 0;
|
|
1970
|
+
}
|
|
1971
|
+
handlePingFailure(name, error) {
|
|
1972
|
+
const state = this.connectionStates.get(name);
|
|
1973
|
+
if (!state)
|
|
1974
|
+
return;
|
|
1975
|
+
state.consecutivePingFailures++;
|
|
1976
|
+
if (state.consecutivePingFailures >= this.pingConfig.failuresBeforeDisconnect) {
|
|
1977
|
+
import_core8.logger.warn(`Ping failures exceeded for ${name}, disconnecting and attempting reconnect.`);
|
|
1978
|
+
this.handleDisconnection(name, error);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
handleDisconnection(name, error) {
|
|
1982
|
+
const state = this.connectionStates.get(name);
|
|
1983
|
+
if (!state)
|
|
1984
|
+
return;
|
|
1985
|
+
state.status = "disconnected";
|
|
1986
|
+
state.lastError = error instanceof Error ? error : new Error(String(error));
|
|
1987
|
+
if (state.pingInterval)
|
|
1988
|
+
clearInterval(state.pingInterval);
|
|
1989
|
+
if (state.reconnectTimeout)
|
|
1990
|
+
clearTimeout(state.reconnectTimeout);
|
|
1991
|
+
if (state.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
1992
|
+
import_core8.logger.error(`Max reconnect attempts reached for ${name}. Giving up.`);
|
|
1993
|
+
return;
|
|
1994
|
+
}
|
|
1995
|
+
const delay = INITIAL_RETRY_DELAY * Math.pow(BACKOFF_MULTIPLIER, state.reconnectAttempts);
|
|
1996
|
+
state.reconnectTimeout = setTimeout(async () => {
|
|
1997
|
+
state.reconnectAttempts++;
|
|
1998
|
+
import_core8.logger.info(`Attempting to reconnect to ${name} (attempt ${state.reconnectAttempts})...`);
|
|
1999
|
+
const config = this.connections.get(name)?.server.config;
|
|
2000
|
+
if (config) {
|
|
2001
|
+
try {
|
|
2002
|
+
await this.initializeConnection(name, JSON.parse(config));
|
|
2003
|
+
} catch (err) {
|
|
2004
|
+
import_core8.logger.error({ error: err instanceof Error ? err.message : String(err), serverName: name }, `Reconnect attempt failed for ${name}`);
|
|
2005
|
+
this.handleDisconnection(name, err);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
}, delay);
|
|
2009
|
+
}
|
|
2010
|
+
async deleteConnection(name) {
|
|
2011
|
+
const connection = this.connections.get(name);
|
|
2012
|
+
if (connection) {
|
|
2013
|
+
try {
|
|
2014
|
+
await connection.transport.close();
|
|
2015
|
+
await connection.client.close();
|
|
2016
|
+
} catch (error) {
|
|
2017
|
+
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName: name }, `Failed to close transport for ${name}`);
|
|
2018
|
+
}
|
|
2019
|
+
this.connections.delete(name);
|
|
2020
|
+
}
|
|
2021
|
+
const state = this.connectionStates.get(name);
|
|
2022
|
+
if (state) {
|
|
2023
|
+
if (state.pingInterval)
|
|
2024
|
+
clearInterval(state.pingInterval);
|
|
2025
|
+
if (state.reconnectTimeout)
|
|
2026
|
+
clearTimeout(state.reconnectTimeout);
|
|
2027
|
+
this.connectionStates.delete(name);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
getServerConnection(serverName) {
|
|
2031
|
+
return this.connections.get(serverName);
|
|
2032
|
+
}
|
|
2033
|
+
async buildStdioClientTransport(name, config) {
|
|
2034
|
+
if (!config.command) {
|
|
2035
|
+
throw new Error(`Missing command for stdio MCP server ${name}`);
|
|
2036
|
+
}
|
|
2037
|
+
return new import_stdio.StdioClientTransport({
|
|
2038
|
+
command: config.command,
|
|
2039
|
+
args: config.args,
|
|
2040
|
+
env: {
|
|
2041
|
+
...config.env,
|
|
2042
|
+
...process.env.PATH ? { PATH: process.env.PATH } : {}
|
|
2043
|
+
},
|
|
2044
|
+
stderr: "pipe",
|
|
2045
|
+
cwd: config.cwd
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
async buildHttpClientTransport(name, config) {
|
|
2049
|
+
if (!config.url) {
|
|
2050
|
+
throw new Error(`Missing URL for HTTP MCP server ${name}`);
|
|
2051
|
+
}
|
|
2052
|
+
if (config.type === "sse") {
|
|
2053
|
+
import_core8.logger.warn(`Server "${name}": "sse" transport type is deprecated. Use "streamable-http" or "http" instead for the modern Streamable HTTP transport.`);
|
|
2054
|
+
}
|
|
2055
|
+
return new import_sse.SSEClientTransport(new URL(config.url));
|
|
2056
|
+
}
|
|
2057
|
+
appendErrorMessage(connection, error) {
|
|
2058
|
+
const newError = connection.server.error ? `${connection.server.error}
|
|
2059
|
+
${error}` : error;
|
|
2060
|
+
connection.server.error = newError;
|
|
2061
|
+
}
|
|
2062
|
+
async fetchToolsList(serverName) {
|
|
2063
|
+
try {
|
|
2064
|
+
const connection = this.getServerConnection(serverName);
|
|
2065
|
+
if (!connection) {
|
|
2066
|
+
return [];
|
|
2067
|
+
}
|
|
2068
|
+
const response = await connection.client.listTools();
|
|
2069
|
+
const tools = (response?.tools || []).map((tool) => {
|
|
2070
|
+
let processedTool = { ...tool };
|
|
2071
|
+
if (tool.inputSchema) {
|
|
2072
|
+
try {
|
|
2073
|
+
if (!this.compatibilityInitialized) {
|
|
2074
|
+
this.initializeToolCompatibility();
|
|
2075
|
+
}
|
|
2076
|
+
processedTool.inputSchema = this.applyToolCompatibility(tool.inputSchema);
|
|
2077
|
+
import_core8.logger.debug(`Applied tool compatibility for: ${tool.name} on server: ${serverName}`);
|
|
2078
|
+
} catch (error) {
|
|
2079
|
+
import_core8.logger.warn({ error, toolName: tool.name, serverName }, `Tool compatibility failed for ${tool.name} on ${serverName}`);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
return processedTool;
|
|
2083
|
+
});
|
|
2084
|
+
import_core8.logger.info(`Fetched ${tools.length} tools for ${serverName}`);
|
|
2085
|
+
for (const tool of tools) {
|
|
2086
|
+
import_core8.logger.info(`[${serverName}] ${tool.name}: ${tool.description}`);
|
|
2087
|
+
}
|
|
2088
|
+
return tools;
|
|
2089
|
+
} catch (error) {
|
|
2090
|
+
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName }, `Failed to fetch tools for ${serverName}`);
|
|
2091
|
+
return [];
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
async fetchResourcesList(serverName) {
|
|
2095
|
+
try {
|
|
2096
|
+
const connection = this.getServerConnection(serverName);
|
|
2097
|
+
if (!connection) {
|
|
2098
|
+
return [];
|
|
2099
|
+
}
|
|
2100
|
+
const response = await connection.client.listResources();
|
|
2101
|
+
return response?.resources || [];
|
|
2102
|
+
} catch (error) {
|
|
2103
|
+
import_core8.logger.warn({ error: error instanceof Error ? error.message : String(error), serverName }, `No resources found for ${serverName}`);
|
|
2104
|
+
return [];
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
async fetchResourceTemplatesList(serverName) {
|
|
2108
|
+
try {
|
|
2109
|
+
const connection = this.getServerConnection(serverName);
|
|
2110
|
+
if (!connection) {
|
|
2111
|
+
return [];
|
|
2112
|
+
}
|
|
2113
|
+
const response = await connection.client.listResourceTemplates();
|
|
2114
|
+
return response?.resourceTemplates || [];
|
|
2115
|
+
} catch (error) {
|
|
2116
|
+
import_core8.logger.warn({ error: error instanceof Error ? error.message : String(error), serverName }, `No resource templates found for ${serverName}`);
|
|
2117
|
+
return [];
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
getServers() {
|
|
2121
|
+
return Array.from(this.connections.values()).filter((conn) => !conn.server.disabled).map((conn) => conn.server);
|
|
2122
|
+
}
|
|
2123
|
+
getProviderData() {
|
|
2124
|
+
return this.mcpProvider;
|
|
2125
|
+
}
|
|
2126
|
+
async callTool(serverName, toolName, toolArguments) {
|
|
2127
|
+
const connection = this.connections.get(serverName);
|
|
2128
|
+
if (!connection) {
|
|
2129
|
+
throw new Error(`No connection found for server: ${serverName}`);
|
|
2130
|
+
}
|
|
2131
|
+
if (connection.server.disabled) {
|
|
2132
|
+
throw new Error(`Server "${serverName}" is disabled`);
|
|
2133
|
+
}
|
|
2134
|
+
let timeout = DEFAULT_MCP_TIMEOUT_SECONDS;
|
|
2135
|
+
try {
|
|
2136
|
+
const config = JSON.parse(connection.server.config);
|
|
2137
|
+
timeout = config.timeoutInMillis || DEFAULT_MCP_TIMEOUT_SECONDS;
|
|
2138
|
+
} catch (error) {
|
|
2139
|
+
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName }, `Failed to parse timeout configuration for server ${serverName}`);
|
|
2140
|
+
}
|
|
2141
|
+
const result = await connection.client.callTool({ name: toolName, arguments: toolArguments }, undefined, { timeout });
|
|
2142
|
+
if (!result.content) {
|
|
2143
|
+
throw new Error("Invalid tool result: missing content array");
|
|
2144
|
+
}
|
|
2145
|
+
return result;
|
|
2146
|
+
}
|
|
2147
|
+
async readResource(serverName, uri) {
|
|
2148
|
+
const connection = this.connections.get(serverName);
|
|
2149
|
+
if (!connection) {
|
|
2150
|
+
throw new Error(`No connection found for server: ${serverName}`);
|
|
2151
|
+
}
|
|
2152
|
+
if (connection.server.disabled) {
|
|
2153
|
+
throw new Error(`Server "${serverName}" is disabled`);
|
|
2154
|
+
}
|
|
2155
|
+
return await connection.client.readResource({ uri });
|
|
2156
|
+
}
|
|
2157
|
+
async restartConnection(serverName) {
|
|
2158
|
+
const connection = this.connections.get(serverName);
|
|
2159
|
+
const config = connection?.server.config;
|
|
2160
|
+
if (config) {
|
|
2161
|
+
import_core8.logger.info(`Restarting ${serverName} MCP server...`);
|
|
2162
|
+
connection.server.status = "connecting";
|
|
2163
|
+
connection.server.error = "";
|
|
2164
|
+
try {
|
|
2165
|
+
await this.deleteConnection(serverName);
|
|
2166
|
+
await this.initializeConnection(serverName, JSON.parse(config));
|
|
2167
|
+
import_core8.logger.info(`${serverName} MCP server connected`);
|
|
2168
|
+
} catch (error) {
|
|
2169
|
+
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName }, `Failed to restart connection for ${serverName}`);
|
|
2170
|
+
throw new Error(`Failed to connect to ${serverName} MCP server`);
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
initializeToolCompatibility() {
|
|
2175
|
+
if (this.compatibilityInitialized)
|
|
2176
|
+
return;
|
|
2177
|
+
this.toolCompatibility = createMcpToolCompatibilitySync(this.runtime);
|
|
2178
|
+
this.compatibilityInitialized = true;
|
|
2179
|
+
if (this.toolCompatibility) {
|
|
2180
|
+
import_core8.logger.info(`Tool compatibility enabled`);
|
|
2181
|
+
} else {
|
|
2182
|
+
import_core8.logger.info(`No tool compatibility needed`);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
applyToolCompatibility(toolSchema) {
|
|
2186
|
+
if (!this.compatibilityInitialized) {
|
|
2187
|
+
this.initializeToolCompatibility();
|
|
2188
|
+
}
|
|
2189
|
+
if (!this.toolCompatibility || !toolSchema) {
|
|
2190
|
+
return toolSchema;
|
|
2191
|
+
}
|
|
2192
|
+
try {
|
|
2193
|
+
return this.toolCompatibility.transformToolSchema(toolSchema);
|
|
2194
|
+
} catch (error) {
|
|
2195
|
+
import_core8.logger.warn({ error }, `Tool compatibility transformation failed`);
|
|
2196
|
+
return toolSchema;
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
// src/index.ts
|
|
2202
|
+
var mcpPlugin = {
|
|
2203
|
+
name: "mcp",
|
|
2204
|
+
description: "Plugin for connecting to MCP (Model Context Protocol) servers",
|
|
2205
|
+
init: async (_config, _runtime) => {
|
|
2206
|
+
import_core9.logger.info("Initializing MCP plugin...");
|
|
2207
|
+
},
|
|
2208
|
+
services: [McpService],
|
|
2209
|
+
actions: [callToolAction, readResourceAction],
|
|
2210
|
+
providers: [provider]
|
|
2211
|
+
};
|
|
2212
|
+
var src_default = mcpPlugin;
|
|
2213
|
+
|
|
2214
|
+
//# debugId=8A51CB12924419AD64756E2164756E21
|