@elizaos/plugin-mcp 1.3.5 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/callToolAction.d.ts +1 -1
- package/dist/actions/callToolAction.d.ts.map +1 -1
- package/dist/cjs/index.cjs +200 -308
- package/dist/cjs/index.js.map +12 -12
- package/dist/index.js +200 -308
- package/dist/index.js.map +12 -12
- package/dist/provider.d.ts.map +1 -1
- package/dist/service.d.ts +4 -8
- package/dist/service.d.ts.map +1 -1
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/mcp.d.ts.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/cjs/index.cjs
CHANGED
|
@@ -711,6 +711,33 @@ async function handleMcpError(state, mcpProvider, error, runtime2, message, type
|
|
|
711
711
|
};
|
|
712
712
|
}
|
|
713
713
|
|
|
714
|
+
// src/utils/handler.ts
|
|
715
|
+
async function handleNoToolAvailable(callback, toolSelection) {
|
|
716
|
+
const responseText = "I don't have a specific tool that can help with that request. Let me try to assist you directly instead.";
|
|
717
|
+
const thoughtText = "No appropriate MCP tool available for this request. Falling back to direct assistance.";
|
|
718
|
+
if (callback && toolSelection?.noToolAvailable) {
|
|
719
|
+
await callback({
|
|
720
|
+
text: responseText,
|
|
721
|
+
thought: thoughtText,
|
|
722
|
+
actions: ["REPLY"]
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
return {
|
|
726
|
+
text: responseText,
|
|
727
|
+
values: {
|
|
728
|
+
success: true,
|
|
729
|
+
noToolAvailable: true,
|
|
730
|
+
fallbackToDirectAssistance: true
|
|
731
|
+
},
|
|
732
|
+
data: {
|
|
733
|
+
actionName: "CALL_MCP_TOOL",
|
|
734
|
+
noToolAvailable: true,
|
|
735
|
+
reason: toolSelection?.reasoning || "No appropriate tool available"
|
|
736
|
+
},
|
|
737
|
+
success: true
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
|
|
714
741
|
// src/utils/processing.ts
|
|
715
742
|
var import_core2 = require("@elizaos/core");
|
|
716
743
|
var import_core3 = require("@elizaos/core");
|
|
@@ -775,82 +802,54 @@ Your response (written as if directly to the user):
|
|
|
775
802
|
`;
|
|
776
803
|
|
|
777
804
|
// src/utils/mcp.ts
|
|
805
|
+
var NO_DESC = "No description";
|
|
778
806
|
async function createMcpMemory(runtime2, message, type, serverName, content, metadata) {
|
|
779
807
|
const memory = await runtime2.addEmbeddingToMemory({
|
|
780
808
|
entityId: message.entityId,
|
|
781
809
|
agentId: runtime2.agentId,
|
|
782
810
|
roomId: message.roomId,
|
|
783
811
|
content: {
|
|
784
|
-
text: `Used
|
|
785
|
-
|
|
786
|
-
metadata: {
|
|
787
|
-
...metadata,
|
|
788
|
-
serverName
|
|
789
|
-
}
|
|
812
|
+
text: `Used "${type}" from "${serverName}". Content: ${content}`,
|
|
813
|
+
metadata: { ...metadata, serverName }
|
|
790
814
|
}
|
|
791
815
|
});
|
|
792
816
|
await runtime2.createMemory(memory, type === "resource" ? "resources" : "tools", true);
|
|
793
817
|
}
|
|
794
818
|
function buildMcpProviderData(servers) {
|
|
795
|
-
const mcpData = {};
|
|
796
|
-
let textContent = "";
|
|
797
819
|
if (servers.length === 0) {
|
|
798
|
-
return {
|
|
799
|
-
values: { mcp: {} },
|
|
800
|
-
data: { mcp: {} },
|
|
801
|
-
text: "No MCP servers are currently connected."
|
|
802
|
-
};
|
|
820
|
+
return { values: { mcp: {} }, data: { mcp: {} }, text: "No MCP servers connected." };
|
|
803
821
|
}
|
|
822
|
+
const mcpData = {};
|
|
823
|
+
const lines = [`# MCP Configuration
|
|
824
|
+
`];
|
|
804
825
|
for (const server of servers) {
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
for (const resource of server.resources) {
|
|
833
|
-
mcpData[server.name].resources[resource.uri] = {
|
|
834
|
-
name: resource.name,
|
|
835
|
-
description: resource.description || "No description available",
|
|
836
|
-
mimeType: resource.mimeType
|
|
837
|
-
};
|
|
838
|
-
textContent += `- **${resource.name}** (${resource.uri}): ${resource.description || "No description available"}
|
|
839
|
-
`;
|
|
840
|
-
}
|
|
841
|
-
textContent += `
|
|
842
|
-
`;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
return {
|
|
846
|
-
values: { mcp: mcpData, mcpText: `# MCP Configuration
|
|
847
|
-
|
|
848
|
-
${textContent}` },
|
|
849
|
-
data: { mcp: mcpData },
|
|
850
|
-
text: `# MCP Configuration
|
|
851
|
-
|
|
852
|
-
${textContent}`
|
|
853
|
-
};
|
|
826
|
+
const tools = {};
|
|
827
|
+
const resources = {};
|
|
828
|
+
lines.push(`## ${server.name} (${server.status})
|
|
829
|
+
`);
|
|
830
|
+
if (server.tools?.length) {
|
|
831
|
+
lines.push(`### Tools
|
|
832
|
+
`);
|
|
833
|
+
for (const t of server.tools) {
|
|
834
|
+
tools[t.name] = { description: t.description || NO_DESC, inputSchema: t.inputSchema || {} };
|
|
835
|
+
lines.push(`- **${t.name}**: ${t.description || NO_DESC}`);
|
|
836
|
+
}
|
|
837
|
+
lines.push("");
|
|
838
|
+
}
|
|
839
|
+
if (server.resources?.length) {
|
|
840
|
+
lines.push(`### Resources
|
|
841
|
+
`);
|
|
842
|
+
for (const r of server.resources) {
|
|
843
|
+
resources[r.uri] = { name: r.name, description: r.description || NO_DESC, mimeType: r.mimeType };
|
|
844
|
+
lines.push(`- **${r.name}** (${r.uri}): ${r.description || NO_DESC}`);
|
|
845
|
+
}
|
|
846
|
+
lines.push("");
|
|
847
|
+
}
|
|
848
|
+
mcpData[server.name] = { status: server.status, tools, resources };
|
|
849
|
+
}
|
|
850
|
+
const text = lines.join(`
|
|
851
|
+
`);
|
|
852
|
+
return { values: { mcp: mcpData, mcpText: text }, data: { mcp: mcpData }, text };
|
|
854
853
|
}
|
|
855
854
|
|
|
856
855
|
// src/utils/processing.ts
|
|
@@ -1112,9 +1111,10 @@ ${JSON.stringify(parsedJson, null, 2)}`);
|
|
|
1112
1111
|
}
|
|
1113
1112
|
function getMaxRetries(runtime2) {
|
|
1114
1113
|
try {
|
|
1115
|
-
const
|
|
1116
|
-
|
|
1117
|
-
|
|
1114
|
+
const rawSettings = runtime2.getSetting("mcp");
|
|
1115
|
+
const settings = rawSettings;
|
|
1116
|
+
if (settings && typeof settings.maxRetries === "number") {
|
|
1117
|
+
const configValue = settings.maxRetries;
|
|
1118
1118
|
if (!Number.isNaN(configValue) && configValue >= 0) {
|
|
1119
1119
|
import_core4.logger.debug(`[WITH-MODEL-RETRY] Using configured selection retries: ${configValue}`);
|
|
1120
1120
|
return configValue;
|
|
@@ -1444,33 +1444,6 @@ function createFeedbackPrompt2(originalResponse, errorMessage, itemType, itemsDe
|
|
|
1444
1444
|
User request: ${userMessage}`;
|
|
1445
1445
|
}
|
|
1446
1446
|
|
|
1447
|
-
// src/utils/handler.ts
|
|
1448
|
-
async function handleNoToolAvailable(callback, toolSelection) {
|
|
1449
|
-
const responseText = "I don't have a specific tool that can help with that request. Let me try to assist you directly instead.";
|
|
1450
|
-
const thoughtText = "No appropriate MCP tool available for this request. Falling back to direct assistance.";
|
|
1451
|
-
if (callback && toolSelection?.noToolAvailable) {
|
|
1452
|
-
await callback({
|
|
1453
|
-
text: responseText,
|
|
1454
|
-
thought: thoughtText,
|
|
1455
|
-
actions: ["REPLY"]
|
|
1456
|
-
});
|
|
1457
|
-
}
|
|
1458
|
-
return {
|
|
1459
|
-
text: responseText,
|
|
1460
|
-
values: {
|
|
1461
|
-
success: true,
|
|
1462
|
-
noToolAvailable: true,
|
|
1463
|
-
fallbackToDirectAssistance: true
|
|
1464
|
-
},
|
|
1465
|
-
data: {
|
|
1466
|
-
actionName: "CALL_MCP_TOOL",
|
|
1467
|
-
noToolAvailable: true,
|
|
1468
|
-
reason: toolSelection?.reasoning || "No appropriate tool available"
|
|
1469
|
-
},
|
|
1470
|
-
success: true
|
|
1471
|
-
};
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
1447
|
// src/actions/callToolAction.ts
|
|
1475
1448
|
var callToolAction = {
|
|
1476
1449
|
name: "CALL_MCP_TOOL",
|
|
@@ -1532,7 +1505,7 @@ ${JSON.stringify(toolSelectionArgument, null, 2)}`);
|
|
|
1532
1505
|
const result = await mcpService.callTool(serverName, toolName, toolSelectionArgument.toolArguments);
|
|
1533
1506
|
const { toolOutput, hasAttachments, attachments } = processToolResult(result, serverName, toolName, runtime2, message.entityId);
|
|
1534
1507
|
const replyMemory = await handleToolResponse(runtime2, message, serverName, toolName, toolSelectionArgument.toolArguments, toolOutput, hasAttachments, attachments, composedState, mcpProvider, callback);
|
|
1535
|
-
|
|
1508
|
+
const actionResult = {
|
|
1536
1509
|
text: `Successfully called tool: ${serverName}/${toolName}. Reasoned response: ${replyMemory.content.text}`,
|
|
1537
1510
|
values: {
|
|
1538
1511
|
success: true,
|
|
@@ -1553,6 +1526,15 @@ ${JSON.stringify(toolSelectionArgument, null, 2)}`);
|
|
|
1553
1526
|
},
|
|
1554
1527
|
success: true
|
|
1555
1528
|
};
|
|
1529
|
+
import_core6.logger.info({
|
|
1530
|
+
serverName,
|
|
1531
|
+
toolName,
|
|
1532
|
+
hasOutput: !!toolOutput,
|
|
1533
|
+
outputLength: toolOutput?.length || 0,
|
|
1534
|
+
hasAttachments,
|
|
1535
|
+
reasoning: toolSelectionName.reasoning
|
|
1536
|
+
}, `[CALL_MCP_TOOL] Action result`);
|
|
1537
|
+
return actionResult;
|
|
1556
1538
|
} catch (error) {
|
|
1557
1539
|
return await handleMcpError(composedState, mcpProvider, error, runtime2, message, "tool", callback);
|
|
1558
1540
|
}
|
|
@@ -1790,19 +1772,20 @@ var readResourceAction = {
|
|
|
1790
1772
|
};
|
|
1791
1773
|
|
|
1792
1774
|
// src/provider.ts
|
|
1775
|
+
var createEmptyProvider = () => ({
|
|
1776
|
+
values: { mcp: {} },
|
|
1777
|
+
data: { mcp: {} },
|
|
1778
|
+
text: "No MCP servers available."
|
|
1779
|
+
});
|
|
1793
1780
|
var provider = {
|
|
1794
1781
|
name: "MCP",
|
|
1795
|
-
description: "
|
|
1782
|
+
description: "Connected MCP servers, tools, and resources",
|
|
1796
1783
|
get: async (runtime2, _message, _state) => {
|
|
1797
|
-
const
|
|
1798
|
-
if (!
|
|
1799
|
-
return
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
text: "No MCP servers are available."
|
|
1803
|
-
};
|
|
1804
|
-
}
|
|
1805
|
-
return mcpService.getProviderData();
|
|
1784
|
+
const svc = runtime2.getService(MCP_SERVICE_NAME);
|
|
1785
|
+
if (!svc)
|
|
1786
|
+
return createEmptyProvider();
|
|
1787
|
+
await svc.waitForInitialization();
|
|
1788
|
+
return svc.getProviderData();
|
|
1806
1789
|
}
|
|
1807
1790
|
};
|
|
1808
1791
|
|
|
@@ -1811,6 +1794,9 @@ var import_core8 = require("@elizaos/core");
|
|
|
1811
1794
|
var import_client = require("@modelcontextprotocol/sdk/client/index.js");
|
|
1812
1795
|
var import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
1813
1796
|
var import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
1797
|
+
var import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
1798
|
+
var errMsg = (e) => e instanceof Error ? e.message : String(e);
|
|
1799
|
+
|
|
1814
1800
|
class McpService extends import_core8.Service {
|
|
1815
1801
|
static serviceType = MCP_SERVICE_NAME;
|
|
1816
1802
|
capabilityDescription = "Enables the agent to interact with MCP (Model Context Protocol) servers";
|
|
@@ -1827,7 +1813,6 @@ class McpService extends import_core8.Service {
|
|
|
1827
1813
|
initializationPromise = null;
|
|
1828
1814
|
constructor(runtime2) {
|
|
1829
1815
|
super(runtime2);
|
|
1830
|
-
import_core8.logger.info("[McpService] Constructor called, starting initialization...");
|
|
1831
1816
|
this.initializationPromise = this.initializeMcpServers();
|
|
1832
1817
|
}
|
|
1833
1818
|
static async start(runtime2) {
|
|
@@ -1843,115 +1828,70 @@ class McpService extends import_core8.Service {
|
|
|
1843
1828
|
}
|
|
1844
1829
|
}
|
|
1845
1830
|
async stop() {
|
|
1846
|
-
for (const
|
|
1831
|
+
for (const name of this.connections.keys()) {
|
|
1847
1832
|
await this.deleteConnection(name);
|
|
1848
1833
|
}
|
|
1849
|
-
this.
|
|
1850
|
-
for (const state of this.connectionStates.values()) {
|
|
1834
|
+
for (const [name, state] of this.connectionStates.entries()) {
|
|
1851
1835
|
if (state.pingInterval)
|
|
1852
1836
|
clearInterval(state.pingInterval);
|
|
1853
1837
|
if (state.reconnectTimeout)
|
|
1854
1838
|
clearTimeout(state.reconnectTimeout);
|
|
1839
|
+
this.connectionStates.delete(name);
|
|
1855
1840
|
}
|
|
1856
|
-
this.connectionStates.clear();
|
|
1857
1841
|
}
|
|
1858
1842
|
async initializeMcpServers() {
|
|
1859
|
-
import_core8.logger.info("[McpService] Starting MCP server initialization...");
|
|
1860
1843
|
try {
|
|
1861
1844
|
const mcpSettings = this.getMcpSettings();
|
|
1862
|
-
|
|
1863
|
-
const serverNames = mcpSettings?.servers ? Object.keys(mcpSettings.servers) : [];
|
|
1864
|
-
import_core8.logger.info(`[McpService] Getting MCP settings... hasSettings=${!!mcpSettings} hasServers=${!!mcpSettings?.servers} serverCount=${serverCount} servers=${JSON.stringify(serverNames)}`);
|
|
1865
|
-
if (!mcpSettings || !mcpSettings.servers) {
|
|
1866
|
-
import_core8.logger.info("[McpService] No MCP servers configured.");
|
|
1867
|
-
this.mcpProvider = buildMcpProviderData([]);
|
|
1868
|
-
return;
|
|
1869
|
-
}
|
|
1870
|
-
if (Object.keys(mcpSettings.servers).length === 0) {
|
|
1871
|
-
import_core8.logger.info("[McpService] MCP settings exist but no servers configured.");
|
|
1845
|
+
if (!mcpSettings?.servers || Object.keys(mcpSettings.servers).length === 0) {
|
|
1872
1846
|
this.mcpProvider = buildMcpProviderData([]);
|
|
1873
1847
|
return;
|
|
1874
1848
|
}
|
|
1875
|
-
import_core8.logger.info(`[McpService] Connecting to ${Object.keys(mcpSettings.servers).length} MCP servers: ${JSON.stringify(Object.keys(mcpSettings.servers))}`);
|
|
1876
1849
|
const connectionStartTime = Date.now();
|
|
1877
1850
|
await this.updateServerConnections(mcpSettings.servers);
|
|
1878
1851
|
const connectionDuration = Date.now() - connectionStartTime;
|
|
1879
1852
|
const servers = this.getServers();
|
|
1880
|
-
const
|
|
1881
|
-
const
|
|
1882
|
-
if (
|
|
1883
|
-
|
|
1884
|
-
import_core8.logger.info(`[McpService] ✓ Successfully connected ${connectedServers.length}/${servers.length} servers in ${connectionDuration}ms: ${toolCounts}`);
|
|
1853
|
+
const connected = servers.filter((s) => s.status === "connected");
|
|
1854
|
+
const failed = servers.filter((s) => s.status !== "connected");
|
|
1855
|
+
if (connected.length > 0) {
|
|
1856
|
+
import_core8.logger.info(`[MCP] Connected ${connected.length}/${servers.length} in ${connectionDuration}ms (${connected.map((s) => `${s.name}:${s.tools?.length || 0}`).join(", ")})`);
|
|
1885
1857
|
}
|
|
1886
|
-
if (
|
|
1887
|
-
|
|
1888
|
-
import_core8.logger.warn(`[McpService] ⚠️ Failed to connect to ${failedServers.length}/${servers.length} servers: ${failedDetails}`);
|
|
1858
|
+
if (failed.length > 0) {
|
|
1859
|
+
import_core8.logger.warn(`[MCP] Failed: ${failed.map((s) => s.name).join(", ")}`);
|
|
1889
1860
|
}
|
|
1890
|
-
if (
|
|
1891
|
-
import_core8.logger.error(`[
|
|
1861
|
+
if (connected.length === 0 && servers.length > 0) {
|
|
1862
|
+
import_core8.logger.error(`[MCP] All servers failed`);
|
|
1892
1863
|
}
|
|
1893
1864
|
this.mcpProvider = buildMcpProviderData(servers);
|
|
1894
|
-
const mcpDataKeys = Object.keys(this.mcpProvider.data?.mcp || {});
|
|
1895
|
-
import_core8.logger.info(`[McpService] MCP provider data built: ${mcpDataKeys.length} server(s) available`);
|
|
1896
1865
|
} catch (error) {
|
|
1897
|
-
import_core8.logger.error({ error:
|
|
1866
|
+
import_core8.logger.error({ error: errMsg(error) }, "[MCP] Initialization failed");
|
|
1898
1867
|
this.mcpProvider = buildMcpProviderData([]);
|
|
1899
1868
|
}
|
|
1900
1869
|
}
|
|
1901
1870
|
getMcpSettings() {
|
|
1902
1871
|
let settings = this.runtime.getSetting("mcp");
|
|
1903
|
-
import_core8.logger.info(`[McpService] getSetting("mcp") result: type=${typeof settings} isNull=${settings === null} hasServers=${!!settings?.servers}`);
|
|
1904
1872
|
if (!settings || typeof settings === "object" && !settings.servers) {
|
|
1905
|
-
|
|
1906
|
-
if (characterSettings?.mcp) {
|
|
1907
|
-
import_core8.logger.info("[McpService] Found MCP settings in character.settings.mcp (fallback)");
|
|
1908
|
-
settings = characterSettings.mcp;
|
|
1909
|
-
}
|
|
1873
|
+
settings = this.runtime.character?.settings?.mcp;
|
|
1910
1874
|
}
|
|
1911
1875
|
if (!settings || typeof settings === "object" && !settings.servers) {
|
|
1912
|
-
|
|
1913
|
-
if (runtimeSettings?.mcp) {
|
|
1914
|
-
import_core8.logger.info("[McpService] Found MCP settings in runtime.settings.mcp (fallback)");
|
|
1915
|
-
settings = runtimeSettings.mcp;
|
|
1916
|
-
}
|
|
1876
|
+
settings = this.runtime.settings?.mcp;
|
|
1917
1877
|
}
|
|
1918
|
-
|
|
1919
|
-
import_core8.logger.info(`[McpService] MCP settings found with ${Object.keys(settings.servers).length} server(s)`);
|
|
1920
|
-
return settings;
|
|
1921
|
-
}
|
|
1922
|
-
import_core8.logger.info("[McpService] No valid MCP settings found");
|
|
1923
|
-
return;
|
|
1878
|
+
return settings && typeof settings === "object" && settings.servers ? settings : undefined;
|
|
1924
1879
|
}
|
|
1925
1880
|
async updateServerConnections(serverConfigs) {
|
|
1926
|
-
const currentNames = new Set(this.connections.keys());
|
|
1927
1881
|
const newNames = new Set(Object.keys(serverConfigs));
|
|
1928
|
-
for (const name of
|
|
1929
|
-
if (!newNames.has(name))
|
|
1882
|
+
for (const name of this.connections.keys()) {
|
|
1883
|
+
if (!newNames.has(name))
|
|
1930
1884
|
await this.deleteConnection(name);
|
|
1931
|
-
import_core8.logger.info(`Deleted MCP server: ${name}`);
|
|
1932
|
-
}
|
|
1933
1885
|
}
|
|
1934
|
-
|
|
1935
|
-
const
|
|
1936
|
-
if (!
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName: name }, `✗ Failed to connect to new MCP server ${name}`);
|
|
1942
|
-
}
|
|
1943
|
-
} else if (JSON.stringify(config) !== currentConnection.server.config) {
|
|
1944
|
-
try {
|
|
1945
|
-
await this.deleteConnection(name);
|
|
1946
|
-
await this.initializeConnection(name, config);
|
|
1947
|
-
import_core8.logger.info(`✓ Reconnected MCP server with updated config: ${name}`);
|
|
1948
|
-
} catch (error) {
|
|
1949
|
-
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName: name }, `✗ Failed to reconnect MCP server ${name}`);
|
|
1950
|
-
}
|
|
1886
|
+
await Promise.allSettled(Object.entries(serverConfigs).map(async ([name, config]) => {
|
|
1887
|
+
const current = this.connections.get(name);
|
|
1888
|
+
if (!current) {
|
|
1889
|
+
await this.initializeConnection(name, config).catch((e) => import_core8.logger.error({ error: errMsg(e), serverName: name }, `[MCP] Failed: ${name}`));
|
|
1890
|
+
} else if (JSON.stringify(config) !== current.server.config) {
|
|
1891
|
+
await this.deleteConnection(name);
|
|
1892
|
+
await this.initializeConnection(name, config).catch((e) => import_core8.logger.error({ error: errMsg(e), serverName: name }, `[MCP] Failed: ${name}`));
|
|
1951
1893
|
}
|
|
1952
|
-
});
|
|
1953
|
-
await Promise.allSettled(connectionPromises);
|
|
1954
|
-
import_core8.logger.info(`[McpService] All server connection attempts completed`);
|
|
1894
|
+
}));
|
|
1955
1895
|
}
|
|
1956
1896
|
async initializeConnection(name, config) {
|
|
1957
1897
|
await this.deleteConnection(name);
|
|
@@ -1965,19 +1905,19 @@ class McpService extends import_core8.Service {
|
|
|
1965
1905
|
const client = new import_client.Client({ name: "ElizaOS", version: "1.0.0" }, { capabilities: {} });
|
|
1966
1906
|
const transport = config.type === "stdio" ? await this.buildStdioClientTransport(name, config) : await this.buildHttpClientTransport(name, config);
|
|
1967
1907
|
const connection = {
|
|
1968
|
-
server: {
|
|
1969
|
-
name,
|
|
1970
|
-
config: JSON.stringify(config),
|
|
1971
|
-
status: "connecting"
|
|
1972
|
-
},
|
|
1908
|
+
server: { name, config: JSON.stringify(config), status: "connecting" },
|
|
1973
1909
|
client,
|
|
1974
1910
|
transport
|
|
1975
1911
|
};
|
|
1976
1912
|
this.connections.set(name, connection);
|
|
1977
1913
|
this.setupTransportHandlers(name, connection, state);
|
|
1978
|
-
|
|
1914
|
+
const connectPromise = client.connect(transport);
|
|
1915
|
+
connectPromise.catch(() => {});
|
|
1916
|
+
await Promise.race([
|
|
1917
|
+
connectPromise,
|
|
1918
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout connecting to ${name}`)), 60000))
|
|
1919
|
+
]);
|
|
1979
1920
|
const capabilities = client.getServerCapabilities();
|
|
1980
|
-
import_core8.logger.debug(`[${name}] Server capabilities:`, JSON.stringify(capabilities || {}));
|
|
1981
1921
|
const tools = await this.fetchToolsList(name);
|
|
1982
1922
|
const resources = capabilities?.resources ? await this.fetchResourcesList(name) : [];
|
|
1983
1923
|
const resourceTemplates = capabilities?.resources ? await this.fetchResourceTemplatesList(name) : [];
|
|
@@ -1995,7 +1935,6 @@ class McpService extends import_core8.Service {
|
|
|
1995
1935
|
state.reconnectAttempts = 0;
|
|
1996
1936
|
state.consecutivePingFailures = 0;
|
|
1997
1937
|
this.startPingMonitoring(name);
|
|
1998
|
-
import_core8.logger.info(`Successfully connected to MCP server: ${name}`);
|
|
1999
1938
|
} catch (error) {
|
|
2000
1939
|
state.status = "disconnected";
|
|
2001
1940
|
state.lastError = error instanceof Error ? error : new Error(String(error));
|
|
@@ -2005,26 +1944,21 @@ class McpService extends import_core8.Service {
|
|
|
2005
1944
|
}
|
|
2006
1945
|
setupTransportHandlers(name, connection, state) {
|
|
2007
1946
|
const config = JSON.parse(connection.server.config);
|
|
2008
|
-
const
|
|
1947
|
+
const isHttp = config.type !== "stdio";
|
|
2009
1948
|
connection.transport.onerror = async (error) => {
|
|
2010
|
-
const
|
|
2011
|
-
const isExpectedTimeout =
|
|
2012
|
-
if (isExpectedTimeout) {
|
|
2013
|
-
import_core8.logger.
|
|
2014
|
-
} else {
|
|
2015
|
-
import_core8.logger.error({ error, serverName: name }, `Transport error for "${name}"`);
|
|
1949
|
+
const msg = error?.message || String(error);
|
|
1950
|
+
const isExpectedTimeout = isHttp && (!msg || msg === "undefined" || msg.includes("SSE") || msg.includes("timeout"));
|
|
1951
|
+
if (!isExpectedTimeout) {
|
|
1952
|
+
import_core8.logger.error({ error, serverName: name }, `Transport error: ${name}`);
|
|
2016
1953
|
connection.server.status = "disconnected";
|
|
2017
|
-
this.appendErrorMessage(connection,
|
|
1954
|
+
this.appendErrorMessage(connection, msg);
|
|
2018
1955
|
}
|
|
2019
|
-
if (!
|
|
1956
|
+
if (!isHttp)
|
|
2020
1957
|
this.handleDisconnection(name, error);
|
|
2021
|
-
}
|
|
2022
1958
|
};
|
|
2023
1959
|
connection.transport.onclose = async () => {
|
|
2024
|
-
if (
|
|
2025
|
-
import_core8.logger.
|
|
2026
|
-
} else {
|
|
2027
|
-
import_core8.logger.warn({ serverName: name }, `Transport closed for "${name}"`);
|
|
1960
|
+
if (!isHttp) {
|
|
1961
|
+
import_core8.logger.warn({ serverName: name }, `Transport closed: ${name}`);
|
|
2028
1962
|
connection.server.status = "disconnected";
|
|
2029
1963
|
this.handleDisconnection(name, new Error("Transport closed"));
|
|
2030
1964
|
}
|
|
@@ -2035,11 +1969,8 @@ class McpService extends import_core8.Service {
|
|
|
2035
1969
|
if (!connection)
|
|
2036
1970
|
return;
|
|
2037
1971
|
const config = JSON.parse(connection.server.config);
|
|
2038
|
-
|
|
2039
|
-
if (isHttpTransport) {
|
|
2040
|
-
import_core8.logger.debug(`[McpService] Skipping ping monitoring for HTTP server: ${name}`);
|
|
1972
|
+
if (config.type !== "stdio")
|
|
2041
1973
|
return;
|
|
2042
|
-
}
|
|
2043
1974
|
const state = this.connectionStates.get(name);
|
|
2044
1975
|
if (!state || !this.pingConfig.enabled)
|
|
2045
1976
|
return;
|
|
@@ -2047,7 +1978,7 @@ class McpService extends import_core8.Service {
|
|
|
2047
1978
|
clearInterval(state.pingInterval);
|
|
2048
1979
|
state.pingInterval = setInterval(() => {
|
|
2049
1980
|
this.sendPing(name).catch((err) => {
|
|
2050
|
-
import_core8.logger.warn({ error:
|
|
1981
|
+
import_core8.logger.warn({ error: errMsg(err), serverName: name }, `Ping failed: ${name}`);
|
|
2051
1982
|
this.handlePingFailure(name, err);
|
|
2052
1983
|
});
|
|
2053
1984
|
}, this.pingConfig.intervalMs);
|
|
@@ -2055,7 +1986,7 @@ class McpService extends import_core8.Service {
|
|
|
2055
1986
|
async sendPing(name) {
|
|
2056
1987
|
const connection = this.connections.get(name);
|
|
2057
1988
|
if (!connection)
|
|
2058
|
-
throw new Error(`No connection
|
|
1989
|
+
throw new Error(`No connection: ${name}`);
|
|
2059
1990
|
await Promise.race([
|
|
2060
1991
|
connection.client.listTools(),
|
|
2061
1992
|
new Promise((_, reject) => setTimeout(() => reject(new Error("Ping timeout")), this.pingConfig.timeoutMs))
|
|
@@ -2097,7 +2028,7 @@ class McpService extends import_core8.Service {
|
|
|
2097
2028
|
try {
|
|
2098
2029
|
await this.initializeConnection(name, JSON.parse(config));
|
|
2099
2030
|
} catch (err) {
|
|
2100
|
-
import_core8.logger.error({ error:
|
|
2031
|
+
import_core8.logger.error({ error: errMsg(err), serverName: name }, `Reconnect attempt failed for ${name}`);
|
|
2101
2032
|
this.handleDisconnection(name, err);
|
|
2102
2033
|
}
|
|
2103
2034
|
}
|
|
@@ -2110,7 +2041,7 @@ class McpService extends import_core8.Service {
|
|
|
2110
2041
|
await connection.transport.close();
|
|
2111
2042
|
await connection.client.close();
|
|
2112
2043
|
} catch (error) {
|
|
2113
|
-
import_core8.logger.error({ error:
|
|
2044
|
+
import_core8.logger.error({ error: errMsg(error), serverName: name }, `Failed to close transport for ${name}`);
|
|
2114
2045
|
}
|
|
2115
2046
|
this.connections.delete(name);
|
|
2116
2047
|
}
|
|
@@ -2123,9 +2054,6 @@ class McpService extends import_core8.Service {
|
|
|
2123
2054
|
this.connectionStates.delete(name);
|
|
2124
2055
|
}
|
|
2125
2056
|
}
|
|
2126
|
-
getServerConnection(serverName) {
|
|
2127
|
-
return this.connections.get(serverName);
|
|
2128
|
-
}
|
|
2129
2057
|
async buildStdioClientTransport(name, config) {
|
|
2130
2058
|
if (!config.command) {
|
|
2131
2059
|
throw new Error(`Missing command for stdio MCP server ${name}`);
|
|
@@ -2142,74 +2070,61 @@ class McpService extends import_core8.Service {
|
|
|
2142
2070
|
});
|
|
2143
2071
|
}
|
|
2144
2072
|
async buildHttpClientTransport(name, config) {
|
|
2145
|
-
if (!config.url)
|
|
2146
|
-
throw new Error(`Missing URL for
|
|
2147
|
-
|
|
2073
|
+
if (!config.url)
|
|
2074
|
+
throw new Error(`Missing URL for MCP server ${name}`);
|
|
2075
|
+
const url = new URL(config.url);
|
|
2076
|
+
const opts = config.headers ? { requestInit: { headers: config.headers } } : undefined;
|
|
2148
2077
|
if (config.type === "sse") {
|
|
2149
|
-
import_core8.logger.warn(`
|
|
2078
|
+
import_core8.logger.warn(`[MCP] "${name}": SSE requires Redis. Use "streamable-http" instead.`);
|
|
2079
|
+
return new import_sse.SSEClientTransport(url, opts);
|
|
2150
2080
|
}
|
|
2151
|
-
return new
|
|
2081
|
+
return new import_streamableHttp.StreamableHTTPClientTransport(url, opts);
|
|
2152
2082
|
}
|
|
2153
2083
|
appendErrorMessage(connection, error) {
|
|
2154
|
-
|
|
2084
|
+
connection.server.error = connection.server.error ? `${connection.server.error}
|
|
2155
2085
|
${error}` : error;
|
|
2156
|
-
connection.server.error = newError;
|
|
2157
2086
|
}
|
|
2158
2087
|
async fetchToolsList(serverName) {
|
|
2088
|
+
const connection = this.connections.get(serverName);
|
|
2089
|
+
if (!connection)
|
|
2090
|
+
return [];
|
|
2159
2091
|
try {
|
|
2160
|
-
const connection = this.getServerConnection(serverName);
|
|
2161
|
-
if (!connection) {
|
|
2162
|
-
return [];
|
|
2163
|
-
}
|
|
2164
2092
|
const response = await connection.client.listTools();
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
} catch (error) {
|
|
2175
|
-
import_core8.logger.warn({ error, toolName: tool.name, serverName }, `Tool compatibility failed for ${tool.name} on ${serverName}`);
|
|
2176
|
-
}
|
|
2093
|
+
return (response?.tools || []).map((tool) => {
|
|
2094
|
+
if (!tool.inputSchema)
|
|
2095
|
+
return tool;
|
|
2096
|
+
if (!this.compatibilityInitialized)
|
|
2097
|
+
this.initializeToolCompatibility();
|
|
2098
|
+
try {
|
|
2099
|
+
return { ...tool, inputSchema: this.applyToolCompatibility(tool.inputSchema) };
|
|
2100
|
+
} catch {
|
|
2101
|
+
return tool;
|
|
2177
2102
|
}
|
|
2178
|
-
return processedTool;
|
|
2179
2103
|
});
|
|
2180
|
-
import_core8.logger.info(`Fetched ${tools.length} tools for ${serverName}`);
|
|
2181
|
-
for (const tool of tools) {
|
|
2182
|
-
import_core8.logger.info(`[${serverName}] ${tool.name}: ${tool.description}`);
|
|
2183
|
-
}
|
|
2184
|
-
return tools;
|
|
2185
2104
|
} catch (error) {
|
|
2186
|
-
import_core8.logger.error({ error:
|
|
2105
|
+
import_core8.logger.error({ error: errMsg(error), serverName }, `Failed to fetch tools: ${serverName}`);
|
|
2187
2106
|
return [];
|
|
2188
2107
|
}
|
|
2189
2108
|
}
|
|
2190
|
-
async fetchResourcesList(
|
|
2109
|
+
async fetchResourcesList(name) {
|
|
2110
|
+
const conn = this.connections.get(name);
|
|
2111
|
+
if (!conn)
|
|
2112
|
+
return [];
|
|
2191
2113
|
try {
|
|
2192
|
-
|
|
2193
|
-
if (!connection) {
|
|
2194
|
-
return [];
|
|
2195
|
-
}
|
|
2196
|
-
const response = await connection.client.listResources();
|
|
2197
|
-
return response?.resources || [];
|
|
2114
|
+
return (await conn.client.listResources())?.resources || [];
|
|
2198
2115
|
} catch (error) {
|
|
2199
|
-
import_core8.logger.
|
|
2116
|
+
import_core8.logger.debug({ error: errMsg(error), serverName: name }, `No resources for ${name}`);
|
|
2200
2117
|
return [];
|
|
2201
2118
|
}
|
|
2202
2119
|
}
|
|
2203
|
-
async fetchResourceTemplatesList(
|
|
2120
|
+
async fetchResourceTemplatesList(name) {
|
|
2121
|
+
const conn = this.connections.get(name);
|
|
2122
|
+
if (!conn)
|
|
2123
|
+
return [];
|
|
2204
2124
|
try {
|
|
2205
|
-
|
|
2206
|
-
if (!connection) {
|
|
2207
|
-
return [];
|
|
2208
|
-
}
|
|
2209
|
-
const response = await connection.client.listResourceTemplates();
|
|
2210
|
-
return response?.resourceTemplates || [];
|
|
2125
|
+
return (await conn.client.listResourceTemplates())?.resourceTemplates || [];
|
|
2211
2126
|
} catch (error) {
|
|
2212
|
-
import_core8.logger.
|
|
2127
|
+
import_core8.logger.debug({ error: errMsg(error), serverName: name }, `No resource templates for ${name}`);
|
|
2213
2128
|
return [];
|
|
2214
2129
|
}
|
|
2215
2130
|
}
|
|
@@ -2219,76 +2134,53 @@ ${error}` : error;
|
|
|
2219
2134
|
getProviderData() {
|
|
2220
2135
|
return this.mcpProvider;
|
|
2221
2136
|
}
|
|
2222
|
-
async callTool(serverName, toolName,
|
|
2223
|
-
const
|
|
2224
|
-
if (!
|
|
2225
|
-
throw new Error(`No connection
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName }, `Failed to parse timeout configuration for server ${serverName}`);
|
|
2236
|
-
}
|
|
2237
|
-
const result = await connection.client.callTool({ name: toolName, arguments: toolArguments }, undefined, { timeout });
|
|
2238
|
-
if (!result.content) {
|
|
2239
|
-
throw new Error("Invalid tool result: missing content array");
|
|
2240
|
-
}
|
|
2137
|
+
async callTool(serverName, toolName, args) {
|
|
2138
|
+
const conn = this.connections.get(serverName);
|
|
2139
|
+
if (!conn)
|
|
2140
|
+
throw new Error(`No connection: ${serverName}`);
|
|
2141
|
+
if (conn.server.disabled)
|
|
2142
|
+
throw new Error(`Server disabled: ${serverName}`);
|
|
2143
|
+
const config = JSON.parse(conn.server.config);
|
|
2144
|
+
const timeout = (config.type === "stdio" ? config.timeoutInMillis : config.timeout) ?? DEFAULT_MCP_TIMEOUT_SECONDS;
|
|
2145
|
+
const result = await conn.client.callTool({ name: toolName, arguments: args }, undefined, {
|
|
2146
|
+
timeout
|
|
2147
|
+
});
|
|
2148
|
+
if (!result.content)
|
|
2149
|
+
throw new Error("Invalid tool result: missing content");
|
|
2241
2150
|
return result;
|
|
2242
2151
|
}
|
|
2243
2152
|
async readResource(serverName, uri) {
|
|
2244
|
-
const
|
|
2245
|
-
if (!
|
|
2246
|
-
throw new Error(`No connection
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
}
|
|
2251
|
-
return await connection.client.readResource({ uri });
|
|
2153
|
+
const conn = this.connections.get(serverName);
|
|
2154
|
+
if (!conn)
|
|
2155
|
+
throw new Error(`No connection: ${serverName}`);
|
|
2156
|
+
if (conn.server.disabled)
|
|
2157
|
+
throw new Error(`Server disabled: ${serverName}`);
|
|
2158
|
+
return conn.client.readResource({ uri });
|
|
2252
2159
|
}
|
|
2253
2160
|
async restartConnection(serverName) {
|
|
2254
|
-
const
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
await this.initializeConnection(serverName, JSON.parse(config));
|
|
2263
|
-
import_core8.logger.info(`${serverName} MCP server connected`);
|
|
2264
|
-
} catch (error) {
|
|
2265
|
-
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName }, `Failed to restart connection for ${serverName}`);
|
|
2266
|
-
throw new Error(`Failed to connect to ${serverName} MCP server`);
|
|
2267
|
-
}
|
|
2268
|
-
}
|
|
2161
|
+
const conn = this.connections.get(serverName);
|
|
2162
|
+
if (!conn)
|
|
2163
|
+
throw new Error(`No connection: ${serverName}`);
|
|
2164
|
+
const config = conn.server.config;
|
|
2165
|
+
conn.server.status = "connecting";
|
|
2166
|
+
conn.server.error = "";
|
|
2167
|
+
await this.deleteConnection(serverName);
|
|
2168
|
+
await this.initializeConnection(serverName, JSON.parse(config));
|
|
2269
2169
|
}
|
|
2270
2170
|
initializeToolCompatibility() {
|
|
2271
2171
|
if (this.compatibilityInitialized)
|
|
2272
2172
|
return;
|
|
2273
2173
|
this.toolCompatibility = createMcpToolCompatibilitySync(this.runtime);
|
|
2274
2174
|
this.compatibilityInitialized = true;
|
|
2275
|
-
if (this.toolCompatibility) {
|
|
2276
|
-
import_core8.logger.info(`Tool compatibility enabled`);
|
|
2277
|
-
} else {
|
|
2278
|
-
import_core8.logger.info(`No tool compatibility needed`);
|
|
2279
|
-
}
|
|
2280
2175
|
}
|
|
2281
2176
|
applyToolCompatibility(toolSchema) {
|
|
2282
|
-
if (!this.compatibilityInitialized)
|
|
2177
|
+
if (!this.compatibilityInitialized)
|
|
2283
2178
|
this.initializeToolCompatibility();
|
|
2284
|
-
|
|
2285
|
-
if (!this.toolCompatibility || !toolSchema) {
|
|
2179
|
+
if (!this.toolCompatibility || !toolSchema)
|
|
2286
2180
|
return toolSchema;
|
|
2287
|
-
}
|
|
2288
2181
|
try {
|
|
2289
2182
|
return this.toolCompatibility.transformToolSchema(toolSchema);
|
|
2290
|
-
} catch
|
|
2291
|
-
import_core8.logger.warn({ error }, `Tool compatibility transformation failed`);
|
|
2183
|
+
} catch {
|
|
2292
2184
|
return toolSchema;
|
|
2293
2185
|
}
|
|
2294
2186
|
}
|
|
@@ -2307,4 +2199,4 @@ var mcpPlugin = {
|
|
|
2307
2199
|
};
|
|
2308
2200
|
var src_default = mcpPlugin;
|
|
2309
2201
|
|
|
2310
|
-
//# debugId=
|
|
2202
|
+
//# debugId=4999FDCEADE0A8A564756E2164756E21
|