@elizaos/plugin-mcp 2.0.0-alpha.7 → 2.0.11-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -0
- package/dist/cjs/index.cjs +917 -375
- package/dist/cjs/index.js.map +21 -20
- package/dist/node/actions/mcp.d.ts +6 -0
- package/dist/node/actions/mcp.d.ts.map +1 -0
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +913 -379
- package/dist/node/index.js.map +21 -20
- package/dist/node/mcp-marketplace.d.ts +69 -0
- package/dist/node/mcp-marketplace.d.ts.map +1 -0
- package/dist/node/prompts.d.ts +23 -0
- package/dist/node/prompts.d.ts.map +1 -0
- package/dist/node/provider.d.ts.map +1 -1
- package/dist/node/routes-mcp.d.ts +48 -0
- package/dist/node/routes-mcp.d.ts.map +1 -0
- package/dist/node/service.d.ts +2 -1
- package/dist/node/service.d.ts.map +1 -1
- package/dist/node/templates/errorAnalysisPrompt.d.ts.map +1 -1
- package/dist/node/templates/feedbackTemplate.d.ts +1 -6
- package/dist/node/templates/feedbackTemplate.d.ts.map +1 -1
- package/dist/node/templates/resourceAnalysisTemplate.d.ts +1 -6
- package/dist/node/templates/resourceAnalysisTemplate.d.ts.map +1 -1
- package/dist/node/templates/resourceSelectionTemplate.d.ts +1 -6
- package/dist/node/templates/resourceSelectionTemplate.d.ts.map +1 -1
- package/dist/node/templates/toolReasoningTemplate.d.ts +1 -6
- package/dist/node/templates/toolReasoningTemplate.d.ts.map +1 -1
- package/dist/node/templates/toolSelectionTemplate.d.ts +2 -7
- package/dist/node/templates/toolSelectionTemplate.d.ts.map +1 -1
- package/dist/node/tool-compatibility/integration-test.d.ts.map +1 -1
- package/dist/node/tool-compatibility/providers/openai.d.ts.map +1 -1
- package/dist/node/tool-compatibility/test-example.d.ts.map +1 -1
- package/dist/node/types.d.ts +1 -4
- package/dist/node/types.d.ts.map +1 -1
- package/dist/node/utils/error.d.ts.map +1 -1
- package/dist/node/utils/handler.d.ts.map +1 -1
- package/dist/node/utils/json.d.ts +1 -0
- package/dist/node/utils/json.d.ts.map +1 -1
- package/dist/node/utils/schemas.d.ts +0 -4
- package/dist/node/utils/schemas.d.ts.map +1 -1
- package/dist/node/utils/selection.d.ts.map +1 -1
- package/dist/node/utils/validation.d.ts.map +1 -1
- package/dist/node/utils/wrapper.d.ts.map +1 -1
- package/package.json +24 -10
- package/dist/node/actions/callToolAction.d.ts +0 -3
- package/dist/node/actions/callToolAction.d.ts.map +0 -1
- package/dist/node/actions/readResourceAction.d.ts +0 -3
- package/dist/node/actions/readResourceAction.d.ts.map +0 -1
- package/dist/node/generated/prompts/typescript/prompts.d.ts +0 -24
- package/dist/node/generated/prompts/typescript/prompts.d.ts.map +0 -1
- package/dist/tsconfig.build.tsbuildinfo +0 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -4,38 +4,59 @@ var __defProp = Object.defineProperty;
|
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
7
12
|
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
8
20
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
21
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
22
|
for (let key of __getOwnPropNames(mod))
|
|
11
23
|
if (!__hasOwnProp.call(to, key))
|
|
12
24
|
__defProp(to, key, {
|
|
13
|
-
get: (
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
14
26
|
enumerable: true
|
|
15
27
|
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
16
30
|
return to;
|
|
17
31
|
};
|
|
18
|
-
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
19
32
|
var __toCommonJS = (from) => {
|
|
20
|
-
var entry = __moduleCache.get(from), desc;
|
|
33
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
21
34
|
if (entry)
|
|
22
35
|
return entry;
|
|
23
36
|
entry = __defProp({}, "__esModule", { value: true });
|
|
24
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
38
|
+
for (var key of __getOwnPropNames(from))
|
|
39
|
+
if (!__hasOwnProp.call(entry, key))
|
|
40
|
+
__defProp(entry, key, {
|
|
41
|
+
get: __accessProp.bind(from, key),
|
|
42
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
43
|
+
});
|
|
44
|
+
}
|
|
29
45
|
__moduleCache.set(from, entry);
|
|
30
46
|
return entry;
|
|
31
47
|
};
|
|
48
|
+
var __moduleCache;
|
|
49
|
+
var __returnValue = (v) => v;
|
|
50
|
+
function __exportSetter(name, newValue) {
|
|
51
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
52
|
+
}
|
|
32
53
|
var __export = (target, all) => {
|
|
33
54
|
for (var name in all)
|
|
34
55
|
__defProp(target, name, {
|
|
35
56
|
get: all[name],
|
|
36
57
|
enumerable: true,
|
|
37
58
|
configurable: true,
|
|
38
|
-
set: (
|
|
59
|
+
set: __exportSetter.bind(all, name)
|
|
39
60
|
});
|
|
40
61
|
};
|
|
41
62
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -349,7 +370,7 @@ IMPORTANT: ${constraintText}`;
|
|
|
349
370
|
if (constraints.maxItems) {
|
|
350
371
|
rules.push(`array must have at most ${constraints.maxItems} items`);
|
|
351
372
|
}
|
|
352
|
-
return rules.length > 0 ? rules.join(", ") :
|
|
373
|
+
return rules.length > 0 ? rules.join(", ") : Object.entries(constraints).map(([key, value]) => `${key}: ${String(value)}`).join(", ");
|
|
353
374
|
}
|
|
354
375
|
};
|
|
355
376
|
});
|
|
@@ -503,51 +524,16 @@ Constraints: ${constraintText}`;
|
|
|
503
524
|
// src/index.ts
|
|
504
525
|
var exports_src = {};
|
|
505
526
|
__export(exports_src, {
|
|
527
|
+
handleMcpRoutes: () => handleMcpRoutes,
|
|
506
528
|
default: () => src_default
|
|
507
529
|
});
|
|
508
530
|
module.exports = __toCommonJS(exports_src);
|
|
509
|
-
var
|
|
531
|
+
var import_core8 = require("@elizaos/core");
|
|
510
532
|
|
|
511
|
-
// src/
|
|
512
|
-
var
|
|
513
|
-
var DEFAULT_MCP_TIMEOUT_SECONDS = 60000;
|
|
514
|
-
var DEFAULT_MAX_RETRIES = 2;
|
|
515
|
-
var ResourceSelectionSchema = {
|
|
516
|
-
type: "object",
|
|
517
|
-
required: ["serverName", "uri"],
|
|
518
|
-
properties: {
|
|
519
|
-
serverName: {
|
|
520
|
-
type: "string",
|
|
521
|
-
minLength: 1,
|
|
522
|
-
errorMessage: "serverName must not be empty"
|
|
523
|
-
},
|
|
524
|
-
uri: {
|
|
525
|
-
type: "string",
|
|
526
|
-
minLength: 1,
|
|
527
|
-
errorMessage: "uri must not be empty"
|
|
528
|
-
},
|
|
529
|
-
reasoning: {
|
|
530
|
-
type: "string"
|
|
531
|
-
},
|
|
532
|
-
noResourceAvailable: {
|
|
533
|
-
type: "boolean"
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
};
|
|
537
|
-
var DEFAULT_PING_CONFIG = {
|
|
538
|
-
enabled: true,
|
|
539
|
-
intervalMs: 1e4,
|
|
540
|
-
timeoutMs: 5000,
|
|
541
|
-
failuresBeforeDisconnect: 3
|
|
542
|
-
};
|
|
543
|
-
var MAX_RECONNECT_ATTEMPTS = 5;
|
|
544
|
-
var BACKOFF_MULTIPLIER = 2;
|
|
545
|
-
var INITIAL_RETRY_DELAY = 2000;
|
|
546
|
-
|
|
547
|
-
// src/utils/error.ts
|
|
548
|
-
var import_core = require("@elizaos/core");
|
|
533
|
+
// src/actions/mcp.ts
|
|
534
|
+
var import_core5 = require("@elizaos/core");
|
|
549
535
|
|
|
550
|
-
// src/
|
|
536
|
+
// src/prompts.ts
|
|
551
537
|
var errorAnalysisTemplate = `{{{mcpProvider.text}}}
|
|
552
538
|
|
|
553
539
|
{{{recentMessages}}}
|
|
@@ -572,15 +558,15 @@ var resourceAnalysisTemplate = `{{{mcpProvider.text}}}
|
|
|
572
558
|
|
|
573
559
|
# Prompt
|
|
574
560
|
|
|
575
|
-
|
|
561
|
+
Respond to the user's request using the resource "{{{uri}}}".
|
|
576
562
|
|
|
577
563
|
Original user request: "{{{userMessage}}}"
|
|
578
564
|
|
|
579
|
-
Resource metadata:
|
|
580
|
-
{{{resourceMeta}}
|
|
565
|
+
Resource metadata:
|
|
566
|
+
{{{resourceMeta}}}
|
|
581
567
|
|
|
582
|
-
Resource content:
|
|
583
|
-
{{{resourceContent}}
|
|
568
|
+
Resource content:
|
|
569
|
+
{{{resourceContent}}}
|
|
584
570
|
|
|
585
571
|
Instructions:
|
|
586
572
|
1. Analyze how well the resource's content addresses the user's specific question or need
|
|
@@ -597,45 +583,50 @@ var resourceSelectionTemplate = `{{{mcpProvider.text}}}
|
|
|
597
583
|
|
|
598
584
|
# Prompt
|
|
599
585
|
|
|
600
|
-
|
|
586
|
+
Select the right resource to address the user's request.
|
|
601
587
|
|
|
602
588
|
CRITICAL INSTRUCTIONS:
|
|
603
589
|
1. You MUST specify both a valid serverName AND uri from the list above
|
|
604
590
|
2. The serverName value should match EXACTLY the server name shown in parentheses (Server: X)
|
|
605
|
-
CORRECT:
|
|
606
|
-
WRONG:
|
|
591
|
+
CORRECT: serverName: github (if the server is called "github")
|
|
592
|
+
WRONG: serverName: GitHub, serverName: Github, or any other variation
|
|
607
593
|
3. The uri value should match EXACTLY the resource uri listed
|
|
608
|
-
CORRECT:
|
|
609
|
-
WRONG:
|
|
594
|
+
CORRECT: uri: weather://San Francisco/current (if that's the exact uri)
|
|
595
|
+
WRONG: uri: weather://sanfrancisco/current or any variation
|
|
610
596
|
4. Identify the user's information need from the conversation context
|
|
611
597
|
5. Select the most appropriate resource based on its description and the request
|
|
612
|
-
6. If no resource seems appropriate,
|
|
598
|
+
6. If no resource seems appropriate, set noResourceAvailable: true
|
|
613
599
|
|
|
614
|
-
|
|
600
|
+
Respond with compact JSON only.
|
|
615
601
|
|
|
616
602
|
STRICT FORMAT REQUIREMENTS:
|
|
617
|
-
-
|
|
618
|
-
- NO
|
|
603
|
+
- Include "noResourceAvailable": false when selecting a resource
|
|
604
|
+
- NO code block formatting (NO backticks)
|
|
605
|
+
- NO comments
|
|
619
606
|
- NO placeholders like "replace with...", "example", "your...", "actual", etc.
|
|
620
607
|
- Every parameter value must be a concrete, usable value (not instructions to replace)
|
|
621
|
-
- Use proper JSON syntax with double quotes for strings
|
|
622
608
|
- NO explanatory text before or after the JSON object
|
|
623
609
|
|
|
624
610
|
EXAMPLE RESPONSE:
|
|
625
611
|
{
|
|
626
612
|
"serverName": "weather-server",
|
|
627
613
|
"uri": "weather://San Francisco/current",
|
|
628
|
-
"reasoning": "
|
|
614
|
+
"reasoning": "The user is asking about current weather in San Francisco. This resource provides up-to-date weather information for that city.",
|
|
615
|
+
"noResourceAvailable": false
|
|
629
616
|
}
|
|
630
617
|
|
|
631
|
-
|
|
618
|
+
NO RESOURCE EXAMPLE:
|
|
619
|
+
{
|
|
620
|
+
"reasoning": "None of the available resources match the user's request.",
|
|
621
|
+
"noResourceAvailable": true
|
|
622
|
+
}`;
|
|
632
623
|
var toolReasoningTemplate = `{{{mcpProvider.text}}}
|
|
633
624
|
|
|
634
625
|
{{{recentMessages}}}
|
|
635
626
|
|
|
636
627
|
# Prompt
|
|
637
628
|
|
|
638
|
-
|
|
629
|
+
Synthesize the result from the "{{{toolName}}}" tool into a response to the user's request.
|
|
639
630
|
|
|
640
631
|
Original user request: "{{{userMessage}}}"
|
|
641
632
|
|
|
@@ -657,34 +648,34 @@ Instructions:
|
|
|
657
648
|
Your response (written as if directly to the user):`;
|
|
658
649
|
var toolSelectionArgumentTemplate = `{{recentMessages}}
|
|
659
650
|
|
|
660
|
-
# TASK: Generate
|
|
651
|
+
# TASK: Generate Tool Arguments for Tool Execution
|
|
661
652
|
|
|
662
653
|
You have chosen the "{{toolSelectionName.toolName}}" tool from the "{{toolSelectionName.serverName}}" server to address the user's request.
|
|
663
654
|
The reasoning behind this selection is: "{{toolSelectionName.reasoning}}"
|
|
664
655
|
|
|
665
656
|
## CRITICAL INSTRUCTIONS
|
|
666
|
-
1. Ensure the
|
|
657
|
+
1. Ensure the toolArguments block strictly adheres to the structure and requirements defined in the schema.
|
|
667
658
|
2. All parameter values must be extracted from the conversation context and must be concrete, usable values.
|
|
668
659
|
3. Avoid placeholders or generic terms unless explicitly provided by the user.
|
|
669
660
|
|
|
670
|
-
|
|
661
|
+
Respond with compact JSON only.
|
|
671
662
|
|
|
672
663
|
## STRICT FORMAT REQUIREMENTS
|
|
673
|
-
- The response MUST be
|
|
674
|
-
- DO NOT wrap
|
|
675
|
-
- DO NOT include comments
|
|
664
|
+
- The response MUST be one JSON object.
|
|
665
|
+
- DO NOT wrap it in triple backticks, code blocks, or include any explanatory text.
|
|
666
|
+
- DO NOT include comments anywhere.
|
|
676
667
|
- DO NOT use placeholders (e.g., "replace with...", "example", "your...", etc.)
|
|
677
|
-
- ALL strings must use double quotes
|
|
678
668
|
|
|
679
669
|
## CRITICAL NOTES
|
|
680
670
|
- All values must be fully grounded in user input or inferred contextually.
|
|
681
671
|
- No missing fields unless they are explicitly optional in the schema.
|
|
682
672
|
- All types must match the schema (strings, numbers, booleans).
|
|
673
|
+
- Put all executable parameters under the toolArguments object.
|
|
683
674
|
|
|
684
|
-
##
|
|
675
|
+
## RESPONSE STRUCTURE
|
|
685
676
|
Your response MUST contain ONLY these two top-level keys:
|
|
686
|
-
1.
|
|
687
|
-
2.
|
|
677
|
+
1. toolArguments - object with fields matching the input schema: {{toolInputSchema}}
|
|
678
|
+
2. reasoning - a short explanation of how the values were inferred from the conversation.
|
|
688
679
|
|
|
689
680
|
## EXAMPLE RESPONSE
|
|
690
681
|
{
|
|
@@ -697,50 +688,46 @@ Your response MUST contain ONLY these two top-level keys:
|
|
|
697
688
|
"reasoning": "The user wants to see the README from the facebook/react repository based on our conversation."
|
|
698
689
|
}
|
|
699
690
|
|
|
700
|
-
|
|
691
|
+
If the tool takes no arguments, use an empty toolArguments object:
|
|
692
|
+
{
|
|
693
|
+
"toolArguments": {},
|
|
694
|
+
"reasoning": "The selected tool does not require arguments for this request."
|
|
695
|
+
}`;
|
|
701
696
|
var toolSelectionNameTemplate = `{{mcpProvider.text}}
|
|
702
697
|
|
|
703
698
|
{{recentMessages}}
|
|
704
699
|
|
|
705
700
|
# TASK: Select the Most Appropriate Tool and Server
|
|
706
701
|
|
|
707
|
-
You must select the most appropriate tool from the list above to fulfill the user's request.
|
|
702
|
+
You must select the most appropriate tool from the list above to fulfill the user's request. Respond with compact JSON.
|
|
708
703
|
|
|
709
704
|
## CRITICAL INSTRUCTIONS
|
|
710
|
-
1. Provide both
|
|
705
|
+
1. Provide both serverName and toolName from the options listed above.
|
|
711
706
|
2. Each name must match EXACTLY as shown in the list:
|
|
712
|
-
- Example (correct):
|
|
713
|
-
- Example (incorrect):
|
|
707
|
+
- Example (correct): serverName: github
|
|
708
|
+
- Example (incorrect): serverName: GitHub, serverName: Github, or variations
|
|
714
709
|
3. Extract ACTUAL parameter values from the conversation context.
|
|
715
710
|
- Do not invent or use placeholders like "octocat" or "Hello-World" unless the user said so.
|
|
716
|
-
4. Include a
|
|
717
|
-
5. If no tool is appropriate,
|
|
718
|
-
{
|
|
719
|
-
"noToolAvailable": true
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
!!! YOUR RESPONSE MUST BE A VALID JSON OBJECT ONLY !!!
|
|
723
|
-
|
|
724
|
-
CRITICAL: Your response must START with { and END with }. DO NOT include ANY text before or after the JSON.
|
|
711
|
+
4. Include a reasoning field explaining why the selected tool fits the request.
|
|
712
|
+
5. If no tool is appropriate, set noToolAvailable: true.
|
|
725
713
|
|
|
726
714
|
## STRICT FORMAT REQUIREMENTS
|
|
727
|
-
- The response MUST be
|
|
728
|
-
- DO NOT wrap
|
|
729
|
-
- DO NOT include comments
|
|
715
|
+
- The response MUST be one JSON object.
|
|
716
|
+
- DO NOT wrap it in triple backticks, code blocks, or include any explanatory text.
|
|
717
|
+
- DO NOT include comments anywhere.
|
|
730
718
|
- DO NOT use placeholders (e.g., "replace with...", "example", "your...", etc.)
|
|
731
|
-
- ALL strings must use double quotes.
|
|
732
719
|
|
|
733
720
|
## CRITICAL NOTES
|
|
734
721
|
- All values must be fully grounded in user input or inferred contextually.
|
|
735
722
|
- No missing fields unless they are explicitly optional in the schema.
|
|
736
723
|
- All types must match the schema (strings, numbers, booleans).
|
|
737
724
|
|
|
738
|
-
##
|
|
725
|
+
## RESPONSE STRUCTURE
|
|
739
726
|
Your response MUST contain ONLY these top-level keys:
|
|
740
|
-
1.
|
|
741
|
-
2.
|
|
742
|
-
3.
|
|
743
|
-
4.
|
|
727
|
+
1. serverName - The name of the server (e.g., github, notion)
|
|
728
|
+
2. toolName - The name of the tool (e.g., get_file_contents, search)
|
|
729
|
+
3. reasoning - A short explanation of how the values were inferred from the conversation
|
|
730
|
+
4. noToolAvailable - true or false
|
|
744
731
|
|
|
745
732
|
## EXAMPLE RESPONSE
|
|
746
733
|
{
|
|
@@ -750,12 +737,68 @@ Your response MUST contain ONLY these top-level keys:
|
|
|
750
737
|
"noToolAvailable": false
|
|
751
738
|
}
|
|
752
739
|
|
|
740
|
+
## NO TOOL EXAMPLE
|
|
741
|
+
{
|
|
742
|
+
"reasoning": "None of the available tools match the user's request.",
|
|
743
|
+
"noToolAvailable": true
|
|
744
|
+
}
|
|
745
|
+
|
|
753
746
|
## REMINDERS
|
|
754
747
|
- Use "github" as serverName for GitHub tools.
|
|
755
748
|
- Use "notion" as serverName for Notion tools.
|
|
756
|
-
- For search and knowledge-based tasks, MCP tools are often appropriate
|
|
749
|
+
- For search and knowledge-based tasks, MCP tools are often appropriate.`;
|
|
750
|
+
|
|
751
|
+
// src/types.ts
|
|
752
|
+
var MCP_SERVICE_NAME = "mcp";
|
|
753
|
+
var DEFAULT_MCP_TIMEOUT_SECONDS = 60000;
|
|
754
|
+
var DEFAULT_MAX_RETRIES = 2;
|
|
755
|
+
function isRecord(value) {
|
|
756
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
757
|
+
}
|
|
758
|
+
function isStdioMcpServerConfig(value) {
|
|
759
|
+
return isRecord(value) && value.type === "stdio" && typeof value.command === "string" && (value.args === undefined || Array.isArray(value.args) && value.args.every((arg) => typeof arg === "string")) && (value.env === undefined || isRecord(value.env) && Object.values(value.env).every((entry) => typeof entry === "string")) && (value.cwd === undefined || typeof value.cwd === "string") && (value.timeoutInMillis === undefined || typeof value.timeoutInMillis === "number");
|
|
760
|
+
}
|
|
761
|
+
function isHttpMcpServerConfig(value) {
|
|
762
|
+
return isRecord(value) && (value.type === "http" || value.type === "streamable-http" || value.type === "sse") && typeof value.url === "string" && (value.timeout === undefined || typeof value.timeout === "number");
|
|
763
|
+
}
|
|
764
|
+
function isMcpSettings(value) {
|
|
765
|
+
if (!isRecord(value) || !isRecord(value.servers)) {
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
return Object.values(value.servers).every((server) => isStdioMcpServerConfig(server) || isHttpMcpServerConfig(server)) && (value.maxRetries === undefined || typeof value.maxRetries === "number");
|
|
769
|
+
}
|
|
770
|
+
var ResourceSelectionSchema = {
|
|
771
|
+
type: "object",
|
|
772
|
+
required: ["serverName", "uri"],
|
|
773
|
+
properties: {
|
|
774
|
+
serverName: {
|
|
775
|
+
type: "string",
|
|
776
|
+
minLength: 1
|
|
777
|
+
},
|
|
778
|
+
uri: {
|
|
779
|
+
type: "string",
|
|
780
|
+
minLength: 1
|
|
781
|
+
},
|
|
782
|
+
reasoning: {
|
|
783
|
+
type: "string"
|
|
784
|
+
},
|
|
785
|
+
noResourceAvailable: {
|
|
786
|
+
type: "boolean"
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
var DEFAULT_PING_CONFIG = {
|
|
791
|
+
enabled: true,
|
|
792
|
+
intervalMs: 1e4,
|
|
793
|
+
timeoutMs: 5000,
|
|
794
|
+
failuresBeforeDisconnect: 3
|
|
795
|
+
};
|
|
796
|
+
var MAX_RECONNECT_ATTEMPTS = 5;
|
|
797
|
+
var BACKOFF_MULTIPLIER = 2;
|
|
798
|
+
var INITIAL_RETRY_DELAY = 2000;
|
|
757
799
|
|
|
758
|
-
|
|
800
|
+
// src/utils/error.ts
|
|
801
|
+
var import_core = require("@elizaos/core");
|
|
759
802
|
|
|
760
803
|
// src/templates/errorAnalysisPrompt.ts
|
|
761
804
|
var errorAnalysisPrompt = errorAnalysisTemplate;
|
|
@@ -796,7 +839,8 @@ async function handleMcpError(state, mcpProvider, error, runtime, message, type,
|
|
|
796
839
|
errorType: type
|
|
797
840
|
},
|
|
798
841
|
data: {
|
|
799
|
-
actionName:
|
|
842
|
+
actionName: "MCP",
|
|
843
|
+
op: type === "tool" ? "call_tool" : "read_resource",
|
|
800
844
|
error: errorMessage,
|
|
801
845
|
mcpType: type
|
|
802
846
|
},
|
|
@@ -822,7 +866,8 @@ async function handleNoToolAvailable(callback, toolSelection) {
|
|
|
822
866
|
fallbackToDirectAssistance: true
|
|
823
867
|
},
|
|
824
868
|
data: {
|
|
825
|
-
actionName: "
|
|
869
|
+
actionName: "MCP",
|
|
870
|
+
op: "call_tool",
|
|
826
871
|
noToolAvailable: true,
|
|
827
872
|
reason: toolSelection?.reasoning ?? "No appropriate tool available"
|
|
828
873
|
},
|
|
@@ -1088,12 +1133,12 @@ function parseJSON(input) {
|
|
|
1088
1133
|
return import_json5.default.parse(cleanedInput);
|
|
1089
1134
|
}
|
|
1090
1135
|
var ajv = new import_ajv.default({
|
|
1091
|
-
allErrors: true
|
|
1092
|
-
strict: false
|
|
1136
|
+
allErrors: true
|
|
1093
1137
|
});
|
|
1094
1138
|
function formatAjvErrors(errors) {
|
|
1095
1139
|
return errors.map((err) => {
|
|
1096
|
-
const
|
|
1140
|
+
const errorPath = err.instancePath ?? err.dataPath ?? "";
|
|
1141
|
+
const path = errorPath ? errorPath.replace(/^\//, "") : "value";
|
|
1097
1142
|
return `${path}: ${err.message ?? "validation failed"}`;
|
|
1098
1143
|
}).join(", ");
|
|
1099
1144
|
}
|
|
@@ -1107,6 +1152,15 @@ function validateJsonSchema(data, schema) {
|
|
|
1107
1152
|
}
|
|
1108
1153
|
return { success: true, data };
|
|
1109
1154
|
}
|
|
1155
|
+
function parseStructuredModelOutput(input) {
|
|
1156
|
+
const errors = [];
|
|
1157
|
+
try {
|
|
1158
|
+
return parseJSON(input);
|
|
1159
|
+
} catch {
|
|
1160
|
+
errors.push("JSON object parse failed");
|
|
1161
|
+
}
|
|
1162
|
+
throw new Error(`No valid JSON object found: ${errors.join("; ")}`);
|
|
1163
|
+
}
|
|
1110
1164
|
|
|
1111
1165
|
// src/utils/schemas.ts
|
|
1112
1166
|
var toolSelectionNameSchema = {
|
|
@@ -1115,13 +1169,11 @@ var toolSelectionNameSchema = {
|
|
|
1115
1169
|
properties: {
|
|
1116
1170
|
serverName: {
|
|
1117
1171
|
type: "string",
|
|
1118
|
-
minLength: 1
|
|
1119
|
-
errorMessage: "serverName must not be empty"
|
|
1172
|
+
minLength: 1
|
|
1120
1173
|
},
|
|
1121
1174
|
toolName: {
|
|
1122
1175
|
type: "string",
|
|
1123
|
-
minLength: 1
|
|
1124
|
-
errorMessage: "toolName must not be empty"
|
|
1176
|
+
minLength: 1
|
|
1125
1177
|
},
|
|
1126
1178
|
reasoning: {
|
|
1127
1179
|
type: "string"
|
|
@@ -1142,7 +1194,24 @@ var toolSelectionArgumentSchema = {
|
|
|
1142
1194
|
};
|
|
1143
1195
|
|
|
1144
1196
|
// src/utils/validation.ts
|
|
1197
|
+
function isRecord2(value) {
|
|
1198
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1199
|
+
}
|
|
1200
|
+
function optionalReasoning(parsed) {
|
|
1201
|
+
return typeof parsed.reasoning === "string" ? { reasoning: parsed.reasoning } : {};
|
|
1202
|
+
}
|
|
1145
1203
|
function validateToolSelectionName(parsed, state) {
|
|
1204
|
+
if (isRecord2(parsed) && parsed.noToolAvailable === true) {
|
|
1205
|
+
return {
|
|
1206
|
+
success: true,
|
|
1207
|
+
data: {
|
|
1208
|
+
serverName: "",
|
|
1209
|
+
toolName: "",
|
|
1210
|
+
noToolAvailable: true,
|
|
1211
|
+
...optionalReasoning(parsed)
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1146
1215
|
const basicResult = validateJsonSchema(parsed, toolSelectionNameSchema);
|
|
1147
1216
|
if (basicResult.success === false) {
|
|
1148
1217
|
return { success: false, error: basicResult.error };
|
|
@@ -1150,7 +1219,7 @@ function validateToolSelectionName(parsed, state) {
|
|
|
1150
1219
|
const data = basicResult.data;
|
|
1151
1220
|
const mcpData = state.values.mcp ?? {};
|
|
1152
1221
|
const server = mcpData[data.serverName];
|
|
1153
|
-
if (
|
|
1222
|
+
if (server?.status !== "connected") {
|
|
1154
1223
|
return {
|
|
1155
1224
|
success: false,
|
|
1156
1225
|
error: `Server "${data.serverName}" not found or not connected`
|
|
@@ -1166,7 +1235,8 @@ function validateToolSelectionName(parsed, state) {
|
|
|
1166
1235
|
return { success: true, data };
|
|
1167
1236
|
}
|
|
1168
1237
|
function validateToolSelectionArgument(parsed, toolInputSchema) {
|
|
1169
|
-
const
|
|
1238
|
+
const normalizedParsed = isRecord2(parsed) && typeof parsed.toolArguments === "string" && ["", "{}"].includes(parsed.toolArguments.trim()) ? { ...parsed, toolArguments: {} } : parsed;
|
|
1239
|
+
const basicResult = validateJsonSchema(normalizedParsed, toolSelectionArgumentSchema);
|
|
1170
1240
|
if (basicResult.success === false) {
|
|
1171
1241
|
return { success: false, error: basicResult.error };
|
|
1172
1242
|
}
|
|
@@ -1181,6 +1251,17 @@ function validateToolSelectionArgument(parsed, toolInputSchema) {
|
|
|
1181
1251
|
return { success: true, data };
|
|
1182
1252
|
}
|
|
1183
1253
|
function validateResourceSelection(selection) {
|
|
1254
|
+
if (isRecord2(selection) && selection.noResourceAvailable === true) {
|
|
1255
|
+
return {
|
|
1256
|
+
success: true,
|
|
1257
|
+
data: {
|
|
1258
|
+
serverName: "",
|
|
1259
|
+
uri: "",
|
|
1260
|
+
noResourceAvailable: true,
|
|
1261
|
+
...optionalReasoning(selection)
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1184
1265
|
return validateJsonSchema(selection, ResourceSelectionSchema);
|
|
1185
1266
|
}
|
|
1186
1267
|
function createToolSelectionFeedbackPrompt(originalResponse, errorMessage, composedState, userMessage) {
|
|
@@ -1228,16 +1309,18 @@ function createResourceSelectionFeedbackPrompt(originalResponse, errorMessage, c
|
|
|
1228
1309
|
return createFeedbackPrompt(originalResponse, errorMessage, "resource", resourcesDescription, userMessage);
|
|
1229
1310
|
}
|
|
1230
1311
|
function createFeedbackPrompt(originalResponse, errorMessage, itemType, itemsDescription, userMessage) {
|
|
1231
|
-
return `
|
|
1312
|
+
return `The previous ${itemType} selection could not be parsed or validated: ${errorMessage}
|
|
1232
1313
|
|
|
1233
1314
|
Your original response:
|
|
1234
1315
|
${originalResponse}
|
|
1235
1316
|
|
|
1236
|
-
|
|
1317
|
+
Reply again as compact JSON for ${itemType} selection.
|
|
1237
1318
|
Available ${itemType}s:
|
|
1238
1319
|
${itemsDescription}
|
|
1239
1320
|
|
|
1240
|
-
User request: ${userMessage}
|
|
1321
|
+
User request: ${userMessage}
|
|
1322
|
+
|
|
1323
|
+
Use exact names from the list. For tools, return a JSON object with serverName, toolName, reasoning, and noToolAvailable. For resources, return a JSON object with serverName, uri, reasoning, and noResourceAvailable.`;
|
|
1241
1324
|
}
|
|
1242
1325
|
|
|
1243
1326
|
// src/utils/wrapper.ts
|
|
@@ -1254,15 +1337,21 @@ async function withModelRetry({
|
|
|
1254
1337
|
retryCount = 0
|
|
1255
1338
|
}) {
|
|
1256
1339
|
const maxRetries = getMaxRetries(runtime);
|
|
1257
|
-
|
|
1258
|
-
|
|
1340
|
+
let validationResult;
|
|
1341
|
+
try {
|
|
1342
|
+
const parsedInput = typeof input === "string" ? parseStructuredModelOutput(input) : input;
|
|
1343
|
+
validationResult = validationFn(parsedInput);
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
const errorMessage2 = error instanceof Error ? error.message : String(error);
|
|
1346
|
+
validationResult = { success: false, error: errorMessage2 };
|
|
1347
|
+
}
|
|
1259
1348
|
if (validationResult.success) {
|
|
1260
1349
|
return validationResult.data;
|
|
1261
1350
|
}
|
|
1262
1351
|
const errorMessage = validationResult.error;
|
|
1263
1352
|
if (retryCount < maxRetries) {
|
|
1264
1353
|
const feedbackPrompt = createFeedbackPromptFn(input, errorMessage, state, message.content.text ?? "");
|
|
1265
|
-
const retrySelection = await runtime.useModel(import_core3.ModelType.
|
|
1354
|
+
const retrySelection = await runtime.useModel(import_core3.ModelType.TEXT_LARGE, {
|
|
1266
1355
|
prompt: feedbackPrompt
|
|
1267
1356
|
});
|
|
1268
1357
|
return withModelRetry({
|
|
@@ -1287,11 +1376,8 @@ async function withModelRetry({
|
|
|
1287
1376
|
}
|
|
1288
1377
|
function getMaxRetries(runtime) {
|
|
1289
1378
|
const rawSettings = runtime.getSetting("mcp");
|
|
1290
|
-
if (rawSettings && typeof rawSettings === "
|
|
1291
|
-
|
|
1292
|
-
if (typeof settings.maxRetries === "number" && settings.maxRetries >= 0) {
|
|
1293
|
-
return settings.maxRetries;
|
|
1294
|
-
}
|
|
1379
|
+
if (isMcpSettings(rawSettings) && typeof rawSettings.maxRetries === "number" && rawSettings.maxRetries >= 0) {
|
|
1380
|
+
return rawSettings.maxRetries;
|
|
1295
1381
|
}
|
|
1296
1382
|
return DEFAULT_MAX_RETRIES;
|
|
1297
1383
|
}
|
|
@@ -1304,8 +1390,16 @@ async function createToolSelectionName({
|
|
|
1304
1390
|
callback,
|
|
1305
1391
|
mcpProvider
|
|
1306
1392
|
}) {
|
|
1393
|
+
const stateWithMcp = {
|
|
1394
|
+
...state,
|
|
1395
|
+
values: {
|
|
1396
|
+
...state.values,
|
|
1397
|
+
mcp: state.values.mcp ?? mcpProvider.data.mcp,
|
|
1398
|
+
mcpProvider
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1307
1401
|
const toolSelectionPrompt = import_core4.composePromptFromState({
|
|
1308
|
-
state:
|
|
1402
|
+
state: stateWithMcp,
|
|
1309
1403
|
template: toolSelectionNameTemplate
|
|
1310
1404
|
});
|
|
1311
1405
|
const toolSelectionName = await runtime.useModel(import_core4.ModelType.TEXT_LARGE, {
|
|
@@ -1314,10 +1408,10 @@ async function createToolSelectionName({
|
|
|
1314
1408
|
return await withModelRetry({
|
|
1315
1409
|
runtime,
|
|
1316
1410
|
message,
|
|
1317
|
-
state,
|
|
1411
|
+
state: stateWithMcp,
|
|
1318
1412
|
callback,
|
|
1319
1413
|
input: toolSelectionName,
|
|
1320
|
-
validationFn: (parsed) => validateToolSelectionName(parsed,
|
|
1414
|
+
validationFn: (parsed) => validateToolSelectionName(parsed, stateWithMcp),
|
|
1321
1415
|
createFeedbackPromptFn: (originalResponse, errorMessage, composedState, userMessage) => createToolSelectionFeedbackPrompt(typeof originalResponse === "string" ? originalResponse : JSON.stringify(originalResponse), errorMessage, composedState, userMessage),
|
|
1322
1416
|
failureMsg: "I'm having trouble figuring out the best way to help with your request."
|
|
1323
1417
|
});
|
|
@@ -1369,138 +1463,48 @@ async function createToolSelectionArgument({
|
|
|
1369
1463
|
});
|
|
1370
1464
|
}
|
|
1371
1465
|
|
|
1372
|
-
// src/actions/
|
|
1373
|
-
var
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
"
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
}
|
|
1415
|
-
},
|
|
1416
|
-
handler: async (runtime, message, _state, _options, callback) => {
|
|
1417
|
-
const composedState = await runtime.composeState(message, ["RECENT_MESSAGES", "MCP"]);
|
|
1418
|
-
const mcpService = runtime.getService(MCP_SERVICE_NAME);
|
|
1419
|
-
if (!mcpService) {
|
|
1420
|
-
throw new Error("MCP service not available");
|
|
1421
|
-
}
|
|
1422
|
-
const mcpProvider = mcpService.getProviderData();
|
|
1423
|
-
try {
|
|
1424
|
-
const toolSelectionName = await createToolSelectionName({
|
|
1425
|
-
runtime,
|
|
1426
|
-
state: composedState,
|
|
1427
|
-
message,
|
|
1428
|
-
callback,
|
|
1429
|
-
mcpProvider
|
|
1430
|
-
});
|
|
1431
|
-
if (!toolSelectionName || toolSelectionName.noToolAvailable) {
|
|
1432
|
-
return await handleNoToolAvailable(callback, toolSelectionName);
|
|
1433
|
-
}
|
|
1434
|
-
const { serverName, toolName } = toolSelectionName;
|
|
1435
|
-
const toolSelectionArgument = await createToolSelectionArgument({
|
|
1436
|
-
runtime,
|
|
1437
|
-
state: composedState,
|
|
1438
|
-
message,
|
|
1439
|
-
callback,
|
|
1440
|
-
mcpProvider,
|
|
1441
|
-
toolSelectionName
|
|
1442
|
-
});
|
|
1443
|
-
if (!toolSelectionArgument) {
|
|
1444
|
-
return await handleNoToolAvailable(callback, toolSelectionName);
|
|
1445
|
-
}
|
|
1446
|
-
const result = await mcpService.callTool(serverName, toolName, toolSelectionArgument.toolArguments);
|
|
1447
|
-
const { toolOutput, hasAttachments, attachments } = processToolResult(result, serverName, toolName, runtime, message.entityId);
|
|
1448
|
-
const replyMemory = await handleToolResponse(runtime, message, serverName, toolName, toolSelectionArgument.toolArguments, toolOutput, hasAttachments, attachments, composedState, mcpProvider, callback);
|
|
1449
|
-
return {
|
|
1450
|
-
text: `Successfully called tool: ${serverName}/${toolName}. Reasoned response: ${replyMemory.content.text}`,
|
|
1451
|
-
values: {
|
|
1452
|
-
success: true,
|
|
1453
|
-
toolExecuted: true,
|
|
1454
|
-
serverName,
|
|
1455
|
-
toolName,
|
|
1456
|
-
hasAttachments,
|
|
1457
|
-
output: toolOutput
|
|
1458
|
-
},
|
|
1459
|
-
data: {
|
|
1460
|
-
actionName: "CALL_MCP_TOOL",
|
|
1461
|
-
serverName,
|
|
1462
|
-
toolName,
|
|
1463
|
-
toolArgumentsJson: JSON.stringify(toolSelectionArgument.toolArguments),
|
|
1464
|
-
reasoning: toolSelectionName.reasoning,
|
|
1465
|
-
output: toolOutput,
|
|
1466
|
-
attachmentCount: attachments?.length ?? 0
|
|
1467
|
-
},
|
|
1468
|
-
success: true
|
|
1469
|
-
};
|
|
1470
|
-
} catch (error) {
|
|
1471
|
-
return await handleMcpError(composedState, mcpProvider, error, runtime, message, "tool", callback);
|
|
1472
|
-
}
|
|
1473
|
-
},
|
|
1474
|
-
examples: [
|
|
1475
|
-
[
|
|
1476
|
-
{
|
|
1477
|
-
name: "{{user}}",
|
|
1478
|
-
content: {
|
|
1479
|
-
text: "Can you search for information about climate change?"
|
|
1480
|
-
}
|
|
1481
|
-
},
|
|
1482
|
-
{
|
|
1483
|
-
name: "{{assistant}}",
|
|
1484
|
-
content: {
|
|
1485
|
-
text: "I'll help you with that request. Let me access the right tool...",
|
|
1486
|
-
actions: ["CALL_MCP_TOOL"]
|
|
1487
|
-
}
|
|
1488
|
-
},
|
|
1489
|
-
{
|
|
1490
|
-
name: "{{assistant}}",
|
|
1491
|
-
content: {
|
|
1492
|
-
text: `I found the following information about climate change:
|
|
1493
|
-
|
|
1494
|
-
Climate change refers to long-term shifts in temperatures and weather patterns. These shifts may be natural, but since the 1800s, human activities have been the main driver of climate change, primarily due to the burning of fossil fuels like coal, oil, and gas, which produces heat-trapping gases.`,
|
|
1495
|
-
actions: ["CALL_MCP_TOOL"]
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
]
|
|
1499
|
-
]
|
|
1500
|
-
};
|
|
1501
|
-
|
|
1502
|
-
// src/actions/readResourceAction.ts
|
|
1503
|
-
var import_core5 = require("@elizaos/core");
|
|
1466
|
+
// src/actions/mcp.ts
|
|
1467
|
+
var MCP_ACTION_CONTEXT = "mcp";
|
|
1468
|
+
function readOptions(options) {
|
|
1469
|
+
const direct = options ?? {};
|
|
1470
|
+
const parameters = direct.parameters && typeof direct.parameters === "object" ? direct.parameters : {};
|
|
1471
|
+
return { ...direct, ...parameters };
|
|
1472
|
+
}
|
|
1473
|
+
function normalizeOp(value) {
|
|
1474
|
+
if (typeof value !== "string")
|
|
1475
|
+
return null;
|
|
1476
|
+
const v = value.trim().toLowerCase();
|
|
1477
|
+
if (v === "call_tool" || v === "tool" || v === "call")
|
|
1478
|
+
return "call_tool";
|
|
1479
|
+
if (v === "read_resource" || v === "resource" || v === "read")
|
|
1480
|
+
return "read_resource";
|
|
1481
|
+
if (v === "search_actions" || v === "search" || v === "discover")
|
|
1482
|
+
return "search_actions";
|
|
1483
|
+
if (v === "list_connections" || v === "list" || v === "connections")
|
|
1484
|
+
return "list_connections";
|
|
1485
|
+
return null;
|
|
1486
|
+
}
|
|
1487
|
+
function inferOpFromText(text) {
|
|
1488
|
+
if (/\b(read|get|fetch|access|open|list)\b.*\b(resource|resources|document|docs?|file)\b/i.test(text)) {
|
|
1489
|
+
return "read_resource";
|
|
1490
|
+
}
|
|
1491
|
+
if (/\b(call|use|run|execute|invoke|search|query)\b.*\b(tool|tools|mcp)\b/i.test(text)) {
|
|
1492
|
+
return "call_tool";
|
|
1493
|
+
}
|
|
1494
|
+
return null;
|
|
1495
|
+
}
|
|
1496
|
+
function getDirectResourceSelection(options) {
|
|
1497
|
+
const params = readOptions(options);
|
|
1498
|
+
const serverName = typeof params.serverName === "string" ? params.serverName.trim() : "";
|
|
1499
|
+
const uri = typeof params.uri === "string" ? params.uri.trim() : "";
|
|
1500
|
+
if (!serverName || !uri)
|
|
1501
|
+
return null;
|
|
1502
|
+
return {
|
|
1503
|
+
serverName,
|
|
1504
|
+
uri,
|
|
1505
|
+
reasoning: typeof params.reasoning === "string" && params.reasoning.trim() ? params.reasoning.trim() : "Selected from structured MCP read_resource parameters."
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1504
1508
|
function createResourceSelectionPrompt(composedState, userMessage) {
|
|
1505
1509
|
const mcpData = composedState.values.mcp ?? {};
|
|
1506
1510
|
const serverNames = Object.keys(mcpData);
|
|
@@ -1536,61 +1540,80 @@ function createResourceSelectionPrompt(composedState, userMessage) {
|
|
|
1536
1540
|
template: resourceSelectionTemplate
|
|
1537
1541
|
});
|
|
1538
1542
|
}
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
"
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
|
|
1557
|
-
const __avRegex = /\b(?:read|mcp|resource)\b/i;
|
|
1558
|
-
const __avRegexOk = __avRegex.test(__avText);
|
|
1559
|
-
const __avSource = String(message?.content?.source ?? message?.source ?? "");
|
|
1560
|
-
const __avExpectedSource = "";
|
|
1561
|
-
const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
|
|
1562
|
-
const __avOptions = options && typeof options === "object" ? options : {};
|
|
1563
|
-
const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
|
|
1564
|
-
if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
|
|
1565
|
-
return false;
|
|
1566
|
-
}
|
|
1567
|
-
const __avLegacyValidate = async (runtime2, _message, _state) => {
|
|
1568
|
-
const mcpService = runtime2.getService(MCP_SERVICE_NAME);
|
|
1569
|
-
if (!mcpService)
|
|
1570
|
-
return false;
|
|
1571
|
-
const servers = mcpService.getServers();
|
|
1572
|
-
return servers.length > 0 && servers.some((server) => server.status === "connected" && server.resources && server.resources.length > 0);
|
|
1573
|
-
};
|
|
1574
|
-
try {
|
|
1575
|
-
return Boolean(await __avLegacyValidate(runtime, message, state, options));
|
|
1576
|
-
} catch {
|
|
1577
|
-
return false;
|
|
1543
|
+
async function handleCallTool(runtime, message, callback) {
|
|
1544
|
+
const composedState = await runtime.composeState(message, ["RECENT_MESSAGES", "MCP"]);
|
|
1545
|
+
const mcpService = runtime.getService(MCP_SERVICE_NAME);
|
|
1546
|
+
if (!mcpService) {
|
|
1547
|
+
throw new Error("MCP service not available");
|
|
1548
|
+
}
|
|
1549
|
+
const mcpProvider = mcpService.getProviderData();
|
|
1550
|
+
try {
|
|
1551
|
+
const toolSelectionName = await createToolSelectionName({
|
|
1552
|
+
runtime,
|
|
1553
|
+
state: composedState,
|
|
1554
|
+
message,
|
|
1555
|
+
callback,
|
|
1556
|
+
mcpProvider
|
|
1557
|
+
});
|
|
1558
|
+
if (!toolSelectionName || toolSelectionName.noToolAvailable) {
|
|
1559
|
+
return await handleNoToolAvailable(callback, toolSelectionName);
|
|
1578
1560
|
}
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1561
|
+
const { serverName, toolName } = toolSelectionName;
|
|
1562
|
+
const toolSelectionArgument = await createToolSelectionArgument({
|
|
1563
|
+
runtime,
|
|
1564
|
+
state: composedState,
|
|
1565
|
+
message,
|
|
1566
|
+
callback,
|
|
1567
|
+
mcpProvider,
|
|
1568
|
+
toolSelectionName
|
|
1569
|
+
});
|
|
1570
|
+
if (!toolSelectionArgument) {
|
|
1571
|
+
return await handleNoToolAvailable(callback, toolSelectionName);
|
|
1585
1572
|
}
|
|
1586
|
-
const
|
|
1587
|
-
|
|
1588
|
-
|
|
1573
|
+
const result = await mcpService.callTool(serverName, toolName, toolSelectionArgument.toolArguments);
|
|
1574
|
+
const { toolOutput, hasAttachments, attachments } = processToolResult(result, serverName, toolName, runtime, message.entityId);
|
|
1575
|
+
const replyMemory = await handleToolResponse(runtime, message, serverName, toolName, toolSelectionArgument.toolArguments, toolOutput, hasAttachments, attachments, composedState, mcpProvider, callback);
|
|
1576
|
+
return {
|
|
1577
|
+
text: `Successfully called tool: ${serverName}/${toolName}. Reasoned response: ${replyMemory.content.text}`,
|
|
1578
|
+
values: {
|
|
1579
|
+
success: true,
|
|
1580
|
+
toolExecuted: true,
|
|
1581
|
+
serverName,
|
|
1582
|
+
toolName,
|
|
1583
|
+
hasAttachments,
|
|
1584
|
+
output: toolOutput
|
|
1585
|
+
},
|
|
1586
|
+
data: {
|
|
1587
|
+
actionName: "MCP",
|
|
1588
|
+
op: "call_tool",
|
|
1589
|
+
serverName,
|
|
1590
|
+
toolName,
|
|
1591
|
+
toolArgumentsJson: JSON.stringify(toolSelectionArgument.toolArguments),
|
|
1592
|
+
reasoning: toolSelectionName.reasoning,
|
|
1593
|
+
output: toolOutput,
|
|
1594
|
+
attachmentCount: attachments?.length ?? 0
|
|
1595
|
+
},
|
|
1596
|
+
success: true
|
|
1597
|
+
};
|
|
1598
|
+
} catch (error) {
|
|
1599
|
+
return await handleMcpError(composedState, mcpProvider, error, runtime, message, "tool", callback);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
async function handleReadResource(runtime, message, options, callback) {
|
|
1603
|
+
const composedState = await runtime.composeState(message, ["RECENT_MESSAGES", "MCP"]);
|
|
1604
|
+
const mcpService = runtime.getService(MCP_SERVICE_NAME);
|
|
1605
|
+
if (!mcpService) {
|
|
1606
|
+
throw new Error("MCP service not available");
|
|
1607
|
+
}
|
|
1608
|
+
const mcpProvider = mcpService.getProviderData();
|
|
1609
|
+
try {
|
|
1610
|
+
await sendInitialResponse(callback);
|
|
1611
|
+
const parsedSelection = getDirectResourceSelection(options) ?? await (async () => {
|
|
1589
1612
|
const resourceSelectionPrompt = createResourceSelectionPrompt(composedState, message.content.text ?? "");
|
|
1590
1613
|
const resourceSelection = await runtime.useModel(import_core5.ModelType.TEXT_SMALL, {
|
|
1591
1614
|
prompt: resourceSelectionPrompt
|
|
1592
1615
|
});
|
|
1593
|
-
|
|
1616
|
+
return withModelRetry({
|
|
1594
1617
|
runtime,
|
|
1595
1618
|
state: composedState,
|
|
1596
1619
|
message,
|
|
@@ -1601,75 +1624,210 @@ var readResourceAction = {
|
|
|
1601
1624
|
failureMsg: `I'm having trouble finding the resource you're looking for. Could you provide more details about what you need?`,
|
|
1602
1625
|
retryCount: 0
|
|
1603
1626
|
});
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
actions: ["REPLY"]
|
|
1610
|
-
});
|
|
1611
|
-
}
|
|
1612
|
-
return {
|
|
1627
|
+
})();
|
|
1628
|
+
if (!parsedSelection || parsedSelection.noResourceAvailable) {
|
|
1629
|
+
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.";
|
|
1630
|
+
if (callback && parsedSelection?.noResourceAvailable) {
|
|
1631
|
+
await callback({
|
|
1613
1632
|
text: responseText,
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
noResourceAvailable: true,
|
|
1617
|
-
fallbackToDirectAssistance: true
|
|
1618
|
-
},
|
|
1619
|
-
data: {
|
|
1620
|
-
actionName: "READ_MCP_RESOURCE",
|
|
1621
|
-
noResourceAvailable: true,
|
|
1622
|
-
reason: parsedSelection?.reasoning ?? "No appropriate resource available"
|
|
1623
|
-
},
|
|
1624
|
-
success: true
|
|
1625
|
-
};
|
|
1633
|
+
actions: ["REPLY"]
|
|
1634
|
+
});
|
|
1626
1635
|
}
|
|
1627
|
-
const { serverName, uri } = parsedSelection;
|
|
1628
|
-
const result = await mcpService.readResource(serverName, uri);
|
|
1629
|
-
const { resourceContent, resourceMeta } = processResourceResult(result, uri);
|
|
1630
|
-
await handleResourceAnalysis(runtime, message, uri, serverName, resourceContent, resourceMeta, callback);
|
|
1631
1636
|
return {
|
|
1632
|
-
text:
|
|
1637
|
+
text: responseText,
|
|
1633
1638
|
values: {
|
|
1634
1639
|
success: true,
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
uri
|
|
1640
|
+
noResourceAvailable: true,
|
|
1641
|
+
fallbackToDirectAssistance: true
|
|
1638
1642
|
},
|
|
1639
1643
|
data: {
|
|
1640
|
-
actionName: "
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
resourceMeta,
|
|
1645
|
-
contentLength: resourceContent?.length ?? 0
|
|
1644
|
+
actionName: "MCP",
|
|
1645
|
+
op: "read_resource",
|
|
1646
|
+
noResourceAvailable: true,
|
|
1647
|
+
reason: parsedSelection?.reasoning ?? "No appropriate resource available"
|
|
1646
1648
|
},
|
|
1647
1649
|
success: true
|
|
1648
1650
|
};
|
|
1649
|
-
} catch (error) {
|
|
1650
|
-
return await handleMcpError(composedState, mcpProvider, error, runtime, message, "resource", callback);
|
|
1651
1651
|
}
|
|
1652
|
+
const { serverName, uri } = parsedSelection;
|
|
1653
|
+
const result = await mcpService.readResource(serverName, uri);
|
|
1654
|
+
const { resourceContent, resourceMeta } = processResourceResult(result, uri);
|
|
1655
|
+
await handleResourceAnalysis(runtime, message, uri, serverName, resourceContent, resourceMeta, callback);
|
|
1656
|
+
return {
|
|
1657
|
+
text: `Successfully read resource: ${uri}`,
|
|
1658
|
+
values: {
|
|
1659
|
+
success: true,
|
|
1660
|
+
resourceRead: true,
|
|
1661
|
+
serverName,
|
|
1662
|
+
uri
|
|
1663
|
+
},
|
|
1664
|
+
data: {
|
|
1665
|
+
actionName: "MCP",
|
|
1666
|
+
op: "read_resource",
|
|
1667
|
+
serverName,
|
|
1668
|
+
uri,
|
|
1669
|
+
reasoning: parsedSelection?.reasoning,
|
|
1670
|
+
resourceMeta,
|
|
1671
|
+
contentLength: resourceContent?.length ?? 0
|
|
1672
|
+
},
|
|
1673
|
+
success: true
|
|
1674
|
+
};
|
|
1675
|
+
} catch (error) {
|
|
1676
|
+
return await handleMcpError(composedState, mcpProvider, error, runtime, message, "resource", callback);
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
function textOf(message) {
|
|
1680
|
+
return typeof message.content?.text === "string" ? message.content.text : "";
|
|
1681
|
+
}
|
|
1682
|
+
function hasConnectedCapability(runtime) {
|
|
1683
|
+
const mcpService = runtime.getService(MCP_SERVICE_NAME);
|
|
1684
|
+
if (!mcpService)
|
|
1685
|
+
return false;
|
|
1686
|
+
return mcpService.getServers().some((server) => {
|
|
1687
|
+
if (server.status !== "connected")
|
|
1688
|
+
return false;
|
|
1689
|
+
return (server.tools?.length ?? 0) > 0 || (server.resources?.length ?? 0) > 0;
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
var mcpAction = {
|
|
1693
|
+
name: "MCP",
|
|
1694
|
+
contexts: ["general", "automation", "knowledge", "connectors", MCP_ACTION_CONTEXT, "files"],
|
|
1695
|
+
contextGate: {
|
|
1696
|
+
anyOf: ["general", "automation", "knowledge", "connectors", MCP_ACTION_CONTEXT, "files"]
|
|
1697
|
+
},
|
|
1698
|
+
roleGate: { minRole: "USER" },
|
|
1699
|
+
similes: [
|
|
1700
|
+
"MCP_ACTION",
|
|
1701
|
+
"MCP_ROUTER",
|
|
1702
|
+
"USE_MCP",
|
|
1703
|
+
"CALL_MCP_TOOL",
|
|
1704
|
+
"CALL_TOOL",
|
|
1705
|
+
"USE_TOOL",
|
|
1706
|
+
"USE_MCP_TOOL",
|
|
1707
|
+
"EXECUTE_TOOL",
|
|
1708
|
+
"EXECUTE_MCP_TOOL",
|
|
1709
|
+
"RUN_TOOL",
|
|
1710
|
+
"RUN_MCP_TOOL",
|
|
1711
|
+
"INVOKE_TOOL",
|
|
1712
|
+
"INVOKE_MCP_TOOL",
|
|
1713
|
+
"READ_MCP_RESOURCE",
|
|
1714
|
+
"READ_RESOURCE",
|
|
1715
|
+
"GET_RESOURCE",
|
|
1716
|
+
"GET_MCP_RESOURCE",
|
|
1717
|
+
"FETCH_RESOURCE",
|
|
1718
|
+
"FETCH_MCP_RESOURCE",
|
|
1719
|
+
"ACCESS_RESOURCE",
|
|
1720
|
+
"ACCESS_MCP_RESOURCE"
|
|
1721
|
+
],
|
|
1722
|
+
description: "Single MCP entry point. Use action=call_tool to invoke an MCP tool, action=read_resource to read an MCP resource. Cloud runtimes also accept action=search_actions and action=list_connections.",
|
|
1723
|
+
descriptionCompressed: "MCP call_tool read_resource search_actions list_connections",
|
|
1724
|
+
parameters: [
|
|
1725
|
+
{
|
|
1726
|
+
name: "action",
|
|
1727
|
+
description: "MCP operation: call_tool | read_resource | search_actions | list_connections",
|
|
1728
|
+
required: false,
|
|
1729
|
+
schema: {
|
|
1730
|
+
type: "string",
|
|
1731
|
+
enum: ["call_tool", "read_resource", "search_actions", "list_connections"]
|
|
1732
|
+
}
|
|
1733
|
+
},
|
|
1734
|
+
{
|
|
1735
|
+
name: "serverName",
|
|
1736
|
+
description: "Optional MCP server name that owns the tool or resource.",
|
|
1737
|
+
required: false,
|
|
1738
|
+
schema: { type: "string" }
|
|
1739
|
+
},
|
|
1740
|
+
{
|
|
1741
|
+
name: "toolName",
|
|
1742
|
+
description: "For action=call_tool: optional exact MCP tool name to call.",
|
|
1743
|
+
required: false,
|
|
1744
|
+
schema: { type: "string" }
|
|
1745
|
+
},
|
|
1746
|
+
{
|
|
1747
|
+
name: "arguments",
|
|
1748
|
+
description: "For action=call_tool: optional JSON arguments to pass to the selected MCP tool.",
|
|
1749
|
+
required: false,
|
|
1750
|
+
schema: { type: "object" }
|
|
1751
|
+
},
|
|
1752
|
+
{
|
|
1753
|
+
name: "uri",
|
|
1754
|
+
description: "For action=read_resource: exact MCP resource URI to read.",
|
|
1755
|
+
required: false,
|
|
1756
|
+
schema: { type: "string" }
|
|
1757
|
+
},
|
|
1758
|
+
{
|
|
1759
|
+
name: "query",
|
|
1760
|
+
description: "Natural-language description of the tool call or resource to select; for action=search_actions, the keyword query.",
|
|
1761
|
+
required: false,
|
|
1762
|
+
schema: { type: "string" }
|
|
1763
|
+
},
|
|
1764
|
+
{
|
|
1765
|
+
name: "platform",
|
|
1766
|
+
description: "For action=search_actions: filter results to a single connected platform.",
|
|
1767
|
+
required: false,
|
|
1768
|
+
schema: { type: "string" }
|
|
1769
|
+
},
|
|
1770
|
+
{
|
|
1771
|
+
name: "limit",
|
|
1772
|
+
description: "For action=search_actions: maximum results to return.",
|
|
1773
|
+
required: false,
|
|
1774
|
+
schema: { type: "number" }
|
|
1775
|
+
},
|
|
1776
|
+
{
|
|
1777
|
+
name: "offset",
|
|
1778
|
+
description: "For action=search_actions: skip first N results for pagination.",
|
|
1779
|
+
required: false,
|
|
1780
|
+
schema: { type: "number" }
|
|
1781
|
+
}
|
|
1782
|
+
],
|
|
1783
|
+
validate: async (runtime) => {
|
|
1784
|
+
if (!hasConnectedCapability(runtime))
|
|
1785
|
+
return false;
|
|
1786
|
+
return true;
|
|
1787
|
+
},
|
|
1788
|
+
handler: async (runtime, message, _state, options, callback) => {
|
|
1789
|
+
const opts = readOptions(options);
|
|
1790
|
+
const requested = normalizeOp(opts.action ?? opts.subaction ?? opts.op ?? opts.operation);
|
|
1791
|
+
const op = requested ?? inferOpFromText(textOf(message)) ?? "call_tool";
|
|
1792
|
+
if (op === "read_resource") {
|
|
1793
|
+
return handleReadResource(runtime, message, options, callback);
|
|
1794
|
+
}
|
|
1795
|
+
if (op === "search_actions" || op === "list_connections") {
|
|
1796
|
+
const text = `MCP op=${op} is only available in the cloud runtime.`;
|
|
1797
|
+
await callback?.({ text, source: message.content?.source });
|
|
1798
|
+
return {
|
|
1799
|
+
success: false,
|
|
1800
|
+
text,
|
|
1801
|
+
values: { error: "OP_NOT_SUPPORTED" },
|
|
1802
|
+
data: { actionName: "MCP", op }
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
return handleCallTool(runtime, message, callback);
|
|
1652
1806
|
},
|
|
1653
1807
|
examples: [
|
|
1654
1808
|
[
|
|
1655
1809
|
{
|
|
1656
1810
|
name: "{{user}}",
|
|
1657
|
-
content: {
|
|
1658
|
-
text: "Can you get the documentation about installing elizaOS?"
|
|
1659
|
-
}
|
|
1811
|
+
content: { text: "Use the MCP GitHub tool to read the repository README" }
|
|
1660
1812
|
},
|
|
1661
1813
|
{
|
|
1662
1814
|
name: "{{assistant}}",
|
|
1663
1815
|
content: {
|
|
1664
|
-
text:
|
|
1665
|
-
actions: ["
|
|
1816
|
+
text: "I'll route that through MCP.",
|
|
1817
|
+
actions: ["MCP"]
|
|
1666
1818
|
}
|
|
1819
|
+
}
|
|
1820
|
+
],
|
|
1821
|
+
[
|
|
1822
|
+
{
|
|
1823
|
+
name: "{{user}}",
|
|
1824
|
+
content: { text: "Can you get the documentation about installing elizaOS?" }
|
|
1667
1825
|
},
|
|
1668
1826
|
{
|
|
1669
1827
|
name: "{{assistant}}",
|
|
1670
1828
|
content: {
|
|
1671
|
-
text:
|
|
1672
|
-
actions: ["
|
|
1829
|
+
text: "I'll read the MCP resource for that.",
|
|
1830
|
+
actions: ["MCP"]
|
|
1673
1831
|
}
|
|
1674
1832
|
}
|
|
1675
1833
|
]
|
|
@@ -1677,10 +1835,36 @@ var readResourceAction = {
|
|
|
1677
1835
|
};
|
|
1678
1836
|
|
|
1679
1837
|
// src/provider.ts
|
|
1838
|
+
var MAX_MCP_SERVERS_IN_STATE = 20;
|
|
1839
|
+
var MAX_MCP_TOOLS_PER_SERVER = 30;
|
|
1840
|
+
var MAX_MCP_RESOURCES_PER_SERVER = 30;
|
|
1841
|
+
function formatMcpServersForPrompt(mcp) {
|
|
1842
|
+
const entries = Object.entries(mcp).slice(0, MAX_MCP_SERVERS_IN_STATE);
|
|
1843
|
+
if (entries.length === 0)
|
|
1844
|
+
return "No MCP servers are available.";
|
|
1845
|
+
return [
|
|
1846
|
+
`mcpServers[${Object.keys(mcp).length}, showing ${entries.length}]:`,
|
|
1847
|
+
...entries.flatMap(([serverName, server]) => {
|
|
1848
|
+
const tools = Object.keys(server.tools ?? {}).slice(0, MAX_MCP_TOOLS_PER_SERVER);
|
|
1849
|
+
const resources = Object.keys(server.resources ?? {}).slice(0, MAX_MCP_RESOURCES_PER_SERVER);
|
|
1850
|
+
return [
|
|
1851
|
+
` - name: ${serverName}`,
|
|
1852
|
+
` status: ${server.status}`,
|
|
1853
|
+
` tools: ${tools.length > 0 ? tools.join(", ") : "none"}`,
|
|
1854
|
+
` resources: ${resources.length > 0 ? resources.join(", ") : "none"}`
|
|
1855
|
+
];
|
|
1856
|
+
})
|
|
1857
|
+
].join(`
|
|
1858
|
+
`);
|
|
1859
|
+
}
|
|
1680
1860
|
var provider = {
|
|
1681
1861
|
name: "MCP",
|
|
1682
1862
|
description: "Information about connected MCP servers, tools, and resources",
|
|
1683
1863
|
dynamic: true,
|
|
1864
|
+
contexts: ["connectors", "settings"],
|
|
1865
|
+
contextGate: { anyOf: ["connectors", "settings"] },
|
|
1866
|
+
cacheStable: false,
|
|
1867
|
+
cacheScope: "turn",
|
|
1684
1868
|
get: async (runtime, _message, _state) => {
|
|
1685
1869
|
const mcpService = runtime.getService(MCP_SERVICE_NAME);
|
|
1686
1870
|
if (!mcpService) {
|
|
@@ -1690,17 +1874,31 @@ var provider = {
|
|
|
1690
1874
|
text: "No MCP servers are available."
|
|
1691
1875
|
};
|
|
1692
1876
|
}
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1877
|
+
try {
|
|
1878
|
+
const providerData = mcpService.getProviderData();
|
|
1879
|
+
const mcp = providerData.values.mcp;
|
|
1880
|
+
const serverEntries = Object.entries(providerData.data.mcp).slice(0, MAX_MCP_SERVERS_IN_STATE);
|
|
1881
|
+
return {
|
|
1882
|
+
values: { mcpServers: formatMcpServersForPrompt(mcp) },
|
|
1883
|
+
data: {
|
|
1884
|
+
mcpServerCount: Object.keys(providerData.data.mcp).length,
|
|
1885
|
+
shownMcpServerCount: serverEntries.length
|
|
1886
|
+
},
|
|
1887
|
+
text: formatMcpServersForPrompt(mcp)
|
|
1888
|
+
};
|
|
1889
|
+
} catch (error) {
|
|
1890
|
+
return {
|
|
1891
|
+
values: {},
|
|
1892
|
+
data: { error: error instanceof Error ? error.message : String(error) },
|
|
1893
|
+
text: "No MCP servers are available."
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1699
1896
|
}
|
|
1700
1897
|
};
|
|
1701
1898
|
|
|
1702
1899
|
// src/service.ts
|
|
1703
1900
|
var import_core6 = require("@elizaos/core");
|
|
1901
|
+
var import_mcp_server_config = require("@elizaos/security/mcp-server-config");
|
|
1704
1902
|
var import_client = require("@modelcontextprotocol/sdk/client/index.js");
|
|
1705
1903
|
var import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
1706
1904
|
var import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
@@ -1742,7 +1940,9 @@ class McpService extends import_core6.Service {
|
|
|
1742
1940
|
initializationPromise = null;
|
|
1743
1941
|
constructor(runtime) {
|
|
1744
1942
|
super(runtime);
|
|
1745
|
-
|
|
1943
|
+
if (runtime) {
|
|
1944
|
+
this.initializationPromise = this.initializeMcpServers();
|
|
1945
|
+
}
|
|
1746
1946
|
}
|
|
1747
1947
|
static async start(runtime) {
|
|
1748
1948
|
const service = new McpService(runtime);
|
|
@@ -1771,7 +1971,7 @@ class McpService extends import_core6.Service {
|
|
|
1771
1971
|
}
|
|
1772
1972
|
async initializeMcpServers() {
|
|
1773
1973
|
const mcpSettings = this.getMcpSettings();
|
|
1774
|
-
if (!mcpSettings
|
|
1974
|
+
if (!mcpSettings?.servers || Object.keys(mcpSettings.servers).length === 0) {
|
|
1775
1975
|
this.mcpProvider = buildMcpProviderData([]);
|
|
1776
1976
|
return;
|
|
1777
1977
|
}
|
|
@@ -1782,17 +1982,14 @@ class McpService extends import_core6.Service {
|
|
|
1782
1982
|
getMcpSettings() {
|
|
1783
1983
|
const rawSettings = this.runtime.getSetting("mcp");
|
|
1784
1984
|
let settings = null;
|
|
1785
|
-
if (
|
|
1786
|
-
|
|
1787
|
-
if ("servers" in parsed && typeof parsed.servers === "object" && parsed.servers !== null) {
|
|
1788
|
-
settings = parsed;
|
|
1789
|
-
}
|
|
1985
|
+
if (isMcpSettings(rawSettings)) {
|
|
1986
|
+
settings = rawSettings;
|
|
1790
1987
|
}
|
|
1791
|
-
if (!settings
|
|
1988
|
+
if (!settings?.servers) {
|
|
1792
1989
|
const characterSettings = this.runtime.character.settings;
|
|
1793
1990
|
if (characterSettings && typeof characterSettings === "object" && "mcp" in characterSettings) {
|
|
1794
1991
|
const characterMcpSettings = characterSettings.mcp;
|
|
1795
|
-
if (characterMcpSettings
|
|
1992
|
+
if (isMcpSettings(characterMcpSettings)) {
|
|
1796
1993
|
settings = characterMcpSettings;
|
|
1797
1994
|
}
|
|
1798
1995
|
}
|
|
@@ -1802,15 +1999,28 @@ class McpService extends import_core6.Service {
|
|
|
1802
1999
|
}
|
|
1803
2000
|
return;
|
|
1804
2001
|
}
|
|
2002
|
+
async filterValidatedServerConfigs(serverConfigs) {
|
|
2003
|
+
const validated = {};
|
|
2004
|
+
for (const [name, config] of Object.entries(serverConfigs)) {
|
|
2005
|
+
const rejection = await import_mcp_server_config.validateMcpServerConfig(config);
|
|
2006
|
+
if (rejection) {
|
|
2007
|
+
import_core6.logger.error({ server: name, rejection }, "Skipping MCP server with invalid or unsafe config");
|
|
2008
|
+
continue;
|
|
2009
|
+
}
|
|
2010
|
+
validated[name] = config;
|
|
2011
|
+
}
|
|
2012
|
+
return validated;
|
|
2013
|
+
}
|
|
1805
2014
|
async updateServerConnections(serverConfigs) {
|
|
2015
|
+
const safeConfigs = await this.filterValidatedServerConfigs(serverConfigs);
|
|
1806
2016
|
const currentNames = new Set(this.connections.keys());
|
|
1807
|
-
const newNames = new Set(Object.keys(
|
|
2017
|
+
const newNames = new Set(Object.keys(safeConfigs));
|
|
1808
2018
|
for (const name of currentNames) {
|
|
1809
2019
|
if (!newNames.has(name)) {
|
|
1810
2020
|
await this.deleteConnection(name);
|
|
1811
2021
|
}
|
|
1812
2022
|
}
|
|
1813
|
-
const connectionPromises = Object.entries(
|
|
2023
|
+
const connectionPromises = Object.entries(safeConfigs).map(async ([name, config]) => {
|
|
1814
2024
|
const currentConnection = this.connections.get(name);
|
|
1815
2025
|
if (!currentConnection) {
|
|
1816
2026
|
await this.initializeConnection(name, config);
|
|
@@ -1956,9 +2166,16 @@ class McpService extends import_core6.Service {
|
|
|
1956
2166
|
async deleteConnection(name) {
|
|
1957
2167
|
const connection = this.connections.get(name);
|
|
1958
2168
|
if (connection) {
|
|
1959
|
-
await
|
|
1960
|
-
|
|
2169
|
+
const closeResults = await Promise.allSettled([
|
|
2170
|
+
connection.transport.close(),
|
|
2171
|
+
connection.client.close()
|
|
2172
|
+
]);
|
|
1961
2173
|
this.connections.delete(name);
|
|
2174
|
+
for (const result of closeResults) {
|
|
2175
|
+
if (result.status === "rejected") {
|
|
2176
|
+
import_core6.logger.warn({ error: result.reason, serverName: name }, `Failed to close MCP connection resource for "${name}"`);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
1962
2179
|
}
|
|
1963
2180
|
const state = this.connectionStates.get(name);
|
|
1964
2181
|
if (state) {
|
|
@@ -1976,6 +2193,17 @@ class McpService extends import_core6.Service {
|
|
|
1976
2193
|
if (!config.command) {
|
|
1977
2194
|
throw new Error(`Missing command for stdio MCP server ${name}`);
|
|
1978
2195
|
}
|
|
2196
|
+
const rejection = await import_mcp_server_config.validateMcpServerConfig({
|
|
2197
|
+
type: "stdio",
|
|
2198
|
+
command: config.command,
|
|
2199
|
+
args: config.args,
|
|
2200
|
+
env: config.env,
|
|
2201
|
+
cwd: config.cwd,
|
|
2202
|
+
timeoutInMillis: config.timeoutInMillis
|
|
2203
|
+
});
|
|
2204
|
+
if (rejection) {
|
|
2205
|
+
throw new Error(`MCP stdio server "${name}" rejected at spawn: ${rejection}`);
|
|
2206
|
+
}
|
|
1979
2207
|
return new import_stdio.StdioClientTransport({
|
|
1980
2208
|
command: config.command,
|
|
1981
2209
|
args: config.args ? [...config.args] : undefined,
|
|
@@ -1991,6 +2219,13 @@ class McpService extends import_core6.Service {
|
|
|
1991
2219
|
if (!config.url) {
|
|
1992
2220
|
throw new Error(`Missing URL for HTTP MCP server ${name}`);
|
|
1993
2221
|
}
|
|
2222
|
+
const rejection = await import_mcp_server_config.validateMcpServerConfig({
|
|
2223
|
+
type: config.type,
|
|
2224
|
+
url: config.url
|
|
2225
|
+
});
|
|
2226
|
+
if (rejection) {
|
|
2227
|
+
throw new Error(`MCP remote server "${name}" rejected at connect: ${rejection}`);
|
|
2228
|
+
}
|
|
1994
2229
|
return new import_sse.SSEClientTransport(new URL(config.url));
|
|
1995
2230
|
}
|
|
1996
2231
|
appendErrorMessage(connection, error) {
|
|
@@ -2097,17 +2332,324 @@ ${error}` : error;
|
|
|
2097
2332
|
}
|
|
2098
2333
|
}
|
|
2099
2334
|
|
|
2335
|
+
// src/routes-mcp.ts
|
|
2336
|
+
var import_core7 = require("@elizaos/core");
|
|
2337
|
+
|
|
2338
|
+
// src/mcp-marketplace.ts
|
|
2339
|
+
var MCP_REGISTRY_BASE_URL = "https://registry.modelcontextprotocol.io";
|
|
2340
|
+
async function searchMcpMarketplace(query, limit = 30) {
|
|
2341
|
+
const resp = await fetch(`${MCP_REGISTRY_BASE_URL}/v0/servers`, {
|
|
2342
|
+
headers: {
|
|
2343
|
+
Accept: "application/json"
|
|
2344
|
+
}
|
|
2345
|
+
});
|
|
2346
|
+
if (!resp.ok) {
|
|
2347
|
+
throw new Error(`Registry API error: ${resp.status} ${resp.statusText}`);
|
|
2348
|
+
}
|
|
2349
|
+
const data = await resp.json();
|
|
2350
|
+
const results = [];
|
|
2351
|
+
const seenNames = new Set;
|
|
2352
|
+
for (const entry of data.servers) {
|
|
2353
|
+
const server = entry.server;
|
|
2354
|
+
const meta = entry._meta?.["io.modelcontextprotocol.registry/official"];
|
|
2355
|
+
if (!meta?.isLatest)
|
|
2356
|
+
continue;
|
|
2357
|
+
if (seenNames.has(server.name))
|
|
2358
|
+
continue;
|
|
2359
|
+
seenNames.add(server.name);
|
|
2360
|
+
if (query) {
|
|
2361
|
+
const q = query.toLowerCase();
|
|
2362
|
+
const matchName = server.name.toLowerCase().includes(q);
|
|
2363
|
+
const matchTitle = server.title?.toLowerCase().includes(q);
|
|
2364
|
+
const matchDesc = server.description?.toLowerCase().includes(q);
|
|
2365
|
+
if (!matchName && !matchTitle && !matchDesc)
|
|
2366
|
+
continue;
|
|
2367
|
+
}
|
|
2368
|
+
let connectionType = "remote";
|
|
2369
|
+
let connectionUrl;
|
|
2370
|
+
let npmPackage;
|
|
2371
|
+
let dockerImage;
|
|
2372
|
+
if (server.remotes && server.remotes.length > 0) {
|
|
2373
|
+
connectionType = "remote";
|
|
2374
|
+
connectionUrl = server.remotes[0].url;
|
|
2375
|
+
} else if (server.packages && server.packages.length > 0) {
|
|
2376
|
+
const pkg = server.packages[0];
|
|
2377
|
+
connectionType = "stdio";
|
|
2378
|
+
if (pkg.registryType === "npm") {
|
|
2379
|
+
npmPackage = pkg.identifier;
|
|
2380
|
+
} else if (pkg.registryType === "oci") {
|
|
2381
|
+
dockerImage = pkg.identifier;
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
results.push({
|
|
2385
|
+
id: `${server.name}@${server.version}`,
|
|
2386
|
+
name: server.name,
|
|
2387
|
+
title: server.title || server.name.split("/").pop() || server.name,
|
|
2388
|
+
description: server.description || "No description",
|
|
2389
|
+
version: server.version,
|
|
2390
|
+
connectionType,
|
|
2391
|
+
connectionUrl,
|
|
2392
|
+
npmPackage,
|
|
2393
|
+
dockerImage,
|
|
2394
|
+
repositoryUrl: server.repository?.url,
|
|
2395
|
+
websiteUrl: server.websiteUrl,
|
|
2396
|
+
iconUrl: server.icons?.[0]?.src,
|
|
2397
|
+
publishedAt: meta?.publishedAt,
|
|
2398
|
+
isLatest: true
|
|
2399
|
+
});
|
|
2400
|
+
if (results.length >= limit)
|
|
2401
|
+
break;
|
|
2402
|
+
}
|
|
2403
|
+
return { results };
|
|
2404
|
+
}
|
|
2405
|
+
async function getMcpServerDetails(name) {
|
|
2406
|
+
const resp = await fetch(`${MCP_REGISTRY_BASE_URL}/v0/servers/${encodeURIComponent(name)}`, {
|
|
2407
|
+
headers: { Accept: "application/json" }
|
|
2408
|
+
});
|
|
2409
|
+
if (!resp.ok) {
|
|
2410
|
+
if (resp.status === 404) {
|
|
2411
|
+
return null;
|
|
2412
|
+
}
|
|
2413
|
+
throw new Error(`Registry API error: ${resp.status}`);
|
|
2414
|
+
}
|
|
2415
|
+
const data = await resp.json();
|
|
2416
|
+
return data.server;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
// src/routes-mcp.ts
|
|
2420
|
+
var MCP_MARKETPLACE_QUERY_MAX_LENGTH = 200;
|
|
2421
|
+
var MCP_MARKETPLACE_SERVER_NAME_MAX_LENGTH = 200;
|
|
2422
|
+
function parseClampedInteger(value, options = {}) {
|
|
2423
|
+
const raw = value == null ? "" : value.trim();
|
|
2424
|
+
if (!raw)
|
|
2425
|
+
return Number.isFinite(options.fallback) ? options.fallback : undefined;
|
|
2426
|
+
if (!/^[+-]?\d+$/.test(raw)) {
|
|
2427
|
+
return Number.isFinite(options.fallback) ? options.fallback : undefined;
|
|
2428
|
+
}
|
|
2429
|
+
const parsed = Number(raw);
|
|
2430
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
2431
|
+
return Number.isFinite(options.fallback) ? options.fallback : undefined;
|
|
2432
|
+
}
|
|
2433
|
+
if (options.min !== undefined && parsed < options.min)
|
|
2434
|
+
return options.min;
|
|
2435
|
+
if (options.max !== undefined && parsed > options.max)
|
|
2436
|
+
return options.max;
|
|
2437
|
+
return parsed;
|
|
2438
|
+
}
|
|
2439
|
+
function normalizeBoundedString(value, maxLength, label) {
|
|
2440
|
+
const normalized = value.trim();
|
|
2441
|
+
if (normalized.length > maxLength) {
|
|
2442
|
+
throw new RangeError(`${label} must be ${maxLength} characters or fewer`);
|
|
2443
|
+
}
|
|
2444
|
+
return normalized;
|
|
2445
|
+
}
|
|
2446
|
+
async function handleMcpRoutes(ctx) {
|
|
2447
|
+
const { req, res, method, pathname, url, state, json, error, readJsonBody } = ctx;
|
|
2448
|
+
if (method === "GET" && pathname === "/api/mcp/marketplace/search") {
|
|
2449
|
+
let query;
|
|
2450
|
+
try {
|
|
2451
|
+
query = normalizeBoundedString(url.searchParams.get("q") ?? "", MCP_MARKETPLACE_QUERY_MAX_LENGTH, "Marketplace search query");
|
|
2452
|
+
} catch (err) {
|
|
2453
|
+
error(res, err instanceof Error ? err.message : String(err), 400);
|
|
2454
|
+
return true;
|
|
2455
|
+
}
|
|
2456
|
+
const limitStr = url.searchParams.get("limit");
|
|
2457
|
+
const limit = limitStr ? parseClampedInteger(limitStr, { min: 1, max: 50, fallback: 30 }) : 30;
|
|
2458
|
+
try {
|
|
2459
|
+
const result = await searchMcpMarketplace(query || undefined, limit);
|
|
2460
|
+
json(res, { ok: true, results: result.results });
|
|
2461
|
+
} catch (err) {
|
|
2462
|
+
error(res, `MCP marketplace search failed: ${err instanceof Error ? err.message : err}`, 502);
|
|
2463
|
+
}
|
|
2464
|
+
return true;
|
|
2465
|
+
}
|
|
2466
|
+
if (method === "GET" && pathname.startsWith("/api/mcp/marketplace/details/")) {
|
|
2467
|
+
const serverName = ctx.decodePathComponent(pathname.slice("/api/mcp/marketplace/details/".length), res, "server name");
|
|
2468
|
+
if (serverName === null)
|
|
2469
|
+
return true;
|
|
2470
|
+
let normalizedServerName;
|
|
2471
|
+
try {
|
|
2472
|
+
normalizedServerName = normalizeBoundedString(serverName, MCP_MARKETPLACE_SERVER_NAME_MAX_LENGTH, "Server name");
|
|
2473
|
+
} catch (err) {
|
|
2474
|
+
error(res, err instanceof Error ? err.message : String(err), 400);
|
|
2475
|
+
return true;
|
|
2476
|
+
}
|
|
2477
|
+
if (!normalizedServerName) {
|
|
2478
|
+
error(res, "Server name is required", 400);
|
|
2479
|
+
return true;
|
|
2480
|
+
}
|
|
2481
|
+
try {
|
|
2482
|
+
const details = await getMcpServerDetails(normalizedServerName);
|
|
2483
|
+
if (!details) {
|
|
2484
|
+
error(res, `MCP server "${normalizedServerName}" not found`, 404);
|
|
2485
|
+
return true;
|
|
2486
|
+
}
|
|
2487
|
+
json(res, { ok: true, server: details });
|
|
2488
|
+
} catch (err) {
|
|
2489
|
+
error(res, `Failed to fetch server details: ${err instanceof Error ? err.message : err}`, 502);
|
|
2490
|
+
}
|
|
2491
|
+
return true;
|
|
2492
|
+
}
|
|
2493
|
+
if (method === "GET" && pathname === "/api/mcp/config") {
|
|
2494
|
+
const servers = state.config.mcp?.servers ?? {};
|
|
2495
|
+
json(res, { ok: true, servers: ctx.redactDeep(servers) });
|
|
2496
|
+
return true;
|
|
2497
|
+
}
|
|
2498
|
+
if (method === "POST" && pathname === "/api/mcp/config/server") {
|
|
2499
|
+
const body = await readJsonBody(req, res);
|
|
2500
|
+
if (!body)
|
|
2501
|
+
return true;
|
|
2502
|
+
const serverName = body.name?.trim();
|
|
2503
|
+
if (!serverName) {
|
|
2504
|
+
error(res, "Server name is required", 400);
|
|
2505
|
+
return true;
|
|
2506
|
+
}
|
|
2507
|
+
if (ctx.isBlockedObjectKey(serverName)) {
|
|
2508
|
+
error(res, 'Invalid server name: "__proto__", "constructor", and "prototype" are reserved', 400);
|
|
2509
|
+
return true;
|
|
2510
|
+
}
|
|
2511
|
+
const config = body.config;
|
|
2512
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
2513
|
+
error(res, "Server config object is required", 400);
|
|
2514
|
+
return true;
|
|
2515
|
+
}
|
|
2516
|
+
const mcpRejection = await ctx.resolveMcpServersRejection({
|
|
2517
|
+
[serverName]: config
|
|
2518
|
+
});
|
|
2519
|
+
if (mcpRejection) {
|
|
2520
|
+
error(res, mcpRejection, 400);
|
|
2521
|
+
return true;
|
|
2522
|
+
}
|
|
2523
|
+
const mcpTerminalRejection = ctx.resolveMcpTerminalAuthorizationRejection(req, { [serverName]: config }, body);
|
|
2524
|
+
if (mcpTerminalRejection) {
|
|
2525
|
+
error(res, `Configuring stdio MCP servers requires terminal authorization. ${mcpTerminalRejection.reason}`, mcpTerminalRejection.status);
|
|
2526
|
+
return true;
|
|
2527
|
+
}
|
|
2528
|
+
if (!state.config.mcp)
|
|
2529
|
+
state.config.mcp = {};
|
|
2530
|
+
if (!state.config.mcp.servers)
|
|
2531
|
+
state.config.mcp.servers = {};
|
|
2532
|
+
const sanitized = ctx.cloneWithoutBlockedObjectKeys(config);
|
|
2533
|
+
state.config.mcp.servers[serverName] = sanitized;
|
|
2534
|
+
try {
|
|
2535
|
+
ctx.saveElizaConfig(state.config);
|
|
2536
|
+
} catch (err) {
|
|
2537
|
+
import_core7.logger.warn(`[api] Config save failed: ${err instanceof Error ? err.message : err}`);
|
|
2538
|
+
}
|
|
2539
|
+
json(res, { ok: true, name: serverName, requiresRestart: true });
|
|
2540
|
+
return true;
|
|
2541
|
+
}
|
|
2542
|
+
if (method === "DELETE" && pathname.startsWith("/api/mcp/config/server/")) {
|
|
2543
|
+
const serverName = ctx.decodePathComponent(pathname.slice("/api/mcp/config/server/".length), res, "server name");
|
|
2544
|
+
if (serverName === null)
|
|
2545
|
+
return true;
|
|
2546
|
+
if (ctx.isBlockedObjectKey(serverName)) {
|
|
2547
|
+
error(res, 'Invalid server name: "__proto__", "constructor", and "prototype" are reserved', 400);
|
|
2548
|
+
return true;
|
|
2549
|
+
}
|
|
2550
|
+
if (state.config.mcp?.servers?.[serverName]) {
|
|
2551
|
+
delete state.config.mcp.servers[serverName];
|
|
2552
|
+
try {
|
|
2553
|
+
ctx.saveElizaConfig(state.config);
|
|
2554
|
+
} catch (err) {
|
|
2555
|
+
import_core7.logger.warn(`[api] Config save failed: ${err instanceof Error ? err.message : err}`);
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
json(res, { ok: true, requiresRestart: true });
|
|
2559
|
+
return true;
|
|
2560
|
+
}
|
|
2561
|
+
if (method === "PUT" && pathname === "/api/mcp/config") {
|
|
2562
|
+
const body = await readJsonBody(req, res);
|
|
2563
|
+
if (!body)
|
|
2564
|
+
return true;
|
|
2565
|
+
if (!state.config.mcp)
|
|
2566
|
+
state.config.mcp = {};
|
|
2567
|
+
if (body.servers !== undefined) {
|
|
2568
|
+
if (!body.servers || typeof body.servers !== "object" || Array.isArray(body.servers)) {
|
|
2569
|
+
error(res, "servers must be a JSON object", 400);
|
|
2570
|
+
return true;
|
|
2571
|
+
}
|
|
2572
|
+
for (const serverName of Object.keys(body.servers)) {
|
|
2573
|
+
if (ctx.isBlockedObjectKey(serverName)) {
|
|
2574
|
+
error(res, 'Invalid server name: "__proto__", "constructor", and "prototype" are reserved', 400);
|
|
2575
|
+
return true;
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
const mcpRejection = await ctx.resolveMcpServersRejection(body.servers);
|
|
2579
|
+
if (mcpRejection) {
|
|
2580
|
+
error(res, mcpRejection, 400);
|
|
2581
|
+
return true;
|
|
2582
|
+
}
|
|
2583
|
+
const mcpTerminalRejection = ctx.resolveMcpTerminalAuthorizationRejection(req, body.servers, body);
|
|
2584
|
+
if (mcpTerminalRejection) {
|
|
2585
|
+
error(res, `Configuring stdio MCP servers requires terminal authorization. ${mcpTerminalRejection.reason}`, mcpTerminalRejection.status);
|
|
2586
|
+
return true;
|
|
2587
|
+
}
|
|
2588
|
+
const sanitized = ctx.cloneWithoutBlockedObjectKeys(body.servers);
|
|
2589
|
+
state.config.mcp.servers = sanitized;
|
|
2590
|
+
}
|
|
2591
|
+
try {
|
|
2592
|
+
ctx.saveElizaConfig(state.config);
|
|
2593
|
+
} catch (err) {
|
|
2594
|
+
import_core7.logger.warn(`[api] Config save failed: ${err instanceof Error ? err.message : err}`);
|
|
2595
|
+
}
|
|
2596
|
+
json(res, { ok: true });
|
|
2597
|
+
return true;
|
|
2598
|
+
}
|
|
2599
|
+
if (method === "GET" && pathname === "/api/mcp/status") {
|
|
2600
|
+
const servers = [];
|
|
2601
|
+
if (state.runtime) {
|
|
2602
|
+
try {
|
|
2603
|
+
const mcpService = state.runtime.getService("MCP");
|
|
2604
|
+
if (mcpService && typeof mcpService.getServers === "function") {
|
|
2605
|
+
for (const s of mcpService.getServers()) {
|
|
2606
|
+
servers.push({
|
|
2607
|
+
name: s.name,
|
|
2608
|
+
status: s.status,
|
|
2609
|
+
toolCount: Array.isArray(s.tools) ? s.tools.length : 0,
|
|
2610
|
+
resourceCount: Array.isArray(s.resources) ? s.resources.length : 0
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
} catch (err) {
|
|
2615
|
+
import_core7.logger.debug(`[api] Service not available: ${err instanceof Error ? err.message : err}`);
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
json(res, { ok: true, servers });
|
|
2619
|
+
return true;
|
|
2620
|
+
}
|
|
2621
|
+
return false;
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2100
2624
|
// src/index.ts
|
|
2625
|
+
function withMcpContext(action) {
|
|
2626
|
+
return {
|
|
2627
|
+
...action,
|
|
2628
|
+
contexts: [
|
|
2629
|
+
...new Set([
|
|
2630
|
+
...action.contexts ?? [],
|
|
2631
|
+
"general",
|
|
2632
|
+
"automation",
|
|
2633
|
+
"knowledge",
|
|
2634
|
+
MCP_ACTION_CONTEXT
|
|
2635
|
+
])
|
|
2636
|
+
]
|
|
2637
|
+
};
|
|
2638
|
+
}
|
|
2101
2639
|
var mcpPlugin = {
|
|
2102
2640
|
name: "mcp",
|
|
2103
2641
|
description: "Plugin for connecting to MCP (Model Context Protocol) servers",
|
|
2104
2642
|
init: async (_config, _runtime) => {
|
|
2105
|
-
|
|
2643
|
+
import_core8.logger.info("Initializing MCP plugin...");
|
|
2644
|
+
},
|
|
2645
|
+
async dispose(runtime) {
|
|
2646
|
+
const svc = runtime.getService(McpService.serviceType);
|
|
2647
|
+
await svc?.stop();
|
|
2106
2648
|
},
|
|
2107
2649
|
services: [McpService],
|
|
2108
|
-
actions: [
|
|
2650
|
+
actions: [...import_core8.promoteSubactionsToActions(withMcpContext(mcpAction))],
|
|
2109
2651
|
providers: [provider]
|
|
2110
2652
|
};
|
|
2111
2653
|
var src_default = mcpPlugin;
|
|
2112
2654
|
|
|
2113
|
-
//# debugId=
|
|
2655
|
+
//# debugId=E8D9359237465E8664756E2164756E21
|