@rainfall-devkit/sdk 0.2.0 → 0.2.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/chunk-EI7SJH5K.mjs +85 -0
- package/dist/chunk-NTTAVKRT.mjs +89 -0
- package/dist/chunk-RVKW5KBT.mjs +269 -0
- package/dist/chunk-XAHJQRBJ.mjs +269 -0
- package/dist/chunk-XEQ6U3JQ.mjs +269 -0
- package/dist/cli/index.js +1088 -9
- package/dist/cli/index.mjs +102 -3
- package/dist/config-7UT7GYSN.mjs +16 -0
- package/dist/config-MD45VGWD.mjs +14 -0
- package/dist/daemon/index.d.mts +35 -3
- package/dist/daemon/index.d.ts +35 -3
- package/dist/daemon/index.js +716 -7
- package/dist/daemon/index.mjs +720 -8
- package/dist/index.d.mts +248 -3
- package/dist/index.d.ts +248 -3
- package/dist/index.js +490 -2
- package/dist/index.mjs +214 -1
- package/dist/listeners-B5Vy9Ao5.d.ts +372 -0
- package/dist/listeners-DRwITBW_.d.mts +372 -0
- package/dist/listeners-DrMrvFT5.d.ts +372 -0
- package/dist/listeners-MNAnpZj-.d.mts +372 -0
- package/dist/listeners-PZI7iT85.d.ts +372 -0
- package/dist/listeners-jLwetUnx.d.mts +372 -0
- package/dist/mcp.d.mts +6 -2
- package/dist/mcp.d.ts +6 -2
- package/dist/sdk-4OvXPr8E.d.mts +1054 -0
- package/dist/sdk-4OvXPr8E.d.ts +1054 -0
- package/dist/sdk-CN1ezZrI.d.mts +1054 -0
- package/dist/sdk-CN1ezZrI.d.ts +1054 -0
- package/dist/sdk-Xw0BjsLd.d.mts +1054 -0
- package/dist/sdk-Xw0BjsLd.d.ts +1054 -0
- package/package.json +4 -1
package/dist/daemon/index.js
CHANGED
|
@@ -40,12 +40,16 @@ var init_cjs_shims = __esm({
|
|
|
40
40
|
// src/cli/config.ts
|
|
41
41
|
var config_exports = {};
|
|
42
42
|
__export(config_exports, {
|
|
43
|
+
getConfigDir: () => getConfigDir,
|
|
43
44
|
getLLMConfig: () => getLLMConfig,
|
|
44
45
|
getProviderBaseUrl: () => getProviderBaseUrl,
|
|
45
46
|
isLocalProvider: () => isLocalProvider,
|
|
46
47
|
loadConfig: () => loadConfig,
|
|
47
48
|
saveConfig: () => saveConfig
|
|
48
49
|
});
|
|
50
|
+
function getConfigDir() {
|
|
51
|
+
return CONFIG_DIR;
|
|
52
|
+
}
|
|
49
53
|
function loadConfig() {
|
|
50
54
|
let config = {};
|
|
51
55
|
if ((0, import_fs.existsSync)(CONFIG_FILE)) {
|
|
@@ -110,6 +114,7 @@ function getProviderBaseUrl(config) {
|
|
|
110
114
|
case "ollama":
|
|
111
115
|
return config.llm?.baseUrl || "http://localhost:11434/v1";
|
|
112
116
|
case "local":
|
|
117
|
+
case "custom":
|
|
113
118
|
return config.llm?.baseUrl || "http://localhost:1234/v1";
|
|
114
119
|
case "rainfall":
|
|
115
120
|
default:
|
|
@@ -132,6 +137,7 @@ var init_config = __esm({
|
|
|
132
137
|
// src/daemon/index.ts
|
|
133
138
|
var daemon_exports = {};
|
|
134
139
|
__export(daemon_exports, {
|
|
140
|
+
MCPProxyHub: () => MCPProxyHub,
|
|
135
141
|
RainfallDaemon: () => RainfallDaemon,
|
|
136
142
|
getDaemonInstance: () => getDaemonInstance,
|
|
137
143
|
getDaemonStatus: () => getDaemonStatus,
|
|
@@ -140,7 +146,7 @@ __export(daemon_exports, {
|
|
|
140
146
|
});
|
|
141
147
|
module.exports = __toCommonJS(daemon_exports);
|
|
142
148
|
init_cjs_shims();
|
|
143
|
-
var
|
|
149
|
+
var import_ws2 = require("ws");
|
|
144
150
|
var import_express = __toESM(require("express"));
|
|
145
151
|
|
|
146
152
|
// src/sdk.ts
|
|
@@ -1645,6 +1651,566 @@ var RainfallListenerRegistry = class {
|
|
|
1645
1651
|
}
|
|
1646
1652
|
};
|
|
1647
1653
|
|
|
1654
|
+
// src/services/mcp-proxy.ts
|
|
1655
|
+
init_cjs_shims();
|
|
1656
|
+
var import_ws = require("ws");
|
|
1657
|
+
var import_client2 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
1658
|
+
var import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
1659
|
+
var import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
1660
|
+
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
1661
|
+
var MCPProxyHub = class {
|
|
1662
|
+
clients = /* @__PURE__ */ new Map();
|
|
1663
|
+
options;
|
|
1664
|
+
refreshTimer;
|
|
1665
|
+
reconnectTimeouts = /* @__PURE__ */ new Map();
|
|
1666
|
+
requestId = 0;
|
|
1667
|
+
constructor(options = {}) {
|
|
1668
|
+
this.options = {
|
|
1669
|
+
debug: options.debug ?? false,
|
|
1670
|
+
autoReconnect: options.autoReconnect ?? true,
|
|
1671
|
+
reconnectDelay: options.reconnectDelay ?? 5e3,
|
|
1672
|
+
toolTimeout: options.toolTimeout ?? 3e4,
|
|
1673
|
+
refreshInterval: options.refreshInterval ?? 3e4
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Initialize the MCP proxy hub
|
|
1678
|
+
*/
|
|
1679
|
+
async initialize() {
|
|
1680
|
+
this.log("\u{1F50C} Initializing MCP Proxy Hub...");
|
|
1681
|
+
this.startRefreshTimer();
|
|
1682
|
+
this.log("\u2705 MCP Proxy Hub initialized");
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Shutdown the MCP proxy hub and disconnect all clients
|
|
1686
|
+
*/
|
|
1687
|
+
async shutdown() {
|
|
1688
|
+
this.log("\u{1F6D1} Shutting down MCP Proxy Hub...");
|
|
1689
|
+
if (this.refreshTimer) {
|
|
1690
|
+
clearInterval(this.refreshTimer);
|
|
1691
|
+
this.refreshTimer = void 0;
|
|
1692
|
+
}
|
|
1693
|
+
for (const timeout of this.reconnectTimeouts.values()) {
|
|
1694
|
+
clearTimeout(timeout);
|
|
1695
|
+
}
|
|
1696
|
+
this.reconnectTimeouts.clear();
|
|
1697
|
+
const disconnectPromises = Array.from(this.clients.entries()).map(
|
|
1698
|
+
async ([name, client]) => {
|
|
1699
|
+
try {
|
|
1700
|
+
await this.disconnectClient(name);
|
|
1701
|
+
} catch (error) {
|
|
1702
|
+
this.log(`Error disconnecting ${name}:`, error);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
);
|
|
1706
|
+
await Promise.allSettled(disconnectPromises);
|
|
1707
|
+
this.clients.clear();
|
|
1708
|
+
this.log("\u{1F44B} MCP Proxy Hub shut down");
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Connect to an MCP server
|
|
1712
|
+
*/
|
|
1713
|
+
async connectClient(config) {
|
|
1714
|
+
const { name, transport } = config;
|
|
1715
|
+
if (this.clients.has(name)) {
|
|
1716
|
+
this.log(`Reconnecting client: ${name}`);
|
|
1717
|
+
await this.disconnectClient(name);
|
|
1718
|
+
}
|
|
1719
|
+
this.log(`Connecting to MCP server: ${name} (${transport})...`);
|
|
1720
|
+
try {
|
|
1721
|
+
const client = new import_client2.Client(
|
|
1722
|
+
{
|
|
1723
|
+
name: `rainfall-daemon-${name}`,
|
|
1724
|
+
version: "0.2.0"
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
1727
|
+
capabilities: {}
|
|
1728
|
+
}
|
|
1729
|
+
);
|
|
1730
|
+
let lastErrorTime = 0;
|
|
1731
|
+
client.onerror = (error) => {
|
|
1732
|
+
const now = Date.now();
|
|
1733
|
+
if (now - lastErrorTime > 5e3) {
|
|
1734
|
+
this.log(`MCP Server Error (${name}):`, error.message);
|
|
1735
|
+
lastErrorTime = now;
|
|
1736
|
+
}
|
|
1737
|
+
if (this.options.autoReconnect) {
|
|
1738
|
+
this.scheduleReconnect(name, config);
|
|
1739
|
+
}
|
|
1740
|
+
};
|
|
1741
|
+
let transportInstance;
|
|
1742
|
+
if (transport === "stdio" && config.command) {
|
|
1743
|
+
const env = {};
|
|
1744
|
+
for (const [key, value] of Object.entries({ ...process.env, ...config.env })) {
|
|
1745
|
+
if (value !== void 0) {
|
|
1746
|
+
env[key] = value;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
transportInstance = new import_stdio.StdioClientTransport({
|
|
1750
|
+
command: config.command,
|
|
1751
|
+
args: config.args,
|
|
1752
|
+
env
|
|
1753
|
+
});
|
|
1754
|
+
} else if (transport === "http" && config.url) {
|
|
1755
|
+
transportInstance = new import_streamableHttp.StreamableHTTPClientTransport(
|
|
1756
|
+
new URL(config.url),
|
|
1757
|
+
{
|
|
1758
|
+
requestInit: {
|
|
1759
|
+
headers: config.headers
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
);
|
|
1763
|
+
} else if (transport === "websocket" && config.url) {
|
|
1764
|
+
transportInstance = new import_ws.WebSocket(config.url);
|
|
1765
|
+
await new Promise((resolve, reject) => {
|
|
1766
|
+
transportInstance.on("open", () => resolve());
|
|
1767
|
+
transportInstance.on("error", reject);
|
|
1768
|
+
setTimeout(() => reject(new Error("WebSocket connection timeout")), 1e4);
|
|
1769
|
+
});
|
|
1770
|
+
} else {
|
|
1771
|
+
throw new Error(`Invalid transport configuration for ${name}`);
|
|
1772
|
+
}
|
|
1773
|
+
await client.connect(transportInstance);
|
|
1774
|
+
const toolsResult = await client.request(
|
|
1775
|
+
{
|
|
1776
|
+
method: "tools/list",
|
|
1777
|
+
params: {}
|
|
1778
|
+
},
|
|
1779
|
+
import_types.ListToolsResultSchema
|
|
1780
|
+
);
|
|
1781
|
+
const tools = toolsResult.tools.map((tool) => ({
|
|
1782
|
+
name: tool.name,
|
|
1783
|
+
description: tool.description || "",
|
|
1784
|
+
inputSchema: tool.inputSchema,
|
|
1785
|
+
serverName: name
|
|
1786
|
+
}));
|
|
1787
|
+
const clientInfo = {
|
|
1788
|
+
name,
|
|
1789
|
+
client,
|
|
1790
|
+
transport: transportInstance,
|
|
1791
|
+
transportType: transport,
|
|
1792
|
+
tools,
|
|
1793
|
+
connectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1794
|
+
lastUsed: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1795
|
+
config,
|
|
1796
|
+
status: "connected"
|
|
1797
|
+
};
|
|
1798
|
+
this.clients.set(name, clientInfo);
|
|
1799
|
+
this.log(`\u2705 Connected to ${name} (${tools.length} tools)`);
|
|
1800
|
+
this.printAvailableTools(name, tools);
|
|
1801
|
+
return name;
|
|
1802
|
+
} catch (error) {
|
|
1803
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1804
|
+
this.log(`\u274C Failed to connect to ${name}:`, errorMessage);
|
|
1805
|
+
if (this.options.autoReconnect) {
|
|
1806
|
+
this.scheduleReconnect(name, config);
|
|
1807
|
+
}
|
|
1808
|
+
throw error;
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1812
|
+
* Disconnect a specific MCP client
|
|
1813
|
+
*/
|
|
1814
|
+
async disconnectClient(name) {
|
|
1815
|
+
const client = this.clients.get(name);
|
|
1816
|
+
if (!client) return;
|
|
1817
|
+
const timeout = this.reconnectTimeouts.get(name);
|
|
1818
|
+
if (timeout) {
|
|
1819
|
+
clearTimeout(timeout);
|
|
1820
|
+
this.reconnectTimeouts.delete(name);
|
|
1821
|
+
}
|
|
1822
|
+
try {
|
|
1823
|
+
await client.client.close();
|
|
1824
|
+
if ("close" in client.transport && typeof client.transport.close === "function") {
|
|
1825
|
+
await client.transport.close();
|
|
1826
|
+
}
|
|
1827
|
+
} catch (error) {
|
|
1828
|
+
this.log(`Error closing client ${name}:`, error);
|
|
1829
|
+
}
|
|
1830
|
+
this.clients.delete(name);
|
|
1831
|
+
this.log(`Disconnected from ${name}`);
|
|
1832
|
+
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Schedule a reconnection attempt
|
|
1835
|
+
*/
|
|
1836
|
+
scheduleReconnect(name, config) {
|
|
1837
|
+
if (this.reconnectTimeouts.has(name)) return;
|
|
1838
|
+
const timeout = setTimeout(async () => {
|
|
1839
|
+
this.reconnectTimeouts.delete(name);
|
|
1840
|
+
this.log(`Attempting to reconnect to ${name}...`);
|
|
1841
|
+
try {
|
|
1842
|
+
await this.connectClient(config);
|
|
1843
|
+
} catch (error) {
|
|
1844
|
+
this.log(`Reconnection failed for ${name}`);
|
|
1845
|
+
}
|
|
1846
|
+
}, this.options.reconnectDelay);
|
|
1847
|
+
this.reconnectTimeouts.set(name, timeout);
|
|
1848
|
+
}
|
|
1849
|
+
/**
|
|
1850
|
+
* Call a tool on the appropriate MCP client
|
|
1851
|
+
*/
|
|
1852
|
+
async callTool(toolName, args, options = {}) {
|
|
1853
|
+
const timeout = options.timeout ?? this.options.toolTimeout;
|
|
1854
|
+
let clientInfo;
|
|
1855
|
+
let actualToolName = toolName;
|
|
1856
|
+
if (options.namespace) {
|
|
1857
|
+
clientInfo = this.clients.get(options.namespace);
|
|
1858
|
+
if (!clientInfo) {
|
|
1859
|
+
throw new Error(`Namespace '${options.namespace}' not found`);
|
|
1860
|
+
}
|
|
1861
|
+
const prefix = `${options.namespace}-`;
|
|
1862
|
+
if (actualToolName.startsWith(prefix)) {
|
|
1863
|
+
actualToolName = actualToolName.slice(prefix.length);
|
|
1864
|
+
}
|
|
1865
|
+
if (!clientInfo.tools.some((t) => t.name === actualToolName)) {
|
|
1866
|
+
throw new Error(`Tool '${actualToolName}' not found in namespace '${options.namespace}'`);
|
|
1867
|
+
}
|
|
1868
|
+
} else {
|
|
1869
|
+
for (const [, info] of this.clients) {
|
|
1870
|
+
const tool = info.tools.find((t) => t.name === toolName);
|
|
1871
|
+
if (tool) {
|
|
1872
|
+
clientInfo = info;
|
|
1873
|
+
break;
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
if (!clientInfo) {
|
|
1878
|
+
throw new Error(`Tool '${toolName}' not found on any connected MCP server`);
|
|
1879
|
+
}
|
|
1880
|
+
const requestId = `req_${++this.requestId}`;
|
|
1881
|
+
clientInfo.lastUsed = (/* @__PURE__ */ new Date()).toISOString();
|
|
1882
|
+
try {
|
|
1883
|
+
this.log(`[${requestId}] Calling '${actualToolName}' on '${clientInfo.name}'`);
|
|
1884
|
+
const result = await Promise.race([
|
|
1885
|
+
clientInfo.client.request(
|
|
1886
|
+
{
|
|
1887
|
+
method: "tools/call",
|
|
1888
|
+
params: {
|
|
1889
|
+
name: actualToolName,
|
|
1890
|
+
arguments: args
|
|
1891
|
+
}
|
|
1892
|
+
},
|
|
1893
|
+
import_types.CallToolResultSchema
|
|
1894
|
+
),
|
|
1895
|
+
new Promise(
|
|
1896
|
+
(_, reject) => setTimeout(() => reject(new Error(`Tool call timeout after ${timeout}ms`)), timeout)
|
|
1897
|
+
)
|
|
1898
|
+
]);
|
|
1899
|
+
this.log(`[${requestId}] Completed successfully`);
|
|
1900
|
+
return this.formatToolResult(result);
|
|
1901
|
+
} catch (error) {
|
|
1902
|
+
this.log(`[${requestId}] Failed:`, error instanceof Error ? error.message : error);
|
|
1903
|
+
if (error instanceof import_types.McpError) {
|
|
1904
|
+
throw new Error(`MCP Error (${toolName}): ${error.message} (code: ${error.code})`);
|
|
1905
|
+
}
|
|
1906
|
+
throw error;
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
/**
|
|
1910
|
+
* Format MCP tool result for consistent output
|
|
1911
|
+
*/
|
|
1912
|
+
formatToolResult(result) {
|
|
1913
|
+
if (!result || !result.content) {
|
|
1914
|
+
return "";
|
|
1915
|
+
}
|
|
1916
|
+
return result.content.map((item) => {
|
|
1917
|
+
if (item.type === "text") {
|
|
1918
|
+
return item.text || "";
|
|
1919
|
+
} else if (item.type === "resource") {
|
|
1920
|
+
return `[Resource: ${item.resource?.uri || "unknown"}]`;
|
|
1921
|
+
} else if (item.type === "image") {
|
|
1922
|
+
return `[Image: ${item.mimeType || "unknown"}]`;
|
|
1923
|
+
} else if (item.type === "audio") {
|
|
1924
|
+
return `[Audio: ${item.mimeType || "unknown"}]`;
|
|
1925
|
+
} else {
|
|
1926
|
+
return JSON.stringify(item);
|
|
1927
|
+
}
|
|
1928
|
+
}).join("\n");
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Get all tools from all connected MCP clients
|
|
1932
|
+
* Optionally with namespace prefix
|
|
1933
|
+
*/
|
|
1934
|
+
getAllTools(options = {}) {
|
|
1935
|
+
const allTools = [];
|
|
1936
|
+
for (const [clientName, client] of this.clients) {
|
|
1937
|
+
for (const tool of client.tools) {
|
|
1938
|
+
if (options.namespacePrefix) {
|
|
1939
|
+
allTools.push({
|
|
1940
|
+
...tool,
|
|
1941
|
+
name: `${clientName}-${tool.name}`
|
|
1942
|
+
});
|
|
1943
|
+
} else {
|
|
1944
|
+
allTools.push(tool);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
return allTools;
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* Get tools from a specific client
|
|
1952
|
+
*/
|
|
1953
|
+
getClientTools(clientName) {
|
|
1954
|
+
const client = this.clients.get(clientName);
|
|
1955
|
+
return client?.tools || [];
|
|
1956
|
+
}
|
|
1957
|
+
/**
|
|
1958
|
+
* Get list of connected MCP clients
|
|
1959
|
+
*/
|
|
1960
|
+
listClients() {
|
|
1961
|
+
return Array.from(this.clients.entries()).map(([name, info]) => ({
|
|
1962
|
+
name,
|
|
1963
|
+
status: info.status,
|
|
1964
|
+
toolCount: info.tools.length,
|
|
1965
|
+
connectedAt: info.connectedAt,
|
|
1966
|
+
lastUsed: info.lastUsed,
|
|
1967
|
+
transportType: info.transportType
|
|
1968
|
+
}));
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Get client info by name
|
|
1972
|
+
*/
|
|
1973
|
+
getClient(name) {
|
|
1974
|
+
return this.clients.get(name);
|
|
1975
|
+
}
|
|
1976
|
+
/**
|
|
1977
|
+
* Refresh tool lists from all connected clients
|
|
1978
|
+
*/
|
|
1979
|
+
async refreshTools() {
|
|
1980
|
+
for (const [name, client] of this.clients) {
|
|
1981
|
+
try {
|
|
1982
|
+
const toolsResult = await client.client.request(
|
|
1983
|
+
{
|
|
1984
|
+
method: "tools/list",
|
|
1985
|
+
params: {}
|
|
1986
|
+
},
|
|
1987
|
+
import_types.ListToolsResultSchema
|
|
1988
|
+
);
|
|
1989
|
+
client.tools = toolsResult.tools.map((tool) => ({
|
|
1990
|
+
name: tool.name,
|
|
1991
|
+
description: tool.description || "",
|
|
1992
|
+
inputSchema: tool.inputSchema,
|
|
1993
|
+
serverName: name
|
|
1994
|
+
}));
|
|
1995
|
+
this.log(`Refreshed ${name}: ${client.tools.length} tools`);
|
|
1996
|
+
} catch (error) {
|
|
1997
|
+
this.log(`Failed to refresh tools for ${name}:`, error);
|
|
1998
|
+
client.status = "error";
|
|
1999
|
+
client.error = error instanceof Error ? error.message : String(error);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* List resources from a specific client or all clients
|
|
2005
|
+
*/
|
|
2006
|
+
async listResources(clientName) {
|
|
2007
|
+
const results = [];
|
|
2008
|
+
const clients = clientName ? [clientName] : Array.from(this.clients.keys());
|
|
2009
|
+
for (const name of clients) {
|
|
2010
|
+
const client = this.clients.get(name);
|
|
2011
|
+
if (!client) continue;
|
|
2012
|
+
try {
|
|
2013
|
+
const result = await client.client.request(
|
|
2014
|
+
{
|
|
2015
|
+
method: "resources/list",
|
|
2016
|
+
params: {}
|
|
2017
|
+
},
|
|
2018
|
+
import_types.ListResourcesResultSchema
|
|
2019
|
+
);
|
|
2020
|
+
results.push({
|
|
2021
|
+
clientName: name,
|
|
2022
|
+
resources: result.resources
|
|
2023
|
+
});
|
|
2024
|
+
} catch (error) {
|
|
2025
|
+
this.log(`Failed to list resources for ${name}:`, error);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
return results;
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
2031
|
+
* Read a resource from a specific client
|
|
2032
|
+
*/
|
|
2033
|
+
async readResource(uri, clientName) {
|
|
2034
|
+
if (clientName) {
|
|
2035
|
+
const client = this.clients.get(clientName);
|
|
2036
|
+
if (!client) {
|
|
2037
|
+
throw new Error(`Client '${clientName}' not found`);
|
|
2038
|
+
}
|
|
2039
|
+
const result = await client.client.request(
|
|
2040
|
+
{
|
|
2041
|
+
method: "resources/read",
|
|
2042
|
+
params: { uri }
|
|
2043
|
+
},
|
|
2044
|
+
import_types.ReadResourceResultSchema
|
|
2045
|
+
);
|
|
2046
|
+
return result;
|
|
2047
|
+
} else {
|
|
2048
|
+
for (const [name, client] of this.clients) {
|
|
2049
|
+
try {
|
|
2050
|
+
const result = await client.client.request(
|
|
2051
|
+
{
|
|
2052
|
+
method: "resources/read",
|
|
2053
|
+
params: { uri }
|
|
2054
|
+
},
|
|
2055
|
+
import_types.ReadResourceResultSchema
|
|
2056
|
+
);
|
|
2057
|
+
return { clientName: name, ...result };
|
|
2058
|
+
} catch {
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
throw new Error(`Resource '${uri}' not found on any client`);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
/**
|
|
2065
|
+
* List prompts from a specific client or all clients
|
|
2066
|
+
*/
|
|
2067
|
+
async listPrompts(clientName) {
|
|
2068
|
+
const results = [];
|
|
2069
|
+
const clients = clientName ? [clientName] : Array.from(this.clients.keys());
|
|
2070
|
+
for (const name of clients) {
|
|
2071
|
+
const client = this.clients.get(name);
|
|
2072
|
+
if (!client) continue;
|
|
2073
|
+
try {
|
|
2074
|
+
const result = await client.client.request(
|
|
2075
|
+
{
|
|
2076
|
+
method: "prompts/list",
|
|
2077
|
+
params: {}
|
|
2078
|
+
},
|
|
2079
|
+
import_types.ListPromptsResultSchema
|
|
2080
|
+
);
|
|
2081
|
+
results.push({
|
|
2082
|
+
clientName: name,
|
|
2083
|
+
prompts: result.prompts
|
|
2084
|
+
});
|
|
2085
|
+
} catch (error) {
|
|
2086
|
+
this.log(`Failed to list prompts for ${name}:`, error);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
return results;
|
|
2090
|
+
}
|
|
2091
|
+
/**
|
|
2092
|
+
* Get a prompt from a specific client
|
|
2093
|
+
*/
|
|
2094
|
+
async getPrompt(name, args, clientName) {
|
|
2095
|
+
if (clientName) {
|
|
2096
|
+
const client = this.clients.get(clientName);
|
|
2097
|
+
if (!client) {
|
|
2098
|
+
throw new Error(`Client '${clientName}' not found`);
|
|
2099
|
+
}
|
|
2100
|
+
const result = await client.client.request(
|
|
2101
|
+
{
|
|
2102
|
+
method: "prompts/get",
|
|
2103
|
+
params: { name, arguments: args }
|
|
2104
|
+
},
|
|
2105
|
+
import_types.GetPromptResultSchema
|
|
2106
|
+
);
|
|
2107
|
+
return result;
|
|
2108
|
+
} else {
|
|
2109
|
+
for (const [cName, client] of this.clients) {
|
|
2110
|
+
try {
|
|
2111
|
+
const result = await client.client.request(
|
|
2112
|
+
{
|
|
2113
|
+
method: "prompts/get",
|
|
2114
|
+
params: { name, arguments: args }
|
|
2115
|
+
},
|
|
2116
|
+
import_types.GetPromptResultSchema
|
|
2117
|
+
);
|
|
2118
|
+
return { clientName: cName, ...result };
|
|
2119
|
+
} catch {
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
throw new Error(`Prompt '${name}' not found on any client`);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
/**
|
|
2126
|
+
* Health check for all connected clients
|
|
2127
|
+
*/
|
|
2128
|
+
async healthCheck() {
|
|
2129
|
+
const results = /* @__PURE__ */ new Map();
|
|
2130
|
+
for (const [name, client] of this.clients) {
|
|
2131
|
+
try {
|
|
2132
|
+
const startTime = Date.now();
|
|
2133
|
+
await client.client.request(
|
|
2134
|
+
{
|
|
2135
|
+
method: "tools/list",
|
|
2136
|
+
params: {}
|
|
2137
|
+
},
|
|
2138
|
+
import_types.ListToolsResultSchema
|
|
2139
|
+
);
|
|
2140
|
+
results.set(name, {
|
|
2141
|
+
status: "healthy",
|
|
2142
|
+
responseTime: Date.now() - startTime
|
|
2143
|
+
});
|
|
2144
|
+
} catch (error) {
|
|
2145
|
+
results.set(name, {
|
|
2146
|
+
status: "unhealthy",
|
|
2147
|
+
responseTime: 0,
|
|
2148
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2149
|
+
});
|
|
2150
|
+
if (this.options.autoReconnect) {
|
|
2151
|
+
this.scheduleReconnect(name, client.config);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
return results;
|
|
2156
|
+
}
|
|
2157
|
+
/**
|
|
2158
|
+
* Start the automatic refresh timer
|
|
2159
|
+
*/
|
|
2160
|
+
startRefreshTimer() {
|
|
2161
|
+
if (this.refreshTimer) {
|
|
2162
|
+
clearInterval(this.refreshTimer);
|
|
2163
|
+
}
|
|
2164
|
+
if (this.options.refreshInterval > 0) {
|
|
2165
|
+
this.refreshTimer = setInterval(async () => {
|
|
2166
|
+
try {
|
|
2167
|
+
await this.refreshTools();
|
|
2168
|
+
} catch (error) {
|
|
2169
|
+
this.log("Auto-refresh failed:", error);
|
|
2170
|
+
}
|
|
2171
|
+
}, this.options.refreshInterval);
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
/**
|
|
2175
|
+
* Print available tools for a client
|
|
2176
|
+
*/
|
|
2177
|
+
printAvailableTools(clientName, tools) {
|
|
2178
|
+
if (tools.length === 0) {
|
|
2179
|
+
this.log(` No tools available from ${clientName}`);
|
|
2180
|
+
return;
|
|
2181
|
+
}
|
|
2182
|
+
this.log(`
|
|
2183
|
+
--- ${clientName} Tools (${tools.length}) ---`);
|
|
2184
|
+
for (const tool of tools) {
|
|
2185
|
+
this.log(` \u2022 ${tool.name}: ${tool.description.slice(0, 60)}${tool.description.length > 60 ? "..." : ""}`);
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
/**
|
|
2189
|
+
* Debug logging
|
|
2190
|
+
*/
|
|
2191
|
+
log(...args) {
|
|
2192
|
+
if (this.options.debug) {
|
|
2193
|
+
console.log("[MCP-Proxy]", ...args);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
/**
|
|
2197
|
+
* Get statistics about the MCP proxy hub
|
|
2198
|
+
*/
|
|
2199
|
+
getStats() {
|
|
2200
|
+
const clients = Array.from(this.clients.entries()).map(([name, info]) => ({
|
|
2201
|
+
name,
|
|
2202
|
+
toolCount: info.tools.length,
|
|
2203
|
+
status: info.status,
|
|
2204
|
+
transportType: info.transportType
|
|
2205
|
+
}));
|
|
2206
|
+
return {
|
|
2207
|
+
totalClients: this.clients.size,
|
|
2208
|
+
totalTools: clients.reduce((sum, c) => sum + c.toolCount, 0),
|
|
2209
|
+
clients
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
};
|
|
2213
|
+
|
|
1648
2214
|
// src/daemon/index.ts
|
|
1649
2215
|
var RainfallDaemon = class {
|
|
1650
2216
|
wss;
|
|
@@ -1661,11 +2227,16 @@ var RainfallDaemon = class {
|
|
|
1661
2227
|
networkedExecutor;
|
|
1662
2228
|
context;
|
|
1663
2229
|
listeners;
|
|
2230
|
+
mcpProxy;
|
|
2231
|
+
enableMcpProxy;
|
|
2232
|
+
mcpNamespacePrefix;
|
|
1664
2233
|
constructor(config = {}) {
|
|
1665
2234
|
this.port = config.port || 8765;
|
|
1666
2235
|
this.openaiPort = config.openaiPort || 8787;
|
|
1667
2236
|
this.rainfallConfig = config.rainfallConfig;
|
|
1668
2237
|
this.debug = config.debug || false;
|
|
2238
|
+
this.enableMcpProxy = config.enableMcpProxy ?? true;
|
|
2239
|
+
this.mcpNamespacePrefix = config.mcpNamespacePrefix ?? true;
|
|
1669
2240
|
this.openaiApp = (0, import_express.default)();
|
|
1670
2241
|
this.openaiApp.use(import_express.default.json());
|
|
1671
2242
|
}
|
|
@@ -1701,6 +2272,19 @@ var RainfallDaemon = class {
|
|
|
1701
2272
|
this.networkedExecutor
|
|
1702
2273
|
);
|
|
1703
2274
|
await this.loadTools();
|
|
2275
|
+
if (this.enableMcpProxy) {
|
|
2276
|
+
this.mcpProxy = new MCPProxyHub({ debug: this.debug });
|
|
2277
|
+
await this.mcpProxy.initialize();
|
|
2278
|
+
if (this.rainfallConfig?.mcpClients) {
|
|
2279
|
+
for (const clientConfig of this.rainfallConfig.mcpClients) {
|
|
2280
|
+
try {
|
|
2281
|
+
await this.mcpProxy.connectClient(clientConfig);
|
|
2282
|
+
} catch (error) {
|
|
2283
|
+
this.log(`Failed to connect MCP client ${clientConfig.name}:`, error);
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
1704
2288
|
await this.startWebSocketServer();
|
|
1705
2289
|
await this.startOpenAIProxy();
|
|
1706
2290
|
console.log(`\u{1F680} Rainfall daemon running`);
|
|
@@ -1721,6 +2305,10 @@ var RainfallDaemon = class {
|
|
|
1721
2305
|
if (this.networkedExecutor) {
|
|
1722
2306
|
await this.networkedExecutor.unregisterEdgeNode();
|
|
1723
2307
|
}
|
|
2308
|
+
if (this.mcpProxy) {
|
|
2309
|
+
await this.mcpProxy.shutdown();
|
|
2310
|
+
this.mcpProxy = void 0;
|
|
2311
|
+
}
|
|
1724
2312
|
for (const client of this.clients) {
|
|
1725
2313
|
client.close();
|
|
1726
2314
|
}
|
|
@@ -1749,6 +2337,30 @@ var RainfallDaemon = class {
|
|
|
1749
2337
|
getListenerRegistry() {
|
|
1750
2338
|
return this.listeners;
|
|
1751
2339
|
}
|
|
2340
|
+
/**
|
|
2341
|
+
* Get the MCP Proxy Hub for managing external MCP clients
|
|
2342
|
+
*/
|
|
2343
|
+
getMCPProxy() {
|
|
2344
|
+
return this.mcpProxy;
|
|
2345
|
+
}
|
|
2346
|
+
/**
|
|
2347
|
+
* Connect an MCP client dynamically
|
|
2348
|
+
*/
|
|
2349
|
+
async connectMCPClient(config) {
|
|
2350
|
+
if (!this.mcpProxy) {
|
|
2351
|
+
throw new Error("MCP Proxy Hub is not enabled");
|
|
2352
|
+
}
|
|
2353
|
+
return this.mcpProxy.connectClient(config);
|
|
2354
|
+
}
|
|
2355
|
+
/**
|
|
2356
|
+
* Disconnect an MCP client
|
|
2357
|
+
*/
|
|
2358
|
+
async disconnectMCPClient(name) {
|
|
2359
|
+
if (!this.mcpProxy) {
|
|
2360
|
+
throw new Error("MCP Proxy Hub is not enabled");
|
|
2361
|
+
}
|
|
2362
|
+
return this.mcpProxy.disconnectClient(name);
|
|
2363
|
+
}
|
|
1752
2364
|
async initializeRainfall() {
|
|
1753
2365
|
if (this.rainfallConfig?.apiKey) {
|
|
1754
2366
|
this.rainfall = new Rainfall(this.rainfallConfig);
|
|
@@ -1789,7 +2401,7 @@ var RainfallDaemon = class {
|
|
|
1789
2401
|
}
|
|
1790
2402
|
}
|
|
1791
2403
|
async startWebSocketServer() {
|
|
1792
|
-
this.wss = new
|
|
2404
|
+
this.wss = new import_ws2.WebSocketServer({ port: this.port });
|
|
1793
2405
|
this.wss.on("connection", (ws) => {
|
|
1794
2406
|
this.log("\u{1F7E2} MCP client connected");
|
|
1795
2407
|
this.clients.add(ws);
|
|
@@ -1851,7 +2463,7 @@ var RainfallDaemon = class {
|
|
|
1851
2463
|
const toolParams = params?.arguments;
|
|
1852
2464
|
try {
|
|
1853
2465
|
const startTime = Date.now();
|
|
1854
|
-
const result = await this.
|
|
2466
|
+
const result = await this.executeToolWithMCP(toolName, toolParams);
|
|
1855
2467
|
const duration = Date.now() - startTime;
|
|
1856
2468
|
if (this.context) {
|
|
1857
2469
|
this.context.recordExecution(toolName, toolParams || {}, result, { duration });
|
|
@@ -1916,6 +2528,16 @@ var RainfallDaemon = class {
|
|
|
1916
2528
|
});
|
|
1917
2529
|
}
|
|
1918
2530
|
}
|
|
2531
|
+
if (this.mcpProxy) {
|
|
2532
|
+
const proxyTools = this.mcpProxy.getAllTools({ namespacePrefix: this.mcpNamespacePrefix });
|
|
2533
|
+
for (const tool of proxyTools) {
|
|
2534
|
+
mcpTools.push({
|
|
2535
|
+
name: this.mcpNamespacePrefix ? `${tool.serverName}-${tool.name}` : tool.name,
|
|
2536
|
+
description: tool.description,
|
|
2537
|
+
inputSchema: tool.inputSchema || { type: "object", properties: {} }
|
|
2538
|
+
});
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
1919
2541
|
return mcpTools;
|
|
1920
2542
|
}
|
|
1921
2543
|
async executeTool(toolId, params) {
|
|
@@ -1924,6 +2546,30 @@ var RainfallDaemon = class {
|
|
|
1924
2546
|
}
|
|
1925
2547
|
return this.rainfall.executeTool(toolId, params);
|
|
1926
2548
|
}
|
|
2549
|
+
/**
|
|
2550
|
+
* Execute a tool, trying MCP proxy first, then falling back to Rainfall tools
|
|
2551
|
+
*/
|
|
2552
|
+
async executeToolWithMCP(toolName, params) {
|
|
2553
|
+
if (this.mcpProxy) {
|
|
2554
|
+
try {
|
|
2555
|
+
if (this.mcpNamespacePrefix && toolName.includes("-")) {
|
|
2556
|
+
const namespace = toolName.split("-")[0];
|
|
2557
|
+
const actualToolName = toolName.slice(namespace.length + 1);
|
|
2558
|
+
if (this.mcpProxy.getClient(namespace)) {
|
|
2559
|
+
return await this.mcpProxy.callTool(toolName, params || {}, {
|
|
2560
|
+
namespace
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
return await this.mcpProxy.callTool(toolName, params || {});
|
|
2565
|
+
} catch (error) {
|
|
2566
|
+
if (error instanceof Error && !error.message.includes("not found")) {
|
|
2567
|
+
throw error;
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
return this.executeTool(toolName, params);
|
|
2572
|
+
}
|
|
1927
2573
|
async startOpenAIProxy() {
|
|
1928
2574
|
this.openaiApp.get("/v1/models", async (_req, res) => {
|
|
1929
2575
|
try {
|
|
@@ -2039,6 +2685,10 @@ var RainfallDaemon = class {
|
|
|
2039
2685
|
this.log(` \u2192 Executing locally`);
|
|
2040
2686
|
const args = JSON.parse(toolArgsStr);
|
|
2041
2687
|
toolResult = await this.executeLocalTool(localTool.id, args);
|
|
2688
|
+
} else if (this.mcpProxy) {
|
|
2689
|
+
this.log(` \u2192 Trying MCP proxy`);
|
|
2690
|
+
const args = JSON.parse(toolArgsStr);
|
|
2691
|
+
toolResult = await this.executeToolWithMCP(toolName.replace(/_/g, "-"), args);
|
|
2042
2692
|
} else {
|
|
2043
2693
|
const shouldExecuteLocal = body.tool_priority === "local" || body.tool_priority === "stacked";
|
|
2044
2694
|
if (shouldExecuteLocal) {
|
|
@@ -2088,15 +2738,52 @@ var RainfallDaemon = class {
|
|
|
2088
2738
|
}
|
|
2089
2739
|
});
|
|
2090
2740
|
this.openaiApp.get("/health", (_req, res) => {
|
|
2741
|
+
const mcpStats = this.mcpProxy?.getStats();
|
|
2091
2742
|
res.json({
|
|
2092
2743
|
status: "ok",
|
|
2093
2744
|
daemon: "rainfall",
|
|
2094
|
-
version: "0.
|
|
2745
|
+
version: "0.2.0",
|
|
2095
2746
|
tools_loaded: this.tools.length,
|
|
2747
|
+
mcp_clients: mcpStats?.totalClients || 0,
|
|
2748
|
+
mcp_tools: mcpStats?.totalTools || 0,
|
|
2096
2749
|
edge_node_id: this.networkedExecutor?.getEdgeNodeId(),
|
|
2097
2750
|
clients_connected: this.clients.size
|
|
2098
2751
|
});
|
|
2099
2752
|
});
|
|
2753
|
+
this.openaiApp.get("/v1/mcp/clients", (_req, res) => {
|
|
2754
|
+
if (!this.mcpProxy) {
|
|
2755
|
+
res.status(503).json({ error: "MCP proxy not enabled" });
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
res.json(this.mcpProxy.listClients());
|
|
2759
|
+
});
|
|
2760
|
+
this.openaiApp.post("/v1/mcp/connect", async (req, res) => {
|
|
2761
|
+
if (!this.mcpProxy) {
|
|
2762
|
+
res.status(503).json({ error: "MCP proxy not enabled" });
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2765
|
+
try {
|
|
2766
|
+
const name = await this.mcpProxy.connectClient(req.body);
|
|
2767
|
+
res.json({ success: true, client: name });
|
|
2768
|
+
} catch (error) {
|
|
2769
|
+
res.status(500).json({
|
|
2770
|
+
error: error instanceof Error ? error.message : "Failed to connect MCP client"
|
|
2771
|
+
});
|
|
2772
|
+
}
|
|
2773
|
+
});
|
|
2774
|
+
this.openaiApp.post("/v1/mcp/disconnect", async (req, res) => {
|
|
2775
|
+
if (!this.mcpProxy) {
|
|
2776
|
+
res.status(503).json({ error: "MCP proxy not enabled" });
|
|
2777
|
+
return;
|
|
2778
|
+
}
|
|
2779
|
+
const { name } = req.body;
|
|
2780
|
+
if (!name) {
|
|
2781
|
+
res.status(400).json({ error: "Missing required field: name" });
|
|
2782
|
+
return;
|
|
2783
|
+
}
|
|
2784
|
+
await this.mcpProxy.disconnectClient(name);
|
|
2785
|
+
res.json({ success: true });
|
|
2786
|
+
});
|
|
2100
2787
|
this.openaiApp.get("/status", (_req, res) => {
|
|
2101
2788
|
res.json(this.getStatus());
|
|
2102
2789
|
});
|
|
@@ -2231,6 +2918,7 @@ var RainfallDaemon = class {
|
|
|
2231
2918
|
switch (provider) {
|
|
2232
2919
|
case "local":
|
|
2233
2920
|
case "ollama":
|
|
2921
|
+
case "custom":
|
|
2234
2922
|
return this.callLocalLLM(params, config);
|
|
2235
2923
|
case "openai":
|
|
2236
2924
|
case "anthropic":
|
|
@@ -2267,7 +2955,8 @@ var RainfallDaemon = class {
|
|
|
2267
2955
|
method: "POST",
|
|
2268
2956
|
headers: {
|
|
2269
2957
|
"Content-Type": "application/json",
|
|
2270
|
-
"Authorization": `Bearer ${apiKey}
|
|
2958
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
2959
|
+
"User-Agent": "Rainfall-DevKit/1.0"
|
|
2271
2960
|
},
|
|
2272
2961
|
body: JSON.stringify({
|
|
2273
2962
|
model,
|
|
@@ -2298,7 +2987,8 @@ var RainfallDaemon = class {
|
|
|
2298
2987
|
method: "POST",
|
|
2299
2988
|
headers: {
|
|
2300
2989
|
"Content-Type": "application/json",
|
|
2301
|
-
"Authorization": `Bearer ${apiKey}
|
|
2990
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
2991
|
+
"User-Agent": "Rainfall-DevKit/1.0"
|
|
2302
2992
|
},
|
|
2303
2993
|
body: JSON.stringify({
|
|
2304
2994
|
model,
|
|
@@ -2379,7 +3069,7 @@ var RainfallDaemon = class {
|
|
|
2379
3069
|
}
|
|
2380
3070
|
async getOpenAITools() {
|
|
2381
3071
|
const tools = [];
|
|
2382
|
-
for (const tool of this.tools.slice(0,
|
|
3072
|
+
for (const tool of this.tools.slice(0, 100)) {
|
|
2383
3073
|
const schema = await this.getToolSchema(tool.id);
|
|
2384
3074
|
if (schema) {
|
|
2385
3075
|
const toolSchema = schema;
|
|
@@ -2403,6 +3093,24 @@ var RainfallDaemon = class {
|
|
|
2403
3093
|
});
|
|
2404
3094
|
}
|
|
2405
3095
|
}
|
|
3096
|
+
if (this.mcpProxy) {
|
|
3097
|
+
const proxyTools = this.mcpProxy.getAllTools({ namespacePrefix: this.mcpNamespacePrefix });
|
|
3098
|
+
for (const tool of proxyTools.slice(0, 28)) {
|
|
3099
|
+
const inputSchema = tool.inputSchema || {};
|
|
3100
|
+
tools.push({
|
|
3101
|
+
type: "function",
|
|
3102
|
+
function: {
|
|
3103
|
+
name: this.mcpNamespacePrefix ? `${tool.serverName}_${tool.name}`.replace(/-/g, "_") : tool.name.replace(/-/g, "_"),
|
|
3104
|
+
description: `[${tool.serverName}] ${tool.description}`,
|
|
3105
|
+
parameters: {
|
|
3106
|
+
type: "object",
|
|
3107
|
+
properties: inputSchema.properties || {},
|
|
3108
|
+
required: inputSchema.required || []
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
});
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
2406
3114
|
return tools;
|
|
2407
3115
|
}
|
|
2408
3116
|
buildResponseContent() {
|
|
@@ -2465,6 +3173,7 @@ function getDaemonInstance() {
|
|
|
2465
3173
|
}
|
|
2466
3174
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2467
3175
|
0 && (module.exports = {
|
|
3176
|
+
MCPProxyHub,
|
|
2468
3177
|
RainfallDaemon,
|
|
2469
3178
|
getDaemonInstance,
|
|
2470
3179
|
getDaemonStatus,
|