@tryhamster/gerbil 1.0.0-rc.0
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/LICENSE +23 -0
- package/README.md +253 -0
- package/bin/cli.js +2 -0
- package/dist/auto-update-BbNHbSU1.mjs +3 -0
- package/dist/browser/index.d.mts +262 -0
- package/dist/browser/index.d.mts.map +1 -0
- package/dist/browser/index.mjs +755 -0
- package/dist/browser/index.mjs.map +1 -0
- package/dist/chrome-backend-C5Un08O4.mjs +771 -0
- package/dist/chrome-backend-C5Un08O4.mjs.map +1 -0
- package/dist/chrome-backend-CtwPENIW.mjs +3 -0
- package/dist/chunk-Ct1HF2bE.mjs +7 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +7078 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/frameworks/express.d.mts +22 -0
- package/dist/frameworks/express.d.mts.map +1 -0
- package/dist/frameworks/express.mjs +123 -0
- package/dist/frameworks/express.mjs.map +1 -0
- package/dist/frameworks/fastify.d.mts +11 -0
- package/dist/frameworks/fastify.d.mts.map +1 -0
- package/dist/frameworks/fastify.mjs +73 -0
- package/dist/frameworks/fastify.mjs.map +1 -0
- package/dist/frameworks/hono.d.mts +14 -0
- package/dist/frameworks/hono.d.mts.map +1 -0
- package/dist/frameworks/hono.mjs +82 -0
- package/dist/frameworks/hono.mjs.map +1 -0
- package/dist/frameworks/next.d.mts +31 -0
- package/dist/frameworks/next.d.mts.map +1 -0
- package/dist/frameworks/next.mjs +116 -0
- package/dist/frameworks/next.mjs.map +1 -0
- package/dist/frameworks/react.d.mts +56 -0
- package/dist/frameworks/react.d.mts.map +1 -0
- package/dist/frameworks/react.mjs +172 -0
- package/dist/frameworks/react.mjs.map +1 -0
- package/dist/frameworks/trpc.d.mts +12 -0
- package/dist/frameworks/trpc.d.mts.map +1 -0
- package/dist/frameworks/trpc.mjs +80 -0
- package/dist/frameworks/trpc.mjs.map +1 -0
- package/dist/gerbil-BfnsFWRE.mjs +644 -0
- package/dist/gerbil-BfnsFWRE.mjs.map +1 -0
- package/dist/gerbil-BjW-z7Fq.mjs +5 -0
- package/dist/gerbil-DZ1k3ChC.d.mts +138 -0
- package/dist/gerbil-DZ1k3ChC.d.mts.map +1 -0
- package/dist/index.d.mts +223 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +13 -0
- package/dist/index.mjs.map +1 -0
- package/dist/integrations/ai-sdk.d.mts +78 -0
- package/dist/integrations/ai-sdk.d.mts.map +1 -0
- package/dist/integrations/ai-sdk.mjs +199 -0
- package/dist/integrations/ai-sdk.mjs.map +1 -0
- package/dist/integrations/langchain.d.mts +41 -0
- package/dist/integrations/langchain.d.mts.map +1 -0
- package/dist/integrations/langchain.mjs +93 -0
- package/dist/integrations/langchain.mjs.map +1 -0
- package/dist/integrations/llamaindex.d.mts +45 -0
- package/dist/integrations/llamaindex.d.mts.map +1 -0
- package/dist/integrations/llamaindex.mjs +86 -0
- package/dist/integrations/llamaindex.mjs.map +1 -0
- package/dist/integrations/mcp-client.d.mts +206 -0
- package/dist/integrations/mcp-client.d.mts.map +1 -0
- package/dist/integrations/mcp-client.mjs +507 -0
- package/dist/integrations/mcp-client.mjs.map +1 -0
- package/dist/integrations/mcp.d.mts +177 -0
- package/dist/integrations/mcp.d.mts.map +1 -0
- package/dist/integrations/mcp.mjs +8 -0
- package/dist/mcp-R8kRLIKb.mjs +348 -0
- package/dist/mcp-R8kRLIKb.mjs.map +1 -0
- package/dist/models-DKULvhOr.mjs +136 -0
- package/dist/models-DKULvhOr.mjs.map +1 -0
- package/dist/models-De2-_GmQ.d.mts +22 -0
- package/dist/models-De2-_GmQ.d.mts.map +1 -0
- package/dist/one-liner-BUQR0nqq.mjs +98 -0
- package/dist/one-liner-BUQR0nqq.mjs.map +1 -0
- package/dist/skills/index.d.mts +390 -0
- package/dist/skills/index.d.mts.map +1 -0
- package/dist/skills/index.mjs +7 -0
- package/dist/skills-D3CEpgDc.mjs +630 -0
- package/dist/skills-D3CEpgDc.mjs.map +1 -0
- package/dist/tools-BsiEE6f2.mjs +567 -0
- package/dist/tools-BsiEE6f2.mjs.map +1 -0
- package/dist/types-BS1N92Jt.d.mts +183 -0
- package/dist/types-BS1N92Jt.d.mts.map +1 -0
- package/dist/utils-7vXqtq2Q.mjs +63 -0
- package/dist/utils-7vXqtq2Q.mjs.map +1 -0
- package/docs/ai-sdk.md +80 -0
- package/docs/architecture/README.md +84 -0
- package/docs/architecture/caching.md +227 -0
- package/docs/architecture/inference.md +176 -0
- package/docs/architecture/overview.md +179 -0
- package/docs/architecture/streaming.md +261 -0
- package/docs/architecture/webgpu.md +213 -0
- package/docs/browser.md +328 -0
- package/docs/cli.md +155 -0
- package/docs/frameworks.md +90 -0
- package/docs/mcp-client.md +224 -0
- package/docs/mcp.md +109 -0
- package/docs/memory.md +229 -0
- package/docs/repl.md +473 -0
- package/docs/skills.md +261 -0
- package/docs/tools.md +304 -0
- package/package.json +207 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
//#region src/integrations/mcp-client.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Gerbil MCP Client
|
|
4
|
+
*
|
|
5
|
+
* Connect to external MCP servers and use their tools.
|
|
6
|
+
* Uses the official @modelcontextprotocol/sdk for protocol compliance.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { MCPClient, MCPHub } from "gerbil/mcp-client";
|
|
11
|
+
*
|
|
12
|
+
* // Connect to a single server
|
|
13
|
+
* const client = new MCPClient({
|
|
14
|
+
* name: "filesystem",
|
|
15
|
+
* command: "uvx",
|
|
16
|
+
* args: ["mcp-server-filesystem", "/tmp"]
|
|
17
|
+
* });
|
|
18
|
+
* await client.connect();
|
|
19
|
+
* const tools = await client.listTools();
|
|
20
|
+
* const result = await client.callTool("read_file", { path: "/tmp/test.txt" });
|
|
21
|
+
*
|
|
22
|
+
* // Or use the hub for multiple servers
|
|
23
|
+
* const hub = new MCPHub();
|
|
24
|
+
* await hub.addServer("filesystem", { command: "uvx", args: ["mcp-server-filesystem", "/tmp"] });
|
|
25
|
+
* await hub.addServer("browser", { command: "npx", args: ["-y", "@anthropic/mcp-server-puppeteer"] });
|
|
26
|
+
*
|
|
27
|
+
* // All tools from all servers are available
|
|
28
|
+
* const allTools = hub.getAllTools();
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
type MCPServerConfig = {
|
|
32
|
+
/** Command to run the MCP server */
|
|
33
|
+
command: string;
|
|
34
|
+
/** Arguments for the command */
|
|
35
|
+
args?: string[];
|
|
36
|
+
/** Environment variables */
|
|
37
|
+
env?: Record<string, string>;
|
|
38
|
+
/** Working directory */
|
|
39
|
+
cwd?: string;
|
|
40
|
+
/** Timeout for requests (ms, default: 30000) */
|
|
41
|
+
timeout?: number;
|
|
42
|
+
};
|
|
43
|
+
type MCPTool = {
|
|
44
|
+
name: string;
|
|
45
|
+
description?: string;
|
|
46
|
+
inputSchema?: {
|
|
47
|
+
type: string;
|
|
48
|
+
properties?: Record<string, any>;
|
|
49
|
+
required?: string[];
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
type MCPResource = {
|
|
53
|
+
uri: string;
|
|
54
|
+
name: string;
|
|
55
|
+
description?: string;
|
|
56
|
+
mimeType?: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Client for a single MCP server.
|
|
60
|
+
*
|
|
61
|
+
* Uses @modelcontextprotocol/sdk when available, falls back to
|
|
62
|
+
* a minimal stdio implementation otherwise.
|
|
63
|
+
*/
|
|
64
|
+
declare class MCPClient {
|
|
65
|
+
private readonly config;
|
|
66
|
+
private client;
|
|
67
|
+
private transport;
|
|
68
|
+
private connected;
|
|
69
|
+
private _tools;
|
|
70
|
+
private _resources;
|
|
71
|
+
private readonly serverName;
|
|
72
|
+
private useFallback;
|
|
73
|
+
private fallbackProcess;
|
|
74
|
+
private fallbackRequestId;
|
|
75
|
+
private readonly fallbackPending;
|
|
76
|
+
private fallbackBuffer;
|
|
77
|
+
constructor(config: MCPServerConfig & {
|
|
78
|
+
name?: string;
|
|
79
|
+
});
|
|
80
|
+
/**
|
|
81
|
+
* Connect to the MCP server
|
|
82
|
+
*/
|
|
83
|
+
connect(): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Try to load the official MCP SDK
|
|
86
|
+
* Uses dynamic import to avoid bundling issues when SDK is not installed
|
|
87
|
+
*/
|
|
88
|
+
private loadSDK;
|
|
89
|
+
/**
|
|
90
|
+
* Connect using the official SDK
|
|
91
|
+
*/
|
|
92
|
+
private connectWithSDK;
|
|
93
|
+
/**
|
|
94
|
+
* Fallback: Connect without SDK using raw stdio
|
|
95
|
+
*/
|
|
96
|
+
private connectFallback;
|
|
97
|
+
private processFallbackBuffer;
|
|
98
|
+
private fallbackRequest;
|
|
99
|
+
private fallbackNotify;
|
|
100
|
+
/**
|
|
101
|
+
* Disconnect from the MCP server
|
|
102
|
+
*/
|
|
103
|
+
disconnect(): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Refresh tools and resources from the server
|
|
106
|
+
*/
|
|
107
|
+
refreshCapabilities(): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* List available tools
|
|
110
|
+
*/
|
|
111
|
+
listTools(): MCPTool[];
|
|
112
|
+
/**
|
|
113
|
+
* List available resources
|
|
114
|
+
*/
|
|
115
|
+
listResources(): MCPResource[];
|
|
116
|
+
/**
|
|
117
|
+
* Call a tool
|
|
118
|
+
*/
|
|
119
|
+
callTool(name: string, args?: Record<string, any>): Promise<any>;
|
|
120
|
+
/**
|
|
121
|
+
* Read a resource
|
|
122
|
+
*/
|
|
123
|
+
readResource(uri: string): Promise<string>;
|
|
124
|
+
/**
|
|
125
|
+
* Check if connected
|
|
126
|
+
*/
|
|
127
|
+
isConnected(): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Get server name
|
|
130
|
+
*/
|
|
131
|
+
getName(): string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Hub for managing multiple MCP server connections
|
|
135
|
+
*/
|
|
136
|
+
declare class MCPHub {
|
|
137
|
+
private readonly clients;
|
|
138
|
+
private readonly toolPrefix;
|
|
139
|
+
/**
|
|
140
|
+
* Add and connect to an MCP server
|
|
141
|
+
*/
|
|
142
|
+
addServer(name: string, config: MCPServerConfig): Promise<MCPClient>;
|
|
143
|
+
/**
|
|
144
|
+
* Remove and disconnect an MCP server
|
|
145
|
+
*/
|
|
146
|
+
removeServer(name: string): Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* Get a specific client
|
|
149
|
+
*/
|
|
150
|
+
getClient(name: string): MCPClient | undefined;
|
|
151
|
+
/**
|
|
152
|
+
* List connected servers
|
|
153
|
+
*/
|
|
154
|
+
listServers(): string[];
|
|
155
|
+
/**
|
|
156
|
+
* Get all tools from all servers
|
|
157
|
+
*/
|
|
158
|
+
getAllTools(): Array<{
|
|
159
|
+
server: string;
|
|
160
|
+
tool: MCPTool;
|
|
161
|
+
}>;
|
|
162
|
+
/**
|
|
163
|
+
* Call a tool from any connected server
|
|
164
|
+
* Tool name format: "server_name:tool_name" or just "tool_name" (searches all)
|
|
165
|
+
*/
|
|
166
|
+
callTool(toolName: string, args?: Record<string, any>): Promise<any>;
|
|
167
|
+
/**
|
|
168
|
+
* Disconnect all servers
|
|
169
|
+
*/
|
|
170
|
+
disconnectAll(): Promise<void>;
|
|
171
|
+
/**
|
|
172
|
+
* Register tools from an MCP server into Gerbil's tool registry
|
|
173
|
+
*/
|
|
174
|
+
private registerServerTools;
|
|
175
|
+
/**
|
|
176
|
+
* Convert MCP JSON Schema to Zod schema (simplified)
|
|
177
|
+
*/
|
|
178
|
+
private mcpSchemaToZod;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get the default MCP hub (singleton)
|
|
182
|
+
*/
|
|
183
|
+
declare function getMCPHub(): MCPHub;
|
|
184
|
+
/**
|
|
185
|
+
* Connect to an MCP server
|
|
186
|
+
*/
|
|
187
|
+
declare function connectMCP(name: string, config: MCPServerConfig): Promise<MCPClient>;
|
|
188
|
+
/**
|
|
189
|
+
* Disconnect from an MCP server
|
|
190
|
+
*/
|
|
191
|
+
declare function disconnectMCP(name: string): Promise<void>;
|
|
192
|
+
/**
|
|
193
|
+
* Call an MCP tool
|
|
194
|
+
*/
|
|
195
|
+
declare function callMCPTool(toolName: string, args?: Record<string, any>): Promise<any>;
|
|
196
|
+
declare const _default: {
|
|
197
|
+
MCPClient: typeof MCPClient;
|
|
198
|
+
MCPHub: typeof MCPHub;
|
|
199
|
+
getMCPHub: typeof getMCPHub;
|
|
200
|
+
connectMCP: typeof connectMCP;
|
|
201
|
+
disconnectMCP: typeof disconnectMCP;
|
|
202
|
+
callMCPTool: typeof callMCPTool;
|
|
203
|
+
};
|
|
204
|
+
//#endregion
|
|
205
|
+
export { MCPClient, MCPHub, MCPResource, MCPServerConfig, MCPTool, callMCPTool, connectMCP, _default as default, disconnectMCP, getMCPHub };
|
|
206
|
+
//# sourceMappingURL=mcp-client.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-client.d.mts","names":[],"sources":["../../src/integrations/mcp-client.ts"],"sourcesContent":[],"mappings":";;AAqCA;AAiBA;AAUA;AAiBA;;;;;;;;;;;AAyUA;;;;;;;;;;;;AAmLA;AAUA;AAAuD,KAljB3C,eAAA,GAkjB2C;EAA0B;EAAR,OAAA,EAAA,MAAA;EAAO;EAO1D,IAAA,CAAA,EAAA,MAAA,EAAA;EAOA;EAErB,GAAA,CAAA,EA1jBO,MA0jBP,CAAA,MAAA,EAAA,MAAA,CAAA;;;;;;KAjjBW,OAAA;;;;;iBAKK;;;;KAKL,WAAA;;;;;;;;;;;;cAiBC,SAAA;;;;;;;;;;;;;sBAmBS;;;;;;aAWH;;;;;;;;;;;;;;;;;;;;gBAyKG;;;;yBAsBS;;;;eA6BhB;;;;mBAOI;;;;gCAOkB,sBAA2B;;;;6BAyB7B;;;;;;;;;;;;;cAwCtB,MAAA;;;;;;kCAO2B,kBAAkB,QAAQ;;;;8BAmB9B;;;;2BAWT;;;;;;;;iBAcV;;UAA8B;;;;;;oCAgBN,sBAA2B;;;;mBAyB3C;;;;;;;;;;;;;iBAuFT,SAAA,CAAA,GAAa;;;;iBAUP,UAAA,uBAAiC,kBAAkB,QAAQ;;;;iBAO3D,aAAA,gBAA6B;;;;iBAO7B,WAAA,0BAAqC,sBAAsB;cAEhF"}
|
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import "../utils-7vXqtq2Q.mjs";
|
|
2
|
+
import { t as defineTool } from "../tools-BsiEE6f2.mjs";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/integrations/mcp-client.ts
|
|
6
|
+
/**
|
|
7
|
+
* Gerbil MCP Client
|
|
8
|
+
*
|
|
9
|
+
* Connect to external MCP servers and use their tools.
|
|
10
|
+
* Uses the official @modelcontextprotocol/sdk for protocol compliance.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { MCPClient, MCPHub } from "gerbil/mcp-client";
|
|
15
|
+
*
|
|
16
|
+
* // Connect to a single server
|
|
17
|
+
* const client = new MCPClient({
|
|
18
|
+
* name: "filesystem",
|
|
19
|
+
* command: "uvx",
|
|
20
|
+
* args: ["mcp-server-filesystem", "/tmp"]
|
|
21
|
+
* });
|
|
22
|
+
* await client.connect();
|
|
23
|
+
* const tools = await client.listTools();
|
|
24
|
+
* const result = await client.callTool("read_file", { path: "/tmp/test.txt" });
|
|
25
|
+
*
|
|
26
|
+
* // Or use the hub for multiple servers
|
|
27
|
+
* const hub = new MCPHub();
|
|
28
|
+
* await hub.addServer("filesystem", { command: "uvx", args: ["mcp-server-filesystem", "/tmp"] });
|
|
29
|
+
* await hub.addServer("browser", { command: "npx", args: ["-y", "@anthropic/mcp-server-puppeteer"] });
|
|
30
|
+
*
|
|
31
|
+
* // All tools from all servers are available
|
|
32
|
+
* const allTools = hub.getAllTools();
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Client for a single MCP server.
|
|
37
|
+
*
|
|
38
|
+
* Uses @modelcontextprotocol/sdk when available, falls back to
|
|
39
|
+
* a minimal stdio implementation otherwise.
|
|
40
|
+
*/
|
|
41
|
+
var MCPClient = class {
|
|
42
|
+
config;
|
|
43
|
+
client = null;
|
|
44
|
+
transport = null;
|
|
45
|
+
connected = false;
|
|
46
|
+
_tools = [];
|
|
47
|
+
_resources = [];
|
|
48
|
+
serverName;
|
|
49
|
+
useFallback = false;
|
|
50
|
+
fallbackProcess = null;
|
|
51
|
+
fallbackRequestId = 0;
|
|
52
|
+
fallbackPending = /* @__PURE__ */ new Map();
|
|
53
|
+
fallbackBuffer = "";
|
|
54
|
+
constructor(config) {
|
|
55
|
+
this.config = {
|
|
56
|
+
timeout: 3e4,
|
|
57
|
+
...config
|
|
58
|
+
};
|
|
59
|
+
this.serverName = config.name || "mcp";
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Connect to the MCP server
|
|
63
|
+
*/
|
|
64
|
+
async connect() {
|
|
65
|
+
if (this.connected) return;
|
|
66
|
+
try {
|
|
67
|
+
const sdk = await this.loadSDK();
|
|
68
|
+
if (sdk) await this.connectWithSDK(sdk);
|
|
69
|
+
else await this.connectFallback();
|
|
70
|
+
this.connected = true;
|
|
71
|
+
await this.refreshCapabilities();
|
|
72
|
+
} catch (e) {
|
|
73
|
+
this.connected = false;
|
|
74
|
+
throw e;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Try to load the official MCP SDK
|
|
79
|
+
* Uses dynamic import to avoid bundling issues when SDK is not installed
|
|
80
|
+
*/
|
|
81
|
+
async loadSDK() {
|
|
82
|
+
try {
|
|
83
|
+
const importDynamic = new Function("modulePath", "return import(modulePath)");
|
|
84
|
+
const clientModule = await importDynamic("@modelcontextprotocol/sdk/client");
|
|
85
|
+
const stdioModule = await importDynamic("@modelcontextprotocol/sdk/client/stdio");
|
|
86
|
+
return {
|
|
87
|
+
Client: clientModule.Client,
|
|
88
|
+
StdioClientTransport: stdioModule.StdioClientTransport
|
|
89
|
+
};
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Connect using the official SDK
|
|
96
|
+
*/
|
|
97
|
+
async connectWithSDK(sdk) {
|
|
98
|
+
const { Client, StdioClientTransport } = sdk;
|
|
99
|
+
this.transport = new StdioClientTransport({
|
|
100
|
+
command: this.config.command,
|
|
101
|
+
args: this.config.args || [],
|
|
102
|
+
env: this.config.env
|
|
103
|
+
});
|
|
104
|
+
this.client = new Client({
|
|
105
|
+
name: "gerbil",
|
|
106
|
+
version: "1.0.0"
|
|
107
|
+
}, { capabilities: {} });
|
|
108
|
+
await this.client.connect(this.transport);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Fallback: Connect without SDK using raw stdio
|
|
112
|
+
*/
|
|
113
|
+
async connectFallback() {
|
|
114
|
+
this.useFallback = true;
|
|
115
|
+
const { spawn } = await import("child_process");
|
|
116
|
+
this.fallbackProcess = spawn(this.config.command, this.config.args || [], {
|
|
117
|
+
cwd: this.config.cwd,
|
|
118
|
+
env: {
|
|
119
|
+
...process.env,
|
|
120
|
+
...this.config.env
|
|
121
|
+
},
|
|
122
|
+
stdio: [
|
|
123
|
+
"pipe",
|
|
124
|
+
"pipe",
|
|
125
|
+
"pipe"
|
|
126
|
+
]
|
|
127
|
+
});
|
|
128
|
+
this.fallbackProcess.stdout?.on("data", (data) => {
|
|
129
|
+
this.fallbackBuffer += data.toString();
|
|
130
|
+
this.processFallbackBuffer();
|
|
131
|
+
});
|
|
132
|
+
this.fallbackProcess.on("error", (err) => {
|
|
133
|
+
this.connected = false;
|
|
134
|
+
throw new Error(`MCP server error: ${err.message}`);
|
|
135
|
+
});
|
|
136
|
+
this.fallbackProcess.on("exit", (code) => {
|
|
137
|
+
this.connected = false;
|
|
138
|
+
for (const [_, req] of this.fallbackPending) req.reject(/* @__PURE__ */ new Error(`MCP server exited with code ${code}`));
|
|
139
|
+
this.fallbackPending.clear();
|
|
140
|
+
});
|
|
141
|
+
await this.fallbackRequest("initialize", {
|
|
142
|
+
protocolVersion: "2024-11-05",
|
|
143
|
+
capabilities: {},
|
|
144
|
+
clientInfo: {
|
|
145
|
+
name: "gerbil",
|
|
146
|
+
version: "0.1.0"
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
this.fallbackNotify("notifications/initialized", {});
|
|
150
|
+
}
|
|
151
|
+
processFallbackBuffer() {
|
|
152
|
+
const lines = this.fallbackBuffer.split("\n");
|
|
153
|
+
this.fallbackBuffer = lines.pop() || "";
|
|
154
|
+
for (const line of lines) {
|
|
155
|
+
if (!line.trim()) continue;
|
|
156
|
+
try {
|
|
157
|
+
const response = JSON.parse(line);
|
|
158
|
+
if (response.id !== void 0) {
|
|
159
|
+
const pending = this.fallbackPending.get(response.id);
|
|
160
|
+
if (pending) {
|
|
161
|
+
this.fallbackPending.delete(response.id);
|
|
162
|
+
if (response.error) pending.reject(new Error(response.error.message));
|
|
163
|
+
else pending.resolve(response.result);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
} catch {}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
fallbackRequest(method, params) {
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
if (!this.fallbackProcess?.stdin) {
|
|
172
|
+
reject(/* @__PURE__ */ new Error("Not connected"));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const id = ++this.fallbackRequestId;
|
|
176
|
+
const request = {
|
|
177
|
+
jsonrpc: "2.0",
|
|
178
|
+
id,
|
|
179
|
+
method,
|
|
180
|
+
params
|
|
181
|
+
};
|
|
182
|
+
const timeout = setTimeout(() => {
|
|
183
|
+
this.fallbackPending.delete(id);
|
|
184
|
+
reject(/* @__PURE__ */ new Error(`Request timeout: ${method}`));
|
|
185
|
+
}, this.config.timeout);
|
|
186
|
+
this.fallbackPending.set(id, {
|
|
187
|
+
resolve: (v) => {
|
|
188
|
+
clearTimeout(timeout);
|
|
189
|
+
resolve(v);
|
|
190
|
+
},
|
|
191
|
+
reject: (e) => {
|
|
192
|
+
clearTimeout(timeout);
|
|
193
|
+
reject(e);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
this.fallbackProcess.stdin.write(`${JSON.stringify(request)}\n`);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
fallbackNotify(method, params) {
|
|
200
|
+
if (!this.fallbackProcess?.stdin) return;
|
|
201
|
+
this.fallbackProcess.stdin.write(`${JSON.stringify({
|
|
202
|
+
jsonrpc: "2.0",
|
|
203
|
+
method,
|
|
204
|
+
params
|
|
205
|
+
})}\n`);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Disconnect from the MCP server
|
|
209
|
+
*/
|
|
210
|
+
async disconnect() {
|
|
211
|
+
if (!this.connected) return;
|
|
212
|
+
if (this.useFallback && this.fallbackProcess) {
|
|
213
|
+
this.fallbackProcess.kill("SIGTERM");
|
|
214
|
+
this.fallbackProcess = null;
|
|
215
|
+
} else if (this.client) {
|
|
216
|
+
await this.client.close();
|
|
217
|
+
this.client = null;
|
|
218
|
+
this.transport = null;
|
|
219
|
+
}
|
|
220
|
+
this.connected = false;
|
|
221
|
+
this._tools = [];
|
|
222
|
+
this._resources = [];
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Refresh tools and resources from the server
|
|
226
|
+
*/
|
|
227
|
+
async refreshCapabilities() {
|
|
228
|
+
try {
|
|
229
|
+
if (this.useFallback) this._tools = (await this.fallbackRequest("tools/list", {})).tools || [];
|
|
230
|
+
else if (this.client) this._tools = (await this.client.listTools()).tools || [];
|
|
231
|
+
} catch {
|
|
232
|
+
this._tools = [];
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
if (this.useFallback) this._resources = (await this.fallbackRequest("resources/list", {})).resources || [];
|
|
236
|
+
else if (this.client) this._resources = (await this.client.listResources()).resources || [];
|
|
237
|
+
} catch {
|
|
238
|
+
this._resources = [];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* List available tools
|
|
243
|
+
*/
|
|
244
|
+
listTools() {
|
|
245
|
+
return this._tools;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* List available resources
|
|
249
|
+
*/
|
|
250
|
+
listResources() {
|
|
251
|
+
return this._resources;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Call a tool
|
|
255
|
+
*/
|
|
256
|
+
async callTool(name, args = {}) {
|
|
257
|
+
let result;
|
|
258
|
+
if (this.useFallback) result = await this.fallbackRequest("tools/call", {
|
|
259
|
+
name,
|
|
260
|
+
arguments: args
|
|
261
|
+
});
|
|
262
|
+
else if (this.client) result = await this.client.callTool({
|
|
263
|
+
name,
|
|
264
|
+
arguments: args
|
|
265
|
+
});
|
|
266
|
+
else throw new Error("Not connected");
|
|
267
|
+
if (result.content && Array.isArray(result.content)) return result.content.map((c) => c.text || JSON.stringify(c)).join("\n");
|
|
268
|
+
return result;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Read a resource
|
|
272
|
+
*/
|
|
273
|
+
async readResource(uri) {
|
|
274
|
+
let result;
|
|
275
|
+
if (this.useFallback) result = await this.fallbackRequest("resources/read", { uri });
|
|
276
|
+
else if (this.client) result = await this.client.readResource({ uri });
|
|
277
|
+
else throw new Error("Not connected");
|
|
278
|
+
if (result.contents && Array.isArray(result.contents)) return result.contents.map((c) => c.text || c.blob || "").join("\n");
|
|
279
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Check if connected
|
|
283
|
+
*/
|
|
284
|
+
isConnected() {
|
|
285
|
+
return this.connected;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Get server name
|
|
289
|
+
*/
|
|
290
|
+
getName() {
|
|
291
|
+
return this.serverName;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* Hub for managing multiple MCP server connections
|
|
296
|
+
*/
|
|
297
|
+
var MCPHub = class {
|
|
298
|
+
clients = /* @__PURE__ */ new Map();
|
|
299
|
+
toolPrefix = "mcp";
|
|
300
|
+
/**
|
|
301
|
+
* Add and connect to an MCP server
|
|
302
|
+
*/
|
|
303
|
+
async addServer(name, config) {
|
|
304
|
+
if (this.clients.has(name)) await this.removeServer(name);
|
|
305
|
+
const client = new MCPClient({
|
|
306
|
+
...config,
|
|
307
|
+
name
|
|
308
|
+
});
|
|
309
|
+
await client.connect();
|
|
310
|
+
this.clients.set(name, client);
|
|
311
|
+
this.registerServerTools(name, client);
|
|
312
|
+
return client;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Remove and disconnect an MCP server
|
|
316
|
+
*/
|
|
317
|
+
async removeServer(name) {
|
|
318
|
+
const client = this.clients.get(name);
|
|
319
|
+
if (client) {
|
|
320
|
+
await client.disconnect();
|
|
321
|
+
this.clients.delete(name);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Get a specific client
|
|
326
|
+
*/
|
|
327
|
+
getClient(name) {
|
|
328
|
+
return this.clients.get(name);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* List connected servers
|
|
332
|
+
*/
|
|
333
|
+
listServers() {
|
|
334
|
+
return Array.from(this.clients.keys());
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Get all tools from all servers
|
|
338
|
+
*/
|
|
339
|
+
getAllTools() {
|
|
340
|
+
const tools = [];
|
|
341
|
+
for (const [serverName, client] of this.clients) for (const tool of client.listTools()) tools.push({
|
|
342
|
+
server: serverName,
|
|
343
|
+
tool
|
|
344
|
+
});
|
|
345
|
+
return tools;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Call a tool from any connected server
|
|
349
|
+
* Tool name format: "server_name:tool_name" or just "tool_name" (searches all)
|
|
350
|
+
*/
|
|
351
|
+
async callTool(toolName, args = {}) {
|
|
352
|
+
if (toolName.includes(":")) {
|
|
353
|
+
const [serverName, actualToolName] = toolName.split(":", 2);
|
|
354
|
+
const client = this.clients.get(serverName);
|
|
355
|
+
if (!client) throw new Error(`MCP server "${serverName}" not connected`);
|
|
356
|
+
return client.callTool(actualToolName, args);
|
|
357
|
+
}
|
|
358
|
+
for (const [_, client] of this.clients) if (client.listTools().some((t) => t.name === toolName)) return client.callTool(toolName, args);
|
|
359
|
+
throw new Error(`Tool "${toolName}" not found in any connected MCP server`);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Disconnect all servers
|
|
363
|
+
*/
|
|
364
|
+
async disconnectAll() {
|
|
365
|
+
for (const [name] of this.clients) await this.removeServer(name);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Register tools from an MCP server into Gerbil's tool registry
|
|
369
|
+
*/
|
|
370
|
+
registerServerTools(serverName, client) {
|
|
371
|
+
const tools = client.listTools();
|
|
372
|
+
for (const tool of tools) {
|
|
373
|
+
const gerbilToolName = `${this.toolPrefix}_${serverName}_${tool.name}`;
|
|
374
|
+
const zodSchema = this.mcpSchemaToZod(tool.inputSchema);
|
|
375
|
+
defineTool({
|
|
376
|
+
name: gerbilToolName,
|
|
377
|
+
description: `[MCP:${serverName}] ${tool.description || tool.name}`,
|
|
378
|
+
parameters: zodSchema,
|
|
379
|
+
execute: async (params) => {
|
|
380
|
+
const result = await client.callTool(tool.name, params);
|
|
381
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Convert MCP JSON Schema to Zod schema (simplified)
|
|
388
|
+
*/
|
|
389
|
+
mcpSchemaToZod(schema) {
|
|
390
|
+
if (!schema || schema.type !== "object") return z.object({}).passthrough();
|
|
391
|
+
const shape = {};
|
|
392
|
+
const properties = schema.properties || {};
|
|
393
|
+
const required = new Set(schema.required || []);
|
|
394
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
395
|
+
let zodType;
|
|
396
|
+
switch (prop.type) {
|
|
397
|
+
case "string":
|
|
398
|
+
zodType = z.string();
|
|
399
|
+
if (prop.description) zodType = zodType.describe(prop.description);
|
|
400
|
+
break;
|
|
401
|
+
case "number":
|
|
402
|
+
case "integer":
|
|
403
|
+
zodType = z.number();
|
|
404
|
+
break;
|
|
405
|
+
case "boolean":
|
|
406
|
+
zodType = z.boolean();
|
|
407
|
+
break;
|
|
408
|
+
case "array":
|
|
409
|
+
zodType = z.array(z.any());
|
|
410
|
+
break;
|
|
411
|
+
default: zodType = z.any();
|
|
412
|
+
}
|
|
413
|
+
if (!required.has(key)) zodType = zodType.optional();
|
|
414
|
+
shape[key] = zodType;
|
|
415
|
+
}
|
|
416
|
+
return z.object(shape).passthrough();
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
let defaultHub = null;
|
|
420
|
+
/**
|
|
421
|
+
* Get the default MCP hub (singleton)
|
|
422
|
+
*/
|
|
423
|
+
function getMCPHub() {
|
|
424
|
+
if (!defaultHub) defaultHub = new MCPHub();
|
|
425
|
+
return defaultHub;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Connect to an MCP server
|
|
429
|
+
*/
|
|
430
|
+
async function connectMCP(name, config) {
|
|
431
|
+
return getMCPHub().addServer(name, config);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Disconnect from an MCP server
|
|
435
|
+
*/
|
|
436
|
+
async function disconnectMCP(name) {
|
|
437
|
+
return getMCPHub().removeServer(name);
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Call an MCP tool
|
|
441
|
+
*/
|
|
442
|
+
async function callMCPTool(toolName, args) {
|
|
443
|
+
return getMCPHub().callTool(toolName, args);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Tool for the LLM to call MCP tools
|
|
447
|
+
*/
|
|
448
|
+
defineTool({
|
|
449
|
+
name: "mcp_call",
|
|
450
|
+
description: "Call a tool from a connected MCP server. Use this when you need to interact with external tools like file systems, browsers, databases, etc.",
|
|
451
|
+
parameters: z.object({
|
|
452
|
+
server: z.string().describe("The MCP server name (e.g., 'filesystem', 'browser')"),
|
|
453
|
+
tool: z.string().describe("The tool name to call"),
|
|
454
|
+
args: z.record(z.any()).optional().describe("Arguments to pass to the tool")
|
|
455
|
+
}),
|
|
456
|
+
execute: async ({ server, tool, args }) => {
|
|
457
|
+
const hub = getMCPHub();
|
|
458
|
+
const client = hub.getClient(server);
|
|
459
|
+
if (!client) {
|
|
460
|
+
const servers = hub.listServers();
|
|
461
|
+
if (servers.length === 0) return "No MCP servers are connected. Use the MCP settings to connect to servers first.";
|
|
462
|
+
return `Server "${server}" not found. Available servers: ${servers.join(", ")}`;
|
|
463
|
+
}
|
|
464
|
+
try {
|
|
465
|
+
const result = await client.callTool(tool, args || {});
|
|
466
|
+
return typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
467
|
+
} catch (e) {
|
|
468
|
+
return `Error calling ${server}:${tool}: ${e}`;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
/**
|
|
473
|
+
* Tool to list available MCP tools
|
|
474
|
+
*/
|
|
475
|
+
defineTool({
|
|
476
|
+
name: "mcp_list",
|
|
477
|
+
description: "List all tools available from connected MCP servers",
|
|
478
|
+
parameters: z.object({ server: z.string().optional().describe("Filter by server name (optional)") }),
|
|
479
|
+
execute: async ({ server }) => {
|
|
480
|
+
const hub = getMCPHub();
|
|
481
|
+
if (server) {
|
|
482
|
+
const client = hub.getClient(server);
|
|
483
|
+
if (!client) return `Server "${server}" not connected`;
|
|
484
|
+
return client.listTools().map((t) => `- ${t.name}: ${t.description || "(no description)"}`).join("\n");
|
|
485
|
+
}
|
|
486
|
+
const allTools = hub.getAllTools();
|
|
487
|
+
if (allTools.length === 0) return "No MCP servers connected. Connect to MCP servers to use external tools.";
|
|
488
|
+
const byServer = {};
|
|
489
|
+
for (const { server: s, tool } of allTools) {
|
|
490
|
+
if (!byServer[s]) byServer[s] = [];
|
|
491
|
+
byServer[s].push(` - ${tool.name}: ${tool.description || "(no description)"}`);
|
|
492
|
+
}
|
|
493
|
+
return Object.entries(byServer).map(([s, tools]) => `[${s}]\n${tools.join("\n")}`).join("\n\n");
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
var mcp_client_default = {
|
|
497
|
+
MCPClient,
|
|
498
|
+
MCPHub,
|
|
499
|
+
getMCPHub,
|
|
500
|
+
connectMCP,
|
|
501
|
+
disconnectMCP,
|
|
502
|
+
callMCPTool
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
//#endregion
|
|
506
|
+
export { MCPClient, MCPHub, callMCPTool, connectMCP, mcp_client_default as default, disconnectMCP, getMCPHub };
|
|
507
|
+
//# sourceMappingURL=mcp-client.mjs.map
|