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