@pi-unipi/mcp 0.1.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/README.md +109 -0
- package/data/seed-servers.json +727 -0
- package/package.json +47 -0
- package/skills/mcp/SKILL.md +104 -0
- package/src/bridge/client.ts +365 -0
- package/src/bridge/registry.ts +281 -0
- package/src/bridge/translator.ts +100 -0
- package/src/config/manager.ts +267 -0
- package/src/config/schema.ts +114 -0
- package/src/config/sync.ts +416 -0
- package/src/index.ts +297 -0
- package/src/tui/add-overlay.ts +436 -0
- package/src/tui/settings-overlay.ts +369 -0
- package/src/types.ts +162 -0
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pi-unipi/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server management extension for Pi coding agent — browse, add, configure, and use MCP servers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Neuron Mr White",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Neuron-Mr-White/unipi.git",
|
|
11
|
+
"directory": "packages/mcp"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"pi-package",
|
|
15
|
+
"pi-extension",
|
|
16
|
+
"pi-coding-agent",
|
|
17
|
+
"unipi",
|
|
18
|
+
"mcp",
|
|
19
|
+
"model-context-protocol",
|
|
20
|
+
"tools"
|
|
21
|
+
],
|
|
22
|
+
"pi": {
|
|
23
|
+
"extensions": [
|
|
24
|
+
"src/index.ts"
|
|
25
|
+
],
|
|
26
|
+
"skills": [
|
|
27
|
+
"skills"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"src/**/*.ts",
|
|
32
|
+
"data/**/*",
|
|
33
|
+
"skills/**/*",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@pi-unipi/core": "*"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@mariozechner/pi-coding-agent": "*",
|
|
41
|
+
"@mariozechner/pi-tui": "*",
|
|
42
|
+
"@sinclair/typebox": "*"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^25.6.0"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mcp
|
|
3
|
+
description: "MCP server management — discover, connect, and use Model Context Protocol tools"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# MCP Tools
|
|
7
|
+
|
|
8
|
+
MCP (Model Context Protocol) servers provide external tools that extend pi's capabilities.
|
|
9
|
+
Tools from MCP servers are named `{serverName}__{toolName}` — e.g., `github__search_code`.
|
|
10
|
+
|
|
11
|
+
## Adding Servers
|
|
12
|
+
|
|
13
|
+
Use `/unipi:mcp-add` to browse the catalog of 7,800+ MCP servers and add them interactively.
|
|
14
|
+
The split-pane overlay lets you:
|
|
15
|
+
- **Browse**: Search the cached server catalog by name, description, or category
|
|
16
|
+
- **Select**: Pick a server to get a pre-filled config template
|
|
17
|
+
- **Custom**: Press `c` for an empty editor to add unlisted servers manually
|
|
18
|
+
- **Save**: Press Enter or Ctrl+S to validate and save
|
|
19
|
+
|
|
20
|
+
## Managing Servers
|
|
21
|
+
|
|
22
|
+
Use `/unipi:mcp-settings` to manage configured servers:
|
|
23
|
+
- **Toggle**: Press Space to enable/disable a server
|
|
24
|
+
- **Delete**: Press `d` then `y` to remove a server
|
|
25
|
+
- **Scope**: Press `g` for global config, `p` for project config
|
|
26
|
+
- **Sync**: Press `s` to refresh the catalog from GitHub
|
|
27
|
+
|
|
28
|
+
## Quick Status
|
|
29
|
+
|
|
30
|
+
Use `/unipi:mcp-status` for a text summary of all servers and their status.
|
|
31
|
+
|
|
32
|
+
## Config Hierarchy
|
|
33
|
+
|
|
34
|
+
MCP config supports **global defaults** with **project overrides**:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
~/.unipi/config/mcp/ ← Global defaults (shared across all projects)
|
|
38
|
+
{project}/.unipi/config/mcp/ ← Project overrides (when present)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Merge rules:**
|
|
42
|
+
1. Server only in global → used normally
|
|
43
|
+
2. Server only in project → used normally
|
|
44
|
+
3. Server in both → project wins entirely
|
|
45
|
+
4. `enabled: false` in project metadata → disabled even if defined globally
|
|
46
|
+
|
|
47
|
+
### Config Files
|
|
48
|
+
|
|
49
|
+
Each level has three files:
|
|
50
|
+
- `mcp-config.json` — Server definitions (standard MCP format, portable)
|
|
51
|
+
- `config.json` — Metadata (enabled/disabled, sync prefs)
|
|
52
|
+
- `auth.json` — Sensitive env vars (optional, chmod 600)
|
|
53
|
+
|
|
54
|
+
### Example mcp-config.json
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"github": {
|
|
60
|
+
"command": "docker",
|
|
61
|
+
"args": ["run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server"],
|
|
62
|
+
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxx" }
|
|
63
|
+
},
|
|
64
|
+
"filesystem": {
|
|
65
|
+
"command": "npx",
|
|
66
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/pi/projects"]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Using MCP Tools
|
|
73
|
+
|
|
74
|
+
Once an MCP server is running, its tools are available as pi tools.
|
|
75
|
+
They're named with the pattern `{serverName}__{toolName}`.
|
|
76
|
+
|
|
77
|
+
For example, if you add the GitHub MCP server, you'll have tools like:
|
|
78
|
+
- `github__search_code`
|
|
79
|
+
- `github__create_issue`
|
|
80
|
+
- `github__list_pull_requests`
|
|
81
|
+
|
|
82
|
+
You can call these tools directly in conversations.
|
|
83
|
+
|
|
84
|
+
## Troubleshooting
|
|
85
|
+
|
|
86
|
+
### Server won't start
|
|
87
|
+
- Check `/unipi:mcp-status` for error details
|
|
88
|
+
- Verify the command exists: `which npx` or `which docker`
|
|
89
|
+
- Check that required env vars are set in the config
|
|
90
|
+
|
|
91
|
+
### Tools not appearing
|
|
92
|
+
- Ensure the server status shows "running"
|
|
93
|
+
- Check if the server supports the MCP tools protocol
|
|
94
|
+
- Try restarting pi after adding a new server
|
|
95
|
+
|
|
96
|
+
### Config issues
|
|
97
|
+
- Validate JSON syntax in your config files
|
|
98
|
+
- Check file permissions (mcp-config.json should be readable)
|
|
99
|
+
- Use `/unipi:mcp-settings` to view current configuration
|
|
100
|
+
|
|
101
|
+
### Catalog sync issues
|
|
102
|
+
- Run `/unipi:mcp-sync` to force a refresh
|
|
103
|
+
- Check network connectivity to GitHub
|
|
104
|
+
- The seed catalog (49 servers) is available offline as fallback
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/mcp — MCP JSON-RPC Client
|
|
3
|
+
*
|
|
4
|
+
* Spawns an MCP server as a child process, performs the JSON-RPC initialize
|
|
5
|
+
* handshake, and provides listTools/callTool/disconnect methods via stdio.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { type ChildProcess, spawn } from "node:child_process";
|
|
9
|
+
import type { McpTool, McpToolResult } from "../types.js";
|
|
10
|
+
import { MCP_DEFAULTS } from "@pi-unipi/core";
|
|
11
|
+
|
|
12
|
+
/** JSON-RPC request */
|
|
13
|
+
interface JsonRpcRequest {
|
|
14
|
+
jsonrpc: "2.0";
|
|
15
|
+
id: number;
|
|
16
|
+
method: string;
|
|
17
|
+
params?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** JSON-RPC response */
|
|
21
|
+
interface JsonRpcResponse {
|
|
22
|
+
jsonrpc: "2.0";
|
|
23
|
+
id: number;
|
|
24
|
+
result?: unknown;
|
|
25
|
+
error?: {
|
|
26
|
+
code: number;
|
|
27
|
+
message: string;
|
|
28
|
+
data?: unknown;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** JSON-RPC notification (no id) */
|
|
33
|
+
interface JsonRpcNotification {
|
|
34
|
+
jsonrpc: "2.0";
|
|
35
|
+
method: string;
|
|
36
|
+
params?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Pending request handler */
|
|
40
|
+
interface PendingRequest {
|
|
41
|
+
resolve: (value: unknown) => void;
|
|
42
|
+
reject: (reason: Error) => void;
|
|
43
|
+
timer: ReturnType<typeof setTimeout>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Options for McpClient */
|
|
47
|
+
export interface McpClientOptions {
|
|
48
|
+
/** Per-request timeout in ms (default: MCP_DEFAULTS.STARTUP_TIMEOUT_MS) */
|
|
49
|
+
timeoutMs?: number;
|
|
50
|
+
/** Working directory for the spawned process */
|
|
51
|
+
cwd?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* MCP JSON-RPC client over stdio transport.
|
|
56
|
+
*
|
|
57
|
+
* Spawns a child process, sends JSON-RPC messages to stdin,
|
|
58
|
+
* reads responses from stdout, and correlates by request ID.
|
|
59
|
+
*/
|
|
60
|
+
export class McpClient {
|
|
61
|
+
private process: ChildProcess | null = null;
|
|
62
|
+
private nextId = 1;
|
|
63
|
+
private pending = new Map<number, PendingRequest>();
|
|
64
|
+
private buffer = "";
|
|
65
|
+
private connected = false;
|
|
66
|
+
private stderrBuffer = "";
|
|
67
|
+
private readonly timeoutMs: number;
|
|
68
|
+
private readonly cwd?: string;
|
|
69
|
+
|
|
70
|
+
constructor(options?: McpClientOptions) {
|
|
71
|
+
this.timeoutMs = options?.timeoutMs ?? MCP_DEFAULTS.STARTUP_TIMEOUT_MS;
|
|
72
|
+
this.cwd = options?.cwd;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Spawn the MCP server process and perform the initialize handshake.
|
|
77
|
+
*/
|
|
78
|
+
async connect(
|
|
79
|
+
command: string,
|
|
80
|
+
args: string[],
|
|
81
|
+
env?: Record<string, string>,
|
|
82
|
+
): Promise<void> {
|
|
83
|
+
if (this.connected) {
|
|
84
|
+
throw new Error("McpClient is already connected");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return new Promise<void>((resolve, reject) => {
|
|
88
|
+
try {
|
|
89
|
+
const mergedEnv = { ...process.env, ...env };
|
|
90
|
+
|
|
91
|
+
this.process = spawn(command, args, {
|
|
92
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
93
|
+
env: mergedEnv,
|
|
94
|
+
cwd: this.cwd,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
this.process.on("error", (err) => {
|
|
98
|
+
this.cleanup();
|
|
99
|
+
reject(new Error(`Failed to spawn MCP server: ${err.message}`));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
this.process.on("exit", (code, signal) => {
|
|
103
|
+
if (!this.connected) {
|
|
104
|
+
this.cleanup();
|
|
105
|
+
reject(
|
|
106
|
+
new Error(
|
|
107
|
+
`MCP server exited during startup: code=${code}, signal=${signal}\nStderr: ${this.stderrBuffer}`,
|
|
108
|
+
),
|
|
109
|
+
);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// Unexpected exit after connection
|
|
113
|
+
this.connected = false;
|
|
114
|
+
this.rejectAllPending(
|
|
115
|
+
new Error(
|
|
116
|
+
`MCP server exited unexpectedly: code=${code}, signal=${signal}`,
|
|
117
|
+
),
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Set up stdout reading
|
|
122
|
+
this.process.stdout!.on("data", (chunk: Buffer) => {
|
|
123
|
+
this.handleStdoutData(chunk);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Capture stderr for error reporting
|
|
127
|
+
this.process.stderr!.on("data", (chunk: Buffer) => {
|
|
128
|
+
this.stderrBuffer += chunk.toString();
|
|
129
|
+
// Keep stderr buffer manageable
|
|
130
|
+
if (this.stderrBuffer.length > 10000) {
|
|
131
|
+
this.stderrBuffer = this.stderrBuffer.slice(-5000);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Perform initialize handshake
|
|
136
|
+
this.sendRequest("initialize", {
|
|
137
|
+
protocolVersion: "2024-11-05",
|
|
138
|
+
capabilities: {},
|
|
139
|
+
clientInfo: {
|
|
140
|
+
name: "@pi-unipi/mcp",
|
|
141
|
+
version: "0.1.0",
|
|
142
|
+
},
|
|
143
|
+
})
|
|
144
|
+
.then(() => {
|
|
145
|
+
// Send initialized notification
|
|
146
|
+
this.sendNotification("notifications/initialized", {});
|
|
147
|
+
this.connected = true;
|
|
148
|
+
resolve();
|
|
149
|
+
})
|
|
150
|
+
.catch((err) => {
|
|
151
|
+
this.cleanup();
|
|
152
|
+
reject(
|
|
153
|
+
new Error(
|
|
154
|
+
`MCP initialize handshake failed: ${(err as Error).message}\nStderr: ${this.stderrBuffer}`,
|
|
155
|
+
),
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
} catch (err) {
|
|
159
|
+
this.cleanup();
|
|
160
|
+
reject(err);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Query available tools from the MCP server.
|
|
167
|
+
*/
|
|
168
|
+
async listTools(): Promise<McpTool[]> {
|
|
169
|
+
this.ensureConnected();
|
|
170
|
+
const result = (await this.sendRequest("tools/list", {})) as {
|
|
171
|
+
tools: McpTool[];
|
|
172
|
+
};
|
|
173
|
+
return result.tools ?? [];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Execute a tool call on the MCP server.
|
|
178
|
+
*/
|
|
179
|
+
async callTool(
|
|
180
|
+
name: string,
|
|
181
|
+
args: Record<string, unknown>,
|
|
182
|
+
): Promise<McpToolResult> {
|
|
183
|
+
this.ensureConnected();
|
|
184
|
+
const result = (await this.sendRequest("tools/call", {
|
|
185
|
+
name,
|
|
186
|
+
arguments: args,
|
|
187
|
+
})) as McpToolResult;
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Gracefully disconnect from the MCP server.
|
|
193
|
+
*/
|
|
194
|
+
async disconnect(): Promise<void> {
|
|
195
|
+
if (!this.process) return;
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
// Send shutdown notification
|
|
199
|
+
this.sendNotification("shutdown", {});
|
|
200
|
+
} catch {
|
|
201
|
+
// Ignore errors during shutdown
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Give the process a moment to clean up
|
|
205
|
+
await new Promise<void>((resolve) => {
|
|
206
|
+
const timer = setTimeout(() => resolve(), 500);
|
|
207
|
+
|
|
208
|
+
this.process!.on("exit", () => {
|
|
209
|
+
clearTimeout(timer);
|
|
210
|
+
resolve();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Send SIGTERM
|
|
214
|
+
try {
|
|
215
|
+
this.process!.kill("SIGTERM");
|
|
216
|
+
} catch {
|
|
217
|
+
resolve();
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Force kill if still running
|
|
222
|
+
if (this.process && !this.process.killed) {
|
|
223
|
+
try {
|
|
224
|
+
this.process.kill("SIGKILL");
|
|
225
|
+
} catch {
|
|
226
|
+
// Already dead
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
this.cleanup();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Whether the client is currently connected */
|
|
234
|
+
get isConnected(): boolean {
|
|
235
|
+
return this.connected;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Process ID of the spawned MCP server, or undefined if not connected */
|
|
239
|
+
get pid(): number | undefined {
|
|
240
|
+
return this.process?.pid;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Captured stderr output */
|
|
244
|
+
get stderr(): string {
|
|
245
|
+
return this.stderrBuffer;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ── Internal methods ────────────────────────────────────────────
|
|
249
|
+
|
|
250
|
+
private ensureConnected(): void {
|
|
251
|
+
if (!this.connected || !this.process) {
|
|
252
|
+
throw new Error("McpClient is not connected");
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private sendRequest(
|
|
257
|
+
method: string,
|
|
258
|
+
params?: Record<string, unknown>,
|
|
259
|
+
): Promise<unknown> {
|
|
260
|
+
return new Promise<unknown>((resolve, reject) => {
|
|
261
|
+
if (!this.process?.stdin) {
|
|
262
|
+
reject(new Error("MCP server process not available"));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const id = this.nextId++;
|
|
267
|
+
const request: JsonRpcRequest = {
|
|
268
|
+
jsonrpc: "2.0",
|
|
269
|
+
id,
|
|
270
|
+
method,
|
|
271
|
+
params,
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const timer = setTimeout(() => {
|
|
275
|
+
this.pending.delete(id);
|
|
276
|
+
reject(
|
|
277
|
+
new Error(
|
|
278
|
+
`MCP request timed out after ${this.timeoutMs}ms: ${method}`,
|
|
279
|
+
),
|
|
280
|
+
);
|
|
281
|
+
}, this.timeoutMs);
|
|
282
|
+
|
|
283
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
284
|
+
|
|
285
|
+
const message = JSON.stringify(request) + "\n";
|
|
286
|
+
this.process.stdin.write(message);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private sendNotification(
|
|
291
|
+
method: string,
|
|
292
|
+
params?: Record<string, unknown>,
|
|
293
|
+
): void {
|
|
294
|
+
if (!this.process?.stdin) return;
|
|
295
|
+
|
|
296
|
+
const notification: JsonRpcNotification = {
|
|
297
|
+
jsonrpc: "2.0",
|
|
298
|
+
method,
|
|
299
|
+
params,
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const message = JSON.stringify(notification) + "\n";
|
|
303
|
+
this.process.stdin.write(message);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private handleStdoutData(chunk: Buffer): void {
|
|
307
|
+
this.buffer += chunk.toString();
|
|
308
|
+
|
|
309
|
+
// Process complete lines
|
|
310
|
+
let newlineIdx: number;
|
|
311
|
+
while ((newlineIdx = this.buffer.indexOf("\n")) !== -1) {
|
|
312
|
+
const line = this.buffer.slice(0, newlineIdx).trim();
|
|
313
|
+
this.buffer = this.buffer.slice(newlineIdx + 1);
|
|
314
|
+
|
|
315
|
+
if (!line) continue;
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
const message = JSON.parse(line) as
|
|
319
|
+
| JsonRpcResponse
|
|
320
|
+
| JsonRpcNotification;
|
|
321
|
+
|
|
322
|
+
if ("id" in message && message.id !== undefined) {
|
|
323
|
+
// This is a response
|
|
324
|
+
this.handleResponse(message as JsonRpcResponse);
|
|
325
|
+
}
|
|
326
|
+
// Notifications are ignored for now
|
|
327
|
+
} catch {
|
|
328
|
+
// Skip malformed JSON lines
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private handleResponse(response: JsonRpcResponse): void {
|
|
334
|
+
const pending = this.pending.get(response.id);
|
|
335
|
+
if (!pending) return;
|
|
336
|
+
|
|
337
|
+
this.pending.delete(response.id);
|
|
338
|
+
clearTimeout(pending.timer);
|
|
339
|
+
|
|
340
|
+
if (response.error) {
|
|
341
|
+
pending.reject(
|
|
342
|
+
new Error(
|
|
343
|
+
`MCP error ${response.error.code}: ${response.error.message}`,
|
|
344
|
+
),
|
|
345
|
+
);
|
|
346
|
+
} else {
|
|
347
|
+
pending.resolve(response.result);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
private rejectAllPending(error: Error): void {
|
|
352
|
+
for (const [id, pending] of this.pending) {
|
|
353
|
+
clearTimeout(pending.timer);
|
|
354
|
+
pending.reject(error);
|
|
355
|
+
this.pending.delete(id);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
private cleanup(): void {
|
|
360
|
+
this.connected = false;
|
|
361
|
+
this.rejectAllPending(new Error("McpClient disconnected"));
|
|
362
|
+
this.process = null;
|
|
363
|
+
this.buffer = "";
|
|
364
|
+
}
|
|
365
|
+
}
|