@elizaos/plugin-mcp 1.7.0 → 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 +199 -309
- package/dist/cjs/index.js.map +9 -9
- package/dist/index.js +199 -309
- package/dist/index.js.map +9 -9
- 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/package.json +1 -1
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
|
|
@@ -1445,33 +1444,6 @@ function createFeedbackPrompt2(originalResponse, errorMessage, itemType, itemsDe
|
|
|
1445
1444
|
User request: ${userMessage}`;
|
|
1446
1445
|
}
|
|
1447
1446
|
|
|
1448
|
-
// src/utils/handler.ts
|
|
1449
|
-
async function handleNoToolAvailable(callback, toolSelection) {
|
|
1450
|
-
const responseText = "I don't have a specific tool that can help with that request. Let me try to assist you directly instead.";
|
|
1451
|
-
const thoughtText = "No appropriate MCP tool available for this request. Falling back to direct assistance.";
|
|
1452
|
-
if (callback && toolSelection?.noToolAvailable) {
|
|
1453
|
-
await callback({
|
|
1454
|
-
text: responseText,
|
|
1455
|
-
thought: thoughtText,
|
|
1456
|
-
actions: ["REPLY"]
|
|
1457
|
-
});
|
|
1458
|
-
}
|
|
1459
|
-
return {
|
|
1460
|
-
text: responseText,
|
|
1461
|
-
values: {
|
|
1462
|
-
success: true,
|
|
1463
|
-
noToolAvailable: true,
|
|
1464
|
-
fallbackToDirectAssistance: true
|
|
1465
|
-
},
|
|
1466
|
-
data: {
|
|
1467
|
-
actionName: "CALL_MCP_TOOL",
|
|
1468
|
-
noToolAvailable: true,
|
|
1469
|
-
reason: toolSelection?.reasoning || "No appropriate tool available"
|
|
1470
|
-
},
|
|
1471
|
-
success: true
|
|
1472
|
-
};
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
1447
|
// src/actions/callToolAction.ts
|
|
1476
1448
|
var callToolAction = {
|
|
1477
1449
|
name: "CALL_MCP_TOOL",
|
|
@@ -1533,7 +1505,7 @@ ${JSON.stringify(toolSelectionArgument, null, 2)}`);
|
|
|
1533
1505
|
const result = await mcpService.callTool(serverName, toolName, toolSelectionArgument.toolArguments);
|
|
1534
1506
|
const { toolOutput, hasAttachments, attachments } = processToolResult(result, serverName, toolName, runtime2, message.entityId);
|
|
1535
1507
|
const replyMemory = await handleToolResponse(runtime2, message, serverName, toolName, toolSelectionArgument.toolArguments, toolOutput, hasAttachments, attachments, composedState, mcpProvider, callback);
|
|
1536
|
-
|
|
1508
|
+
const actionResult = {
|
|
1537
1509
|
text: `Successfully called tool: ${serverName}/${toolName}. Reasoned response: ${replyMemory.content.text}`,
|
|
1538
1510
|
values: {
|
|
1539
1511
|
success: true,
|
|
@@ -1554,6 +1526,15 @@ ${JSON.stringify(toolSelectionArgument, null, 2)}`);
|
|
|
1554
1526
|
},
|
|
1555
1527
|
success: true
|
|
1556
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;
|
|
1557
1538
|
} catch (error) {
|
|
1558
1539
|
return await handleMcpError(composedState, mcpProvider, error, runtime2, message, "tool", callback);
|
|
1559
1540
|
}
|
|
@@ -1791,19 +1772,20 @@ var readResourceAction = {
|
|
|
1791
1772
|
};
|
|
1792
1773
|
|
|
1793
1774
|
// src/provider.ts
|
|
1775
|
+
var createEmptyProvider = () => ({
|
|
1776
|
+
values: { mcp: {} },
|
|
1777
|
+
data: { mcp: {} },
|
|
1778
|
+
text: "No MCP servers available."
|
|
1779
|
+
});
|
|
1794
1780
|
var provider = {
|
|
1795
1781
|
name: "MCP",
|
|
1796
|
-
description: "
|
|
1782
|
+
description: "Connected MCP servers, tools, and resources",
|
|
1797
1783
|
get: async (runtime2, _message, _state) => {
|
|
1798
|
-
const
|
|
1799
|
-
if (!
|
|
1800
|
-
return
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
text: "No MCP servers are available."
|
|
1804
|
-
};
|
|
1805
|
-
}
|
|
1806
|
-
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();
|
|
1807
1789
|
}
|
|
1808
1790
|
};
|
|
1809
1791
|
|
|
@@ -1812,6 +1794,9 @@ var import_core8 = require("@elizaos/core");
|
|
|
1812
1794
|
var import_client = require("@modelcontextprotocol/sdk/client/index.js");
|
|
1813
1795
|
var import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
1814
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
|
+
|
|
1815
1800
|
class McpService extends import_core8.Service {
|
|
1816
1801
|
static serviceType = MCP_SERVICE_NAME;
|
|
1817
1802
|
capabilityDescription = "Enables the agent to interact with MCP (Model Context Protocol) servers";
|
|
@@ -1828,7 +1813,6 @@ class McpService extends import_core8.Service {
|
|
|
1828
1813
|
initializationPromise = null;
|
|
1829
1814
|
constructor(runtime2) {
|
|
1830
1815
|
super(runtime2);
|
|
1831
|
-
import_core8.logger.info("[McpService] Constructor called, starting initialization...");
|
|
1832
1816
|
this.initializationPromise = this.initializeMcpServers();
|
|
1833
1817
|
}
|
|
1834
1818
|
static async start(runtime2) {
|
|
@@ -1844,116 +1828,70 @@ class McpService extends import_core8.Service {
|
|
|
1844
1828
|
}
|
|
1845
1829
|
}
|
|
1846
1830
|
async stop() {
|
|
1847
|
-
for (const
|
|
1831
|
+
for (const name of this.connections.keys()) {
|
|
1848
1832
|
await this.deleteConnection(name);
|
|
1849
1833
|
}
|
|
1850
|
-
this.
|
|
1851
|
-
for (const state of this.connectionStates.values()) {
|
|
1834
|
+
for (const [name, state] of this.connectionStates.entries()) {
|
|
1852
1835
|
if (state.pingInterval)
|
|
1853
1836
|
clearInterval(state.pingInterval);
|
|
1854
1837
|
if (state.reconnectTimeout)
|
|
1855
1838
|
clearTimeout(state.reconnectTimeout);
|
|
1839
|
+
this.connectionStates.delete(name);
|
|
1856
1840
|
}
|
|
1857
|
-
this.connectionStates.clear();
|
|
1858
1841
|
}
|
|
1859
1842
|
async initializeMcpServers() {
|
|
1860
|
-
import_core8.logger.info("[McpService] Starting MCP server initialization...");
|
|
1861
1843
|
try {
|
|
1862
1844
|
const mcpSettings = this.getMcpSettings();
|
|
1863
|
-
|
|
1864
|
-
const serverNames = mcpSettings?.servers ? Object.keys(mcpSettings.servers) : [];
|
|
1865
|
-
import_core8.logger.info(`[McpService] Getting MCP settings... hasSettings=${!!mcpSettings} hasServers=${!!mcpSettings?.servers} serverCount=${serverCount} servers=${JSON.stringify(serverNames)}`);
|
|
1866
|
-
if (!mcpSettings || !mcpSettings.servers) {
|
|
1867
|
-
import_core8.logger.info("[McpService] No MCP servers configured.");
|
|
1868
|
-
this.mcpProvider = buildMcpProviderData([]);
|
|
1869
|
-
return;
|
|
1870
|
-
}
|
|
1871
|
-
if (Object.keys(mcpSettings.servers).length === 0) {
|
|
1872
|
-
import_core8.logger.info("[McpService] MCP settings exist but no servers configured.");
|
|
1845
|
+
if (!mcpSettings?.servers || Object.keys(mcpSettings.servers).length === 0) {
|
|
1873
1846
|
this.mcpProvider = buildMcpProviderData([]);
|
|
1874
1847
|
return;
|
|
1875
1848
|
}
|
|
1876
|
-
import_core8.logger.info(`[McpService] Connecting to ${Object.keys(mcpSettings.servers).length} MCP servers: ${JSON.stringify(Object.keys(mcpSettings.servers))}`);
|
|
1877
1849
|
const connectionStartTime = Date.now();
|
|
1878
1850
|
await this.updateServerConnections(mcpSettings.servers);
|
|
1879
1851
|
const connectionDuration = Date.now() - connectionStartTime;
|
|
1880
1852
|
const servers = this.getServers();
|
|
1881
|
-
const
|
|
1882
|
-
const
|
|
1883
|
-
if (
|
|
1884
|
-
|
|
1885
|
-
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(", ")})`);
|
|
1886
1857
|
}
|
|
1887
|
-
if (
|
|
1888
|
-
|
|
1889
|
-
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(", ")}`);
|
|
1890
1860
|
}
|
|
1891
|
-
if (
|
|
1892
|
-
import_core8.logger.error(`[
|
|
1861
|
+
if (connected.length === 0 && servers.length > 0) {
|
|
1862
|
+
import_core8.logger.error(`[MCP] All servers failed`);
|
|
1893
1863
|
}
|
|
1894
1864
|
this.mcpProvider = buildMcpProviderData(servers);
|
|
1895
|
-
const mcpDataKeys = Object.keys(this.mcpProvider.data?.mcp || {});
|
|
1896
|
-
import_core8.logger.info(`[McpService] MCP provider data built: ${mcpDataKeys.length} server(s) available`);
|
|
1897
1865
|
} catch (error) {
|
|
1898
|
-
import_core8.logger.error({ error:
|
|
1866
|
+
import_core8.logger.error({ error: errMsg(error) }, "[MCP] Initialization failed");
|
|
1899
1867
|
this.mcpProvider = buildMcpProviderData([]);
|
|
1900
1868
|
}
|
|
1901
1869
|
}
|
|
1902
1870
|
getMcpSettings() {
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
if (!settings || !settings.servers) {
|
|
1907
|
-
const characterSettings = this.runtime.character?.settings;
|
|
1908
|
-
if (characterSettings?.mcp) {
|
|
1909
|
-
import_core8.logger.info("[McpService] Found MCP settings in character.settings.mcp (fallback)");
|
|
1910
|
-
settings = characterSettings.mcp;
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1913
|
-
if (!settings || !settings.servers) {
|
|
1914
|
-
const runtimeSettings = this.runtime.settings;
|
|
1915
|
-
if (runtimeSettings?.mcp) {
|
|
1916
|
-
import_core8.logger.info("[McpService] Found MCP settings in runtime.settings.mcp (fallback)");
|
|
1917
|
-
settings = runtimeSettings.mcp;
|
|
1918
|
-
}
|
|
1871
|
+
let settings = this.runtime.getSetting("mcp");
|
|
1872
|
+
if (!settings || typeof settings === "object" && !settings.servers) {
|
|
1873
|
+
settings = this.runtime.character?.settings?.mcp;
|
|
1919
1874
|
}
|
|
1920
|
-
if (settings
|
|
1921
|
-
|
|
1922
|
-
return settings;
|
|
1875
|
+
if (!settings || typeof settings === "object" && !settings.servers) {
|
|
1876
|
+
settings = this.runtime.settings?.mcp;
|
|
1923
1877
|
}
|
|
1924
|
-
|
|
1925
|
-
return;
|
|
1878
|
+
return settings && typeof settings === "object" && settings.servers ? settings : undefined;
|
|
1926
1879
|
}
|
|
1927
1880
|
async updateServerConnections(serverConfigs) {
|
|
1928
|
-
const currentNames = new Set(this.connections.keys());
|
|
1929
1881
|
const newNames = new Set(Object.keys(serverConfigs));
|
|
1930
|
-
for (const name of
|
|
1931
|
-
if (!newNames.has(name))
|
|
1882
|
+
for (const name of this.connections.keys()) {
|
|
1883
|
+
if (!newNames.has(name))
|
|
1932
1884
|
await this.deleteConnection(name);
|
|
1933
|
-
import_core8.logger.info(`Deleted MCP server: ${name}`);
|
|
1934
|
-
}
|
|
1935
1885
|
}
|
|
1936
|
-
|
|
1937
|
-
const
|
|
1938
|
-
if (!
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName: name }, `✗ Failed to connect to new MCP server ${name}`);
|
|
1944
|
-
}
|
|
1945
|
-
} else if (JSON.stringify(config) !== currentConnection.server.config) {
|
|
1946
|
-
try {
|
|
1947
|
-
await this.deleteConnection(name);
|
|
1948
|
-
await this.initializeConnection(name, config);
|
|
1949
|
-
import_core8.logger.info(`✓ Reconnected MCP server with updated config: ${name}`);
|
|
1950
|
-
} catch (error) {
|
|
1951
|
-
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName: name }, `✗ Failed to reconnect MCP server ${name}`);
|
|
1952
|
-
}
|
|
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}`));
|
|
1953
1893
|
}
|
|
1954
|
-
});
|
|
1955
|
-
await Promise.allSettled(connectionPromises);
|
|
1956
|
-
import_core8.logger.info(`[McpService] All server connection attempts completed`);
|
|
1894
|
+
}));
|
|
1957
1895
|
}
|
|
1958
1896
|
async initializeConnection(name, config) {
|
|
1959
1897
|
await this.deleteConnection(name);
|
|
@@ -1967,19 +1905,19 @@ class McpService extends import_core8.Service {
|
|
|
1967
1905
|
const client = new import_client.Client({ name: "ElizaOS", version: "1.0.0" }, { capabilities: {} });
|
|
1968
1906
|
const transport = config.type === "stdio" ? await this.buildStdioClientTransport(name, config) : await this.buildHttpClientTransport(name, config);
|
|
1969
1907
|
const connection = {
|
|
1970
|
-
server: {
|
|
1971
|
-
name,
|
|
1972
|
-
config: JSON.stringify(config),
|
|
1973
|
-
status: "connecting"
|
|
1974
|
-
},
|
|
1908
|
+
server: { name, config: JSON.stringify(config), status: "connecting" },
|
|
1975
1909
|
client,
|
|
1976
1910
|
transport
|
|
1977
1911
|
};
|
|
1978
1912
|
this.connections.set(name, connection);
|
|
1979
1913
|
this.setupTransportHandlers(name, connection, state);
|
|
1980
|
-
|
|
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
|
+
]);
|
|
1981
1920
|
const capabilities = client.getServerCapabilities();
|
|
1982
|
-
import_core8.logger.debug(`[${name}] Server capabilities:`, JSON.stringify(capabilities || {}));
|
|
1983
1921
|
const tools = await this.fetchToolsList(name);
|
|
1984
1922
|
const resources = capabilities?.resources ? await this.fetchResourcesList(name) : [];
|
|
1985
1923
|
const resourceTemplates = capabilities?.resources ? await this.fetchResourceTemplatesList(name) : [];
|
|
@@ -1997,7 +1935,6 @@ class McpService extends import_core8.Service {
|
|
|
1997
1935
|
state.reconnectAttempts = 0;
|
|
1998
1936
|
state.consecutivePingFailures = 0;
|
|
1999
1937
|
this.startPingMonitoring(name);
|
|
2000
|
-
import_core8.logger.info(`Successfully connected to MCP server: ${name}`);
|
|
2001
1938
|
} catch (error) {
|
|
2002
1939
|
state.status = "disconnected";
|
|
2003
1940
|
state.lastError = error instanceof Error ? error : new Error(String(error));
|
|
@@ -2007,26 +1944,21 @@ class McpService extends import_core8.Service {
|
|
|
2007
1944
|
}
|
|
2008
1945
|
setupTransportHandlers(name, connection, state) {
|
|
2009
1946
|
const config = JSON.parse(connection.server.config);
|
|
2010
|
-
const
|
|
1947
|
+
const isHttp = config.type !== "stdio";
|
|
2011
1948
|
connection.transport.onerror = async (error) => {
|
|
2012
|
-
const
|
|
2013
|
-
const isExpectedTimeout =
|
|
2014
|
-
if (isExpectedTimeout) {
|
|
2015
|
-
import_core8.logger.
|
|
2016
|
-
} else {
|
|
2017
|
-
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}`);
|
|
2018
1953
|
connection.server.status = "disconnected";
|
|
2019
|
-
this.appendErrorMessage(connection,
|
|
1954
|
+
this.appendErrorMessage(connection, msg);
|
|
2020
1955
|
}
|
|
2021
|
-
if (!
|
|
1956
|
+
if (!isHttp)
|
|
2022
1957
|
this.handleDisconnection(name, error);
|
|
2023
|
-
}
|
|
2024
1958
|
};
|
|
2025
1959
|
connection.transport.onclose = async () => {
|
|
2026
|
-
if (
|
|
2027
|
-
import_core8.logger.
|
|
2028
|
-
} else {
|
|
2029
|
-
import_core8.logger.warn({ serverName: name }, `Transport closed for "${name}"`);
|
|
1960
|
+
if (!isHttp) {
|
|
1961
|
+
import_core8.logger.warn({ serverName: name }, `Transport closed: ${name}`);
|
|
2030
1962
|
connection.server.status = "disconnected";
|
|
2031
1963
|
this.handleDisconnection(name, new Error("Transport closed"));
|
|
2032
1964
|
}
|
|
@@ -2037,11 +1969,8 @@ class McpService extends import_core8.Service {
|
|
|
2037
1969
|
if (!connection)
|
|
2038
1970
|
return;
|
|
2039
1971
|
const config = JSON.parse(connection.server.config);
|
|
2040
|
-
|
|
2041
|
-
if (isHttpTransport) {
|
|
2042
|
-
import_core8.logger.debug(`[McpService] Skipping ping monitoring for HTTP server: ${name}`);
|
|
1972
|
+
if (config.type !== "stdio")
|
|
2043
1973
|
return;
|
|
2044
|
-
}
|
|
2045
1974
|
const state = this.connectionStates.get(name);
|
|
2046
1975
|
if (!state || !this.pingConfig.enabled)
|
|
2047
1976
|
return;
|
|
@@ -2049,7 +1978,7 @@ class McpService extends import_core8.Service {
|
|
|
2049
1978
|
clearInterval(state.pingInterval);
|
|
2050
1979
|
state.pingInterval = setInterval(() => {
|
|
2051
1980
|
this.sendPing(name).catch((err) => {
|
|
2052
|
-
import_core8.logger.warn({ error:
|
|
1981
|
+
import_core8.logger.warn({ error: errMsg(err), serverName: name }, `Ping failed: ${name}`);
|
|
2053
1982
|
this.handlePingFailure(name, err);
|
|
2054
1983
|
});
|
|
2055
1984
|
}, this.pingConfig.intervalMs);
|
|
@@ -2057,7 +1986,7 @@ class McpService extends import_core8.Service {
|
|
|
2057
1986
|
async sendPing(name) {
|
|
2058
1987
|
const connection = this.connections.get(name);
|
|
2059
1988
|
if (!connection)
|
|
2060
|
-
throw new Error(`No connection
|
|
1989
|
+
throw new Error(`No connection: ${name}`);
|
|
2061
1990
|
await Promise.race([
|
|
2062
1991
|
connection.client.listTools(),
|
|
2063
1992
|
new Promise((_, reject) => setTimeout(() => reject(new Error("Ping timeout")), this.pingConfig.timeoutMs))
|
|
@@ -2099,7 +2028,7 @@ class McpService extends import_core8.Service {
|
|
|
2099
2028
|
try {
|
|
2100
2029
|
await this.initializeConnection(name, JSON.parse(config));
|
|
2101
2030
|
} catch (err) {
|
|
2102
|
-
import_core8.logger.error({ error:
|
|
2031
|
+
import_core8.logger.error({ error: errMsg(err), serverName: name }, `Reconnect attempt failed for ${name}`);
|
|
2103
2032
|
this.handleDisconnection(name, err);
|
|
2104
2033
|
}
|
|
2105
2034
|
}
|
|
@@ -2112,7 +2041,7 @@ class McpService extends import_core8.Service {
|
|
|
2112
2041
|
await connection.transport.close();
|
|
2113
2042
|
await connection.client.close();
|
|
2114
2043
|
} catch (error) {
|
|
2115
|
-
import_core8.logger.error({ error:
|
|
2044
|
+
import_core8.logger.error({ error: errMsg(error), serverName: name }, `Failed to close transport for ${name}`);
|
|
2116
2045
|
}
|
|
2117
2046
|
this.connections.delete(name);
|
|
2118
2047
|
}
|
|
@@ -2125,9 +2054,6 @@ class McpService extends import_core8.Service {
|
|
|
2125
2054
|
this.connectionStates.delete(name);
|
|
2126
2055
|
}
|
|
2127
2056
|
}
|
|
2128
|
-
getServerConnection(serverName) {
|
|
2129
|
-
return this.connections.get(serverName);
|
|
2130
|
-
}
|
|
2131
2057
|
async buildStdioClientTransport(name, config) {
|
|
2132
2058
|
if (!config.command) {
|
|
2133
2059
|
throw new Error(`Missing command for stdio MCP server ${name}`);
|
|
@@ -2144,74 +2070,61 @@ class McpService extends import_core8.Service {
|
|
|
2144
2070
|
});
|
|
2145
2071
|
}
|
|
2146
2072
|
async buildHttpClientTransport(name, config) {
|
|
2147
|
-
if (!config.url)
|
|
2148
|
-
throw new Error(`Missing URL for
|
|
2149
|
-
|
|
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;
|
|
2150
2077
|
if (config.type === "sse") {
|
|
2151
|
-
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);
|
|
2152
2080
|
}
|
|
2153
|
-
return new
|
|
2081
|
+
return new import_streamableHttp.StreamableHTTPClientTransport(url, opts);
|
|
2154
2082
|
}
|
|
2155
2083
|
appendErrorMessage(connection, error) {
|
|
2156
|
-
|
|
2084
|
+
connection.server.error = connection.server.error ? `${connection.server.error}
|
|
2157
2085
|
${error}` : error;
|
|
2158
|
-
connection.server.error = newError;
|
|
2159
2086
|
}
|
|
2160
2087
|
async fetchToolsList(serverName) {
|
|
2088
|
+
const connection = this.connections.get(serverName);
|
|
2089
|
+
if (!connection)
|
|
2090
|
+
return [];
|
|
2161
2091
|
try {
|
|
2162
|
-
const connection = this.getServerConnection(serverName);
|
|
2163
|
-
if (!connection) {
|
|
2164
|
-
return [];
|
|
2165
|
-
}
|
|
2166
2092
|
const response = await connection.client.listTools();
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
} catch (error) {
|
|
2177
|
-
import_core8.logger.warn({ error, toolName: tool.name, serverName }, `Tool compatibility failed for ${tool.name} on ${serverName}`);
|
|
2178
|
-
}
|
|
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;
|
|
2179
2102
|
}
|
|
2180
|
-
return processedTool;
|
|
2181
2103
|
});
|
|
2182
|
-
import_core8.logger.info(`Fetched ${tools.length} tools for ${serverName}`);
|
|
2183
|
-
for (const tool of tools) {
|
|
2184
|
-
import_core8.logger.info(`[${serverName}] ${tool.name}: ${tool.description}`);
|
|
2185
|
-
}
|
|
2186
|
-
return tools;
|
|
2187
2104
|
} catch (error) {
|
|
2188
|
-
import_core8.logger.error({ error:
|
|
2105
|
+
import_core8.logger.error({ error: errMsg(error), serverName }, `Failed to fetch tools: ${serverName}`);
|
|
2189
2106
|
return [];
|
|
2190
2107
|
}
|
|
2191
2108
|
}
|
|
2192
|
-
async fetchResourcesList(
|
|
2109
|
+
async fetchResourcesList(name) {
|
|
2110
|
+
const conn = this.connections.get(name);
|
|
2111
|
+
if (!conn)
|
|
2112
|
+
return [];
|
|
2193
2113
|
try {
|
|
2194
|
-
|
|
2195
|
-
if (!connection) {
|
|
2196
|
-
return [];
|
|
2197
|
-
}
|
|
2198
|
-
const response = await connection.client.listResources();
|
|
2199
|
-
return response?.resources || [];
|
|
2114
|
+
return (await conn.client.listResources())?.resources || [];
|
|
2200
2115
|
} catch (error) {
|
|
2201
|
-
import_core8.logger.
|
|
2116
|
+
import_core8.logger.debug({ error: errMsg(error), serverName: name }, `No resources for ${name}`);
|
|
2202
2117
|
return [];
|
|
2203
2118
|
}
|
|
2204
2119
|
}
|
|
2205
|
-
async fetchResourceTemplatesList(
|
|
2120
|
+
async fetchResourceTemplatesList(name) {
|
|
2121
|
+
const conn = this.connections.get(name);
|
|
2122
|
+
if (!conn)
|
|
2123
|
+
return [];
|
|
2206
2124
|
try {
|
|
2207
|
-
|
|
2208
|
-
if (!connection) {
|
|
2209
|
-
return [];
|
|
2210
|
-
}
|
|
2211
|
-
const response = await connection.client.listResourceTemplates();
|
|
2212
|
-
return response?.resourceTemplates || [];
|
|
2125
|
+
return (await conn.client.listResourceTemplates())?.resourceTemplates || [];
|
|
2213
2126
|
} catch (error) {
|
|
2214
|
-
import_core8.logger.
|
|
2127
|
+
import_core8.logger.debug({ error: errMsg(error), serverName: name }, `No resource templates for ${name}`);
|
|
2215
2128
|
return [];
|
|
2216
2129
|
}
|
|
2217
2130
|
}
|
|
@@ -2221,76 +2134,53 @@ ${error}` : error;
|
|
|
2221
2134
|
getProviderData() {
|
|
2222
2135
|
return this.mcpProvider;
|
|
2223
2136
|
}
|
|
2224
|
-
async callTool(serverName, toolName,
|
|
2225
|
-
const
|
|
2226
|
-
if (!
|
|
2227
|
-
throw new Error(`No connection
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName }, `Failed to parse timeout configuration for server ${serverName}`);
|
|
2238
|
-
}
|
|
2239
|
-
const result = await connection.client.callTool({ name: toolName, arguments: toolArguments }, undefined, { timeout });
|
|
2240
|
-
if (!result.content) {
|
|
2241
|
-
throw new Error("Invalid tool result: missing content array");
|
|
2242
|
-
}
|
|
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");
|
|
2243
2150
|
return result;
|
|
2244
2151
|
}
|
|
2245
2152
|
async readResource(serverName, uri) {
|
|
2246
|
-
const
|
|
2247
|
-
if (!
|
|
2248
|
-
throw new Error(`No connection
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
}
|
|
2253
|
-
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 });
|
|
2254
2159
|
}
|
|
2255
2160
|
async restartConnection(serverName) {
|
|
2256
|
-
const
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
await this.initializeConnection(serverName, JSON.parse(config));
|
|
2265
|
-
import_core8.logger.info(`${serverName} MCP server connected`);
|
|
2266
|
-
} catch (error) {
|
|
2267
|
-
import_core8.logger.error({ error: error instanceof Error ? error.message : String(error), serverName }, `Failed to restart connection for ${serverName}`);
|
|
2268
|
-
throw new Error(`Failed to connect to ${serverName} MCP server`);
|
|
2269
|
-
}
|
|
2270
|
-
}
|
|
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));
|
|
2271
2169
|
}
|
|
2272
2170
|
initializeToolCompatibility() {
|
|
2273
2171
|
if (this.compatibilityInitialized)
|
|
2274
2172
|
return;
|
|
2275
2173
|
this.toolCompatibility = createMcpToolCompatibilitySync(this.runtime);
|
|
2276
2174
|
this.compatibilityInitialized = true;
|
|
2277
|
-
if (this.toolCompatibility) {
|
|
2278
|
-
import_core8.logger.info(`Tool compatibility enabled`);
|
|
2279
|
-
} else {
|
|
2280
|
-
import_core8.logger.info(`No tool compatibility needed`);
|
|
2281
|
-
}
|
|
2282
2175
|
}
|
|
2283
2176
|
applyToolCompatibility(toolSchema) {
|
|
2284
|
-
if (!this.compatibilityInitialized)
|
|
2177
|
+
if (!this.compatibilityInitialized)
|
|
2285
2178
|
this.initializeToolCompatibility();
|
|
2286
|
-
|
|
2287
|
-
if (!this.toolCompatibility || !toolSchema) {
|
|
2179
|
+
if (!this.toolCompatibility || !toolSchema)
|
|
2288
2180
|
return toolSchema;
|
|
2289
|
-
}
|
|
2290
2181
|
try {
|
|
2291
2182
|
return this.toolCompatibility.transformToolSchema(toolSchema);
|
|
2292
|
-
} catch
|
|
2293
|
-
import_core8.logger.warn({ error }, `Tool compatibility transformation failed`);
|
|
2183
|
+
} catch {
|
|
2294
2184
|
return toolSchema;
|
|
2295
2185
|
}
|
|
2296
2186
|
}
|
|
@@ -2309,4 +2199,4 @@ var mcpPlugin = {
|
|
|
2309
2199
|
};
|
|
2310
2200
|
var src_default = mcpPlugin;
|
|
2311
2201
|
|
|
2312
|
-
//# debugId=
|
|
2202
|
+
//# debugId=4999FDCEADE0A8A564756E2164756E21
|