@xpufx/paseo-mcp-tools 0.1.3
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 +21 -0
- package/README.md +84 -0
- package/client/__snapshots__/attribution.test.tsx.snap +170 -0
- package/client/commands.ts +113 -0
- package/client/mcp-query.tsx +39 -0
- package/client/pill.tsx +886 -0
- package/client/timeline.tsx +136 -0
- package/client/vendor/paseo-plugin-helper/command-center.ts +43 -0
- package/client/vendor/paseo-plugin-helper/components/AboutSection.tsx +493 -0
- package/client/vendor/paseo-plugin-helper/components/AttentionBeacon.tsx +250 -0
- package/client/vendor/paseo-plugin-helper/components/Badge.tsx +156 -0
- package/client/vendor/paseo-plugin-helper/components/Button.tsx +178 -0
- package/client/vendor/paseo-plugin-helper/components/Card.tsx +225 -0
- package/client/vendor/paseo-plugin-helper/components/CodeBlock.tsx +196 -0
- package/client/vendor/paseo-plugin-helper/components/Collapsible.tsx +277 -0
- package/client/vendor/paseo-plugin-helper/components/CommandBox.tsx +172 -0
- package/client/vendor/paseo-plugin-helper/components/CopyButton.tsx +180 -0
- package/client/vendor/paseo-plugin-helper/components/DataTable.tsx +200 -0
- package/client/vendor/paseo-plugin-helper/components/EmptyState.tsx +97 -0
- package/client/vendor/paseo-plugin-helper/components/HighlightedText.tsx +70 -0
- package/client/vendor/paseo-plugin-helper/components/InlineButton.tsx +73 -0
- package/client/vendor/paseo-plugin-helper/components/KeyValue.tsx +446 -0
- package/client/vendor/paseo-plugin-helper/components/MetricGauge.tsx +247 -0
- package/client/vendor/paseo-plugin-helper/components/ProgressBar.tsx +117 -0
- package/client/vendor/paseo-plugin-helper/components/Responsive.tsx +53 -0
- package/client/vendor/paseo-plugin-helper/components/SearchInput.tsx +118 -0
- package/client/vendor/paseo-plugin-helper/components/SectionHeader.tsx +80 -0
- package/client/vendor/paseo-plugin-helper/components/Select.tsx +215 -0
- package/client/vendor/paseo-plugin-helper/components/StatusDot.tsx +80 -0
- package/client/vendor/paseo-plugin-helper/components/Tabs.tsx +319 -0
- package/client/vendor/paseo-plugin-helper/components/TextInput.tsx +150 -0
- package/client/vendor/paseo-plugin-helper/components/Toggle.tsx +163 -0
- package/client/vendor/paseo-plugin-helper/components/TruncatedText.tsx +157 -0
- package/client/vendor/paseo-plugin-helper/components/index.ts +25 -0
- package/client/vendor/paseo-plugin-helper/custom-pills.tsx +224 -0
- package/client/vendor/paseo-plugin-helper/forge-icon.tsx +79 -0
- package/client/vendor/paseo-plugin-helper/host.ts +277 -0
- package/client/vendor/paseo-plugin-helper/icon.tsx +39 -0
- package/client/vendor/paseo-plugin-helper/index.ts +28 -0
- package/client/vendor/paseo-plugin-helper/layout/ActionBar.tsx +49 -0
- package/client/vendor/paseo-plugin-helper/layout/FormRow.tsx +103 -0
- package/client/vendor/paseo-plugin-helper/layout/Grid.tsx +65 -0
- package/client/vendor/paseo-plugin-helper/layout/ModalBody.tsx +378 -0
- package/client/vendor/paseo-plugin-helper/layout/ModalContent.tsx +49 -0
- package/client/vendor/paseo-plugin-helper/layout/Row.tsx +39 -0
- package/client/vendor/paseo-plugin-helper/layout/Stack.tsx +39 -0
- package/client/vendor/paseo-plugin-helper/layout/index.ts +7 -0
- package/client/vendor/paseo-plugin-helper/panel.tsx +81 -0
- package/client/vendor/paseo-plugin-helper/pill.tsx +884 -0
- package/client/vendor/paseo-plugin-helper/query-refresh.ts +79 -0
- package/client/vendor/paseo-plugin-helper/query.ts +66 -0
- package/client/vendor/paseo-plugin-helper/settings-screen.tsx +372 -0
- package/client/vendor/paseo-plugin-helper/settings.ts +181 -0
- package/client/vendor/paseo-plugin-helper/shared-settings.ts +46 -0
- package/client/vendor/paseo-plugin-helper/snapshot.ts +68 -0
- package/client/vendor/paseo-plugin-helper/surface.tsx +80 -0
- package/client/vendor/paseo-plugin-helper/theme/color-utils.ts +118 -0
- package/client/vendor/paseo-plugin-helper/theme/flair.ts +76 -0
- package/client/vendor/paseo-plugin-helper/theme/host-variables.ts +121 -0
- package/client/vendor/paseo-plugin-helper/theme/index.ts +7 -0
- package/client/vendor/paseo-plugin-helper/theme/provider.tsx +214 -0
- package/client/vendor/paseo-plugin-helper/theme/responsive.ts +213 -0
- package/client/vendor/paseo-plugin-helper/theme/tokens.ts +161 -0
- package/client/vendor/paseo-plugin-helper/theme/useResponsive.ts +57 -0
- package/client/vendor/paseo-plugin-helper/utils/clipboard.ts +149 -0
- package/client/vendor/paseo-plugin-helper/utils/haptics.ts +34 -0
- package/docs/TEST_METHODOLOGY.md +29 -0
- package/package.json +41 -0
- package/paseo-plugin.json +4 -0
- package/scripts/version.mjs +4 -0
- package/server/discovery/extract.ts +277 -0
- package/server/discovery/types.ts +23 -0
- package/server/health/health.ts +274 -0
- package/server/inject.ts +32 -0
- package/server/mcp.ts +365 -0
- package/server/providers/README.md +51 -0
- package/server/providers/antigravity.ts +22 -0
- package/server/providers/catalog.ts +6 -0
- package/server/providers/claude.ts +21 -0
- package/server/providers/codex.ts +85 -0
- package/server/providers/family.ts +24 -0
- package/server/providers/index.ts +11 -0
- package/server/providers/opencode.ts +95 -0
- package/server/providers/paseo.ts +112 -0
- package/server/providers/pi.ts +39 -0
- package/server/settings.ts +19 -0
- package/server/vendor/paseo-plugin-helper/agent.ts +85 -0
- package/server/vendor/paseo-plugin-helper/custom-pills.ts +344 -0
- package/server/vendor/paseo-plugin-helper/index.ts +18 -0
- package/server/vendor/paseo-plugin-helper/jsonc.ts +78 -0
- package/server/vendor/paseo-plugin-helper/logger.ts +210 -0
- package/server/vendor/paseo-plugin-helper/mcp/client.ts +349 -0
- package/server/vendor/paseo-plugin-helper/mcp/http-client.ts +399 -0
- package/server/vendor/paseo-plugin-helper/mcp/index.ts +5 -0
- package/server/vendor/paseo-plugin-helper/mcp/process-killer.ts +65 -0
- package/server/vendor/paseo-plugin-helper/mcp/ring-buffer.ts +29 -0
- package/server/vendor/paseo-plugin-helper/mcp/types.ts +41 -0
- package/server/vendor/paseo-plugin-helper/mcp-config.ts +367 -0
- package/server/vendor/paseo-plugin-helper/mcp-injection.ts +85 -0
- package/server/vendor/paseo-plugin-helper/network.ts +91 -0
- package/server/vendor/paseo-plugin-helper/plugins.ts +160 -0
- package/server/vendor/paseo-plugin-helper/process.ts +186 -0
- package/server/vendor/paseo-plugin-helper/redact.ts +86 -0
- package/server/vendor/paseo-plugin-helper/rpc-guard.ts +77 -0
- package/server/vendor/paseo-plugin-helper/settings.ts +97 -0
- package/server/vendor/paseo-plugin-helper/shared-settings.ts +243 -0
- package/server/vendor/paseo-plugin-helper/storage.ts +244 -0
- package/server/vendor/paseo-plugin-helper/system.ts +128 -0
- package/server/vendor/paseo-plugin-helper/task.ts +116 -0
- package/server/vendor/paseo-plugin-helper/version.ts +153 -0
- package/server/vendor/paseo-plugin-helper/workspace-beacon.ts +418 -0
- package/shared/mcp.ts +207 -0
- package/shared/vendor/paseo-plugin-helper/README.md +11 -0
- package/shared/vendor/paseo-plugin-helper/async.ts +35 -0
- package/shared/vendor/paseo-plugin-helper/custom-pills.ts +169 -0
- package/shared/vendor/paseo-plugin-helper/forge.ts +110 -0
- package/shared/vendor/paseo-plugin-helper/formatters.ts +271 -0
- package/shared/vendor/paseo-plugin-helper/highlight.ts +184 -0
- package/shared/vendor/paseo-plugin-helper/index.ts +10 -0
- package/shared/vendor/paseo-plugin-helper/rpc.ts +72 -0
- package/shared/vendor/paseo-plugin-helper/settings.ts +138 -0
- package/shared/vendor/paseo-plugin-helper/suite-settings.ts +17 -0
- package/shared/vendor/paseo-plugin-helper/suppressed.ts +31 -0
- package/shared/vendor/paseo-plugin-helper/types.ts +36 -0
- package/shared/version.ts +2 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { spawn, type ChildProcess } from "node:child_process";
|
|
2
|
+
import readline from "node:readline";
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import { StderrRingBuffer } from "./ring-buffer";
|
|
5
|
+
import { killProcessTree } from "./process-killer";
|
|
6
|
+
import { McpHttpClient, type McpHttpOptions } from "./http-client";
|
|
7
|
+
import type {
|
|
8
|
+
McpClientOptions,
|
|
9
|
+
McpPingOptions,
|
|
10
|
+
McpPingResult,
|
|
11
|
+
McpToolCallResult,
|
|
12
|
+
McpToolInfo,
|
|
13
|
+
} from "./types";
|
|
14
|
+
|
|
15
|
+
interface PendingRequest {
|
|
16
|
+
resolve: (res: any) => void;
|
|
17
|
+
reject: (err: Error) => void;
|
|
18
|
+
timer: NodeJS.Timeout;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class McpClient {
|
|
22
|
+
private child: ChildProcess | null = null;
|
|
23
|
+
private stderrBuffer = new StderrRingBuffer(30);
|
|
24
|
+
private pendingRequests = new Map<string | number, PendingRequest>();
|
|
25
|
+
private initialized = false;
|
|
26
|
+
private _serverInfo?: { name: string; version?: string };
|
|
27
|
+
private _instructions?: string;
|
|
28
|
+
private _capabilities?: Record<string, unknown>;
|
|
29
|
+
private _protocolVersion?: string;
|
|
30
|
+
private initPromise?: Promise<void>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Server metadata returned during MCP initialize handshake.
|
|
34
|
+
*/
|
|
35
|
+
get serverInfo(): { name: string; version?: string } | undefined {
|
|
36
|
+
return this._serverInfo;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Optional server instructions / system prompt guidance returned during MCP initialize handshake.
|
|
41
|
+
*/
|
|
42
|
+
get instructions(): string | undefined {
|
|
43
|
+
return this._instructions;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Declared server capabilities returned during initialize handshake.
|
|
48
|
+
*/
|
|
49
|
+
get capabilities(): Record<string, unknown> | undefined {
|
|
50
|
+
return this._capabilities;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Protocol version negotiated during initialize handshake.
|
|
55
|
+
*/
|
|
56
|
+
get protocolVersion(): string | undefined {
|
|
57
|
+
return this._protocolVersion;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private constructor(
|
|
61
|
+
private readonly command: string,
|
|
62
|
+
private readonly args: string[] = [],
|
|
63
|
+
private readonly env?: Record<string, string>,
|
|
64
|
+
private readonly options: McpClientOptions = {},
|
|
65
|
+
) {}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Creates an MCP client talking to a local process over stdio.
|
|
69
|
+
*/
|
|
70
|
+
static forStdio(
|
|
71
|
+
command: string,
|
|
72
|
+
args: string[] = [],
|
|
73
|
+
env?: Record<string, string>,
|
|
74
|
+
options?: McpClientOptions,
|
|
75
|
+
): McpClient {
|
|
76
|
+
return new McpClient(command, args, env, options);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Creates an MCP client connecting to an HTTP or Server-Sent Events (SSE) MCP server.
|
|
81
|
+
*/
|
|
82
|
+
static forHttp(url: string, options?: McpHttpOptions): McpHttpClient {
|
|
83
|
+
return new McpHttpClient(url, options);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private startProcess(): Promise<void> {
|
|
87
|
+
if (this.child && !this.child.killed) return Promise.resolve();
|
|
88
|
+
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
try {
|
|
91
|
+
this.child = spawn(this.command, this.args, {
|
|
92
|
+
env: {
|
|
93
|
+
...process.env,
|
|
94
|
+
...this.env,
|
|
95
|
+
},
|
|
96
|
+
shell: false,
|
|
97
|
+
detached: process.platform !== "win32",
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
this.child.on("error", (err: any) => {
|
|
101
|
+
const isNotFound = err.code === "ENOENT" || err.code === "EACCES";
|
|
102
|
+
const formattedErr = isNotFound
|
|
103
|
+
? new Error(`Executable "${this.command}" not found in PATH or cannot be executed (${err.code}).`)
|
|
104
|
+
: err;
|
|
105
|
+
this.rejectAllPending(formattedErr);
|
|
106
|
+
reject(formattedErr);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
this.child.stderr?.on("data", (chunk: Buffer | string) => {
|
|
110
|
+
this.stderrBuffer.push(chunk.toString());
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (this.child.stdout) {
|
|
114
|
+
const rl = readline.createInterface({
|
|
115
|
+
input: this.child.stdout,
|
|
116
|
+
crlfDelay: Infinity,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
rl.on("line", (line) => {
|
|
120
|
+
const trimmed = line.trim();
|
|
121
|
+
if (!trimmed || !trimmed.startsWith("{")) {
|
|
122
|
+
// Ignore non-JSON output (e.g. startup banners or debug logs)
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const msg = JSON.parse(trimmed);
|
|
127
|
+
this.handleMessage(msg);
|
|
128
|
+
} catch {
|
|
129
|
+
// Ignore JSON parse errors from malformed stdout noise
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.child.on("close", (code, signal) => {
|
|
135
|
+
const stderr = this.stderrBuffer.getRecentText();
|
|
136
|
+
const reason = stderr
|
|
137
|
+
? `Process exited with code ${code ?? signal}:\n${stderr}`
|
|
138
|
+
: `Process exited unexpectedly with code ${code ?? signal}.`;
|
|
139
|
+
this.rejectAllPending(new Error(reason));
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
resolve();
|
|
143
|
+
} catch (err: any) {
|
|
144
|
+
const isNotFound = err?.code === "ENOENT" || err?.code === "EACCES";
|
|
145
|
+
const formattedErr = isNotFound
|
|
146
|
+
? new Error(`Executable "${this.command}" not found in PATH or cannot be executed (${err?.code}).`)
|
|
147
|
+
: err;
|
|
148
|
+
reject(formattedErr);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private handleMessage(msg: any): void {
|
|
154
|
+
if (!msg || typeof msg !== "object") return;
|
|
155
|
+
|
|
156
|
+
// Handle responses to requests
|
|
157
|
+
if (msg.id !== undefined && msg.id !== null) {
|
|
158
|
+
const pending = this.pendingRequests.get(msg.id);
|
|
159
|
+
if (pending) {
|
|
160
|
+
clearTimeout(pending.timer);
|
|
161
|
+
this.pendingRequests.delete(msg.id);
|
|
162
|
+
|
|
163
|
+
if (msg.error) {
|
|
164
|
+
const err = new Error(msg.error.message || `JSON-RPC error ${msg.error.code}`);
|
|
165
|
+
(err as any).code = msg.error.code;
|
|
166
|
+
(err as any).data = msg.error.data;
|
|
167
|
+
pending.reject(err);
|
|
168
|
+
} else {
|
|
169
|
+
pending.resolve(msg.result);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private rejectAllPending(err: Error): void {
|
|
176
|
+
for (const [id, pending] of this.pendingRequests.entries()) {
|
|
177
|
+
clearTimeout(pending.timer);
|
|
178
|
+
pending.reject(err);
|
|
179
|
+
}
|
|
180
|
+
this.pendingRequests.clear();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private async sendRequest<T = any>(
|
|
184
|
+
method: string,
|
|
185
|
+
params?: any,
|
|
186
|
+
timeoutMs?: number,
|
|
187
|
+
): Promise<T> {
|
|
188
|
+
await this.startProcess();
|
|
189
|
+
|
|
190
|
+
if (!this.child || !this.child.stdin || this.child.killed) {
|
|
191
|
+
throw new Error(`Process "${this.command}" is not running.`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const id = crypto.randomUUID();
|
|
195
|
+
const timeout = timeoutMs ?? this.options.timeoutMs ?? 10000;
|
|
196
|
+
|
|
197
|
+
const payload = JSON.stringify({
|
|
198
|
+
jsonrpc: "2.0",
|
|
199
|
+
id,
|
|
200
|
+
method,
|
|
201
|
+
...(params !== undefined ? { params } : {}),
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
return new Promise<T>((resolve, reject) => {
|
|
205
|
+
const timer = setTimeout(() => {
|
|
206
|
+
this.pendingRequests.delete(id);
|
|
207
|
+
const stderr = this.stderrBuffer.getRecentText();
|
|
208
|
+
const msg = stderr
|
|
209
|
+
? `Request "${method}" timed out after ${timeout}ms. Stderr output:\n${stderr}`
|
|
210
|
+
: `Request "${method}" timed out after ${timeout}ms.`;
|
|
211
|
+
reject(new Error(msg));
|
|
212
|
+
}, timeout);
|
|
213
|
+
|
|
214
|
+
this.pendingRequests.set(id, { resolve, reject, timer });
|
|
215
|
+
|
|
216
|
+
this.child!.stdin!.write(payload + "\n", (err) => {
|
|
217
|
+
if (err) {
|
|
218
|
+
clearTimeout(timer);
|
|
219
|
+
this.pendingRequests.delete(id);
|
|
220
|
+
reject(err);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private sendNotification(method: string, params?: any): void {
|
|
227
|
+
if (!this.child || !this.child.stdin || this.child.killed) return;
|
|
228
|
+
const payload = JSON.stringify({
|
|
229
|
+
jsonrpc: "2.0",
|
|
230
|
+
method,
|
|
231
|
+
...(params !== undefined ? { params } : {}),
|
|
232
|
+
});
|
|
233
|
+
this.child.stdin.write(payload + "\n");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Performs the MCP initialize handshake and sends the initialized notification.
|
|
238
|
+
*/
|
|
239
|
+
async initialize(): Promise<void> {
|
|
240
|
+
if (this.initialized) return;
|
|
241
|
+
if (this.initPromise) return this.initPromise;
|
|
242
|
+
|
|
243
|
+
this.initPromise = (async () => {
|
|
244
|
+
const result = await this.sendRequest("initialize", {
|
|
245
|
+
protocolVersion: "2024-11-05",
|
|
246
|
+
capabilities: {
|
|
247
|
+
tools: {},
|
|
248
|
+
},
|
|
249
|
+
clientInfo: this.options.clientInfo ?? {
|
|
250
|
+
name: "paseo-plugin",
|
|
251
|
+
version: "1.0.0",
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
this._serverInfo = result?.serverInfo;
|
|
256
|
+
this._instructions = result?.instructions;
|
|
257
|
+
this._capabilities = result?.capabilities;
|
|
258
|
+
this._protocolVersion = result?.protocolVersion;
|
|
259
|
+
this.sendNotification("notifications/initialized");
|
|
260
|
+
this.initialized = true;
|
|
261
|
+
})();
|
|
262
|
+
|
|
263
|
+
return this.initPromise;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Checks the health and responsiveness of the MCP server.
|
|
268
|
+
* If protocol ping fails with -32601 (Method not found), seamlessly falls back
|
|
269
|
+
* to handshake validation.
|
|
270
|
+
*/
|
|
271
|
+
async ping(options: McpPingOptions = {}): Promise<McpPingResult> {
|
|
272
|
+
const start = Date.now();
|
|
273
|
+
const timeout = options.timeoutMs ?? this.options.timeoutMs ?? 10000;
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
await this.initialize();
|
|
277
|
+
|
|
278
|
+
if (options.mode === "tools") {
|
|
279
|
+
await this.listTools();
|
|
280
|
+
} else {
|
|
281
|
+
try {
|
|
282
|
+
await this.sendRequest("ping", {}, timeout);
|
|
283
|
+
} catch (err: any) {
|
|
284
|
+
// Method not found (-32601) is common on minimalist servers; fallback to handshake
|
|
285
|
+
if (err.code !== -32601) {
|
|
286
|
+
throw err;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
healthy: true,
|
|
293
|
+
latencyMs: Date.now() - start,
|
|
294
|
+
serverInfo: this._serverInfo,
|
|
295
|
+
instructions: this._instructions,
|
|
296
|
+
stderr: this.stderrBuffer.getRecentText(),
|
|
297
|
+
};
|
|
298
|
+
} catch (err: any) {
|
|
299
|
+
return {
|
|
300
|
+
healthy: false,
|
|
301
|
+
latencyMs: Date.now() - start,
|
|
302
|
+
error: err.message,
|
|
303
|
+
stderr: this.stderrBuffer.getRecentText(),
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Enumerates available tools on the MCP server.
|
|
310
|
+
*/
|
|
311
|
+
async listTools(): Promise<McpToolInfo[]> {
|
|
312
|
+
await this.initialize();
|
|
313
|
+
const result = await this.sendRequest("tools/list", {});
|
|
314
|
+
return (result?.tools as McpToolInfo[]) ?? [];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Calls a tool by name with the given arguments.
|
|
319
|
+
*/
|
|
320
|
+
async callTool(name: string, args: Record<string, unknown> = {}): Promise<McpToolCallResult> {
|
|
321
|
+
await this.initialize();
|
|
322
|
+
const result = await this.sendRequest("tools/call", {
|
|
323
|
+
name,
|
|
324
|
+
arguments: args,
|
|
325
|
+
});
|
|
326
|
+
return result as McpToolCallResult;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Retrieves the recent stderr lines captured in the ring buffer.
|
|
331
|
+
*/
|
|
332
|
+
getStderr(): string {
|
|
333
|
+
return this.stderrBuffer.getRecentText();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Cleanly closes the client, killing the child process and its process tree.
|
|
338
|
+
*/
|
|
339
|
+
async close(): Promise<void> {
|
|
340
|
+
this.rejectAllPending(new Error("Client was closed."));
|
|
341
|
+
if (this.child) {
|
|
342
|
+
const child = this.child;
|
|
343
|
+
this.child = null;
|
|
344
|
+
await killProcessTree(child);
|
|
345
|
+
}
|
|
346
|
+
this.initialized = false;
|
|
347
|
+
this.initPromise = undefined;
|
|
348
|
+
}
|
|
349
|
+
}
|