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