@elizaos/plugin-mcp 1.7.0 → 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 +857 -1099
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js.map +25 -27
- package/dist/index.d.ts +2 -7
- package/dist/node/actions/callToolAction.d.ts +3 -0
- package/dist/node/actions/callToolAction.d.ts.map +1 -0
- package/dist/node/actions/readResourceAction.d.ts +3 -0
- package/dist/node/actions/readResourceAction.d.ts.map +1 -0
- package/dist/node/generated/prompts/typescript/prompts.d.ts +24 -0
- package/dist/node/generated/prompts/typescript/prompts.d.ts.map +1 -0
- package/dist/node/index.d.ts +4 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/{index.js → node/index.js} +874 -1104
- package/dist/node/index.js.map +31 -0
- package/dist/node/provider.d.ts.map +1 -0
- package/dist/{service.d.ts → node/service.d.ts} +6 -8
- package/dist/node/service.d.ts.map +1 -0
- package/dist/node/templates/errorAnalysisPrompt.d.ts +2 -0
- package/dist/node/templates/errorAnalysisPrompt.d.ts.map +1 -0
- package/dist/node/templates/feedbackTemplate.d.ts +8 -0
- package/dist/node/templates/feedbackTemplate.d.ts.map +1 -0
- package/dist/node/templates/resourceAnalysisTemplate.d.ts +8 -0
- package/dist/node/templates/resourceAnalysisTemplate.d.ts.map +1 -0
- package/dist/node/templates/resourceSelectionTemplate.d.ts +8 -0
- package/dist/node/templates/resourceSelectionTemplate.d.ts.map +1 -0
- package/dist/node/templates/toolReasoningTemplate.d.ts +8 -0
- package/dist/node/templates/toolReasoningTemplate.d.ts.map +1 -0
- package/dist/node/templates/toolSelectionTemplate.d.ts +8 -0
- package/dist/node/templates/toolSelectionTemplate.d.ts.map +1 -0
- package/dist/{tool-compatibility/index.d.ts → node/tool-compatibility/base.d.ts} +15 -16
- package/dist/node/tool-compatibility/base.d.ts.map +1 -0
- package/dist/node/tool-compatibility/index.d.ts +7 -0
- package/dist/node/tool-compatibility/index.d.ts.map +1 -0
- package/dist/node/tool-compatibility/integration-test.d.ts +10 -0
- package/dist/node/tool-compatibility/integration-test.d.ts.map +1 -0
- package/dist/node/tool-compatibility/providers/anthropic.d.ts +11 -0
- package/dist/node/tool-compatibility/providers/anthropic.d.ts.map +1 -0
- package/dist/node/tool-compatibility/providers/google.d.ts +11 -0
- package/dist/node/tool-compatibility/providers/google.d.ts.map +1 -0
- package/dist/node/tool-compatibility/providers/openai.d.ts +18 -0
- package/dist/node/tool-compatibility/providers/openai.d.ts.map +1 -0
- package/dist/node/tool-compatibility/test-example.d.ts +4 -0
- package/dist/node/tool-compatibility/test-example.d.ts.map +1 -0
- package/dist/node/types.d.ts +191 -0
- package/dist/node/types.d.ts.map +1 -0
- package/dist/{utils → node/utils}/error.d.ts +4 -4
- package/dist/node/utils/error.d.ts.map +1 -0
- package/dist/node/utils/handler.d.ts +8 -0
- package/dist/node/utils/handler.d.ts.map +1 -0
- package/dist/node/utils/json.d.ts +11 -0
- package/dist/node/utils/json.d.ts.map +1 -0
- package/dist/node/utils/mcp.d.ts +5 -0
- package/dist/node/utils/mcp.d.ts.map +1 -0
- package/dist/node/utils/processing.d.ts +43 -0
- package/dist/node/utils/processing.d.ts.map +1 -0
- package/dist/node/utils/schemas.d.ts +72 -0
- package/dist/node/utils/schemas.d.ts.map +1 -0
- package/dist/node/utils/selection.d.ts +14 -0
- package/dist/node/utils/selection.d.ts.map +1 -0
- package/dist/node/utils/validation.d.ts +17 -0
- package/dist/node/utils/validation.d.ts.map +1 -0
- package/dist/node/utils/wrapper.d.ts +18 -0
- package/dist/node/utils/wrapper.d.ts.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +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,237 +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 rawSettings = runtime2.getSetting("mcp");
|
|
1084
|
-
const settings = rawSettings;
|
|
1085
|
-
if (settings && typeof settings.maxRetries === "number") {
|
|
1086
|
-
const configValue = settings.maxRetries;
|
|
1087
|
-
if (!Number.isNaN(configValue) && configValue >= 0) {
|
|
1088
|
-
logger3.debug(`[WITH-MODEL-RETRY] Using configured selection retries: ${configValue}`);
|
|
1089
|
-
return configValue;
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
} catch (error) {
|
|
1093
|
-
logger3.debug({ error: error instanceof Error ? error.message : String(error) }, "[WITH-MODEL-RETRY] Error reading selection retries config");
|
|
1094
|
-
}
|
|
1095
|
-
return DEFAULT_MAX_RETRIES;
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
// src/templates/toolSelectionTemplate.ts
|
|
1099
|
-
var toolSelectionNameTemplate = `
|
|
1100
|
-
{{mcpProvider.text}}
|
|
1101
|
-
|
|
1102
|
-
{{recentMessages}}
|
|
1103
|
-
|
|
1104
|
-
# TASK: Select the Most Appropriate Tool and Server
|
|
1105
|
-
|
|
1106
|
-
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.
|
|
1107
|
-
|
|
1108
|
-
## CRITICAL INSTRUCTIONS
|
|
1109
|
-
1. Provide both "serverName" and "toolName" from the options listed above.
|
|
1110
|
-
2. Each name must match EXACTLY as shown in the list:
|
|
1111
|
-
- Example (correct): "serverName": "github"
|
|
1112
|
-
- Example (incorrect): "serverName": "GitHub", "Github", or variations
|
|
1113
|
-
3. Extract ACTUAL parameter values from the conversation context.
|
|
1114
|
-
- Do not invent or use placeholders like "octocat" or "Hello-World" unless the user said so.
|
|
1115
|
-
4. Include a "reasoning" field explaining why the selected tool fits the request.
|
|
1116
|
-
5. If no tool is appropriate, respond with:
|
|
1117
|
-
{
|
|
1118
|
-
"noToolAvailable": true
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
!!! YOUR RESPONSE MUST BE A VALID JSON OBJECT ONLY !!!
|
|
1122
|
-
|
|
1123
|
-
CRITICAL: Your response must START with { and END with }. DO NOT include ANY text before or after the JSON.
|
|
1124
|
-
|
|
1125
|
-
## STRICT FORMAT REQUIREMENTS
|
|
1126
|
-
- The response MUST be a single valid JSON object.
|
|
1127
|
-
- DO NOT wrap the JSON in triple backticks (\`\`\`), code blocks, or include any explanatory text.
|
|
1128
|
-
- DO NOT include comments (// or /* */) anywhere.
|
|
1129
|
-
- DO NOT use placeholders (e.g., "replace with...", "example", "your...", etc.)
|
|
1130
|
-
- ALL strings must use double quotes.
|
|
1131
|
-
|
|
1132
|
-
## CRITICAL NOTES
|
|
1133
|
-
- All values must be fully grounded in user input or inferred contextually.
|
|
1134
|
-
- No missing fields unless they are explicitly optional in the schema.
|
|
1135
|
-
- All types must match the schema (strings, numbers, booleans).
|
|
1136
|
-
|
|
1137
|
-
## JSON OBJECT STRUCTURE
|
|
1138
|
-
Your response MUST contain ONLY these top-level keys:
|
|
1139
|
-
1. "serverName" — The name of the server (e.g., "github", "notion")
|
|
1140
|
-
2. "toolName" — The name of the tool (e.g., "get_file_contents", "search")
|
|
1141
|
-
3. "reasoning" — A string explaining how the values were inferred from the conversation.
|
|
1142
|
-
4. "noToolAvailable" — A boolean indicating if no tool is available (true/false)
|
|
1143
|
-
|
|
1144
|
-
## EXAMPLE RESPONSE
|
|
1145
|
-
{
|
|
1146
|
-
"serverName": "github",
|
|
1147
|
-
"toolName": "get_file_contents",
|
|
1148
|
-
"reasoning": "The user wants to retrieve the README from the facebook/react repository.",
|
|
1149
|
-
"noToolAvailable": false
|
|
1059
|
+
return composePromptFromState2({
|
|
1060
|
+
state: enhancedState,
|
|
1061
|
+
template: toolReasoningTemplate
|
|
1062
|
+
});
|
|
1150
1063
|
}
|
|
1151
1064
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
REMEMBER: This output will be parsed directly as JSON. If the format is incorrect, the operation will fail.
|
|
1158
|
-
`;
|
|
1159
|
-
var toolSelectionArgumentTemplate = `
|
|
1160
|
-
{{recentMessages}}
|
|
1161
|
-
|
|
1162
|
-
# TASK: Generate a Strictly Valid JSON Object for Tool Execution
|
|
1163
|
-
|
|
1164
|
-
You have chosen the "{{toolSelectionName.toolName}}" tool from the "{{toolSelectionName.serverName}}" server to address the user's request.
|
|
1165
|
-
The reasoning behind this selection is: "{{toolSelectionName.reasoning}}"
|
|
1166
|
-
|
|
1167
|
-
## CRITICAL INSTRUCTIONS
|
|
1168
|
-
1. Ensure the "toolArguments" object strictly adheres to the structure and requirements defined in the schema.
|
|
1169
|
-
2. All parameter values must be extracted from the conversation context and must be concrete, usable values.
|
|
1170
|
-
3. Avoid placeholders or generic terms unless explicitly provided by the user.
|
|
1171
|
-
|
|
1172
|
-
!!! YOUR RESPONSE MUST BE A VALID JSON OBJECT ONLY !!!
|
|
1173
|
-
|
|
1174
|
-
## STRICT FORMAT REQUIREMENTS
|
|
1175
|
-
- The response MUST be a single valid JSON object.
|
|
1176
|
-
- DO NOT wrap the JSON in triple backticks (\`\`\`), code blocks, or include any explanatory text.
|
|
1177
|
-
- DO NOT include comments (// or /* */) anywhere.
|
|
1178
|
-
- DO NOT use placeholders (e.g., "replace with...", "example", "your...", etc.)
|
|
1179
|
-
- ALL strings must use double quotes
|
|
1180
|
-
|
|
1181
|
-
## CRITICAL NOTES
|
|
1182
|
-
- All values must be fully grounded in user input or inferred contextually.
|
|
1183
|
-
- No missing fields unless they are explicitly optional in the schema.
|
|
1184
|
-
- All types must match the schema (strings, numbers, booleans).
|
|
1185
|
-
|
|
1186
|
-
## JSON OBJECT STRUCTURE
|
|
1187
|
-
Your response MUST contain ONLY these two top-level keys:
|
|
1188
|
-
1. "toolArguments" — An object matching the input schema: {{toolInputSchema}}
|
|
1189
|
-
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";
|
|
1190
1070
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
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 };
|
|
1200
1103
|
}
|
|
1201
|
-
|
|
1202
|
-
REMEMBER: Your response will be parsed directly as JSON. If it fails to parse, the operation will fail completely.
|
|
1203
|
-
`;
|
|
1204
1104
|
|
|
1205
1105
|
// src/utils/schemas.ts
|
|
1206
1106
|
var toolSelectionNameSchema = {
|
|
@@ -1242,7 +1142,7 @@ function validateToolSelectionName(parsed, state) {
|
|
|
1242
1142
|
return { success: false, error: basicResult.error };
|
|
1243
1143
|
}
|
|
1244
1144
|
const data = basicResult.data;
|
|
1245
|
-
const mcpData = state.values.mcp
|
|
1145
|
+
const mcpData = state.values.mcp ?? {};
|
|
1246
1146
|
const server = mcpData[data.serverName];
|
|
1247
1147
|
if (!server || server.status !== "connected") {
|
|
1248
1148
|
return {
|
|
@@ -1277,19 +1177,46 @@ function validateToolSelectionArgument(parsed, toolInputSchema) {
|
|
|
1277
1177
|
function validateResourceSelection(selection) {
|
|
1278
1178
|
return validateJsonSchema(selection, ResourceSelectionSchema);
|
|
1279
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
|
+
}
|
|
1280
1201
|
function createResourceSelectionFeedbackPrompt(originalResponse, errorMessage, composedState, userMessage) {
|
|
1281
1202
|
let resourcesDescription = "";
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
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})
|
|
1287
1212
|
`;
|
|
1288
|
-
|
|
1213
|
+
resourcesDescription += `Name: ${resource.name ?? "No name available"}
|
|
1289
1214
|
`;
|
|
1290
|
-
|
|
1215
|
+
resourcesDescription += `Description: ${resource.description ?? "No description available"}
|
|
1291
1216
|
|
|
1292
1217
|
`;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1293
1220
|
}
|
|
1294
1221
|
}
|
|
1295
1222
|
return createFeedbackPrompt(originalResponse, errorMessage, "resource", resourcesDescription, userMessage);
|
|
@@ -1307,9 +1234,67 @@ ${itemsDescription}
|
|
|
1307
1234
|
User request: ${userMessage}`;
|
|
1308
1235
|
}
|
|
1309
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
|
+
|
|
1310
1295
|
// src/utils/selection.ts
|
|
1311
1296
|
async function createToolSelectionName({
|
|
1312
|
-
runtime
|
|
1297
|
+
runtime,
|
|
1313
1298
|
state,
|
|
1314
1299
|
message,
|
|
1315
1300
|
callback,
|
|
@@ -1319,26 +1304,22 @@ async function createToolSelectionName({
|
|
|
1319
1304
|
state: { ...state, values: { ...state.values, mcpProvider } },
|
|
1320
1305
|
template: toolSelectionNameTemplate
|
|
1321
1306
|
});
|
|
1322
|
-
|
|
1323
|
-
${toolSelectionPrompt}`);
|
|
1324
|
-
const toolSelectionName = await runtime2.useModel(ModelType4.TEXT_LARGE, {
|
|
1307
|
+
const toolSelectionName = await runtime.useModel(ModelType4.TEXT_LARGE, {
|
|
1325
1308
|
prompt: toolSelectionPrompt
|
|
1326
1309
|
});
|
|
1327
|
-
logger4.debug(`[SELECTION] Tool Selection Name Response:
|
|
1328
|
-
${toolSelectionName}`);
|
|
1329
1310
|
return await withModelRetry({
|
|
1330
|
-
runtime
|
|
1311
|
+
runtime,
|
|
1331
1312
|
message,
|
|
1332
1313
|
state,
|
|
1333
1314
|
callback,
|
|
1334
1315
|
input: toolSelectionName,
|
|
1335
1316
|
validationFn: (parsed) => validateToolSelectionName(parsed, state),
|
|
1336
|
-
createFeedbackPromptFn: (originalResponse, errorMessage,
|
|
1317
|
+
createFeedbackPromptFn: (originalResponse, errorMessage, composedState, userMessage) => createToolSelectionFeedbackPrompt(typeof originalResponse === "string" ? originalResponse : JSON.stringify(originalResponse), errorMessage, composedState, userMessage),
|
|
1337
1318
|
failureMsg: "I'm having trouble figuring out the best way to help with your request."
|
|
1338
1319
|
});
|
|
1339
1320
|
}
|
|
1340
1321
|
async function createToolSelectionArgument({
|
|
1341
|
-
runtime
|
|
1322
|
+
runtime,
|
|
1342
1323
|
state,
|
|
1343
1324
|
message,
|
|
1344
1325
|
callback,
|
|
@@ -1346,13 +1327,18 @@ async function createToolSelectionArgument({
|
|
|
1346
1327
|
toolSelectionName
|
|
1347
1328
|
}) {
|
|
1348
1329
|
if (!toolSelectionName) {
|
|
1349
|
-
|
|
1350
|
-
return null;
|
|
1330
|
+
throw new Error("Tool selection name is required to create tool selection argument");
|
|
1351
1331
|
}
|
|
1352
1332
|
const { serverName, toolName } = toolSelectionName;
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1355
|
-
${
|
|
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 ?? {};
|
|
1356
1342
|
const toolSelectionArgumentPrompt = composePromptFromState3({
|
|
1357
1343
|
state: {
|
|
1358
1344
|
...state,
|
|
@@ -1364,81 +1350,20 @@ ${JSON.stringify({ toolInputSchema }, null, 2)}`);
|
|
|
1364
1350
|
},
|
|
1365
1351
|
template: toolSelectionArgumentTemplate
|
|
1366
1352
|
});
|
|
1367
|
-
|
|
1368
|
-
${toolSelectionArgumentPrompt}`);
|
|
1369
|
-
const toolSelectionArgument = await runtime2.useModel(ModelType4.TEXT_LARGE, {
|
|
1353
|
+
const toolSelectionArgument = await runtime.useModel(ModelType4.TEXT_LARGE, {
|
|
1370
1354
|
prompt: toolSelectionArgumentPrompt
|
|
1371
1355
|
});
|
|
1372
|
-
logger4.debug(`[SELECTION] Tool Selection Argument Response:
|
|
1373
|
-
${toolSelectionArgument}`);
|
|
1374
1356
|
return await withModelRetry({
|
|
1375
|
-
runtime
|
|
1357
|
+
runtime,
|
|
1376
1358
|
message,
|
|
1377
1359
|
state,
|
|
1378
1360
|
callback,
|
|
1379
1361
|
input: toolSelectionArgument,
|
|
1380
|
-
validationFn: (parsed) => validateToolSelectionArgument(parsed,
|
|
1381
|
-
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),
|
|
1382
1364
|
failureMsg: "I'm having trouble figuring out the best way to help with your request."
|
|
1383
1365
|
});
|
|
1384
1366
|
}
|
|
1385
|
-
function createToolSelectionFeedbackPrompt(originalResponse, errorMessage, state, userMessage) {
|
|
1386
|
-
let toolsDescription = "";
|
|
1387
|
-
for (const [serverName, server] of Object.entries(state.values.mcp || {})) {
|
|
1388
|
-
if (server.status !== "connected")
|
|
1389
|
-
continue;
|
|
1390
|
-
for (const [toolName, tool] of Object.entries(server.tools || {})) {
|
|
1391
|
-
toolsDescription += `Tool: ${toolName} (Server: ${serverName})
|
|
1392
|
-
`;
|
|
1393
|
-
toolsDescription += `Description: ${tool.description || "No description available"}
|
|
1394
|
-
|
|
1395
|
-
`;
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
const feedbackPrompt = createFeedbackPrompt2(originalResponse, errorMessage, "tool", toolsDescription, userMessage);
|
|
1399
|
-
logger4.debug(`[SELECTION] Tool Selection Feedback Prompt:
|
|
1400
|
-
${feedbackPrompt}`);
|
|
1401
|
-
return feedbackPrompt;
|
|
1402
|
-
}
|
|
1403
|
-
function createFeedbackPrompt2(originalResponse, errorMessage, itemType, itemsDescription, userMessage) {
|
|
1404
|
-
return `Error parsing JSON: ${errorMessage}
|
|
1405
|
-
|
|
1406
|
-
Your original response:
|
|
1407
|
-
${originalResponse}
|
|
1408
|
-
|
|
1409
|
-
Please try again with valid JSON for ${itemType} selection.
|
|
1410
|
-
Available ${itemType}s:
|
|
1411
|
-
${itemsDescription}
|
|
1412
|
-
|
|
1413
|
-
User request: ${userMessage}`;
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
// src/utils/handler.ts
|
|
1417
|
-
async function handleNoToolAvailable(callback, toolSelection) {
|
|
1418
|
-
const responseText = "I don't have a specific tool that can help with that request. Let me try to assist you directly instead.";
|
|
1419
|
-
const thoughtText = "No appropriate MCP tool available for this request. Falling back to direct assistance.";
|
|
1420
|
-
if (callback && toolSelection?.noToolAvailable) {
|
|
1421
|
-
await callback({
|
|
1422
|
-
text: responseText,
|
|
1423
|
-
thought: thoughtText,
|
|
1424
|
-
actions: ["REPLY"]
|
|
1425
|
-
});
|
|
1426
|
-
}
|
|
1427
|
-
return {
|
|
1428
|
-
text: responseText,
|
|
1429
|
-
values: {
|
|
1430
|
-
success: true,
|
|
1431
|
-
noToolAvailable: true,
|
|
1432
|
-
fallbackToDirectAssistance: true
|
|
1433
|
-
},
|
|
1434
|
-
data: {
|
|
1435
|
-
actionName: "CALL_MCP_TOOL",
|
|
1436
|
-
noToolAvailable: true,
|
|
1437
|
-
reason: toolSelection?.reasoning || "No appropriate tool available"
|
|
1438
|
-
},
|
|
1439
|
-
success: true
|
|
1440
|
-
};
|
|
1441
|
-
}
|
|
1442
1367
|
|
|
1443
1368
|
// src/actions/callToolAction.ts
|
|
1444
1369
|
var callToolAction = {
|
|
@@ -1456,36 +1381,34 @@ var callToolAction = {
|
|
|
1456
1381
|
"INVOKE_MCP_TOOL"
|
|
1457
1382
|
],
|
|
1458
1383
|
description: "Calls a tool from an MCP server to perform a specific task",
|
|
1459
|
-
validate: async (
|
|
1460
|
-
const mcpService =
|
|
1384
|
+
validate: async (runtime, _message, _state) => {
|
|
1385
|
+
const mcpService = runtime.getService(MCP_SERVICE_NAME);
|
|
1461
1386
|
if (!mcpService)
|
|
1462
1387
|
return false;
|
|
1463
1388
|
const servers = mcpService.getServers();
|
|
1464
1389
|
return servers.length > 0 && servers.some((server) => server.status === "connected" && server.tools && server.tools.length > 0);
|
|
1465
1390
|
},
|
|
1466
|
-
handler: async (
|
|
1467
|
-
const composedState = await
|
|
1468
|
-
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);
|
|
1469
1394
|
if (!mcpService) {
|
|
1470
1395
|
throw new Error("MCP service not available");
|
|
1471
1396
|
}
|
|
1472
1397
|
const mcpProvider = mcpService.getProviderData();
|
|
1473
1398
|
try {
|
|
1474
1399
|
const toolSelectionName = await createToolSelectionName({
|
|
1475
|
-
runtime
|
|
1400
|
+
runtime,
|
|
1476
1401
|
state: composedState,
|
|
1477
1402
|
message,
|
|
1478
1403
|
callback,
|
|
1479
1404
|
mcpProvider
|
|
1480
1405
|
});
|
|
1481
1406
|
if (!toolSelectionName || toolSelectionName.noToolAvailable) {
|
|
1482
|
-
logger5.warn("[NO_TOOL_AVAILABLE] No appropriate tool available for the request");
|
|
1483
1407
|
return await handleNoToolAvailable(callback, toolSelectionName);
|
|
1484
1408
|
}
|
|
1485
|
-
const { serverName, toolName
|
|
1486
|
-
logger5.info(`[CALLING] Calling tool "${serverName}/${toolName}" on server with reasoning: "${reasoning}"`);
|
|
1409
|
+
const { serverName, toolName } = toolSelectionName;
|
|
1487
1410
|
const toolSelectionArgument = await createToolSelectionArgument({
|
|
1488
|
-
runtime
|
|
1411
|
+
runtime,
|
|
1489
1412
|
state: composedState,
|
|
1490
1413
|
message,
|
|
1491
1414
|
callback,
|
|
@@ -1493,14 +1416,11 @@ var callToolAction = {
|
|
|
1493
1416
|
toolSelectionName
|
|
1494
1417
|
});
|
|
1495
1418
|
if (!toolSelectionArgument) {
|
|
1496
|
-
logger5.warn("[NO_TOOL_SELECTION_ARGUMENT] No appropriate tool selection argument available");
|
|
1497
1419
|
return await handleNoToolAvailable(callback, toolSelectionName);
|
|
1498
1420
|
}
|
|
1499
|
-
logger5.info(`[SELECTED] Tool Selection result:
|
|
1500
|
-
${JSON.stringify(toolSelectionArgument, null, 2)}`);
|
|
1501
1421
|
const result = await mcpService.callTool(serverName, toolName, toolSelectionArgument.toolArguments);
|
|
1502
|
-
const { toolOutput, hasAttachments, attachments } = processToolResult(result, serverName, toolName,
|
|
1503
|
-
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);
|
|
1504
1424
|
return {
|
|
1505
1425
|
text: `Successfully called tool: ${serverName}/${toolName}. Reasoned response: ${replyMemory.content.text}`,
|
|
1506
1426
|
values: {
|
|
@@ -1515,15 +1435,15 @@ ${JSON.stringify(toolSelectionArgument, null, 2)}`);
|
|
|
1515
1435
|
actionName: "CALL_MCP_TOOL",
|
|
1516
1436
|
serverName,
|
|
1517
1437
|
toolName,
|
|
1518
|
-
|
|
1438
|
+
toolArgumentsJson: JSON.stringify(toolSelectionArgument.toolArguments),
|
|
1519
1439
|
reasoning: toolSelectionName.reasoning,
|
|
1520
1440
|
output: toolOutput,
|
|
1521
|
-
|
|
1441
|
+
attachmentCount: attachments?.length ?? 0
|
|
1522
1442
|
},
|
|
1523
1443
|
success: true
|
|
1524
1444
|
};
|
|
1525
1445
|
} catch (error) {
|
|
1526
|
-
return await handleMcpError(composedState, mcpProvider, error,
|
|
1446
|
+
return await handleMcpError(composedState, mcpProvider, error, runtime, message, "tool", callback);
|
|
1527
1447
|
}
|
|
1528
1448
|
},
|
|
1529
1449
|
examples: [
|
|
@@ -1556,72 +1476,27 @@ Climate change refers to long-term shifts in temperatures and weather patterns.
|
|
|
1556
1476
|
|
|
1557
1477
|
// src/actions/readResourceAction.ts
|
|
1558
1478
|
import {
|
|
1559
|
-
ModelType as ModelType5,
|
|
1560
1479
|
composePromptFromState as composePromptFromState4,
|
|
1561
|
-
|
|
1480
|
+
ModelType as ModelType5
|
|
1562
1481
|
} from "@elizaos/core";
|
|
1563
|
-
|
|
1564
|
-
// src/templates/resourceSelectionTemplate.ts
|
|
1565
|
-
var resourceSelectionTemplate = `
|
|
1566
|
-
{{{mcpProvider.text}}}
|
|
1567
|
-
|
|
1568
|
-
{{{recentMessages}}}
|
|
1569
|
-
|
|
1570
|
-
# Prompt
|
|
1571
|
-
|
|
1572
|
-
You are an intelligent assistant helping select the right resource to address a user's request.
|
|
1573
|
-
|
|
1574
|
-
CRITICAL INSTRUCTIONS:
|
|
1575
|
-
1. You MUST specify both a valid serverName AND uri from the list above
|
|
1576
|
-
2. The serverName value should match EXACTLY the server name shown in parentheses (Server: X)
|
|
1577
|
-
CORRECT: "serverName": "github" (if the server is called "github")
|
|
1578
|
-
WRONG: "serverName": "GitHub" or "Github" or any other variation
|
|
1579
|
-
3. The uri value should match EXACTLY the resource uri listed
|
|
1580
|
-
CORRECT: "uri": "weather://San Francisco/current" (if that's the exact uri)
|
|
1581
|
-
WRONG: "uri": "weather://sanfrancisco/current" or any variation
|
|
1582
|
-
4. Identify the user's information need from the conversation context
|
|
1583
|
-
5. Select the most appropriate resource based on its description and the request
|
|
1584
|
-
6. If no resource seems appropriate, output {"noResourceAvailable": true}
|
|
1585
|
-
|
|
1586
|
-
!!! YOUR RESPONSE MUST BE A VALID JSON OBJECT ONLY !!!
|
|
1587
|
-
|
|
1588
|
-
STRICT FORMAT REQUIREMENTS:
|
|
1589
|
-
- NO code block formatting (NO backticks or \`\`\`)
|
|
1590
|
-
- NO comments (NO // or /* */)
|
|
1591
|
-
- NO placeholders like "replace with...", "example", "your...", "actual", etc.
|
|
1592
|
-
- Every parameter value must be a concrete, usable value (not instructions to replace)
|
|
1593
|
-
- Use proper JSON syntax with double quotes for strings
|
|
1594
|
-
- NO explanatory text before or after the JSON object
|
|
1595
|
-
|
|
1596
|
-
EXAMPLE RESPONSE:
|
|
1597
|
-
{
|
|
1598
|
-
"serverName": "weather-server",
|
|
1599
|
-
"uri": "weather://San Francisco/current",
|
|
1600
|
-
"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."
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
|
-
REMEMBER: Your response will be parsed directly as JSON. If it fails to parse, the operation will fail completely!
|
|
1604
|
-
`;
|
|
1605
|
-
|
|
1606
|
-
// src/actions/readResourceAction.ts
|
|
1607
1482
|
function createResourceSelectionPrompt(composedState, userMessage) {
|
|
1608
|
-
const mcpData = composedState.values.mcp
|
|
1483
|
+
const mcpData = composedState.values.mcp ?? {};
|
|
1609
1484
|
const serverNames = Object.keys(mcpData);
|
|
1610
1485
|
let resourcesDescription = "";
|
|
1611
1486
|
for (const serverName of serverNames) {
|
|
1612
1487
|
const server = mcpData[serverName];
|
|
1613
1488
|
if (server.status !== "connected")
|
|
1614
1489
|
continue;
|
|
1615
|
-
const resourceUris = Object.keys(server.resources
|
|
1490
|
+
const resourceUris = Object.keys(server.resources ?? {});
|
|
1616
1491
|
for (const uri of resourceUris) {
|
|
1617
1492
|
const resource = server.resources[uri];
|
|
1618
1493
|
resourcesDescription += `Resource: ${uri} (Server: ${serverName})
|
|
1619
1494
|
`;
|
|
1620
|
-
resourcesDescription += `Name: ${resource.name
|
|
1495
|
+
resourcesDescription += `Name: ${resource.name ?? "No name available"}
|
|
1621
1496
|
`;
|
|
1622
|
-
resourcesDescription += `Description: ${resource.description
|
|
1497
|
+
resourcesDescription += `Description: ${resource.description ?? "No description available"}
|
|
1623
1498
|
`;
|
|
1624
|
-
resourcesDescription += `MIME Type: ${resource.mimeType
|
|
1499
|
+
resourcesDescription += `MIME Type: ${resource.mimeType ?? "Not specified"}
|
|
1625
1500
|
|
|
1626
1501
|
`;
|
|
1627
1502
|
}
|
|
@@ -1652,44 +1527,42 @@ var readResourceAction = {
|
|
|
1652
1527
|
"ACCESS_MCP_RESOURCE"
|
|
1653
1528
|
],
|
|
1654
1529
|
description: "Reads a resource from an MCP server",
|
|
1655
|
-
validate: async (
|
|
1656
|
-
const mcpService =
|
|
1530
|
+
validate: async (runtime, _message, _state) => {
|
|
1531
|
+
const mcpService = runtime.getService(MCP_SERVICE_NAME);
|
|
1657
1532
|
if (!mcpService)
|
|
1658
1533
|
return false;
|
|
1659
1534
|
const servers = mcpService.getServers();
|
|
1660
1535
|
return servers.length > 0 && servers.some((server) => server.status === "connected" && server.resources && server.resources.length > 0);
|
|
1661
1536
|
},
|
|
1662
|
-
handler: async (
|
|
1663
|
-
const composedState = await
|
|
1664
|
-
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);
|
|
1665
1540
|
if (!mcpService) {
|
|
1666
1541
|
throw new Error("MCP service not available");
|
|
1667
1542
|
}
|
|
1668
1543
|
const mcpProvider = mcpService.getProviderData();
|
|
1669
1544
|
try {
|
|
1670
1545
|
await sendInitialResponse(callback);
|
|
1671
|
-
const resourceSelectionPrompt = createResourceSelectionPrompt(composedState, message.content.text
|
|
1672
|
-
const resourceSelection = await
|
|
1546
|
+
const resourceSelectionPrompt = createResourceSelectionPrompt(composedState, message.content.text ?? "");
|
|
1547
|
+
const resourceSelection = await runtime.useModel(ModelType5.TEXT_SMALL, {
|
|
1673
1548
|
prompt: resourceSelectionPrompt
|
|
1674
1549
|
});
|
|
1675
1550
|
const parsedSelection = await withModelRetry({
|
|
1676
|
-
runtime
|
|
1551
|
+
runtime,
|
|
1677
1552
|
state: composedState,
|
|
1678
1553
|
message,
|
|
1679
1554
|
callback,
|
|
1680
1555
|
input: resourceSelection,
|
|
1681
1556
|
validationFn: (data) => validateResourceSelection(data),
|
|
1682
|
-
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),
|
|
1683
1558
|
failureMsg: `I'm having trouble finding the resource you're looking for. Could you provide more details about what you need?`,
|
|
1684
1559
|
retryCount: 0
|
|
1685
1560
|
});
|
|
1686
1561
|
if (!parsedSelection || parsedSelection.noResourceAvailable) {
|
|
1687
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.";
|
|
1688
|
-
const thoughtText = "No appropriate MCP resource available for this request. Falling back to direct assistance.";
|
|
1689
1563
|
if (callback && parsedSelection?.noResourceAvailable) {
|
|
1690
1564
|
await callback({
|
|
1691
1565
|
text: responseText,
|
|
1692
|
-
thought: thoughtText,
|
|
1693
1566
|
actions: ["REPLY"]
|
|
1694
1567
|
});
|
|
1695
1568
|
}
|
|
@@ -1703,17 +1576,15 @@ var readResourceAction = {
|
|
|
1703
1576
|
data: {
|
|
1704
1577
|
actionName: "READ_MCP_RESOURCE",
|
|
1705
1578
|
noResourceAvailable: true,
|
|
1706
|
-
reason: parsedSelection?.reasoning
|
|
1579
|
+
reason: parsedSelection?.reasoning ?? "No appropriate resource available"
|
|
1707
1580
|
},
|
|
1708
1581
|
success: true
|
|
1709
1582
|
};
|
|
1710
1583
|
}
|
|
1711
|
-
const { serverName, uri
|
|
1712
|
-
logger6.debug(`Selected resource "${uri}" on server "${serverName}" because: ${reasoning}`);
|
|
1584
|
+
const { serverName, uri } = parsedSelection;
|
|
1713
1585
|
const result = await mcpService.readResource(serverName, uri);
|
|
1714
|
-
logger6.debug(`Read resource ${uri} from server ${serverName}`);
|
|
1715
1586
|
const { resourceContent, resourceMeta } = processResourceResult(result, uri);
|
|
1716
|
-
await handleResourceAnalysis(
|
|
1587
|
+
await handleResourceAnalysis(runtime, message, uri, serverName, resourceContent, resourceMeta, callback);
|
|
1717
1588
|
return {
|
|
1718
1589
|
text: `Successfully read resource: ${uri}`,
|
|
1719
1590
|
values: {
|
|
@@ -1726,14 +1597,14 @@ var readResourceAction = {
|
|
|
1726
1597
|
actionName: "READ_MCP_RESOURCE",
|
|
1727
1598
|
serverName,
|
|
1728
1599
|
uri,
|
|
1729
|
-
reasoning,
|
|
1600
|
+
reasoning: parsedSelection?.reasoning,
|
|
1730
1601
|
resourceMeta,
|
|
1731
|
-
contentLength: resourceContent?.length
|
|
1602
|
+
contentLength: resourceContent?.length ?? 0
|
|
1732
1603
|
},
|
|
1733
1604
|
success: true
|
|
1734
1605
|
};
|
|
1735
1606
|
} catch (error) {
|
|
1736
|
-
return await handleMcpError(composedState, mcpProvider, error,
|
|
1607
|
+
return await handleMcpError(composedState, mcpProvider, error, runtime, message, "resource", callback);
|
|
1737
1608
|
}
|
|
1738
1609
|
},
|
|
1739
1610
|
examples: [
|
|
@@ -1741,7 +1612,7 @@ var readResourceAction = {
|
|
|
1741
1612
|
{
|
|
1742
1613
|
name: "{{user}}",
|
|
1743
1614
|
content: {
|
|
1744
|
-
text: "Can you get the documentation about installing
|
|
1615
|
+
text: "Can you get the documentation about installing elizaOS?"
|
|
1745
1616
|
}
|
|
1746
1617
|
},
|
|
1747
1618
|
{
|
|
@@ -1754,7 +1625,7 @@ var readResourceAction = {
|
|
|
1754
1625
|
{
|
|
1755
1626
|
name: "{{assistant}}",
|
|
1756
1627
|
content: {
|
|
1757
|
-
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.`,
|
|
1758
1629
|
actions: ["READ_MCP_RESOURCE"]
|
|
1759
1630
|
}
|
|
1760
1631
|
}
|
|
@@ -1766,24 +1637,51 @@ var readResourceAction = {
|
|
|
1766
1637
|
var provider = {
|
|
1767
1638
|
name: "MCP",
|
|
1768
1639
|
description: "Information about connected MCP servers, tools, and resources",
|
|
1769
|
-
get: async (
|
|
1770
|
-
const mcpService =
|
|
1640
|
+
get: async (runtime, _message, _state) => {
|
|
1641
|
+
const mcpService = runtime.getService(MCP_SERVICE_NAME);
|
|
1771
1642
|
if (!mcpService) {
|
|
1772
1643
|
return {
|
|
1773
|
-
values: {
|
|
1774
|
-
data: {
|
|
1644
|
+
values: {},
|
|
1645
|
+
data: {},
|
|
1775
1646
|
text: "No MCP servers are available."
|
|
1776
1647
|
};
|
|
1777
1648
|
}
|
|
1778
|
-
|
|
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
|
+
};
|
|
1779
1655
|
}
|
|
1780
1656
|
};
|
|
1781
1657
|
|
|
1782
1658
|
// src/service.ts
|
|
1783
|
-
import {
|
|
1659
|
+
import { logger as logger2, Service } from "@elizaos/core";
|
|
1784
1660
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
1785
1661
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
1786
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
|
|
1787
1685
|
class McpService extends Service {
|
|
1788
1686
|
static serviceType = MCP_SERVICE_NAME;
|
|
1789
1687
|
capabilityDescription = "Enables the agent to interact with MCP (Model Context Protocol) servers";
|
|
@@ -1798,13 +1696,12 @@ class McpService extends Service {
|
|
|
1798
1696
|
toolCompatibility = null;
|
|
1799
1697
|
compatibilityInitialized = false;
|
|
1800
1698
|
initializationPromise = null;
|
|
1801
|
-
constructor(
|
|
1802
|
-
super(
|
|
1803
|
-
logger7.info("[McpService] Constructor called, starting initialization...");
|
|
1699
|
+
constructor(runtime) {
|
|
1700
|
+
super(runtime);
|
|
1804
1701
|
this.initializationPromise = this.initializeMcpServers();
|
|
1805
1702
|
}
|
|
1806
|
-
static async start(
|
|
1807
|
-
const service = new McpService(
|
|
1703
|
+
static async start(runtime) {
|
|
1704
|
+
const service = new McpService(runtime);
|
|
1808
1705
|
if (service.initializationPromise) {
|
|
1809
1706
|
await service.initializationPromise;
|
|
1810
1707
|
}
|
|
@@ -1829,71 +1726,36 @@ class McpService extends Service {
|
|
|
1829
1726
|
this.connectionStates.clear();
|
|
1830
1727
|
}
|
|
1831
1728
|
async initializeMcpServers() {
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
const mcpSettings = this.getMcpSettings();
|
|
1835
|
-
const serverCount = mcpSettings?.servers ? Object.keys(mcpSettings.servers).length : 0;
|
|
1836
|
-
const serverNames = mcpSettings?.servers ? Object.keys(mcpSettings.servers) : [];
|
|
1837
|
-
logger7.info(`[McpService] Getting MCP settings... hasSettings=${!!mcpSettings} hasServers=${!!mcpSettings?.servers} serverCount=${serverCount} servers=${JSON.stringify(serverNames)}`);
|
|
1838
|
-
if (!mcpSettings || !mcpSettings.servers) {
|
|
1839
|
-
logger7.info("[McpService] No MCP servers configured.");
|
|
1840
|
-
this.mcpProvider = buildMcpProviderData([]);
|
|
1841
|
-
return;
|
|
1842
|
-
}
|
|
1843
|
-
if (Object.keys(mcpSettings.servers).length === 0) {
|
|
1844
|
-
logger7.info("[McpService] MCP settings exist but no servers configured.");
|
|
1845
|
-
this.mcpProvider = buildMcpProviderData([]);
|
|
1846
|
-
return;
|
|
1847
|
-
}
|
|
1848
|
-
logger7.info(`[McpService] Connecting to ${Object.keys(mcpSettings.servers).length} MCP servers: ${JSON.stringify(Object.keys(mcpSettings.servers))}`);
|
|
1849
|
-
const connectionStartTime = Date.now();
|
|
1850
|
-
await this.updateServerConnections(mcpSettings.servers);
|
|
1851
|
-
const connectionDuration = Date.now() - connectionStartTime;
|
|
1852
|
-
const servers = this.getServers();
|
|
1853
|
-
const connectedServers = servers.filter((s) => s.status === "connected");
|
|
1854
|
-
const failedServers = servers.filter((s) => s.status !== "connected");
|
|
1855
|
-
if (connectedServers.length > 0) {
|
|
1856
|
-
const toolCounts = connectedServers.map((s) => `${s.name}:${s.tools?.length || 0}tools`).join(", ");
|
|
1857
|
-
logger7.info(`[McpService] ✓ Successfully connected ${connectedServers.length}/${servers.length} servers in ${connectionDuration}ms: ${toolCounts}`);
|
|
1858
|
-
}
|
|
1859
|
-
if (failedServers.length > 0) {
|
|
1860
|
-
const failedDetails = failedServers.map((s) => `${s.name}(${s.error || "unknown error"})`).join(", ");
|
|
1861
|
-
logger7.warn(`[McpService] ⚠️ Failed to connect to ${failedServers.length}/${servers.length} servers: ${failedDetails}`);
|
|
1862
|
-
}
|
|
1863
|
-
if (connectedServers.length === 0 && servers.length > 0) {
|
|
1864
|
-
logger7.error(`[McpService] ❌ ALL MCP servers failed to connect! MCP tools will NOT be available.`);
|
|
1865
|
-
}
|
|
1866
|
-
this.mcpProvider = buildMcpProviderData(servers);
|
|
1867
|
-
const mcpDataKeys = Object.keys(this.mcpProvider.data?.mcp || {});
|
|
1868
|
-
logger7.info(`[McpService] MCP provider data built: ${mcpDataKeys.length} server(s) available`);
|
|
1869
|
-
} catch (error) {
|
|
1870
|
-
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) {
|
|
1871
1731
|
this.mcpProvider = buildMcpProviderData([]);
|
|
1732
|
+
return;
|
|
1872
1733
|
}
|
|
1734
|
+
await this.updateServerConnections(mcpSettings.servers);
|
|
1735
|
+
const servers = this.getServers();
|
|
1736
|
+
this.mcpProvider = buildMcpProviderData(servers);
|
|
1873
1737
|
}
|
|
1874
1738
|
getMcpSettings() {
|
|
1875
1739
|
const rawSettings = this.runtime.getSetting("mcp");
|
|
1876
|
-
let settings =
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
logger7.info("[McpService] Found MCP settings in character.settings.mcp (fallback)");
|
|
1882
|
-
settings = characterSettings.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;
|
|
1883
1745
|
}
|
|
1884
1746
|
}
|
|
1885
1747
|
if (!settings || !settings.servers) {
|
|
1886
|
-
const
|
|
1887
|
-
if (
|
|
1888
|
-
|
|
1889
|
-
|
|
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
|
+
}
|
|
1890
1754
|
}
|
|
1891
1755
|
}
|
|
1892
1756
|
if (settings && typeof settings === "object" && settings.servers) {
|
|
1893
|
-
logger7.info(`[McpService] MCP settings found with ${Object.keys(settings.servers).length} server(s)`);
|
|
1894
1757
|
return settings;
|
|
1895
1758
|
}
|
|
1896
|
-
logger7.info("[McpService] No valid MCP settings found");
|
|
1897
1759
|
return;
|
|
1898
1760
|
}
|
|
1899
1761
|
async updateServerConnections(serverConfigs) {
|
|
@@ -1902,30 +1764,18 @@ class McpService extends Service {
|
|
|
1902
1764
|
for (const name of currentNames) {
|
|
1903
1765
|
if (!newNames.has(name)) {
|
|
1904
1766
|
await this.deleteConnection(name);
|
|
1905
|
-
logger7.info(`Deleted MCP server: ${name}`);
|
|
1906
1767
|
}
|
|
1907
1768
|
}
|
|
1908
1769
|
const connectionPromises = Object.entries(serverConfigs).map(async ([name, config]) => {
|
|
1909
1770
|
const currentConnection = this.connections.get(name);
|
|
1910
1771
|
if (!currentConnection) {
|
|
1911
|
-
|
|
1912
|
-
await this.initializeConnection(name, config);
|
|
1913
|
-
logger7.info(`✓ Connected to MCP server: ${name}`);
|
|
1914
|
-
} catch (error) {
|
|
1915
|
-
logger7.error({ error: error instanceof Error ? error.message : String(error), serverName: name }, `✗ Failed to connect to new MCP server ${name}`);
|
|
1916
|
-
}
|
|
1772
|
+
await this.initializeConnection(name, config);
|
|
1917
1773
|
} else if (JSON.stringify(config) !== currentConnection.server.config) {
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
await this.initializeConnection(name, config);
|
|
1921
|
-
logger7.info(`✓ Reconnected MCP server with updated config: ${name}`);
|
|
1922
|
-
} catch (error) {
|
|
1923
|
-
logger7.error({ error: error instanceof Error ? error.message : String(error), serverName: name }, `✗ Failed to reconnect MCP server ${name}`);
|
|
1924
|
-
}
|
|
1774
|
+
await this.deleteConnection(name);
|
|
1775
|
+
await this.initializeConnection(name, config);
|
|
1925
1776
|
}
|
|
1926
1777
|
});
|
|
1927
1778
|
await Promise.allSettled(connectionPromises);
|
|
1928
|
-
logger7.info(`[McpService] All server connection attempts completed`);
|
|
1929
1779
|
}
|
|
1930
1780
|
async initializeConnection(name, config) {
|
|
1931
1781
|
await this.deleteConnection(name);
|
|
@@ -1935,58 +1785,47 @@ class McpService extends Service {
|
|
|
1935
1785
|
consecutivePingFailures: 0
|
|
1936
1786
|
};
|
|
1937
1787
|
this.connectionStates.set(name, state);
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
server: {
|
|
1943
|
-
name,
|
|
1944
|
-
config: JSON.stringify(config),
|
|
1945
|
-
status: "connecting"
|
|
1946
|
-
},
|
|
1947
|
-
client,
|
|
1948
|
-
transport
|
|
1949
|
-
};
|
|
1950
|
-
this.connections.set(name, connection);
|
|
1951
|
-
this.setupTransportHandlers(name, connection, state);
|
|
1952
|
-
await client.connect(transport);
|
|
1953
|
-
const capabilities = client.getServerCapabilities();
|
|
1954
|
-
logger7.debug(`[${name}] Server capabilities:`, JSON.stringify(capabilities || {}));
|
|
1955
|
-
const tools = await this.fetchToolsList(name);
|
|
1956
|
-
const resources = capabilities?.resources ? await this.fetchResourcesList(name) : [];
|
|
1957
|
-
const resourceTemplates = capabilities?.resources ? await this.fetchResourceTemplatesList(name) : [];
|
|
1958
|
-
connection.server = {
|
|
1959
|
-
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: {
|
|
1960
1792
|
name,
|
|
1961
1793
|
config: JSON.stringify(config),
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
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);
|
|
1979
1820
|
}
|
|
1980
|
-
setupTransportHandlers(name, connection,
|
|
1821
|
+
setupTransportHandlers(name, connection, _state) {
|
|
1981
1822
|
const config = JSON.parse(connection.server.config);
|
|
1982
1823
|
const isHttpTransport = config.type !== "stdio";
|
|
1983
1824
|
connection.transport.onerror = async (error) => {
|
|
1984
|
-
const errorMessage = error?.message
|
|
1825
|
+
const errorMessage = error?.message ?? String(error);
|
|
1985
1826
|
const isExpectedTimeout = isHttpTransport && (errorMessage === "undefined" || errorMessage === "" || errorMessage.includes("SSE error") || errorMessage.includes("timeout"));
|
|
1986
|
-
if (isExpectedTimeout) {
|
|
1987
|
-
|
|
1988
|
-
} else {
|
|
1989
|
-
logger7.error({ error, serverName: name }, `Transport error for "${name}"`);
|
|
1827
|
+
if (!isExpectedTimeout) {
|
|
1828
|
+
logger2.error({ error, serverName: name }, `Transport error for "${name}"`);
|
|
1990
1829
|
connection.server.status = "disconnected";
|
|
1991
1830
|
this.appendErrorMessage(connection, error.message);
|
|
1992
1831
|
}
|
|
@@ -1995,10 +1834,7 @@ class McpService extends Service {
|
|
|
1995
1834
|
}
|
|
1996
1835
|
};
|
|
1997
1836
|
connection.transport.onclose = async () => {
|
|
1998
|
-
if (isHttpTransport) {
|
|
1999
|
-
logger7.debug({ serverName: name }, `SSE connection closed for "${name}" (stateless, will reconnect on demand)`);
|
|
2000
|
-
} else {
|
|
2001
|
-
logger7.warn({ serverName: name }, `Transport closed for "${name}"`);
|
|
1837
|
+
if (!isHttpTransport) {
|
|
2002
1838
|
connection.server.status = "disconnected";
|
|
2003
1839
|
this.handleDisconnection(name, new Error("Transport closed"));
|
|
2004
1840
|
}
|
|
@@ -2011,7 +1847,6 @@ class McpService extends Service {
|
|
|
2011
1847
|
const config = JSON.parse(connection.server.config);
|
|
2012
1848
|
const isHttpTransport = config.type !== "stdio";
|
|
2013
1849
|
if (isHttpTransport) {
|
|
2014
|
-
logger7.debug(`[McpService] Skipping ping monitoring for HTTP server: ${name}`);
|
|
2015
1850
|
return;
|
|
2016
1851
|
}
|
|
2017
1852
|
const state = this.connectionStates.get(name);
|
|
@@ -2021,7 +1856,7 @@ class McpService extends Service {
|
|
|
2021
1856
|
clearInterval(state.pingInterval);
|
|
2022
1857
|
state.pingInterval = setInterval(() => {
|
|
2023
1858
|
this.sendPing(name).catch((err) => {
|
|
2024
|
-
|
|
1859
|
+
logger2.warn({ error: err.message, serverName: name }, `Ping failed for ${name}`);
|
|
2025
1860
|
this.handlePingFailure(name, err);
|
|
2026
1861
|
});
|
|
2027
1862
|
}, this.pingConfig.intervalMs);
|
|
@@ -2044,7 +1879,6 @@ class McpService extends Service {
|
|
|
2044
1879
|
return;
|
|
2045
1880
|
state.consecutivePingFailures++;
|
|
2046
1881
|
if (state.consecutivePingFailures >= this.pingConfig.failuresBeforeDisconnect) {
|
|
2047
|
-
logger7.warn(`Ping failures exceeded for ${name}, disconnecting and attempting reconnect.`);
|
|
2048
1882
|
this.handleDisconnection(name, error);
|
|
2049
1883
|
}
|
|
2050
1884
|
}
|
|
@@ -2059,19 +1893,17 @@ class McpService extends Service {
|
|
|
2059
1893
|
if (state.reconnectTimeout)
|
|
2060
1894
|
clearTimeout(state.reconnectTimeout);
|
|
2061
1895
|
if (state.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
2062
|
-
logger7.error(`Max reconnect attempts reached for ${name}. Giving up.`);
|
|
2063
1896
|
return;
|
|
2064
1897
|
}
|
|
2065
|
-
const delay = INITIAL_RETRY_DELAY *
|
|
1898
|
+
const delay = INITIAL_RETRY_DELAY * BACKOFF_MULTIPLIER ** state.reconnectAttempts;
|
|
2066
1899
|
state.reconnectTimeout = setTimeout(async () => {
|
|
2067
1900
|
state.reconnectAttempts++;
|
|
2068
|
-
|
|
2069
|
-
const config =
|
|
1901
|
+
const connection = this.connections.get(name);
|
|
1902
|
+
const config = connection?.server?.config;
|
|
2070
1903
|
if (config) {
|
|
2071
1904
|
try {
|
|
2072
1905
|
await this.initializeConnection(name, JSON.parse(config));
|
|
2073
1906
|
} catch (err) {
|
|
2074
|
-
logger7.error({ error: err instanceof Error ? err.message : String(err), serverName: name }, `Reconnect attempt failed for ${name}`);
|
|
2075
1907
|
this.handleDisconnection(name, err);
|
|
2076
1908
|
}
|
|
2077
1909
|
}
|
|
@@ -2080,12 +1912,8 @@ class McpService extends Service {
|
|
|
2080
1912
|
async deleteConnection(name) {
|
|
2081
1913
|
const connection = this.connections.get(name);
|
|
2082
1914
|
if (connection) {
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
await connection.client.close();
|
|
2086
|
-
} catch (error) {
|
|
2087
|
-
logger7.error({ error: error instanceof Error ? error.message : String(error), serverName: name }, `Failed to close transport for ${name}`);
|
|
2088
|
-
}
|
|
1915
|
+
await connection.transport.close();
|
|
1916
|
+
await connection.client.close();
|
|
2089
1917
|
this.connections.delete(name);
|
|
2090
1918
|
}
|
|
2091
1919
|
const state = this.connectionStates.get(name);
|
|
@@ -2106,7 +1934,7 @@ class McpService extends Service {
|
|
|
2106
1934
|
}
|
|
2107
1935
|
return new StdioClientTransport({
|
|
2108
1936
|
command: config.command,
|
|
2109
|
-
args: config.args,
|
|
1937
|
+
args: config.args ? [...config.args] : undefined,
|
|
2110
1938
|
env: {
|
|
2111
1939
|
...config.env,
|
|
2112
1940
|
...process.env.PATH ? { PATH: process.env.PATH } : {}
|
|
@@ -2119,9 +1947,6 @@ class McpService extends Service {
|
|
|
2119
1947
|
if (!config.url) {
|
|
2120
1948
|
throw new Error(`Missing URL for HTTP MCP server ${name}`);
|
|
2121
1949
|
}
|
|
2122
|
-
if (config.type === "sse") {
|
|
2123
|
-
logger7.warn(`Server "${name}": "sse" transport type is deprecated. Use "streamable-http" or "http" instead for the modern Streamable HTTP transport.`);
|
|
2124
|
-
}
|
|
2125
1950
|
return new SSEClientTransport(new URL(config.url));
|
|
2126
1951
|
}
|
|
2127
1952
|
appendErrorMessage(connection, error) {
|
|
@@ -2130,62 +1955,38 @@ ${error}` : error;
|
|
|
2130
1955
|
connection.server.error = newError;
|
|
2131
1956
|
}
|
|
2132
1957
|
async fetchToolsList(serverName) {
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
if (!connection) {
|
|
2136
|
-
return [];
|
|
2137
|
-
}
|
|
2138
|
-
const response = await connection.client.listTools();
|
|
2139
|
-
const tools = (response?.tools || []).map((tool) => {
|
|
2140
|
-
let processedTool = { ...tool };
|
|
2141
|
-
if (tool.inputSchema) {
|
|
2142
|
-
try {
|
|
2143
|
-
if (!this.compatibilityInitialized) {
|
|
2144
|
-
this.initializeToolCompatibility();
|
|
2145
|
-
}
|
|
2146
|
-
processedTool.inputSchema = this.applyToolCompatibility(tool.inputSchema);
|
|
2147
|
-
logger7.debug(`Applied tool compatibility for: ${tool.name} on server: ${serverName}`);
|
|
2148
|
-
} catch (error) {
|
|
2149
|
-
logger7.warn({ error, toolName: tool.name, serverName }, `Tool compatibility failed for ${tool.name} on ${serverName}`);
|
|
2150
|
-
}
|
|
2151
|
-
}
|
|
2152
|
-
return processedTool;
|
|
2153
|
-
});
|
|
2154
|
-
logger7.info(`Fetched ${tools.length} tools for ${serverName}`);
|
|
2155
|
-
for (const tool of tools) {
|
|
2156
|
-
logger7.info(`[${serverName}] ${tool.name}: ${tool.description}`);
|
|
2157
|
-
}
|
|
2158
|
-
return tools;
|
|
2159
|
-
} catch (error) {
|
|
2160
|
-
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) {
|
|
2161
1960
|
return [];
|
|
2162
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;
|
|
2163
1974
|
}
|
|
2164
1975
|
async fetchResourcesList(serverName) {
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
if (!connection) {
|
|
2168
|
-
return [];
|
|
2169
|
-
}
|
|
2170
|
-
const response = await connection.client.listResources();
|
|
2171
|
-
return response?.resources || [];
|
|
2172
|
-
} catch (error) {
|
|
2173
|
-
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) {
|
|
2174
1978
|
return [];
|
|
2175
1979
|
}
|
|
1980
|
+
const response = await connection.client.listResources();
|
|
1981
|
+
return response?.resources ?? [];
|
|
2176
1982
|
}
|
|
2177
1983
|
async fetchResourceTemplatesList(serverName) {
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
if (!connection) {
|
|
2181
|
-
return [];
|
|
2182
|
-
}
|
|
2183
|
-
const response = await connection.client.listResourceTemplates();
|
|
2184
|
-
return response?.resourceTemplates || [];
|
|
2185
|
-
} catch (error) {
|
|
2186
|
-
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) {
|
|
2187
1986
|
return [];
|
|
2188
1987
|
}
|
|
1988
|
+
const response = await connection.client.listResourceTemplates();
|
|
1989
|
+
return response?.resourceTemplates ?? [];
|
|
2189
1990
|
}
|
|
2190
1991
|
getServers() {
|
|
2191
1992
|
return Array.from(this.connections.values()).filter((conn) => !conn.server.disabled).map((conn) => conn.server);
|
|
@@ -2202,13 +2003,14 @@ ${error}` : error;
|
|
|
2202
2003
|
throw new Error(`Server "${serverName}" is disabled`);
|
|
2203
2004
|
}
|
|
2204
2005
|
let timeout = DEFAULT_MCP_TIMEOUT_SECONDS;
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
timeout = config.timeoutInMillis
|
|
2208
|
-
} catch (error) {
|
|
2209
|
-
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;
|
|
2210
2009
|
}
|
|
2211
|
-
const result = await connection.client.callTool({
|
|
2010
|
+
const result = await connection.client.callTool({
|
|
2011
|
+
name: toolName,
|
|
2012
|
+
arguments: toolArguments ? { ...toolArguments } : undefined
|
|
2013
|
+
}, undefined, { timeout });
|
|
2212
2014
|
if (!result.content) {
|
|
2213
2015
|
throw new Error("Invalid tool result: missing content array");
|
|
2214
2016
|
}
|
|
@@ -2226,19 +2028,12 @@ ${error}` : error;
|
|
|
2226
2028
|
}
|
|
2227
2029
|
async restartConnection(serverName) {
|
|
2228
2030
|
const connection = this.connections.get(serverName);
|
|
2229
|
-
const config = connection?.server
|
|
2031
|
+
const config = connection?.server?.config;
|
|
2230
2032
|
if (config) {
|
|
2231
|
-
logger7.info(`Restarting ${serverName} MCP server...`);
|
|
2232
2033
|
connection.server.status = "connecting";
|
|
2233
2034
|
connection.server.error = "";
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
await this.initializeConnection(serverName, JSON.parse(config));
|
|
2237
|
-
logger7.info(`${serverName} MCP server connected`);
|
|
2238
|
-
} catch (error) {
|
|
2239
|
-
logger7.error({ error: error instanceof Error ? error.message : String(error), serverName }, `Failed to restart connection for ${serverName}`);
|
|
2240
|
-
throw new Error(`Failed to connect to ${serverName} MCP server`);
|
|
2241
|
-
}
|
|
2035
|
+
await this.deleteConnection(serverName);
|
|
2036
|
+
await this.initializeConnection(serverName, JSON.parse(config));
|
|
2242
2037
|
}
|
|
2243
2038
|
}
|
|
2244
2039
|
initializeToolCompatibility() {
|
|
@@ -2246,11 +2041,6 @@ ${error}` : error;
|
|
|
2246
2041
|
return;
|
|
2247
2042
|
this.toolCompatibility = createMcpToolCompatibilitySync(this.runtime);
|
|
2248
2043
|
this.compatibilityInitialized = true;
|
|
2249
|
-
if (this.toolCompatibility) {
|
|
2250
|
-
logger7.info(`Tool compatibility enabled`);
|
|
2251
|
-
} else {
|
|
2252
|
-
logger7.info(`No tool compatibility needed`);
|
|
2253
|
-
}
|
|
2254
2044
|
}
|
|
2255
2045
|
applyToolCompatibility(toolSchema) {
|
|
2256
2046
|
if (!this.compatibilityInitialized) {
|
|
@@ -2259,12 +2049,7 @@ ${error}` : error;
|
|
|
2259
2049
|
if (!this.toolCompatibility || !toolSchema) {
|
|
2260
2050
|
return toolSchema;
|
|
2261
2051
|
}
|
|
2262
|
-
|
|
2263
|
-
return this.toolCompatibility.transformToolSchema(toolSchema);
|
|
2264
|
-
} catch (error) {
|
|
2265
|
-
logger7.warn({ error }, `Tool compatibility transformation failed`);
|
|
2266
|
-
return toolSchema;
|
|
2267
|
-
}
|
|
2052
|
+
return this.toolCompatibility.transformToolSchema(toolSchema);
|
|
2268
2053
|
}
|
|
2269
2054
|
}
|
|
2270
2055
|
|
|
@@ -2273,7 +2058,7 @@ var mcpPlugin = {
|
|
|
2273
2058
|
name: "mcp",
|
|
2274
2059
|
description: "Plugin for connecting to MCP (Model Context Protocol) servers",
|
|
2275
2060
|
init: async (_config, _runtime) => {
|
|
2276
|
-
|
|
2061
|
+
logger3.info("Initializing MCP plugin...");
|
|
2277
2062
|
},
|
|
2278
2063
|
services: [McpService],
|
|
2279
2064
|
actions: [callToolAction, readResourceAction],
|
|
@@ -2281,22 +2066,7 @@ var mcpPlugin = {
|
|
|
2281
2066
|
};
|
|
2282
2067
|
var src_default = mcpPlugin;
|
|
2283
2068
|
export {
|
|
2284
|
-
|
|
2285
|
-
src_default as default,
|
|
2286
|
-
createMcpToolCompatibilitySync,
|
|
2287
|
-
createMcpToolCompatibility,
|
|
2288
|
-
ToolSelectionSchema,
|
|
2289
|
-
ResourceSelectionSchema,
|
|
2290
|
-
McpToolCompatibility,
|
|
2291
|
-
McpService,
|
|
2292
|
-
MIN_MCP_TIMEOUT_SECONDS,
|
|
2293
|
-
MCP_SERVICE_NAME,
|
|
2294
|
-
MAX_RECONNECT_ATTEMPTS,
|
|
2295
|
-
INITIAL_RETRY_DELAY,
|
|
2296
|
-
DEFAULT_PING_CONFIG,
|
|
2297
|
-
DEFAULT_MCP_TIMEOUT_SECONDS,
|
|
2298
|
-
DEFAULT_MAX_RETRIES,
|
|
2299
|
-
BACKOFF_MULTIPLIER
|
|
2069
|
+
src_default as default
|
|
2300
2070
|
};
|
|
2301
2071
|
|
|
2302
|
-
//# debugId=
|
|
2072
|
+
//# debugId=9C08B35E0B9B7AB864756E2164756E21
|