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