run-mcp 1.5.0 → 1.6.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.
Files changed (3) hide show
  1. package/README.md +23 -0
  2. package/dist/index.js +1215 -631
  3. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -3,11 +3,136 @@
3
3
  // src/index.ts
4
4
  import { program } from "commander";
5
5
 
6
- // src/repl.ts
6
+ // src/config-scanner.ts
7
+ import { existsSync } from "fs";
7
8
  import { readFile } from "fs/promises";
8
- import { createInterface } from "readline";
9
- import { checkbox, confirm, input, search } from "@inquirer/prompts";
10
- import pc from "picocolors";
9
+ import { homedir } from "os";
10
+ import path from "path";
11
+ import process2 from "process";
12
+ import { input, select } from "@inquirer/prompts";
13
+ function getConfigPaths() {
14
+ const home = homedir();
15
+ const cwd = process2.cwd();
16
+ const isWin = process2.platform === "win32";
17
+ const isMac = process2.platform === "darwin";
18
+ const appData = process2.env.APPDATA || path.join(home, "AppData", "Roaming");
19
+ const localAppData = process2.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
20
+ let claudeDesktopGlob;
21
+ if (isWin) {
22
+ claudeDesktopGlob = path.join(appData, "Claude", "claude_desktop_config.json");
23
+ } else if (isMac) {
24
+ claudeDesktopGlob = path.join(
25
+ home,
26
+ "Library",
27
+ "Application Support",
28
+ "Claude",
29
+ "claude_desktop_config.json"
30
+ );
31
+ } else {
32
+ claudeDesktopGlob = path.join(home, ".config", "Claude", "claude_desktop_config.json");
33
+ }
34
+ return [
35
+ { source: "Cursor (Global)", file: path.join(home, ".cursor", "mcp.json") },
36
+ { source: "Cursor (Project)", file: path.join(cwd, ".cursor", "mcp.json") },
37
+ { source: "Windsurf", file: path.join(home, ".codeium", "windsurf", "mcp_config.json") },
38
+ { source: "Claude Desktop", file: claudeDesktopGlob },
39
+ { source: "Cline", file: path.join(home, "Documents", "Cline", "MCP", "mcp.json") },
40
+ { source: "VS Code (Project)", file: path.join(cwd, ".vscode", "mcp.json") },
41
+ {
42
+ source: "VS Code (Global)",
43
+ file: path.join(
44
+ isMac ? path.join(home, "Library", "Application Support") : isWin ? appData : path.join(home, ".config"),
45
+ "Code",
46
+ "User",
47
+ "settings.json"
48
+ )
49
+ },
50
+ { source: "Copilot CLI (Global)", file: path.join(home, ".copilot", "mcp-config.json") },
51
+ { source: "Gemini CLI (Global)", file: path.join(home, ".gemini", "settings.json") },
52
+ { source: "Gemini CLI (Project)", file: path.join(cwd, ".gemini", "settings.json") },
53
+ { source: "Claude Code (Global)", file: path.join(home, ".claude.json") },
54
+ { source: "Claude Code (Project)", file: path.join(cwd, ".mcp.json") },
55
+ { source: "Antigravity", file: path.join(home, ".gemini", "antigravity", "mcp_config.json") }
56
+ ];
57
+ }
58
+ async function discoverServers() {
59
+ const servers = [];
60
+ const paths = getConfigPaths();
61
+ for (const { source, file } of paths) {
62
+ if (!existsSync(file)) continue;
63
+ try {
64
+ const content = await readFile(file, "utf8");
65
+ const json = JSON.parse(content);
66
+ let mcpServers;
67
+ if (json.mcpServers && typeof json.mcpServers === "object") {
68
+ mcpServers = json.mcpServers;
69
+ } else if (json.mcp?.servers && typeof json.mcp.servers === "object") {
70
+ mcpServers = json.mcp.servers;
71
+ } else if (json.servers && typeof json.servers === "object") {
72
+ mcpServers = json.servers;
73
+ }
74
+ if (mcpServers) {
75
+ for (const [name, config] of Object.entries(mcpServers)) {
76
+ if (config.command) {
77
+ servers.push({ name, config, source });
78
+ }
79
+ }
80
+ }
81
+ } catch {
82
+ }
83
+ }
84
+ return servers;
85
+ }
86
+ async function pickDiscoveredServer() {
87
+ const servers = await discoverServers();
88
+ if (servers.length === 0) {
89
+ return null;
90
+ }
91
+ const uniqueServers = /* @__PURE__ */ new Map();
92
+ for (const s of servers) {
93
+ const key = `${s.name}::${s.config.command}::${(s.config.args || []).join(" ")}`;
94
+ if (!uniqueServers.has(key)) {
95
+ uniqueServers.set(key, s);
96
+ } else {
97
+ if (s.source.includes("Project")) {
98
+ uniqueServers.set(key, s);
99
+ }
100
+ }
101
+ }
102
+ const choices = Array.from(uniqueServers.values()).map((s) => {
103
+ return {
104
+ name: `${s.name} (from ${s.source})`,
105
+ value: s,
106
+ description: `${s.config.command} ${(s.config.args || []).join(" ")}`
107
+ };
108
+ });
109
+ choices.push({
110
+ name: "Enter custom server command...",
111
+ value: "CUSTOM",
112
+ description: "Manually specify a command, e.g. 'npx foo' or 'python server.py'"
113
+ });
114
+ try {
115
+ const answer = await select({
116
+ message: "Select an MCP server to launch:",
117
+ choices,
118
+ pageSize: 15
119
+ });
120
+ if (answer === "CUSTOM") {
121
+ const customCommand = await input({ message: "Command to spawn target MCP server:" });
122
+ if (!customCommand.trim()) return null;
123
+ const parts = customCommand.trim().match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map((p) => p.replace(/^["']|["']$/g, ""));
124
+ if (!parts || parts.length === 0) return null;
125
+ return {
126
+ name: "Custom",
127
+ config: { command: parts[0], args: parts.slice(1) },
128
+ source: "Manual"
129
+ };
130
+ }
131
+ return answer;
132
+ } catch {
133
+ return null;
134
+ }
135
+ }
11
136
 
12
137
  // src/interceptor.ts
13
138
  import { mkdir, writeFile } from "fs/promises";
@@ -33,36 +158,67 @@ var ResponseInterceptor = class {
33
158
  * with only the content array items modified when interception is needed.
34
159
  */
35
160
  async callTool(target, name, args = {}, timeoutMs) {
161
+ const { result } = await this._callToolInternal(target, name, args, timeoutMs);
162
+ return result;
163
+ }
164
+ /**
165
+ * Call a tool and return both the result and metadata about interception actions.
166
+ * Used by the agent server when `include_metadata` is requested.
167
+ */
168
+ async callToolWithMetadata(target, name, args = {}, timeoutMs) {
169
+ return this._callToolInternal(target, name, args, timeoutMs);
170
+ }
171
+ /**
172
+ * Internal implementation shared by callTool and callToolWithMetadata.
173
+ */
174
+ async _callToolInternal(target, name, args = {}, timeoutMs) {
36
175
  const timeout = timeoutMs ?? this.defaultTimeoutMs;
176
+ const metadata = {
177
+ truncated: false,
178
+ imagesSaved: 0,
179
+ audioSaved: 0,
180
+ originalSizeBytes: 0
181
+ };
37
182
  const targetCall = target.callTool(name, args);
38
183
  targetCall.catch(() => {
39
184
  });
40
185
  const result = await Promise.race([targetCall, this._timeout(timeout, name)]);
41
186
  const content = result.content;
42
187
  if (Array.isArray(content)) {
188
+ for (const item of content) {
189
+ if (item.type === "text" && item.text) {
190
+ metadata.originalSizeBytes += Buffer.byteLength(item.text, "utf8");
191
+ } else if ((item.type === "image" || item.type === "audio") && item.data) {
192
+ metadata.originalSizeBytes += Buffer.byteLength(item.data, "base64");
193
+ }
194
+ }
43
195
  for (let i = 0; i < content.length; i++) {
44
- content[i] = await this._processItem(content[i]);
196
+ content[i] = await this._processItem(content[i], metadata);
45
197
  }
46
198
  }
47
- return result;
199
+ return { result, metadata };
48
200
  }
49
201
  /**
50
202
  * Process a single content item — extract media, truncate text.
51
203
  * Preserves all item properties not related to the intercepted data
52
204
  * (e.g., annotations, _meta).
53
205
  */
54
- async _processItem(item) {
206
+ async _processItem(item, metadata) {
55
207
  if (item.type === "image" && item.data) {
208
+ metadata.imagesSaved++;
56
209
  return this._saveMedia(item.data, item.mimeType ?? "image/png", "image");
57
210
  }
58
211
  if (item.type === "audio" && item.data) {
212
+ metadata.audioSaved++;
59
213
  return this._saveMedia(item.data, item.mimeType ?? "audio/wav", "audio");
60
214
  }
61
215
  if (item.type === "text" && item.text && BASE64_PATTERN.test(item.text.trim())) {
216
+ metadata.imagesSaved++;
62
217
  return this._saveMedia(item.text.trim(), "image/png", "image");
63
218
  }
64
219
  if (item.type === "text" && item.text && item.text.length > this.maxTextLength) {
65
220
  const totalLength = item.text.length;
221
+ metadata.truncated = true;
66
222
  return {
67
223
  ...item,
68
224
  text: item.text.slice(0, this.maxTextLength) + `
@@ -127,219 +283,12 @@ var ResponseInterceptor = class {
127
283
  "audio/ogg": ".ogg",
128
284
  "audio/flac": ".flac",
129
285
  "audio/aac": ".aac",
130
- "audio/webm": ".webm",
131
- "audio/mp4": ".m4a"
132
- };
133
- return map[mimeType] ?? (mimeType.startsWith("audio/") ? ".wav" : ".png");
134
- }
135
- };
136
-
137
- // src/parsing.ts
138
- function parseCommandLine(input3) {
139
- const spaceIdx = input3.indexOf(" ");
140
- if (spaceIdx === -1) {
141
- return { cmd: input3.toLowerCase(), rest: "" };
142
- }
143
- return {
144
- cmd: input3.slice(0, spaceIdx).toLowerCase(),
145
- rest: input3.slice(spaceIdx + 1)
146
- };
147
- }
148
- function parseCallArgs(rest) {
149
- const trimmed = rest.trim();
150
- if (!trimmed) return { toolName: "", jsonArgs: "" };
151
- const spaceIdx = trimmed.indexOf(" ");
152
- if (spaceIdx === -1) {
153
- return { toolName: trimmed, jsonArgs: "" };
154
- }
155
- const toolName = trimmed.slice(0, spaceIdx);
156
- let remainder = trimmed.slice(spaceIdx + 1).trim();
157
- let timeoutMs;
158
- const timeoutMatch = remainder.match(/\s--timeout\s+(\d+)\s*$/);
159
- if (timeoutMatch) {
160
- timeoutMs = parseInt(timeoutMatch[1], 10);
161
- remainder = remainder.slice(0, timeoutMatch.index).trim();
162
- }
163
- return { toolName, jsonArgs: remainder, timeoutMs };
164
- }
165
- function formatJson(obj, indent = 2) {
166
- const json = JSON.stringify(obj, null, indent);
167
- return json.split("\n").map((line) => " ".repeat(indent) + line).join("\n");
168
- }
169
- function levenshtein(a, b) {
170
- const m = a.length;
171
- const n = b.length;
172
- const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
173
- for (let i = 0; i <= m; i++) dp[i][0] = i;
174
- for (let j = 0; j <= n; j++) dp[0][j] = j;
175
- for (let i = 1; i <= m; i++) {
176
- for (let j = 1; j <= n; j++) {
177
- dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
178
- }
179
- }
180
- return dp[m][n];
181
- }
182
- function suggestCommand(input3, commands, threshold = 0.4) {
183
- let best = null;
184
- let bestDist = Infinity;
185
- for (const cmd of commands) {
186
- const dist = levenshtein(input3, cmd);
187
- if (dist < bestDist) {
188
- bestDist = dist;
189
- best = cmd;
190
- }
191
- }
192
- if (best && bestDist <= Math.ceil(input3.length * threshold)) {
193
- return best;
194
- }
195
- return null;
196
- }
197
- function scaffoldArgs(schema) {
198
- return JSON.stringify(scaffoldObject(schema), null, 2);
199
- }
200
- function scaffoldValue(prop) {
201
- switch (prop.type) {
202
- case "string":
203
- return "<string>";
204
- case "number":
205
- case "integer":
206
- return "<number>";
207
- case "boolean":
208
- return "<boolean>";
209
- case "array": {
210
- const items = prop.items;
211
- return items ? [scaffoldValue(items)] : ["<item>"];
212
- }
213
- case "object":
214
- return scaffoldObject(prop);
215
- default:
216
- return `<${prop.type ?? "unknown"}>`;
217
- }
218
- }
219
- function scaffoldObject(schema) {
220
- const properties = schema.properties;
221
- if (!properties) return {};
222
- const result = {};
223
- for (const [key, prop] of Object.entries(properties)) {
224
- result[key] = scaffoldValue(prop);
225
- }
226
- return result;
227
- }
228
- function formatToolDescription(tool) {
229
- const lines = [];
230
- lines.push(` ${tool.name}`);
231
- if (tool.description) {
232
- lines.push(` ${tool.description}`);
233
- }
234
- const schema = tool.inputSchema ?? {};
235
- const properties = schema.properties;
236
- const required = schema.required ?? [];
237
- if (properties && Object.keys(properties).length > 0) {
238
- lines.push("");
239
- lines.push(" Arguments:");
240
- const nameWidth = Math.max(6, ...Object.keys(properties).map((n) => n.length));
241
- const typeWidth = Math.max(4, ...Object.values(properties).map((p) => typeLabel(p).length));
242
- for (const [name, prop] of Object.entries(properties)) {
243
- const type = typeLabel(prop);
244
- const req = required.includes(name) ? "(required)" : "(optional)";
245
- const desc = prop.description ?? "";
246
- lines.push(
247
- ` ${name.padEnd(nameWidth)} ${type.padEnd(typeWidth)} ${req.padEnd(10)} ${desc}`
248
- );
249
- }
250
- } else {
251
- lines.push("");
252
- lines.push(" No arguments required.");
253
- }
254
- lines.push("");
255
- lines.push(" Example:");
256
- if (properties && Object.keys(properties).length > 0) {
257
- const example = scaffoldObject(schema);
258
- lines.push(` tools/call ${tool.name} ${JSON.stringify(example)}`);
259
- } else {
260
- lines.push(` tools/call ${tool.name}`);
261
- }
262
- if (tool.annotations && Object.keys(tool.annotations).length > 0) {
263
- lines.push("");
264
- lines.push(" Annotations:");
265
- const annotationParts = [];
266
- for (const [key, value] of Object.entries(tool.annotations)) {
267
- annotationParts.push(`${key}: ${value}`);
268
- }
269
- lines.push(` ${annotationParts.join(", ")}`);
270
- }
271
- return lines.join("\n");
272
- }
273
- function typeLabel(prop) {
274
- const type = prop.type;
275
- if (!type) return "any";
276
- if (type === "array") {
277
- const items = prop.items;
278
- return items ? `${typeLabel(items)}[]` : "array";
279
- }
280
- return type;
281
- }
282
- function groupToolsByPrefix(toolNames) {
283
- const groups = /* @__PURE__ */ new Map();
284
- for (const name of toolNames) {
285
- const underscoreIdx = name.indexOf("_");
286
- const prefix = underscoreIdx > 0 ? name.slice(0, underscoreIdx) : name;
287
- const list = groups.get(prefix) ?? [];
288
- list.push(name);
289
- groups.set(prefix, list);
290
- }
291
- const meaningfulGroups = [...groups.entries()].filter(([, members]) => members.length >= 2);
292
- if (meaningfulGroups.length < 2) {
293
- const all = /* @__PURE__ */ new Map();
294
- all.set("All", [...toolNames]);
295
- return all;
296
- }
297
- const result = /* @__PURE__ */ new Map();
298
- const other = [];
299
- for (const [prefix, members] of groups) {
300
- if (members.length >= 2) {
301
- const label = prefix.charAt(0).toUpperCase() + prefix.slice(1);
302
- result.set(label, members);
303
- } else {
304
- other.push(...members);
305
- }
306
- }
307
- if (other.length > 0) {
308
- result.set("Other", other);
309
- }
310
- return result;
311
- }
312
- var LOG_LEVELS = [
313
- "debug",
314
- "info",
315
- "notice",
316
- "warning",
317
- "error",
318
- "critical",
319
- "alert",
320
- "emergency"
321
- ];
322
- var ALIASES = {
323
- tl: "tools/list",
324
- td: "tools/describe",
325
- tc: "tools/call",
326
- ts: "tools/scaffold",
327
- rl: "resources/list",
328
- rr: "resources/read",
329
- rt: "resources/templates",
330
- rs: "resources/subscribe",
331
- ru: "resources/unsubscribe",
332
- pl: "prompts/list",
333
- pg: "prompts/get"
334
- };
335
- function resolveAlias(input3) {
336
- const spaceIdx = input3.indexOf(" ");
337
- const token = spaceIdx === -1 ? input3 : input3.slice(0, spaceIdx);
338
- const rest = spaceIdx === -1 ? "" : input3.slice(spaceIdx);
339
- const expanded = ALIASES[token.toLowerCase()];
340
- if (!expanded) return null;
341
- return expanded + rest;
342
- }
286
+ "audio/webm": ".webm",
287
+ "audio/mp4": ".m4a"
288
+ };
289
+ return map[mimeType] ?? (mimeType.startsWith("audio/") ? ".wav" : ".png");
290
+ }
291
+ };
343
292
 
344
293
  // src/target-manager.ts
345
294
  import { EventEmitter } from "events";
@@ -426,7 +375,7 @@ var TargetManager = class _TargetManager extends EventEmitter {
426
375
  this.transport = stdioTransport;
427
376
  }
428
377
  this.client = new Client(
429
- { name: "run-mcp", version: "1.4.0" },
378
+ { name: "run-mcp", version: "1.6.0" },
430
379
  {
431
380
  capabilities: {
432
381
  roots: { listChanged: true },
@@ -965,6 +914,445 @@ var TargetManager = class _TargetManager extends EventEmitter {
965
914
  }
966
915
  };
967
916
 
917
+ // src/headless.ts
918
+ var DEFAULT_HEADLESS_TIMEOUT_MS = 3e4;
919
+ async function runHeadless(targetCommand, operation, opts = {}) {
920
+ const [command, ...args] = targetCommand;
921
+ const target = new TargetManager(command, args);
922
+ const interceptor = new ResponseInterceptor({
923
+ outDir: opts.outDir,
924
+ defaultTimeoutMs: opts.timeoutMs ?? DEFAULT_HEADLESS_TIMEOUT_MS
925
+ });
926
+ target.on("stderr", () => {
927
+ });
928
+ try {
929
+ process.stderr.write(`Connecting to ${targetCommand.join(" ")}...
930
+ `);
931
+ await target.connect();
932
+ const status = target.getStatus();
933
+ process.stderr.write(`Connected (PID: ${status.pid})
934
+ `);
935
+ const result = await executeOperation(target, interceptor, operation, opts);
936
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
937
+ `);
938
+ await target.close();
939
+ process.exit(0);
940
+ } catch (err) {
941
+ const msg = err.message ?? String(err);
942
+ if (msg.includes("ENOENT") || msg.includes("spawn")) {
943
+ process.stderr.write(
944
+ `Error: command "${command}" not found. Check that it is installed and in your PATH.
945
+ `
946
+ );
947
+ } else if (msg.includes("timed out")) {
948
+ process.stderr.write(`Error: ${msg}
949
+ `);
950
+ } else {
951
+ process.stderr.write(`Error: ${msg}
952
+ `);
953
+ }
954
+ await target.close().catch(() => {
955
+ });
956
+ process.exit(1);
957
+ }
958
+ }
959
+ async function executeOperation(target, interceptor, operation, opts) {
960
+ switch (operation.type) {
961
+ case "call": {
962
+ let parsedArgs = {};
963
+ if (operation.args) {
964
+ try {
965
+ parsedArgs = JSON.parse(operation.args);
966
+ } catch (err) {
967
+ process.stderr.write(`Error: Invalid JSON arguments: ${err.message}
968
+ `);
969
+ process.stderr.write(` Received: ${operation.args}
970
+ `);
971
+ process.exit(2);
972
+ }
973
+ }
974
+ const result = await interceptor.callTool(target, operation.tool, parsedArgs);
975
+ if (result.isError) {
976
+ const content = result.content;
977
+ if (Array.isArray(content)) {
978
+ const errorText = content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
979
+ if (errorText) {
980
+ process.stderr.write(`Tool error: ${errorText}
981
+ `);
982
+ }
983
+ }
984
+ if (opts.raw) return result;
985
+ return result.content ?? result;
986
+ }
987
+ if (opts.raw) return result;
988
+ return result.content ?? result;
989
+ }
990
+ case "list-tools": {
991
+ const { tools } = await target.listTools();
992
+ return tools;
993
+ }
994
+ case "list-resources": {
995
+ const { resources } = await target.listResources();
996
+ return resources;
997
+ }
998
+ case "list-prompts": {
999
+ const { prompts } = await target.listPrompts();
1000
+ return prompts;
1001
+ }
1002
+ case "read": {
1003
+ const result = await target.readResource({ uri: operation.uri });
1004
+ return result;
1005
+ }
1006
+ case "describe": {
1007
+ const { tools } = await target.listTools();
1008
+ const tool = tools.find((t) => t.name === operation.tool);
1009
+ if (!tool) {
1010
+ const available = tools.map((t) => t.name).join(", ");
1011
+ process.stderr.write(
1012
+ `Error: Tool "${operation.tool}" not found.
1013
+ Available tools: ${available}
1014
+ `
1015
+ );
1016
+ process.exit(1);
1017
+ }
1018
+ return tool;
1019
+ }
1020
+ case "get-prompt": {
1021
+ let parsedArgs;
1022
+ if (operation.args) {
1023
+ try {
1024
+ parsedArgs = JSON.parse(operation.args);
1025
+ } catch (err) {
1026
+ process.stderr.write(`Error: Invalid JSON arguments: ${err.message}
1027
+ `);
1028
+ process.stderr.write(` Received: ${operation.args}
1029
+ `);
1030
+ process.exit(2);
1031
+ }
1032
+ }
1033
+ const result = await target.getPrompt({
1034
+ name: operation.name,
1035
+ arguments: parsedArgs
1036
+ });
1037
+ return result;
1038
+ }
1039
+ }
1040
+ }
1041
+
1042
+ // src/repl.ts
1043
+ import { readFile as readFile2 } from "fs/promises";
1044
+ import { createInterface } from "readline";
1045
+ import { checkbox, confirm, input as input2, search } from "@inquirer/prompts";
1046
+ import pc2 from "picocolors";
1047
+
1048
+ // src/parsing.ts
1049
+ import pc from "picocolors";
1050
+ function parseCommandLine(input3) {
1051
+ const spaceIdx = input3.indexOf(" ");
1052
+ if (spaceIdx === -1) {
1053
+ return { cmd: input3.toLowerCase(), rest: "" };
1054
+ }
1055
+ return {
1056
+ cmd: input3.slice(0, spaceIdx).toLowerCase(),
1057
+ rest: input3.slice(spaceIdx + 1)
1058
+ };
1059
+ }
1060
+ function parseCallArgs(rest) {
1061
+ const trimmed = rest.trim();
1062
+ if (!trimmed) return { toolName: "", jsonArgs: "" };
1063
+ const spaceIdx = trimmed.indexOf(" ");
1064
+ if (spaceIdx === -1) {
1065
+ return { toolName: trimmed, jsonArgs: "" };
1066
+ }
1067
+ const toolName = trimmed.slice(0, spaceIdx);
1068
+ let remainder = trimmed.slice(spaceIdx + 1).trim();
1069
+ let timeoutMs;
1070
+ const timeoutMatch = remainder.match(/\s--timeout\s+(\d+)\s*$/);
1071
+ if (timeoutMatch) {
1072
+ timeoutMs = parseInt(timeoutMatch[1], 10);
1073
+ remainder = remainder.slice(0, timeoutMatch.index).trim();
1074
+ }
1075
+ return { toolName, jsonArgs: remainder, timeoutMs };
1076
+ }
1077
+ function formatJson(obj, indent = 2, colorize = false) {
1078
+ const json = JSON.stringify(obj, null, indent);
1079
+ const output = colorize ? colorizeJson(json) : json;
1080
+ return output.split("\n").map((line) => " ".repeat(indent) + line).join("\n");
1081
+ }
1082
+ function colorizeJson(json) {
1083
+ const result = [];
1084
+ let i = 0;
1085
+ let expectingValue = false;
1086
+ while (i < json.length) {
1087
+ const ch = json[i];
1088
+ if (ch === '"') {
1089
+ const str = consumeString(json, i);
1090
+ if (expectingValue) {
1091
+ result.push(pc.green(str));
1092
+ expectingValue = false;
1093
+ } else {
1094
+ result.push(pc.cyan(str));
1095
+ }
1096
+ i += str.length;
1097
+ continue;
1098
+ }
1099
+ if (ch === ":") {
1100
+ result.push(ch);
1101
+ expectingValue = true;
1102
+ i++;
1103
+ continue;
1104
+ }
1105
+ if (ch === "," || ch === "}" || ch === "]") {
1106
+ result.push(ch);
1107
+ expectingValue = false;
1108
+ i++;
1109
+ continue;
1110
+ }
1111
+ if (ch === "{" || ch === "[") {
1112
+ result.push(ch);
1113
+ if (ch === "[") expectingValue = true;
1114
+ i++;
1115
+ continue;
1116
+ }
1117
+ if (json.startsWith("true", i)) {
1118
+ result.push(pc.magenta("true"));
1119
+ expectingValue = false;
1120
+ i += 4;
1121
+ continue;
1122
+ }
1123
+ if (json.startsWith("false", i)) {
1124
+ result.push(pc.magenta("false"));
1125
+ expectingValue = false;
1126
+ i += 5;
1127
+ continue;
1128
+ }
1129
+ if (json.startsWith("null", i)) {
1130
+ result.push(pc.dim("null"));
1131
+ expectingValue = false;
1132
+ i += 4;
1133
+ continue;
1134
+ }
1135
+ if (ch === "-" || ch >= "0" && ch <= "9") {
1136
+ let num = "";
1137
+ while (i < json.length && /[0-9.eE+-]/.test(json[i])) {
1138
+ num += json[i];
1139
+ i++;
1140
+ }
1141
+ result.push(pc.yellow(num));
1142
+ expectingValue = false;
1143
+ continue;
1144
+ }
1145
+ result.push(ch);
1146
+ i++;
1147
+ }
1148
+ return result.join("");
1149
+ }
1150
+ function consumeString(json, start) {
1151
+ let i = start + 1;
1152
+ while (i < json.length) {
1153
+ if (json[i] === "\\") {
1154
+ i += 2;
1155
+ continue;
1156
+ }
1157
+ if (json[i] === '"') {
1158
+ return json.slice(start, i + 1);
1159
+ }
1160
+ i++;
1161
+ }
1162
+ return json.slice(start);
1163
+ }
1164
+ function levenshtein(a, b) {
1165
+ const m = a.length;
1166
+ const n = b.length;
1167
+ const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
1168
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
1169
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
1170
+ for (let i = 1; i <= m; i++) {
1171
+ for (let j = 1; j <= n; j++) {
1172
+ dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
1173
+ }
1174
+ }
1175
+ return dp[m][n];
1176
+ }
1177
+ function suggestCommand(input3, commands, threshold = 0.4) {
1178
+ let best = null;
1179
+ let bestDist = Infinity;
1180
+ for (const cmd of commands) {
1181
+ const dist = levenshtein(input3, cmd);
1182
+ if (dist < bestDist) {
1183
+ bestDist = dist;
1184
+ best = cmd;
1185
+ }
1186
+ }
1187
+ if (best && bestDist <= Math.ceil(input3.length * threshold)) {
1188
+ return best;
1189
+ }
1190
+ return null;
1191
+ }
1192
+ function scaffoldArgs(schema) {
1193
+ return JSON.stringify(scaffoldObject(schema), null, 2);
1194
+ }
1195
+ function scaffoldValue(prop) {
1196
+ if (Array.isArray(prop.enum) && prop.enum.length > 0) {
1197
+ return prop.enum[0];
1198
+ }
1199
+ const variants = prop.anyOf ?? prop.oneOf;
1200
+ if (Array.isArray(variants) && variants.length > 0) {
1201
+ return scaffoldValue(variants[0]);
1202
+ }
1203
+ switch (prop.type) {
1204
+ case "string":
1205
+ return "<string>";
1206
+ case "number":
1207
+ case "integer":
1208
+ return "<number>";
1209
+ case "boolean":
1210
+ return "<boolean>";
1211
+ case "array": {
1212
+ const items = prop.items;
1213
+ return items ? [scaffoldValue(items)] : ["<item>"];
1214
+ }
1215
+ case "object":
1216
+ return scaffoldObject(prop);
1217
+ default:
1218
+ return `<${prop.type ?? "unknown"}>`;
1219
+ }
1220
+ }
1221
+ function scaffoldObject(schema) {
1222
+ const properties = schema.properties;
1223
+ if (properties) {
1224
+ const result = {};
1225
+ for (const [key, prop] of Object.entries(properties)) {
1226
+ result[key] = scaffoldValue(prop);
1227
+ }
1228
+ return result;
1229
+ }
1230
+ const additionalProperties = schema.additionalProperties;
1231
+ if (additionalProperties && typeof additionalProperties === "object") {
1232
+ return { "<key>": scaffoldValue(additionalProperties) };
1233
+ }
1234
+ if (additionalProperties === true || schema.type === "object" && !properties) {
1235
+ return { "<key>": "<value>" };
1236
+ }
1237
+ return {};
1238
+ }
1239
+ function formatToolDescription(tool) {
1240
+ const lines = [];
1241
+ lines.push(` ${tool.name}`);
1242
+ if (tool.description) {
1243
+ lines.push(` ${tool.description}`);
1244
+ }
1245
+ const schema = tool.inputSchema ?? {};
1246
+ const properties = schema.properties;
1247
+ const required = schema.required ?? [];
1248
+ if (properties && Object.keys(properties).length > 0) {
1249
+ lines.push("");
1250
+ lines.push(" Arguments:");
1251
+ const nameWidth = Math.max(6, ...Object.keys(properties).map((n) => n.length));
1252
+ const typeWidth = Math.max(4, ...Object.values(properties).map((p) => typeLabel(p).length));
1253
+ for (const [name, prop] of Object.entries(properties)) {
1254
+ const type = typeLabel(prop);
1255
+ const req = required.includes(name) ? "(required)" : "(optional)";
1256
+ const desc = prop.description ?? "";
1257
+ lines.push(
1258
+ ` ${name.padEnd(nameWidth)} ${type.padEnd(typeWidth)} ${req.padEnd(10)} ${desc}`
1259
+ );
1260
+ }
1261
+ } else {
1262
+ lines.push("");
1263
+ lines.push(" No arguments required.");
1264
+ }
1265
+ lines.push("");
1266
+ lines.push(" Example:");
1267
+ if (properties && Object.keys(properties).length > 0) {
1268
+ const example = scaffoldObject(schema);
1269
+ lines.push(` tools/call ${tool.name} ${JSON.stringify(example)}`);
1270
+ } else {
1271
+ lines.push(` tools/call ${tool.name}`);
1272
+ }
1273
+ if (tool.annotations) {
1274
+ const entries = Object.entries(tool.annotations).filter(([key]) => key !== "title");
1275
+ if (entries.length > 0) {
1276
+ lines.push("");
1277
+ lines.push(" Annotations:");
1278
+ for (const [key, value] of entries) {
1279
+ lines.push(` ${key}: ${value}`);
1280
+ }
1281
+ }
1282
+ }
1283
+ return lines.join("\n");
1284
+ }
1285
+ function typeLabel(prop) {
1286
+ const type = prop.type;
1287
+ if (!type) return "any";
1288
+ if (type === "array") {
1289
+ const items = prop.items;
1290
+ return items ? `${typeLabel(items)}[]` : "array";
1291
+ }
1292
+ return type;
1293
+ }
1294
+ function groupToolsByPrefix(toolNames) {
1295
+ const groups = /* @__PURE__ */ new Map();
1296
+ for (const name of toolNames) {
1297
+ const underscoreIdx = name.indexOf("_");
1298
+ const prefix = underscoreIdx > 0 ? name.slice(0, underscoreIdx) : name;
1299
+ const list = groups.get(prefix) ?? [];
1300
+ list.push(name);
1301
+ groups.set(prefix, list);
1302
+ }
1303
+ const meaningfulGroups = [...groups.entries()].filter(([, members]) => members.length >= 2);
1304
+ if (meaningfulGroups.length < 2) {
1305
+ const all = /* @__PURE__ */ new Map();
1306
+ all.set("All", [...toolNames]);
1307
+ return all;
1308
+ }
1309
+ const result = /* @__PURE__ */ new Map();
1310
+ const other = [];
1311
+ for (const [prefix, members] of groups) {
1312
+ if (members.length >= 2) {
1313
+ const label = prefix.charAt(0).toUpperCase() + prefix.slice(1);
1314
+ result.set(label, members);
1315
+ } else {
1316
+ other.push(...members);
1317
+ }
1318
+ }
1319
+ if (other.length > 0) {
1320
+ result.set("Other", other);
1321
+ }
1322
+ return result;
1323
+ }
1324
+ var LOG_LEVELS = [
1325
+ "debug",
1326
+ "info",
1327
+ "notice",
1328
+ "warning",
1329
+ "error",
1330
+ "critical",
1331
+ "alert",
1332
+ "emergency"
1333
+ ];
1334
+ var ALIASES = {
1335
+ tl: "tools/list",
1336
+ td: "tools/describe",
1337
+ tc: "tools/call",
1338
+ ts: "tools/scaffold",
1339
+ rl: "resources/list",
1340
+ rr: "resources/read",
1341
+ rt: "resources/templates",
1342
+ rs: "resources/subscribe",
1343
+ ru: "resources/unsubscribe",
1344
+ pl: "prompts/list",
1345
+ pg: "prompts/get"
1346
+ };
1347
+ function resolveAlias(input3) {
1348
+ const spaceIdx = input3.indexOf(" ");
1349
+ const token = spaceIdx === -1 ? input3 : input3.slice(0, spaceIdx);
1350
+ const rest = spaceIdx === -1 ? "" : input3.slice(spaceIdx);
1351
+ const expanded = ALIASES[token.toLowerCase()];
1352
+ if (!expanded) return null;
1353
+ return expanded + rest;
1354
+ }
1355
+
968
1356
  // src/repl.ts
969
1357
  var KNOWN_COMMANDS = [
970
1358
  "explore",
@@ -994,6 +1382,7 @@ var KNOWN_COMMANDS = [
994
1382
  "!!",
995
1383
  "last",
996
1384
  "help",
1385
+ "?",
997
1386
  "exit",
998
1387
  "quit",
999
1388
  // Short aliases
@@ -1012,8 +1401,22 @@ var KNOWN_COMMANDS = [
1012
1401
  var cachedToolNames = [];
1013
1402
  var cachedResourceUris = [];
1014
1403
  var cachedPromptNames = [];
1404
+ var activeCapabilities = null;
1405
+ function getActiveCommands() {
1406
+ let commands = [...KNOWN_COMMANDS];
1407
+ if (!activeCapabilities?.resources) {
1408
+ commands = commands.filter(
1409
+ (c) => !c.startsWith("resources/") && !["rl", "rr", "rt", "rs", "ru"].includes(c)
1410
+ );
1411
+ }
1412
+ if (!activeCapabilities?.prompts) {
1413
+ commands = commands.filter((c) => !c.startsWith("prompts/") && !["pl", "pg"].includes(c));
1414
+ }
1415
+ return commands;
1416
+ }
1015
1417
  async function refreshCaches(target) {
1016
1418
  const caps = target.getServerCapabilities() ?? {};
1419
+ activeCapabilities = caps;
1017
1420
  try {
1018
1421
  const { tools } = await target.listTools();
1019
1422
  cachedToolNames = tools.map((t) => t.name);
@@ -1058,7 +1461,7 @@ function computeMatches(line) {
1058
1461
  const matches2 = cachedPromptNames.filter((n) => n.startsWith(partial));
1059
1462
  return [matches2.map((m) => `prompts/get ${m}`), effective];
1060
1463
  }
1061
- const matches = KNOWN_COMMANDS.filter((c) => c.startsWith(line));
1464
+ const matches = getActiveCommands().filter((c) => c.startsWith(line));
1062
1465
  return [matches, line];
1063
1466
  }
1064
1467
  var completer = (line) => {
@@ -1122,34 +1525,36 @@ async function withSuspendedReadline(target, interceptor, fn) {
1122
1525
  }
1123
1526
  }
1124
1527
  function getPrompt(target) {
1125
- if (target.connected) return `${pc.green("\u2713")}${pc.cyan("> ")}`;
1126
- return `${pc.red("\u2717")}${pc.cyan("> ")}`;
1528
+ if (target.connected) return `${pc2.green("\u2713")}${pc2.cyan("> ")}`;
1529
+ return `${pc2.red("\u2717")}${pc2.cyan("> ")}`;
1127
1530
  }
1128
1531
  function printBanner(serverName, serverVersion, toolCount, resourceCount, promptCount) {
1129
1532
  const parts = [];
1130
- parts.push(`${pc.bold(toolCount.toString())} tools`);
1131
- if (resourceCount > 0) parts.push(`${pc.bold(resourceCount.toString())} resources`);
1132
- if (promptCount > 0) parts.push(`${pc.bold(promptCount.toString())} prompts`);
1133
- const title = serverVersion ? `${serverName} ${pc.dim(`v${serverVersion}`)}` : serverName;
1533
+ parts.push(`${pc2.bold(toolCount.toString())} tools`);
1534
+ if (resourceCount > 0) parts.push(`${pc2.bold(resourceCount.toString())} resources`);
1535
+ if (promptCount > 0) parts.push(`${pc2.bold(promptCount.toString())} prompts`);
1536
+ const baseTitle = serverVersion ? `${serverName} ${pc2.dim(`v${serverVersion}`)}` : serverName;
1537
+ const isAgentHarness = serverName === "run-mcp";
1538
+ const title = isAgentHarness ? `${baseTitle} ${pc2.bgBlue(pc2.white(" AGENT HARNESS "))}` : baseTitle;
1134
1539
  const BOX_WIDTH = 53;
1135
1540
  const padLine = (content) => {
1136
1541
  const visible = stripAnsi(content).length;
1137
1542
  const padding = Math.max(0, BOX_WIDTH - visible);
1138
- return `${pc.cyan(" \u2502")}${content}${"".padEnd(padding)}${pc.cyan("\u2502")}`;
1543
+ return `${pc2.cyan(" \u2502")}${content}${"".padEnd(padding)}${pc2.cyan("\u2502")}`;
1139
1544
  };
1140
1545
  const partsStr = ` ${parts.join(" \u2022 ")}`;
1141
1546
  console.log();
1142
- console.log(pc.cyan(` \u250C${"\u2500".repeat(BOX_WIDTH)}\u2510`));
1547
+ console.log(pc2.cyan(` \u250C${"\u2500".repeat(BOX_WIDTH)}\u2510`));
1143
1548
  console.log(padLine(` ${title}`));
1144
1549
  console.log(padLine(partsStr));
1145
- console.log(pc.cyan(` \u251C${"\u2500".repeat(BOX_WIDTH)}\u2524`));
1550
+ console.log(pc2.cyan(` \u251C${"\u2500".repeat(BOX_WIDTH)}\u2524`));
1146
1551
  console.log(padLine(" Quick start:"));
1147
- console.log(padLine(` ${pc.green("tools/list")} See all tools`));
1148
- console.log(padLine(` ${pc.green("tools/call")} ${pc.dim("<name>")} Call a tool`));
1149
- console.log(padLine(` ${pc.green("help")} All commands`));
1552
+ console.log(padLine(` ${pc2.green("tools/list")} See all tools`));
1553
+ console.log(padLine(` ${pc2.green("tools/call")} ${pc2.dim("<name>")} Call a tool`));
1554
+ console.log(padLine(` ${pc2.green("help")} All commands`));
1150
1555
  console.log(padLine(""));
1151
- console.log(padLine(pc.dim(" Tab completion is active. Start typing to explore.")));
1152
- console.log(pc.cyan(` \u2514${"\u2500".repeat(BOX_WIDTH)}\u2518`));
1556
+ console.log(padLine(pc2.dim(" Tab completion is active. Start typing to explore.")));
1557
+ console.log(pc2.cyan(` \u2514${"\u2500".repeat(BOX_WIDTH)}\u2518`));
1153
1558
  console.log();
1154
1559
  }
1155
1560
  function stripAnsi(str) {
@@ -1162,47 +1567,47 @@ async function startRepl(targetCommand, opts) {
1162
1567
  isScriptMode = !!opts.script;
1163
1568
  target.on("stderr", (text) => {
1164
1569
  for (const line of text.split("\n")) {
1165
- console.error(pc.dim(`[server] ${line}`));
1570
+ console.error(pc2.dim(`[server] ${line}`));
1166
1571
  }
1167
1572
  });
1168
- console.log(pc.cyan("\u27F3 Connecting to target MCP server..."));
1169
- console.log(pc.dim(` Command: ${targetCommand.join(" ")}`));
1573
+ console.log(pc2.cyan("\u27F3 Connecting to target MCP server..."));
1574
+ console.log(pc2.dim(` Command: ${targetCommand.join(" ")}`));
1170
1575
  try {
1171
1576
  await target.connect();
1172
1577
  } catch (err) {
1173
1578
  const msg = err.message ?? String(err);
1174
1579
  if (msg.includes("ENOENT") || msg.includes("spawn")) {
1175
- console.error(pc.red(`\u2717 Failed to start server: command "${command}" not found.`));
1176
- console.error(pc.dim(` Check that "${command}" is installed and in your PATH.`));
1580
+ console.error(pc2.red(`\u2717 Failed to start server: command "${command}" not found.`));
1581
+ console.error(pc2.dim(` Check that "${command}" is installed and in your PATH.`));
1177
1582
  } else {
1178
- console.error(pc.red(`\u2717 Failed to connect: ${msg}`));
1179
- console.error(pc.dim(` Check that the target command starts a valid MCP server on stdio.`));
1583
+ console.error(pc2.red(`\u2717 Failed to connect: ${msg}`));
1584
+ console.error(pc2.dim(` Check that the target command starts a valid MCP server on stdio.`));
1180
1585
  }
1181
1586
  process.exit(1);
1182
1587
  }
1183
1588
  const status = target.getStatus();
1184
- console.log(pc.green(`\u2713 Connected (PID: ${status.pid})`));
1589
+ console.log(pc2.green(`\u2713 Connected (PID: ${status.pid})`));
1185
1590
  if (!isScriptMode) {
1186
1591
  target.enableAutoReconnect();
1187
1592
  target.on(
1188
1593
  "reconnecting",
1189
1594
  ({ attempt, maxAttempts }) => {
1190
1595
  console.log(
1191
- pc.yellow(`
1596
+ pc2.yellow(`
1192
1597
  \u27F3 Server disconnected. Reconnecting (${attempt}/${maxAttempts})...`)
1193
1598
  );
1194
1599
  }
1195
1600
  );
1196
1601
  target.on("reconnected", async ({ attempt }) => {
1197
1602
  const s = target.getStatus();
1198
- console.log(pc.green(`\u2713 Reconnected (PID: ${s.pid}, attempt ${attempt})`));
1603
+ console.log(pc2.green(`\u2713 Reconnected (PID: ${s.pid}, attempt ${attempt})`));
1199
1604
  await refreshCaches(target);
1200
1605
  });
1201
1606
  target.on("reconnect_failed", ({ reason, message }) => {
1202
- console.error(pc.red(`\u2717 ${message}`));
1607
+ console.error(pc2.red(`\u2717 ${message}`));
1203
1608
  if (reason === "max_retries") {
1204
1609
  console.log(
1205
- pc.dim(" Use 'exit' to quit or wait for the server to be fixed and restart manually.")
1610
+ pc2.dim(" Use 'exit' to quit or wait for the server to be fixed and restart manually.")
1206
1611
  );
1207
1612
  }
1208
1613
  });
@@ -1212,38 +1617,38 @@ async function startRepl(targetCommand, opts) {
1212
1617
  const lvl = notification.params?.level ?? "info";
1213
1618
  const data = notification.params?.data ?? "";
1214
1619
  const text = typeof data === "string" ? data : JSON.stringify(data);
1215
- console.log(pc.dim(`
1620
+ console.log(pc2.dim(`
1216
1621
  [${lvl}] ${text}`));
1217
1622
  } else if (method === "notifications/tools/list_changed") {
1218
- console.log(pc.yellow("\n \u27F3 Server tools changed. Run tools/list to see updates."));
1623
+ console.log(pc2.yellow("\n \u27F3 Server tools changed. Run tools/list to see updates."));
1219
1624
  refreshCaches(target).catch(() => {
1220
1625
  });
1221
1626
  } else if (method === "notifications/resources/list_changed") {
1222
- console.log(pc.yellow("\n \u27F3 Server resources changed. Run resources/list to see."));
1627
+ console.log(pc2.yellow("\n \u27F3 Server resources changed. Run resources/list to see."));
1223
1628
  refreshCaches(target).catch(() => {
1224
1629
  });
1225
1630
  } else if (method === "notifications/resources/updated") {
1226
1631
  const uri = notification.params?.uri ?? "unknown";
1227
- console.log(pc.yellow(`
1632
+ console.log(pc2.yellow(`
1228
1633
  \u27F3 Resource updated: ${uri}`));
1229
1634
  } else if (method === "notifications/prompts/list_changed") {
1230
- console.log(pc.yellow("\n \u27F3 Server prompts changed. Run prompts/list to see."));
1635
+ console.log(pc2.yellow("\n \u27F3 Server prompts changed. Run prompts/list to see."));
1231
1636
  refreshCaches(target).catch(() => {
1232
1637
  });
1233
1638
  }
1234
1639
  });
1235
1640
  target.on("sampling_request", async ({ request, respond, reject: rejectFn }) => {
1236
- console.log(pc.magenta("\n \u2554\u2550\u2550 Sampling Request \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
1641
+ console.log(pc2.magenta("\n \u2554\u2550\u2550 Sampling Request \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
1237
1642
  const messages = request?.messages ?? [];
1238
1643
  for (const msg of messages) {
1239
- const role = msg.role === "user" ? pc.blue("user") : pc.magenta("assistant");
1644
+ const role = msg.role === "user" ? pc2.blue("user") : pc2.magenta("assistant");
1240
1645
  const text = msg.content?.text ?? JSON.stringify(msg.content);
1241
- console.log(pc.magenta(` \u2551 ${role}: ${text}`));
1646
+ console.log(pc2.magenta(` \u2551 ${role}: ${text}`));
1242
1647
  }
1243
- console.log(pc.magenta(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
1648
+ console.log(pc2.magenta(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
1244
1649
  if (activeRl) {
1245
1650
  try {
1246
- const answer = await question(activeRl, ` ${pc.bold("Approve? [y/N/text]:")} `);
1651
+ const answer = await question(activeRl, ` ${pc2.bold("Approve? [y/N/text]:")} `);
1247
1652
  const trimmed = answer.trim().toLowerCase();
1248
1653
  if (trimmed === "y" || trimmed === "yes") {
1249
1654
  respond({
@@ -1272,14 +1677,14 @@ async function startRepl(targetCommand, opts) {
1272
1677
  }
1273
1678
  });
1274
1679
  target.on("elicitation_request", async ({ request, respond, reject: rejectFn }) => {
1275
- console.log(pc.cyan("\n \u2554\u2550\u2550 Elicitation Request \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
1276
- console.log(pc.cyan(` \u2551 ${request?.message ?? "Server requests input"}`));
1277
- console.log(pc.cyan(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
1680
+ console.log(pc2.cyan("\n \u2554\u2550\u2550 Elicitation Request \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
1681
+ console.log(pc2.cyan(` \u2551 ${request?.message ?? "Server requests input"}`));
1682
+ console.log(pc2.cyan(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
1278
1683
  if (activeRl) {
1279
1684
  try {
1280
1685
  const answer = await question(
1281
1686
  activeRl,
1282
- ` ${pc.bold("Your response (empty to decline):")} `
1687
+ ` ${pc2.bold("Your response (empty to decline):")} `
1283
1688
  );
1284
1689
  if (answer.trim() === "") {
1285
1690
  respond({ action: "decline" });
@@ -1334,13 +1739,13 @@ async function startRepl(targetCommand, opts) {
1334
1739
  );
1335
1740
  if (!groups.has("All")) {
1336
1741
  for (const [label, members] of groups) {
1337
- console.log(` ${pc.bold(label.padEnd(16))} ${pc.dim(members.join(", "))}`);
1742
+ console.log(` ${pc2.bold(label.padEnd(16))} ${pc2.dim(members.join(", "))}`);
1338
1743
  }
1339
1744
  console.log();
1340
1745
  }
1341
1746
  }
1342
1747
  } catch (err) {
1343
- console.log(pc.yellow(` Warning: Could not list tools: ${err.message}
1748
+ console.log(pc2.yellow(` Warning: Could not list tools: ${err.message}
1344
1749
  `));
1345
1750
  }
1346
1751
  await refreshCaches(target);
@@ -1352,13 +1757,22 @@ async function startRepl(targetCommand, opts) {
1352
1757
  try {
1353
1758
  await handleCommand(trimmed, target, interceptor);
1354
1759
  } catch (err) {
1355
- console.error(pc.red(`\u2717 Error: ${err.message}`));
1356
- console.log(pc.dim("\nShutting down..."));
1760
+ if (err?.message?.includes("-32601") || err?.code === -32601) {
1761
+ let msg = "Server does not support this feature (Method not found)";
1762
+ if (trimmed.startsWith("prompts/")) msg = "This server does not have any prompts.";
1763
+ else if (trimmed.startsWith("resources/"))
1764
+ msg = "This server does not have any resources.";
1765
+ else if (trimmed.startsWith("tools/")) msg = "This server does not have any tools.";
1766
+ console.log(pc2.yellow(` ${msg}`));
1767
+ } else {
1768
+ console.error(pc2.red(`\u2717 Error: ${err.message}`));
1769
+ }
1770
+ console.log(pc2.dim("\nShutting down..."));
1357
1771
  await target.close();
1358
1772
  process.exit(1);
1359
1773
  }
1360
1774
  }
1361
- console.log(pc.dim("\nShutting down..."));
1775
+ console.log(pc2.dim("\nShutting down..."));
1362
1776
  await target.close();
1363
1777
  process.exit(0);
1364
1778
  } else {
@@ -1400,9 +1814,16 @@ function startReadlineLoop(target, interceptor) {
1400
1814
  await handleCommand(trimmed, target, interceptor);
1401
1815
  } catch (err) {
1402
1816
  if (err instanceof AbortFlowError) {
1403
- console.log(pc.yellow(" Aborted."));
1817
+ console.log(pc2.yellow(" Aborted."));
1818
+ } else if (err?.message?.includes("-32601") || err?.code === -32601) {
1819
+ let msg = "Server does not support this feature (Method not found)";
1820
+ if (trimmed.startsWith("prompts/")) msg = "This server does not have any prompts.";
1821
+ else if (trimmed.startsWith("resources/"))
1822
+ msg = "This server does not have any resources.";
1823
+ else if (trimmed.startsWith("tools/")) msg = "This server does not have any tools.";
1824
+ console.log(pc2.yellow(` ${msg}`));
1404
1825
  } else {
1405
- console.error(pc.red(`\u2717 Error: ${err.message}`));
1826
+ console.error(pc2.red(`\u2717 Error: ${err.message}`));
1406
1827
  }
1407
1828
  }
1408
1829
  if (activeRl) {
@@ -1431,7 +1852,7 @@ function startReadlineLoop(target, interceptor) {
1431
1852
  closed = true;
1432
1853
  activeRl = null;
1433
1854
  if (!globalPauseReadlineClose) {
1434
- console.log(pc.dim("\nShutting down..."));
1855
+ console.log(pc2.dim("\nShutting down..."));
1435
1856
  await target.close();
1436
1857
  process.exit(0);
1437
1858
  }
@@ -1448,6 +1869,9 @@ async function handleCommand(input3, target, interceptor) {
1448
1869
  case "help":
1449
1870
  printHelp();
1450
1871
  return;
1872
+ case "?":
1873
+ printShortHelp();
1874
+ return;
1451
1875
  case "explore":
1452
1876
  case "interactive":
1453
1877
  await withSuspendedReadline(target, interceptor, async () => {
@@ -1473,7 +1897,7 @@ async function handleCommand(input3, target, interceptor) {
1473
1897
  await cmdResourcesList(target);
1474
1898
  return;
1475
1899
  case "resources/read":
1476
- await cmdResourcesRead(target, rest);
1900
+ await cmdResourcesRead(target, rest, interceptor);
1477
1901
  return;
1478
1902
  case "resources/templates":
1479
1903
  await cmdResourcesTemplates(target);
@@ -1482,7 +1906,7 @@ async function handleCommand(input3, target, interceptor) {
1482
1906
  await cmdPromptsList(target);
1483
1907
  return;
1484
1908
  case "prompts/get":
1485
- await cmdPromptsGet(target, rest);
1909
+ await cmdPromptsGet(target, rest, interceptor);
1486
1910
  return;
1487
1911
  case "timing":
1488
1912
  cmdTiming();
@@ -1520,27 +1944,30 @@ async function handleCommand(input3, target, interceptor) {
1520
1944
  case "!!":
1521
1945
  case "last":
1522
1946
  if (lastCommand) {
1523
- console.log(pc.dim(` Re-running: ${lastCommand}`));
1947
+ console.log(pc2.dim(` Re-running: ${lastCommand}`));
1524
1948
  await handleCommand(lastCommand, target, interceptor);
1525
1949
  } else {
1526
- console.log(pc.yellow("No previous command to re-run."));
1950
+ console.log(pc2.yellow("No previous command to re-run."));
1527
1951
  }
1528
1952
  return;
1529
1953
  case "status":
1530
1954
  cmdStatus(target);
1531
1955
  return;
1532
1956
  case "exit":
1533
- case "quit":
1534
- process.emit("SIGINT", "SIGINT");
1957
+ case "quit": {
1958
+ console.log(pc2.dim("Shutting down..."));
1959
+ await target.close();
1960
+ process.exit(0);
1535
1961
  return;
1962
+ }
1536
1963
  default: {
1537
- const suggestion = suggestCommand(cmd, KNOWN_COMMANDS);
1964
+ const suggestion = suggestCommand(cmd, getActiveCommands());
1538
1965
  if (suggestion) {
1539
- console.log(pc.yellow(`Unknown command: ${cmd}.`));
1966
+ console.log(pc2.yellow(`Unknown command: ${cmd}.`));
1540
1967
  try {
1541
1968
  await withSuspendedReadline(target, interceptor, async () => {
1542
1969
  const runIt = await confirm({
1543
- message: `Did you mean ${pc.bold(suggestion)}?`,
1970
+ message: `Did you mean ${pc2.bold(suggestion)}?`,
1544
1971
  default: true
1545
1972
  });
1546
1973
  if (runIt) {
@@ -1553,7 +1980,7 @@ async function handleCommand(input3, target, interceptor) {
1553
1980
  throw new AbortFlowError();
1554
1981
  }
1555
1982
  } else {
1556
- console.log(pc.yellow(`Unknown command: ${cmd}. Type ${pc.bold("help")} for usage.`));
1983
+ console.log(pc2.yellow(`Unknown command: ${cmd}. Type ${pc2.bold("help")} for usage.`));
1557
1984
  }
1558
1985
  }
1559
1986
  }
@@ -1561,25 +1988,25 @@ async function handleCommand(input3, target, interceptor) {
1561
1988
  async function cmdToolsList(target) {
1562
1989
  const { tools } = await target.listTools();
1563
1990
  if (tools.length === 0) {
1564
- console.log(pc.dim(" No tools available."));
1991
+ console.log(pc2.dim(" No tools available."));
1565
1992
  return;
1566
1993
  }
1567
1994
  const nameWidth = Math.max(8, ...tools.map((t) => t.name.length));
1568
- console.log(pc.bold(` ${"Name".padEnd(nameWidth)} Description`));
1569
- console.log(pc.dim(` ${"\u2500".repeat(nameWidth)} ${"\u2500".repeat(50)}`));
1995
+ console.log(pc2.bold(` ${"Name".padEnd(nameWidth)} Description`));
1996
+ console.log(pc2.dim(` ${"\u2500".repeat(nameWidth)} ${"\u2500".repeat(50)}`));
1570
1997
  for (const tool of tools) {
1571
- const desc = tool.description ? tool.description.length > 60 ? `${tool.description.slice(0, 57)}...` : tool.description : pc.dim("(no description)");
1572
- console.log(` ${pc.green(tool.name.padEnd(nameWidth))} ${desc}`);
1998
+ const desc = tool.description ? tool.description.length > 60 ? `${tool.description.slice(0, 57)}...` : tool.description : pc2.dim("(no description)");
1999
+ console.log(` ${pc2.green(tool.name.padEnd(nameWidth))} ${desc}`);
1573
2000
  }
1574
- console.log(pc.dim(`
2001
+ console.log(pc2.dim(`
1575
2002
  ${tools.length} tool(s) total.`));
1576
2003
  if (tools.length >= 10) {
1577
2004
  const groups = groupToolsByPrefix(tools.map((t) => t.name));
1578
2005
  if (!groups.has("All")) {
1579
2006
  console.log();
1580
- console.log(pc.bold(" Groups:"));
2007
+ console.log(pc2.bold(" Groups:"));
1581
2008
  for (const [label, members] of groups) {
1582
- console.log(` ${pc.cyan(label.padEnd(16))} ${pc.dim(members.join(", "))}`);
2009
+ console.log(` ${pc2.cyan(label.padEnd(16))} ${pc2.dim(members.join(", "))}`);
1583
2010
  }
1584
2011
  }
1585
2012
  }
@@ -1587,28 +2014,28 @@ async function cmdToolsList(target) {
1587
2014
  async function cmdToolsDescribe(target, rest) {
1588
2015
  const name = rest.trim();
1589
2016
  if (!name) {
1590
- console.log(pc.yellow(" Usage: tools/describe <name>"));
2017
+ console.log(pc2.yellow(" Usage: tools/describe <name>"));
1591
2018
  if (cachedToolNames.length > 0) {
1592
2019
  const preview = cachedToolNames.slice(0, 6);
1593
2020
  const more = cachedToolNames.length > 6 ? `, ... (${cachedToolNames.length} total)` : "";
1594
- console.log(pc.dim(`
2021
+ console.log(pc2.dim(`
1595
2022
  Available tools: ${preview.join(", ")}${more}`));
1596
- console.log(pc.dim(` Type ${pc.bold("tools/list")} for all.`));
2023
+ console.log(pc2.dim(` Type ${pc2.bold("tools/list")} for all.`));
1597
2024
  }
1598
2025
  return;
1599
2026
  }
1600
2027
  const { tools } = await target.listTools();
1601
2028
  const tool = tools.find((t) => t.name === name);
1602
2029
  if (!tool) {
1603
- console.log(pc.red(`Tool "${name}" not found.`));
2030
+ console.log(pc2.red(`Tool "${name}" not found.`));
1604
2031
  const suggestion = suggestCommand(
1605
2032
  name,
1606
2033
  tools.map((t) => t.name)
1607
2034
  );
1608
2035
  if (suggestion) {
1609
- console.log(pc.yellow(`Did you mean ${pc.bold(suggestion)}?`));
2036
+ console.log(pc2.yellow(`Did you mean ${pc2.bold(suggestion)}?`));
1610
2037
  } else {
1611
- console.log(pc.dim(`Available: ${tools.map((t) => t.name).join(", ")}`));
2038
+ console.log(pc2.dim(`Available: ${tools.map((t) => t.name).join(", ")}`));
1612
2039
  }
1613
2040
  return;
1614
2041
  }
@@ -1628,14 +2055,25 @@ async function cmdToolsCall(target, interceptor, rest) {
1628
2055
  const cleanedRest = clearPrevious ? rest.replace(/\s*--clear/, "").trim() : rest;
1629
2056
  const { toolName, jsonArgs, timeoutMs } = parseCallArgs(cleanedRest);
1630
2057
  if (!toolName) {
1631
- console.log(pc.yellow(" Usage: tools/call <name> [json_args] [--timeout <ms>]"));
2058
+ if (!isScriptMode && cachedToolNames.length > 0 && process.stdin.isTTY) {
2059
+ const picked = await withSuspendedReadline(target, interceptor, async () => {
2060
+ const tools = await target.listTools();
2061
+ return pickInteractive(
2062
+ tools.tools.map((t) => ({ name: t.name, description: t.description })),
2063
+ "Pick a tool to call:"
2064
+ );
2065
+ });
2066
+ if (!picked) return;
2067
+ return cmdToolsCall(target, interceptor, picked);
2068
+ }
2069
+ console.log(pc2.yellow(" Usage: tools/call <name> [json_args] [--timeout <ms>]"));
1632
2070
  if (cachedToolNames.length > 0) {
1633
2071
  const preview = cachedToolNames.slice(0, 6);
1634
2072
  const more = cachedToolNames.length > 6 ? `, ... (${cachedToolNames.length} total)` : "";
1635
- console.log(pc.dim(`
2073
+ console.log(pc2.dim(`
1636
2074
  Available tools: ${preview.join(", ")}${more}`));
1637
2075
  console.log(
1638
- pc.dim(` Run without args for ${pc.bold("interactive mode")}: tools/call <name>`)
2076
+ pc2.dim(` Run without args for ${pc2.bold("interactive mode")}: tools/call <name>`)
1639
2077
  );
1640
2078
  }
1641
2079
  return;
@@ -1645,8 +2083,8 @@ async function cmdToolsCall(target, interceptor, rest) {
1645
2083
  try {
1646
2084
  args = JSON.parse(jsonArgs);
1647
2085
  } catch (err) {
1648
- console.error(pc.red(`Invalid JSON: ${err.message}`));
1649
- console.log(pc.dim(` Received: ${jsonArgs}`));
2086
+ console.error(pc2.red(`Invalid JSON: ${err.message}`));
2087
+ console.log(pc2.dim(` Received: ${jsonArgs}`));
1650
2088
  return;
1651
2089
  }
1652
2090
  const { tools } = await target.listTools();
@@ -1656,14 +2094,14 @@ async function cmdToolsCall(target, interceptor, rest) {
1656
2094
  const required = schema.required ?? [];
1657
2095
  const missing = required.filter((r) => !(r in args));
1658
2096
  if (missing.length > 0) {
1659
- console.log(pc.yellow(`
2097
+ console.log(pc2.yellow(`
1660
2098
  Missing required arguments: ${missing.join(", ")}`));
1661
2099
  console.log();
1662
2100
  const scaffolded = scaffoldArgs(schema);
1663
- console.log(pc.dim(" Try:"));
2101
+ console.log(pc2.dim(" Try:"));
1664
2102
  console.log(` tools/call ${toolName} ${scaffolded}`);
1665
2103
  console.log();
1666
- console.log(pc.dim(" Or run without args for interactive mode:"));
2104
+ console.log(pc2.dim(" Or run without args for interactive mode:"));
1667
2105
  console.log(` tools/call ${toolName}`);
1668
2106
  console.log();
1669
2107
  return;
@@ -1681,56 +2119,68 @@ async function cmdToolsCall(target, interceptor, rest) {
1681
2119
  }
1682
2120
  }
1683
2121
  }
1684
- console.log(pc.dim(` Calling ${toolName}...`));
2122
+ console.log(pc2.dim(` Calling ${toolName}...`));
1685
2123
  const startTime = Date.now();
1686
2124
  const result = await interceptor.callTool(target, toolName, args, timeoutMs);
1687
2125
  const elapsed = Date.now() - startTime;
1688
2126
  callHistory.push({ toolName, durationMs: elapsed, timestamp: startTime });
1689
2127
  lastToolArgsMap.set(toolName, { ...args });
1690
- const width = 60;
1691
2128
  const isError = result.isError === true;
1692
- const topText = isError ? "Error" : "Result";
1693
- const resultLabel = isError ? pc.red("Error") : pc.green("Result");
1694
- const topPads = Math.max(0, width - 4 - topText.length);
1695
- console.log(`
1696
- ${pc.dim("\u2500\u2500")} ${resultLabel} ${pc.dim("\u2500".repeat(topPads))}`);
2129
+ console.log();
2130
+ printResultBlock({
2131
+ label: isError ? "Error" : "Result",
2132
+ labelColor: isError ? "red" : "green",
2133
+ elapsed,
2134
+ toolName
2135
+ });
1697
2136
  const content = result.content;
1698
2137
  if (Array.isArray(content)) {
1699
2138
  for (const item of content) {
1700
2139
  if (item.type === "text") {
1701
- console.log(isError ? pc.red(` \u2717 ${item.text}`) : ` ${item.text}`);
2140
+ if (isError) {
2141
+ console.log(pc2.red(` \u2717 ${item.text}`));
2142
+ } else {
2143
+ try {
2144
+ const parsed = JSON.parse(item.text);
2145
+ if (typeof parsed === "object" && parsed !== null) {
2146
+ console.log(formatJson(parsed, 2, true));
2147
+ } else {
2148
+ console.log(pc2.yellow(` ${item.text}`));
2149
+ }
2150
+ } catch {
2151
+ console.log(pc2.yellow(` ${item.text}`));
2152
+ }
2153
+ }
1702
2154
  } else {
1703
- console.log(formatJson(item, 2));
2155
+ console.log(formatJson(item, 2, true));
1704
2156
  }
1705
2157
  }
1706
2158
  } else {
1707
- console.log(formatJson(result, 2));
2159
+ console.log(formatJson(result, 2, true));
1708
2160
  }
1709
2161
  if (isError) {
1710
2162
  console.log(
1711
- pc.yellow(
2163
+ pc2.yellow(
1712
2164
  ` \u{1F4A1} Tip: Check the tool arguments via 'tools/describe ${toolName}'
1713
2165
  or view the raw server stderr above.`
1714
2166
  )
1715
2167
  );
1716
2168
  }
1717
- const elapsedStr = `${elapsed}ms`;
1718
- const bottomPads = Math.max(0, width - 4 - elapsedStr.length);
1719
- console.log(` ${pc.dim("\u2500".repeat(bottomPads))} ${pc.dim(elapsedStr)} ${pc.dim("\u2500\u2500")}`);
2169
+ console.log();
1720
2170
  }
1721
2171
  async function interactiveArgPrompt(target, interceptor, toolName, clearPrevious = false) {
1722
2172
  const { tools } = await target.listTools();
1723
2173
  const tool = tools.find((t) => t.name === toolName);
1724
2174
  if (!tool) {
1725
- console.log(pc.red(`Tool "${toolName}" not found.`));
2175
+ console.log(pc2.red(`Tool "${toolName}" not found.`));
1726
2176
  const suggestion = suggestCommand(
1727
2177
  toolName,
1728
2178
  tools.map((t) => t.name)
1729
2179
  );
1730
2180
  if (suggestion) {
1731
- console.log(pc.yellow(`Did you mean ${pc.bold(suggestion)}?`));
2181
+ console.log(pc2.yellow(`Did you mean ${pc2.bold(suggestion)}?`));
1732
2182
  } else {
1733
- console.log(pc.dim(`Available: ${tools.map((t) => t.name).join(", ")}`));
2183
+ console.log(pc2.dim(`Available: ${tools.map((t) => t.name).join(", ")}`));
1734
2184
  }
1735
2185
  return null;
1736
2186
  }
@@ -1740,9 +2190,9 @@ async function interactiveArgPrompt(target, interceptor, toolName, clearPrevious
1740
2190
  return {};
1741
2191
  }
1742
2192
  if (isScriptMode) {
1743
- console.log(pc.yellow(` Tool "${toolName}" requires arguments.`));
2193
+ console.log(pc2.yellow(` Tool "${toolName}" requires arguments.`));
1744
2194
  const scaffolded = scaffoldArgs(schema);
1745
- console.log(pc.dim(` Usage: tools/call ${toolName} ${scaffolded}`));
2195
+ console.log(pc2.dim(` Usage: tools/call ${toolName} ${scaffolded}`));
1746
2196
  return null;
1747
2197
  }
1748
2198
  const required = schema.required ?? [];
@@ -1751,10 +2201,10 @@ async function interactiveArgPrompt(target, interceptor, toolName, clearPrevious
1751
2201
  const optionalProps = allProps.filter(([name]) => !required.includes(name));
1752
2202
  const previousArgs = clearPrevious ? void 0 : lastToolArgsMap.get(toolName);
1753
2203
  console.log();
1754
- console.log(` ${pc.bold(tool.name)}${tool.description ? pc.dim(` \u2014 ${tool.description}`) : ""}`);
2204
+ console.log(` ${pc2.bold(tool.name)}${tool.description ? pc2.dim(` \u2014 ${tool.description}`) : ""}`);
1755
2205
  if (previousArgs) {
1756
- console.log(pc.dim(` Previous: ${JSON.stringify(previousArgs)}`));
1757
- console.log(pc.dim(" Press Enter to reuse values, or type to override."));
2206
+ console.log(pc2.dim(` Previous: ${JSON.stringify(previousArgs)}`));
2207
+ console.log(pc2.dim(" Press Enter to reuse values, or type to override."));
1758
2208
  }
1759
2209
  console.log();
1760
2210
  const collectedArgs = {};
@@ -1772,9 +2222,21 @@ async function interactiveArgPrompt(target, interceptor, toolName, clearPrevious
1772
2222
  const typeStr = prop.type ?? "any";
1773
2223
  const desc = prop.description ?? "";
1774
2224
  const prevVal = previousArgs?.[name];
1775
- const label = desc ? `${name} ${pc.dim(`(${typeStr})`)} ${pc.dim(desc)}` : `${name} ${pc.dim(`(${typeStr})`)}`;
1776
- const answerStr = await input(
1777
- { message: label, default: prevVal !== void 0 ? String(prevVal) : void 0 },
2225
+ const label = desc ? `${name} ${pc2.dim(`(${typeStr})`)} ${pc2.dim(desc)}` : `${name} ${pc2.dim(`(${typeStr})`)}`;
2226
+ const answerStr = await input2(
2227
+ {
2228
+ message: label,
2229
+ default: prevVal !== void 0 ? String(prevVal) : void 0,
2230
+ validate: (val) => {
2231
+ if (!val && typeStr !== "string") {
2232
+ return "This argument is required and cannot be empty.";
2233
+ }
2234
+ if (val && (typeStr === "number" || typeStr === "integer") && Number.isNaN(Number(val))) {
2235
+ return "Must be a valid number.";
2236
+ }
2237
+ return true;
2238
+ }
2239
+ },
1778
2240
  { signal: abortController.signal }
1779
2241
  );
1780
2242
  collectedArgs[name] = coerceValue(answerStr, typeStr);
@@ -1802,16 +2264,25 @@ async function interactiveArgPrompt(target, interceptor, toolName, clearPrevious
1802
2264
  const typeStr = prop.type ?? "any";
1803
2265
  const desc = prop.description ?? "";
1804
2266
  const prevVal = previousArgs?.[name];
1805
- const label = desc ? `${name} ${pc.dim(`(${typeStr})`)} ${pc.dim(desc)}` : `${name} ${pc.dim(`(${typeStr})`)}`;
1806
- const answerStr = await input(
1807
- { message: label, default: prevVal !== void 0 ? String(prevVal) : void 0 },
2267
+ const label = desc ? `${name} ${pc2.dim(`(${typeStr})`)} ${pc2.dim(desc)}` : `${name} ${pc2.dim(`(${typeStr})`)}`;
2268
+ const answerStr = await input2(
2269
+ {
2270
+ message: label,
2271
+ default: prevVal !== void 0 ? String(prevVal) : void 0,
2272
+ validate: (val) => {
2273
+ if (val && (typeStr === "number" || typeStr === "integer") && Number.isNaN(Number(val))) {
2274
+ return "Must be a valid number.";
2275
+ }
2276
+ return true;
2277
+ }
2278
+ },
1808
2279
  { signal: abortController.signal }
1809
2280
  );
1810
2281
  collectedArgs[name] = coerceValue(answerStr, typeStr);
1811
2282
  }
1812
2283
  }
1813
2284
  console.log();
1814
- console.log(pc.dim(` ${JSON.stringify(collectedArgs)}`));
2285
+ console.log(formatJson(collectedArgs, 2, true));
1815
2286
  const shouldExecute = await confirm(
1816
2287
  { message: "Execute?", default: true },
1817
2288
  { signal: abortController.signal }
@@ -1832,14 +2303,14 @@ function printJsonTemplate(filled, allProps, currentProp) {
1832
2303
  const parts = [];
1833
2304
  for (const [name] of allProps) {
1834
2305
  if (name in filled) {
1835
- parts.push(`"${name}": ${pc.green(JSON.stringify(filled[name]))}`);
2306
+ parts.push(`"${name}": ${pc2.green(JSON.stringify(filled[name]))}`);
1836
2307
  } else if (name === currentProp) {
1837
- parts.push(`"${name}": ${pc.yellow("\u2592")}`);
2308
+ parts.push(`"${name}": ${pc2.yellow("\u2592")}`);
1838
2309
  } else {
1839
- parts.push(`"${name}": ${pc.dim("\u2592")}`);
2310
+ parts.push(`"${name}": ${pc2.dim("\u2592")}`);
1840
2311
  }
1841
2312
  }
1842
- console.log(pc.dim(" { ") + parts.join(pc.dim(", ")) + pc.dim(" }"));
2313
+ console.log(pc2.dim(" { ") + parts.join(pc2.dim(", ")) + pc2.dim(" }"));
1843
2314
  console.log();
1844
2315
  }
1845
2316
  function coerceValue(input3, type) {
@@ -1888,11 +2359,11 @@ function question(rl, prompt) {
1888
2359
  async function cmdToolsScaffold(target, rest) {
1889
2360
  const name = rest.trim();
1890
2361
  if (!name) {
1891
- console.log(pc.yellow(" Usage: tools/scaffold <name>"));
2362
+ console.log(pc2.yellow(" Usage: tools/scaffold <name>"));
1892
2363
  if (cachedToolNames.length > 0) {
1893
2364
  const preview = cachedToolNames.slice(0, 6);
1894
2365
  const more = cachedToolNames.length > 6 ? `, ... (${cachedToolNames.length} total)` : "";
1895
- console.log(pc.dim(`
2366
+ console.log(pc2.dim(`
1896
2367
  Available tools: ${preview.join(", ")}${more}`));
1897
2368
  }
1898
2369
  return;
@@ -1900,107 +2371,147 @@ async function cmdToolsScaffold(target, rest) {
1900
2371
  const { tools } = await target.listTools();
1901
2372
  const tool = tools.find((t) => t.name === name);
1902
2373
  if (!tool) {
1903
- console.log(pc.red(`Tool "${name}" not found.`));
2374
+ console.log(pc2.red(`Tool "${name}" not found.`));
1904
2375
  const suggestion = suggestCommand(
1905
2376
  name,
1906
2377
  tools.map((t) => t.name)
1907
2378
  );
1908
2379
  if (suggestion) {
1909
- console.log(pc.yellow(`Did you mean ${pc.bold(suggestion)}?`));
2380
+ console.log(pc2.yellow(`Did you mean ${pc2.bold(suggestion)}?`));
1910
2381
  } else {
1911
- console.log(pc.dim(`Available: ${tools.map((t) => t.name).join(", ")}`));
2382
+ console.log(pc2.dim(`Available: ${tools.map((t) => t.name).join(", ")}`));
1912
2383
  }
1913
2384
  return;
1914
2385
  }
1915
2386
  const scaffolded = scaffoldArgs(tool.inputSchema);
1916
- console.log(pc.cyan("\n Ready-to-paste command:"));
2387
+ console.log(pc2.cyan("\n Ready-to-paste command:"));
1917
2388
  console.log(` tools/call ${name} ${scaffolded}
1918
2389
  `);
1919
2390
  }
1920
2391
  async function cmdResourcesList(target) {
1921
2392
  const { resources } = await target.listResources();
1922
2393
  if (resources.length === 0) {
1923
- console.log(pc.dim(" No resources available."));
2394
+ console.log(pc2.dim(" No resources available."));
1924
2395
  return;
1925
2396
  }
1926
2397
  const uriWidth = Math.max(6, ...resources.map((r) => r.uri.length));
1927
- console.log(pc.bold(` ${"URI".padEnd(uriWidth)} Name`));
1928
- console.log(pc.dim(` ${"\u2500".repeat(uriWidth)} ${"\u2500".repeat(40)}`));
2398
+ console.log(pc2.bold(` ${"URI".padEnd(uriWidth)} Name`));
2399
+ console.log(pc2.dim(` ${"\u2500".repeat(uriWidth)} ${"\u2500".repeat(40)}`));
1929
2400
  for (const r of resources) {
1930
2401
  const uri = r.uri;
1931
- const name = r.name ?? pc.dim("(unnamed)");
1932
- console.log(` ${pc.green(uri.padEnd(uriWidth))} ${name}`);
2402
+ const name = r.name ?? pc2.dim("(unnamed)");
2403
+ console.log(` ${pc2.green(uri.padEnd(uriWidth))} ${name}`);
1933
2404
  }
1934
- console.log(pc.dim(`
2405
+ console.log(pc2.dim(`
1935
2406
  ${resources.length} resource(s) total.`));
1936
2407
  }
1937
- async function cmdResourcesRead(target, rest) {
2408
+ async function cmdResourcesRead(target, rest, interceptor) {
1938
2409
  const uri = rest.trim();
1939
2410
  if (!uri) {
1940
- console.log(pc.yellow(" Usage: resources/read <uri>"));
2411
+ if (!isScriptMode && cachedResourceUris.length > 0 && process.stdin.isTTY && interceptor) {
2412
+ const picked = await withSuspendedReadline(target, interceptor, async () => {
2413
+ const { resources } = await target.listResources();
2414
+ return pickInteractive(
2415
+ resources.map((r) => ({
2416
+ name: r.uri,
2417
+ description: r.description || r.name
2418
+ })),
2419
+ "Pick a resource to read:"
2420
+ );
2421
+ });
2422
+ if (!picked) return;
2423
+ return cmdResourcesRead(target, picked, interceptor);
2424
+ }
2425
+ console.log(pc2.yellow(" Usage: resources/read <uri>"));
1941
2426
  if (cachedResourceUris.length > 0) {
1942
2427
  const preview = cachedResourceUris.slice(0, 5);
1943
2428
  const more = cachedResourceUris.length > 5 ? `, ... (${cachedResourceUris.length} total)` : "";
1944
- console.log(pc.dim(`
2429
+ console.log(pc2.dim(`
1945
2430
  Available resources: ${preview.join(", ")}${more}`));
1946
2431
  }
1947
2432
  return;
1948
2433
  }
2434
+ const startTime = Date.now();
1949
2435
  const result = await target.readResource({ uri });
2436
+ const elapsed = Date.now() - startTime;
2437
+ console.log();
2438
+ printResultBlock({ label: "Resource", labelColor: "cyan", elapsed, detail: uri });
1950
2439
  for (const item of result.contents) {
1951
2440
  if (item.text !== void 0) {
1952
- console.log(item.text);
2441
+ const text = item.text;
2442
+ try {
2443
+ const parsed = JSON.parse(text);
2444
+ if (typeof parsed === "object" && parsed !== null) {
2445
+ console.log(formatJson(parsed, 2, true));
2446
+ } else {
2447
+ console.log(pc2.yellow(` ${text}`));
2448
+ }
2449
+ } catch {
2450
+ console.log(pc2.yellow(` ${text}`));
2451
+ }
1953
2452
  } else if (item.blob !== void 0) {
1954
2453
  const mimeType = item.mimeType ?? "application/octet-stream";
1955
2454
  const sizeBytes = Buffer.from(item.blob, "base64").length;
1956
- console.log(pc.dim(`[Binary: ${mimeType}, ${sizeBytes} bytes]`));
2455
+ console.log(pc2.dim(` [Binary: ${mimeType}, ${sizeBytes} bytes]`));
1957
2456
  } else {
1958
- console.log(formatJson(item, 2));
2457
+ console.log(formatJson(item, 2, true));
1959
2458
  }
1960
2459
  }
2460
+ console.log();
1961
2461
  }
1962
2462
  async function cmdResourcesTemplates(target) {
1963
2463
  const { resourceTemplates } = await target.listResourceTemplates();
1964
2464
  if (resourceTemplates.length === 0) {
1965
- console.log(pc.dim(" No resource templates available."));
2465
+ console.log(pc2.dim(" No resource templates available."));
1966
2466
  return;
1967
2467
  }
1968
2468
  const uriWidth = Math.max(
1969
2469
  12,
1970
2470
  ...resourceTemplates.map((t) => t.uriTemplate.length)
1971
2471
  );
1972
- console.log(pc.bold(` ${"URI Template".padEnd(uriWidth)} Name`));
1973
- console.log(pc.dim(` ${"\u2500".repeat(uriWidth)} ${"\u2500".repeat(40)}`));
2472
+ console.log(pc2.bold(` ${"URI Template".padEnd(uriWidth)} Name`));
2473
+ console.log(pc2.dim(` ${"\u2500".repeat(uriWidth)} ${"\u2500".repeat(40)}`));
1974
2474
  for (const t of resourceTemplates) {
1975
2475
  const uriTemplate = t.uriTemplate;
1976
- const name = t.name ?? pc.dim("(unnamed)");
1977
- console.log(` ${pc.green(uriTemplate.padEnd(uriWidth))} ${name}`);
2476
+ const name = t.name ?? pc2.dim("(unnamed)");
2477
+ console.log(` ${pc2.green(uriTemplate.padEnd(uriWidth))} ${name}`);
1978
2478
  }
1979
- console.log(pc.dim(`
2479
+ console.log(pc2.dim(`
1980
2480
  ${resourceTemplates.length} template(s) total.`));
1981
2481
  }
1982
2482
  async function cmdPromptsList(target) {
1983
2483
  const { prompts } = await target.listPrompts();
1984
2484
  if (prompts.length === 0) {
1985
- console.log(pc.dim(" No prompts available."));
2485
+ console.log(pc2.dim(" No prompts available."));
1986
2486
  return;
1987
2487
  }
1988
2488
  const nameWidth = Math.max(8, ...prompts.map((p) => p.name.length));
1989
- console.log(pc.bold(` ${"Name".padEnd(nameWidth)} Description`));
1990
- console.log(pc.dim(` ${"\u2500".repeat(nameWidth)} ${"\u2500".repeat(50)}`));
2489
+ console.log(pc2.bold(` ${"Name".padEnd(nameWidth)} Description`));
2490
+ console.log(pc2.dim(` ${"\u2500".repeat(nameWidth)} ${"\u2500".repeat(50)}`));
1991
2491
  for (const p of prompts) {
1992
- const desc = p.description ? p.description.length > 60 ? `${p.description.slice(0, 57)}...` : p.description : pc.dim("(no description)");
1993
- console.log(` ${pc.green(p.name.padEnd(nameWidth))} ${desc}`);
2492
+ const desc = p.description ? p.description.length > 60 ? `${p.description.slice(0, 57)}...` : p.description : pc2.dim("(no description)");
2493
+ console.log(` ${pc2.green(p.name.padEnd(nameWidth))} ${desc}`);
1994
2494
  }
1995
- console.log(pc.dim(`
2495
+ console.log(pc2.dim(`
1996
2496
  ${prompts.length} prompt(s) total.`));
1997
2497
  }
1998
- async function cmdPromptsGet(target, rest) {
2498
+ async function cmdPromptsGet(target, rest, interceptor) {
1999
2499
  const { toolName: promptName, jsonArgs } = parseCallArgs(rest);
2000
2500
  if (!promptName) {
2001
- console.log(pc.yellow(" Usage: prompts/get <name> [json_args]"));
2501
+ if (!isScriptMode && cachedPromptNames.length > 0 && process.stdin.isTTY && interceptor) {
2502
+ const picked = await withSuspendedReadline(target, interceptor, async () => {
2503
+ const { prompts } = await target.listPrompts();
2504
+ return pickInteractive(
2505
+ prompts.map((p) => ({ name: p.name, description: p.description })),
2506
+ "Pick a prompt to get:"
2507
+ );
2508
+ });
2509
+ if (!picked) return;
2510
+ return cmdPromptsGet(target, picked);
2511
+ }
2512
+ console.log(pc2.yellow(" Usage: prompts/get <name> [json_args]"));
2002
2513
  if (cachedPromptNames.length > 0) {
2003
- console.log(pc.dim(`
2514
+ console.log(pc2.dim(`
2004
2515
  Available prompts: ${cachedPromptNames.join(", ")}`));
2005
2516
  }
2006
2517
  return;
@@ -2010,48 +2521,71 @@ async function cmdPromptsGet(target, rest) {
2010
2521
  try {
2011
2522
  promptArgs = JSON.parse(jsonArgs);
2012
2523
  } catch (err) {
2013
- console.error(pc.red(`Invalid JSON: ${err.message}`));
2014
- console.log(pc.dim(` Received: ${jsonArgs}`));
2524
+ console.error(pc2.red(`Invalid JSON: ${err.message}`));
2525
+ console.log(pc2.dim(` Received: ${jsonArgs}`));
2015
2526
  return;
2016
2527
  }
2017
2528
  }
2529
+ const startTime = Date.now();
2018
2530
  const result = await target.getPrompt({ name: promptName, arguments: promptArgs });
2531
+ const elapsed = Date.now() - startTime;
2532
+ if (result.messages.length === 0) {
2533
+ console.log(pc2.dim(" No messages returned."));
2534
+ return;
2535
+ }
2536
+ console.log();
2537
+ printResultBlock({ label: "Prompt", labelColor: "blue", elapsed, detail: promptName });
2019
2538
  for (const msg of result.messages) {
2020
- const role = msg.role === "user" ? pc.blue("user") : pc.magenta("assistant");
2539
+ const role = msg.role === "user" ? pc2.blue("user") : pc2.magenta("assistant");
2021
2540
  const text = msg.content.text ?? JSON.stringify(msg.content);
2022
- console.log(` ${pc.bold(role)}: ${text}`);
2541
+ try {
2542
+ const parsed = JSON.parse(text);
2543
+ if (typeof parsed === "object" && parsed !== null) {
2544
+ console.log(` ${pc2.bold(role)}:`);
2545
+ console.log(formatJson(parsed, 4, true));
2546
+ } else {
2547
+ console.log(` ${pc2.bold(role)}: ${pc2.yellow(text)}`);
2548
+ }
2549
+ } catch {
2550
+ console.log(` ${pc2.bold(role)}: ${pc2.yellow(text)}`);
2551
+ }
2023
2552
  }
2553
+ console.log();
2024
2554
  }
2025
2555
  async function cmdPing(target) {
2026
2556
  try {
2027
2557
  const elapsed = await target.ping();
2028
- console.log(pc.green(` \u2713 Pong! Round-trip: ${elapsed}ms`));
2558
+ console.log();
2559
+ console.log(pc2.green(` \u2713 Pong! Round-trip: ${elapsed}ms`));
2560
+ console.log();
2029
2561
  } catch (err) {
2030
- console.error(pc.red(` \u2717 Ping failed: ${err.message}`));
2562
+ console.log();
2563
+ console.error(pc2.red(` \u2717 Ping failed: ${err.message}`));
2564
+ console.log();
2031
2565
  }
2032
2566
  }
2033
2567
  async function cmdLogLevel(target, rest) {
2034
2568
  const level = rest.trim().toLowerCase();
2035
2569
  if (!level) {
2036
- console.log(pc.yellow(" Usage: log-level <level>"));
2037
- console.log(pc.dim(` Valid levels: ${LOG_LEVELS.join(", ")}`));
2570
+ console.log(pc2.yellow(" Usage: log-level <level>"));
2571
+ console.log(pc2.dim(` Valid levels: ${LOG_LEVELS.join(", ")}`));
2038
2572
  return;
2039
2573
  }
2040
2574
  if (!LOG_LEVELS.includes(level)) {
2041
2575
  const suggestion = suggestCommand(level, [...LOG_LEVELS]);
2042
2576
  const hint = suggestion ? ` Did you mean "${suggestion}"?` : "";
2043
- console.log(pc.red(` Unknown log level: "${level}".${hint}`));
2044
- console.log(pc.dim(` Valid levels: ${LOG_LEVELS.join(", ")}`));
2577
+ console.log(pc2.red(` Unknown log level: "${level}".${hint}`));
2578
+ console.log(pc2.dim(` Valid levels: ${LOG_LEVELS.join(", ")}`));
2045
2579
  return;
2046
2580
  }
2047
2581
  try {
2048
2582
  await target.setLoggingLevel(level);
2049
- console.log(pc.green(` \u2713 Logging level set to: ${level}`));
2583
+ console.log(pc2.green(` \u2713 Logging level set to: ${level}`));
2050
2584
  } catch (err) {
2051
- console.error(pc.red(` \u2717 Failed to set log level: ${err.message}`));
2585
+ console.error(pc2.red(` \u2717 Failed to set log level: ${err.message}`));
2052
2586
  const caps = target.getServerCapabilities();
2053
2587
  if (!caps?.logging) {
2054
- console.log(pc.dim(" The server does not advertise logging support."));
2588
+ console.log(pc2.dim(" The server does not advertise logging support."));
2055
2589
  }
2056
2590
  }
2057
2591
  }
@@ -2059,28 +2593,28 @@ function cmdHistory(target, rest) {
2059
2593
  const arg = rest.trim();
2060
2594
  if (arg === "clear") {
2061
2595
  target.clearHistory();
2062
- console.log(pc.dim(" History cleared."));
2596
+ console.log(pc2.dim(" History cleared."));
2063
2597
  return;
2064
2598
  }
2065
2599
  const count = arg ? Number.parseInt(arg, 10) : 20;
2066
2600
  const records = target.getHistory(Number.isNaN(count) ? 20 : count);
2067
2601
  if (records.length === 0) {
2068
- console.log(pc.dim(" No request history yet."));
2602
+ console.log(pc2.dim(" No request history yet."));
2069
2603
  return;
2070
2604
  }
2071
- console.log(pc.bold("\n Request History"));
2072
- console.log(pc.dim(` ${"\u2500".repeat(70)}`));
2605
+ console.log(pc2.bold("\n Request History"));
2606
+ console.log(pc2.dim(` ${"\u2500".repeat(70)}`));
2073
2607
  for (const rec of records) {
2074
2608
  const time = new Date(rec.timestamp).toLocaleTimeString();
2075
2609
  const dur = `${rec.durationMs}ms`;
2076
- const hasError = rec.error ? pc.red(" \u2717") : "";
2610
+ const hasError = rec.error ? pc2.red(" \u2717") : "";
2077
2611
  console.log(
2078
- ` ${pc.dim(`#${rec.id}`)} ${pc.dim(time)} ${pc.green(rec.method.padEnd(30))} ${pc.cyan(dur.padStart(8))}${hasError}`
2612
+ ` ${pc2.dim(`#${rec.id}`)} ${pc2.dim(time)} ${pc2.green(rec.method.padEnd(30))} ${pc2.cyan(dur.padStart(8))}${hasError}`
2079
2613
  );
2080
2614
  }
2081
2615
  const total = target.getHistory().length;
2082
2616
  console.log(
2083
- pc.dim(`
2617
+ pc2.dim(`
2084
2618
  Showing ${records.length} of ${total} total. Use "history clear" to reset.`)
2085
2619
  );
2086
2620
  console.log();
@@ -2089,26 +2623,26 @@ function cmdNotifications(target, rest) {
2089
2623
  const arg = rest.trim();
2090
2624
  if (arg === "clear") {
2091
2625
  target.clearNotifications();
2092
- console.log(pc.dim(" Notifications cleared."));
2626
+ console.log(pc2.dim(" Notifications cleared."));
2093
2627
  return;
2094
2628
  }
2095
2629
  const count = arg ? Number.parseInt(arg, 10) : 20;
2096
2630
  const records = target.getNotifications(Number.isNaN(count) ? 20 : count);
2097
2631
  if (records.length === 0) {
2098
- console.log(pc.dim(" No notifications received yet."));
2099
- console.log(pc.dim(" Notifications appear inline as they arrive from the server."));
2632
+ console.log(pc2.dim(" No notifications received yet."));
2633
+ console.log(pc2.dim(" Notifications appear inline as they arrive from the server."));
2100
2634
  return;
2101
2635
  }
2102
- console.log(pc.bold("\n Server Notifications"));
2103
- console.log(pc.dim(` ${"\u2500".repeat(70)}`));
2636
+ console.log(pc2.bold("\n Server Notifications"));
2637
+ console.log(pc2.dim(` ${"\u2500".repeat(70)}`));
2104
2638
  for (const n of records) {
2105
2639
  const time = new Date(n.timestamp).toLocaleTimeString();
2106
- const params = n.params ? ` ${pc.dim(JSON.stringify(n.params))}` : "";
2107
- console.log(` ${pc.dim(time)} ${pc.yellow(n.method)}${params}`);
2640
+ const params = n.params ? ` ${pc2.dim(JSON.stringify(n.params))}` : "";
2641
+ console.log(` ${pc2.dim(time)} ${pc2.yellow(n.method)}${params}`);
2108
2642
  }
2109
2643
  const total = target.getNotifications().length;
2110
2644
  console.log(
2111
- pc.dim(`
2645
+ pc2.dim(`
2112
2646
  Showing ${records.length} of ${total} total. Use "notifications clear" to reset.`)
2113
2647
  );
2114
2648
  console.log();
@@ -2116,44 +2650,44 @@ function cmdNotifications(target, rest) {
2116
2650
  async function cmdResourcesSubscribe(target, rest) {
2117
2651
  const uri = rest.trim();
2118
2652
  if (!uri) {
2119
- console.log(pc.yellow(" Usage: resources/subscribe <uri>"));
2653
+ console.log(pc2.yellow(" Usage: resources/subscribe <uri>"));
2120
2654
  if (cachedResourceUris.length > 0) {
2121
- console.log(pc.dim(` Available: ${cachedResourceUris.join(", ")}`));
2655
+ console.log(pc2.dim(` Available: ${cachedResourceUris.join(", ")}`));
2122
2656
  }
2123
2657
  return;
2124
2658
  }
2125
2659
  try {
2126
2660
  await target.subscribeResource({ uri });
2127
- console.log(pc.green(` \u2713 Subscribed to: ${uri}`));
2128
- console.log(pc.dim(" You'll see notifications when this resource changes."));
2661
+ console.log(pc2.green(` \u2713 Subscribed to: ${uri}`));
2662
+ console.log(pc2.dim(" You'll see notifications when this resource changes."));
2129
2663
  } catch (err) {
2130
- console.error(pc.red(` \u2717 Subscribe failed: ${err.message}`));
2664
+ console.error(pc2.red(` \u2717 Subscribe failed: ${err.message}`));
2131
2665
  }
2132
2666
  }
2133
2667
  async function cmdResourcesUnsubscribe(target, rest) {
2134
2668
  const uri = rest.trim();
2135
2669
  if (!uri) {
2136
- console.log(pc.yellow(" Usage: resources/unsubscribe <uri>"));
2670
+ console.log(pc2.yellow(" Usage: resources/unsubscribe <uri>"));
2137
2671
  return;
2138
2672
  }
2139
2673
  try {
2140
2674
  await target.unsubscribeResource({ uri });
2141
- console.log(pc.green(` \u2713 Unsubscribed from: ${uri}`));
2675
+ console.log(pc2.green(` \u2713 Unsubscribed from: ${uri}`));
2142
2676
  } catch (err) {
2143
- console.error(pc.red(` \u2717 Unsubscribe failed: ${err.message}`));
2677
+ console.error(pc2.red(` \u2717 Unsubscribe failed: ${err.message}`));
2144
2678
  }
2145
2679
  }
2146
2680
  function cmdRootsList(target) {
2147
2681
  const roots = target.getRoots();
2148
2682
  if (roots.length === 0) {
2149
- console.log(pc.dim(" No roots configured."));
2150
- console.log(pc.dim(" Use roots/add <uri> [name] to add one."));
2683
+ console.log(pc2.dim(" No roots configured."));
2684
+ console.log(pc2.dim(" Use roots/add <uri> [name] to add one."));
2151
2685
  return;
2152
2686
  }
2153
- console.log(pc.bold("\n Client Roots"));
2687
+ console.log(pc2.bold("\n Client Roots"));
2154
2688
  for (const r of roots) {
2155
2689
  const name = r.name ? ` (${r.name})` : "";
2156
- console.log(` ${pc.green(r.uri)}${pc.dim(name)}`);
2690
+ console.log(` ${pc2.green(r.uri)}${pc2.dim(name)}`);
2157
2691
  }
2158
2692
  console.log();
2159
2693
  }
@@ -2162,53 +2696,53 @@ async function cmdRootsAdd(target, rest) {
2162
2696
  const uri = parts[0];
2163
2697
  const name = parts.slice(1).join(" ") || void 0;
2164
2698
  if (!uri) {
2165
- console.log(pc.yellow(" Usage: roots/add <uri> [name]"));
2166
- console.log(pc.dim(' Example: roots/add file:///Users/me/project "My Project"'));
2699
+ console.log(pc2.yellow(" Usage: roots/add <uri> [name]"));
2700
+ console.log(pc2.dim(' Example: roots/add file:///Users/me/project "My Project"'));
2167
2701
  return;
2168
2702
  }
2169
2703
  await target.addRoot({ uri, name });
2170
- console.log(pc.green(` \u2713 Root added: ${uri}`));
2171
- console.log(pc.dim(" Server has been notified of the change."));
2704
+ console.log(pc2.green(` \u2713 Root added: ${uri}`));
2705
+ console.log(pc2.dim(" Server has been notified of the change."));
2172
2706
  }
2173
2707
  async function cmdRootsRemove(target, rest) {
2174
2708
  const uri = rest.trim();
2175
2709
  if (!uri) {
2176
- console.log(pc.yellow(" Usage: roots/remove <uri>"));
2710
+ console.log(pc2.yellow(" Usage: roots/remove <uri>"));
2177
2711
  const roots = target.getRoots();
2178
2712
  if (roots.length > 0) {
2179
- console.log(pc.dim(` Current roots: ${roots.map((r) => r.uri).join(", ")}`));
2713
+ console.log(pc2.dim(` Current roots: ${roots.map((r) => r.uri).join(", ")}`));
2180
2714
  }
2181
2715
  return;
2182
2716
  }
2183
2717
  const removed = await target.removeRoot(uri);
2184
2718
  if (removed) {
2185
- console.log(pc.green(` \u2713 Root removed: ${uri}`));
2719
+ console.log(pc2.green(` \u2713 Root removed: ${uri}`));
2186
2720
  } else {
2187
- console.log(pc.yellow(` Root not found: ${uri}`));
2721
+ console.log(pc2.yellow(` Root not found: ${uri}`));
2188
2722
  }
2189
2723
  }
2190
2724
  async function cmdReconnect(target) {
2191
- console.log(pc.cyan("\u27F3 Disconnecting..."));
2725
+ console.log(pc2.cyan("\u27F3 Disconnecting..."));
2192
2726
  await target.close();
2193
2727
  await new Promise((resolve) => setTimeout(resolve, 200));
2194
- console.log(pc.cyan("\u27F3 Reconnecting..."));
2728
+ console.log(pc2.cyan("\u27F3 Reconnecting..."));
2195
2729
  const { command, args } = target.getStatus();
2196
- console.log(pc.dim(` Command: ${command} ${args.join(" ")}`));
2730
+ console.log(pc2.dim(` Command: ${command} ${args.join(" ")}`));
2197
2731
  try {
2198
2732
  await target.connect();
2199
2733
  const s = target.getStatus();
2200
- console.log(pc.green(`\u2713 Reconnected (PID: ${s.pid})`));
2734
+ console.log(pc2.green(`\u2713 Reconnected (PID: ${s.pid})`));
2201
2735
  const { tools } = await target.listTools();
2202
- console.log(pc.cyan(` ${tools.length} tool(s) available.
2736
+ console.log(pc2.cyan(` ${tools.length} tool(s) available.
2203
2737
  `));
2204
2738
  await refreshCaches(target);
2205
2739
  } catch (err) {
2206
- console.error(pc.red(`\u2717 Failed to reconnect: ${err.message}`));
2740
+ console.error(pc2.red(`\u2717 Failed to reconnect: ${err.message}`));
2207
2741
  }
2208
2742
  }
2209
2743
  function cmdTiming() {
2210
2744
  if (callHistory.length === 0) {
2211
- console.log(pc.dim(" No tool calls recorded yet."));
2745
+ console.log(pc2.dim(" No tool calls recorded yet."));
2212
2746
  return;
2213
2747
  }
2214
2748
  const groups = /* @__PURE__ */ new Map();
@@ -2218,8 +2752,8 @@ function cmdTiming() {
2218
2752
  groups.set(record.toolName, list);
2219
2753
  }
2220
2754
  const nameWidth = Math.max(8, ...[...groups.keys()].map((n) => n.length));
2221
- console.log(pc.bold("\n Tool Call Performance"));
2222
- console.log(pc.dim(` ${"\u2500".repeat(nameWidth + 50)}`));
2755
+ console.log(pc2.bold("\n Tool Call Performance"));
2756
+ console.log(pc2.dim(` ${"\u2500".repeat(nameWidth + 50)}`));
2223
2757
  let totalCalls = 0;
2224
2758
  let totalMs = 0;
2225
2759
  let slowestName = "";
@@ -2237,12 +2771,12 @@ function cmdTiming() {
2237
2771
  slowestName = name;
2238
2772
  }
2239
2773
  console.log(
2240
- ` ${pc.green(name.padEnd(nameWidth))} \xD7 ${count} avg: ${avg}ms p95: ${p95}ms max: ${max}ms`
2774
+ ` ${pc2.green(name.padEnd(nameWidth))} \xD7 ${count} avg: ${avg}ms p95: ${p95}ms max: ${max}ms`
2241
2775
  );
2242
2776
  }
2243
2777
  const overallAvg = Math.round(totalMs / totalCalls);
2244
2778
  console.log(
2245
- pc.dim(
2779
+ pc2.dim(
2246
2780
  `
2247
2781
  Total: ${totalCalls} call(s), avg ${overallAvg}ms, slowest: ${slowestName} (${slowestMs}ms)`
2248
2782
  )
@@ -2253,79 +2787,122 @@ function cmdStatus(target) {
2253
2787
  const s = target.getStatus();
2254
2788
  const uptimeStr = s.uptime >= 60 ? `${Math.floor(s.uptime / 60)}m ${(s.uptime % 60).toFixed(0)}s` : `${s.uptime.toFixed(1)}s`;
2255
2789
  const lastRespStr = s.lastResponseTime ? `${((Date.now() - s.lastResponseTime) / 1e3).toFixed(1)}s ago` : "never";
2256
- console.log(pc.bold("\n Target Server Status"));
2257
- console.log(` ${pc.dim("Connected:")} ${s.connected ? pc.green("yes") : pc.red("no")}`);
2258
- console.log(` ${pc.dim("PID:")} ${s.pid ?? "N/A"}`);
2259
- console.log(` ${pc.dim("Uptime:")} ${uptimeStr}`);
2260
- console.log(` ${pc.dim("Last response:")} ${lastRespStr}`);
2261
- console.log(` ${pc.dim("Stderr lines:")} ${s.stderrLineCount.toLocaleString()}`);
2262
- console.log(` ${pc.dim("Reconnects:")} ${s.reconnectAttempts}/${s.maxReconnectAttempts}`);
2263
- console.log(` ${pc.dim("Command:")} ${s.command} ${s.args.join(" ")}`);
2790
+ console.log(pc2.bold("\n Target Server Status"));
2791
+ console.log(` ${pc2.dim("Connected:")} ${s.connected ? pc2.green("yes") : pc2.red("no")}`);
2792
+ console.log(` ${pc2.dim("PID:")} ${s.pid ?? "N/A"}`);
2793
+ console.log(` ${pc2.dim("Uptime:")} ${uptimeStr}`);
2794
+ console.log(` ${pc2.dim("Last response:")} ${lastRespStr}`);
2795
+ console.log(` ${pc2.dim("Stderr lines:")} ${s.stderrLineCount.toLocaleString()}`);
2796
+ console.log(` ${pc2.dim("Reconnects:")} ${s.reconnectAttempts}/${s.maxReconnectAttempts}`);
2797
+ console.log(` ${pc2.dim("Command:")} ${s.command} ${s.args.join(" ")}`);
2264
2798
  console.log();
2265
2799
  }
2800
+ function printResultBlock(opts) {
2801
+ const colorFn = pc2[opts.labelColor];
2802
+ const elapsedStr = opts.elapsed < 1e3 ? `${opts.elapsed}ms` : `${(opts.elapsed / 1e3).toFixed(1)}s`;
2803
+ const detail = opts.detail ?? opts.toolName ?? "";
2804
+ console.log(` ${colorFn(opts.label)} ${pc2.dim(detail)} ${pc2.dim(`(${elapsedStr})`)}`);
2805
+ console.log(pc2.dim(` ${"\u2500".repeat(60)}`));
2806
+ }
2807
+ function printShortHelp() {
2808
+ const hasTools = !!activeCapabilities?.tools;
2809
+ const hasResources = !!activeCapabilities?.resources;
2810
+ const hasPrompts = !!activeCapabilities?.prompts;
2811
+ const tC = hasTools ? pc2.green : pc2.dim;
2812
+ const rC = hasResources ? pc2.green : pc2.dim;
2813
+ const pC = hasPrompts ? pc2.green : pc2.dim;
2814
+ console.log(`
2815
+ ${pc2.bold("Quick Reference:")}
2816
+
2817
+ ${tC("tl")} ${tC("tools/list")} ${rC("rl")} ${rC("resources/list")} ${pC("pl")} ${pC("prompts/list")}
2818
+ ${tC("td")} ${tC("tools/describe")} ${rC("rr")} ${rC("resources/read")} ${pC("pg")} ${pC("prompts/get")}
2819
+ ${tC("tc")} ${tC("tools/call")} ${rC("rt")} ${rC("resources/templates")}
2820
+ ${tC("ts")} ${tC("tools/scaffold")} ${rC("rs")} ${rC("resources/subscribe")}
2821
+ ${rC("ru")} ${rC("resources/unsubscribe")}
2822
+
2823
+ ${pc2.green("ping")} ${pc2.green("status")} ${pc2.green("timing")} ${pc2.green("history")} ${pc2.green("!!")} ${pc2.green("explore")} ${pc2.green("reconnect")}
2824
+
2825
+ ${pc2.dim("Type 'help' for full command reference.")}
2826
+ `);
2827
+ }
2266
2828
  function printHelp() {
2829
+ const hasTools = !!activeCapabilities?.tools;
2830
+ const hasResources = !!activeCapabilities?.resources;
2831
+ const hasPrompts = !!activeCapabilities?.prompts;
2832
+ const hasLogging = !!activeCapabilities?.logging;
2833
+ const tC = hasTools ? pc2.green : pc2.dim;
2834
+ const rC = hasResources ? pc2.green : pc2.dim;
2835
+ const pC = hasPrompts ? pc2.green : pc2.dim;
2836
+ const lC = hasLogging ? pc2.green : pc2.dim;
2837
+ const tD = hasTools ? (s) => s : pc2.dim;
2838
+ const rD = hasResources ? (s) => s : pc2.dim;
2839
+ const pD = hasPrompts ? (s) => s : pc2.dim;
2840
+ const lD = hasLogging ? (s) => s : pc2.dim;
2841
+ const tH = hasTools ? pc2.bold("Tool Commands:") : pc2.dim(pc2.bold("Tool Commands:")) + pc2.dim(" (Unsupported)");
2842
+ const rH = hasResources ? pc2.bold("Resource Commands:") : pc2.dim(pc2.bold("Resource Commands:")) + pc2.dim(" (Unsupported)");
2843
+ const pH = hasPrompts ? pc2.bold("Prompt Commands:") : pc2.dim(pc2.bold("Prompt Commands:")) + pc2.dim(" (Unsupported)");
2267
2844
  console.log(`
2268
- ${pc.bold("Tool Commands:")}
2845
+ ${tH}
2269
2846
 
2270
- ${pc.green("tools/list")} List all available tools
2271
- ${pc.green("tools/describe")} <name> Show a tool's input schema
2272
- ${pc.green("tools/call")} <name> [json] [opts] Call a tool (interactive if no json)
2273
- Options: ${pc.dim("--timeout <ms>")} Override default timeout (60s)
2274
- ${pc.dim("--clear")} Ignore remembered argument defaults
2275
- ${pc.green("tools/scaffold")} <name> Generate a template for a tool's arguments
2276
- ${pc.green("tools/forget")} [name] Clear remembered interactive defaults
2847
+ ${tC("tools/list")} ${tD("List all available tools")}
2848
+ ${tC("tools/describe")} <name> ${tD("Show a tool's input schema")}
2849
+ ${tC("tools/call")} <name> [json] [opts] ${tD("Call a tool (interactive if no json)")}
2850
+ ${tD("Options:")} ${pc2.dim("--timeout <ms>")} ${tD("Override default timeout (60s)")}
2851
+ ${pc2.dim("--clear")} ${tD("Ignore remembered argument defaults")}
2852
+ ${tC("tools/scaffold")} <name> ${tD("Generate a template for a tool's arguments")}
2853
+ ${tC("tools/forget")} [name] ${tD("Clear remembered interactive defaults")}
2277
2854
 
2278
- ${pc.bold("Resource Commands:")}
2855
+ ${rH}
2279
2856
 
2280
- ${pc.green("resources/list")} List all available resources
2281
- ${pc.green("resources/read")} <uri> Read a resource by URI
2282
- ${pc.green("resources/templates")} List resource templates
2283
- ${pc.green("resources/subscribe")} <uri> Subscribe to resource changes
2284
- ${pc.green("resources/unsubscribe")} <uri> Unsubscribe from resource changes
2857
+ ${rC("resources/list")} ${rD("List all available resources")}
2858
+ ${rC("resources/read")} <uri> ${rD("Read a resource by URI")}
2859
+ ${rC("resources/templates")} ${rD("List resource templates")}
2860
+ ${rC("resources/subscribe")} <uri> ${rD("Subscribe to resource changes")}
2861
+ ${rC("resources/unsubscribe")} <uri> ${rD("Unsubscribe from resource changes")}
2285
2862
 
2286
- ${pc.bold("Prompt Commands:")}
2863
+ ${pH}
2287
2864
 
2288
- ${pc.green("prompts/list")} List all available prompts
2289
- ${pc.green("prompts/get")} <name> [json_args] Get a prompt with arguments
2865
+ ${pC("prompts/list")} ${pD("List all available prompts")}
2866
+ ${pC("prompts/get")} <name> [json_args] ${pD("Get a prompt with arguments")}
2290
2867
 
2291
- ${pc.bold("Protocol Commands:")}
2868
+ ${pc2.bold("Protocol Commands:")}
2292
2869
 
2293
- ${pc.green("ping")} Verify connection, show round-trip time
2294
- ${pc.green("log-level")} <level> Set server logging verbosity
2295
- ${pc.green("history")} [count|clear] Show request/response history
2296
- ${pc.green("notifications")} [count|clear] Show server notifications
2870
+ ${pc2.green("ping")} Verify connection, show round-trip time
2871
+ ${lC("log-level")} <level> ${lD("Set server logging verbosity")}${hasLogging ? "" : pc2.dim(" (Unsupported)")}
2872
+ ${pc2.green("history")} [count|clear] Show request/response history
2873
+ ${pc2.green("notifications")} [count|clear] Show server notifications
2297
2874
 
2298
- ${pc.bold("Roots Management:")}
2875
+ ${pc2.bold("Roots Management:")}
2299
2876
 
2300
- ${pc.green("roots/list")} Show configured client roots
2301
- ${pc.green("roots/add")} <uri> [name] Add a root directory
2302
- ${pc.green("roots/remove")} <uri> Remove a root directory
2877
+ ${pc2.green("roots/list")} Show configured client roots
2878
+ ${pc2.green("roots/add")} <uri> [name] Add a root directory
2879
+ ${pc2.green("roots/remove")} <uri> Remove a root directory
2303
2880
 
2304
- ${pc.bold("Session Commands:")}
2881
+ ${pc2.bold("Session Commands:")}
2305
2882
 
2306
- ${pc.green("!!")} / ${pc.green("last")} Re-run the last command
2307
- ${pc.green("reconnect")} Disconnect and reconnect to the server
2308
- ${pc.green("timing")} Show tool call performance stats
2309
- ${pc.green("status")} Show target server status
2310
- ${pc.green("help")} Show this help
2311
- ${pc.green("exit")} / ${pc.green("quit")} Disconnect and exit
2883
+ ${pc2.green("!!")} / ${pc2.green("last")} Re-run the last command
2884
+ ${pc2.green("reconnect")} Disconnect and reconnect to the server
2885
+ ${pc2.green("timing")} Show tool call performance stats
2886
+ ${pc2.green("status")} Show target server status
2887
+ ${pc2.green("help")} Show this help
2888
+ ${pc2.green("exit")} / ${pc2.green("quit")} Disconnect and exit
2312
2889
 
2313
- ${pc.bold("Shortcuts:")}
2890
+ ${pc2.bold("Shortcuts:")}
2314
2891
 
2315
- ${pc.green("tl")} tools/list ${pc.green("rl")} resources/list ${pc.green("pl")} prompts/list
2316
- ${pc.green("td")} tools/describe ${pc.green("rr")} resources/read ${pc.green("pg")} prompts/get
2317
- ${pc.green("tc")} tools/call ${pc.green("rt")} resources/templates
2318
- ${pc.green("ts")} tools/scaffold ${pc.green("rs")} resources/subscribe
2319
- ${pc.green("ru")} resources/unsubscribe
2892
+ ${tC("tl")} ${tC("tools/list")} ${rC("rl")} ${rC("resources/list")} ${pC("pl")} ${pC("prompts/list")}
2893
+ ${tC("td")} ${tC("tools/describe")} ${rC("rr")} ${rC("resources/read")} ${pC("pg")} ${pC("prompts/get")}
2894
+ ${tC("tc")} ${tC("tools/call")} ${rC("rt")} ${rC("resources/templates")}
2895
+ ${tC("ts")} ${tC("tools/scaffold")} ${rC("rs")} ${rC("resources/subscribe")}
2896
+ ${rC("ru")} ${rC("resources/unsubscribe")}
2320
2897
 
2321
- ${pc.dim("Lines starting with # are treated as comments.")}
2322
- ${pc.dim('JSON arguments can contain spaces: tools/call say {"message": "hello world"}')}
2323
- ${pc.dim("Run tools/call <name> without JSON for interactive argument prompting.")}
2324
- ${pc.dim("Use tools/call <name> --clear to ignore remembered defaults.")}
2898
+ ${pc2.dim("Lines starting with # are treated as comments.")}
2899
+ ${pc2.dim('JSON arguments can contain spaces: tools/call say {"message": "hello world"}')}
2900
+ ${pc2.dim("Run tools/call <name> without JSON for interactive argument prompting.")}
2901
+ ${pc2.dim("Use tools/call <name> --clear to ignore remembered defaults.")}
2325
2902
  `);
2326
2903
  }
2327
2904
  async function readScriptLines(filepath) {
2328
- const content = await readFile(filepath, "utf-8");
2905
+ const content = await readFile2(filepath, "utf-8");
2329
2906
  return content.split("\n");
2330
2907
  }
2331
2908
  function cmdToolsForget(rest) {
@@ -2333,14 +2910,39 @@ function cmdToolsForget(rest) {
2333
2910
  if (toolName) {
2334
2911
  if (lastToolArgsMap.has(toolName)) {
2335
2912
  lastToolArgsMap.delete(toolName);
2336
- console.log(pc.green(` Cleared remembered args for ${pc.bold(toolName)}.`));
2913
+ console.log(pc2.green(` Cleared remembered args for ${pc2.bold(toolName)}.`));
2337
2914
  } else {
2338
- console.log(pc.yellow(` No remembered args for "${toolName}".`));
2915
+ console.log(pc2.yellow(` No remembered args for "${toolName}".`));
2339
2916
  }
2340
2917
  } else {
2341
2918
  const count = lastToolArgsMap.size;
2342
2919
  lastToolArgsMap.clear();
2343
- console.log(pc.green(` Cleared remembered args for ${count} tool${count === 1 ? "" : "s"}.`));
2920
+ console.log(pc2.green(` Cleared remembered args for ${count} tool${count === 1 ? "" : "s"}.`));
2921
+ }
2922
+ }
2923
+ async function pickInteractive(items, message) {
2924
+ if (items.length === 0) return null;
2925
+ if (!process.stdin.isTTY) return null;
2926
+ const choices = items.map((item) => ({
2927
+ name: item.name,
2928
+ value: item.name,
2929
+ description: item.description || ""
2930
+ }));
2931
+ try {
2932
+ const picked = await search({
2933
+ message,
2934
+ source: async (term) => {
2935
+ if (!term) return choices;
2936
+ const lower = term.toLowerCase();
2937
+ return choices.filter(
2938
+ (c) => c.name.toLowerCase().includes(lower) || c.description.toLowerCase().includes(lower)
2939
+ );
2940
+ }
2941
+ });
2942
+ return picked;
2943
+ } catch (err) {
2944
+ if (isAbortError(err)) return null;
2945
+ throw err;
2344
2946
  }
2345
2947
  }
2346
2948
  async function cmdExplore(target, interceptor) {
@@ -2384,19 +2986,19 @@ async function cmdExplore(target, interceptor) {
2384
2986
  }
2385
2987
  }
2386
2988
  if (choices.length === 0) {
2387
- console.log(pc.yellow("No tools, resources, or prompts found."));
2989
+ console.log(pc2.yellow("No tools, resources, or prompts found."));
2388
2990
  return;
2389
2991
  }
2390
2992
  if (!process.stdin.isTTY) {
2391
- console.log(pc.bold("\n Server Capabilities\n"));
2993
+ console.log(pc2.bold("\n Server Capabilities\n"));
2392
2994
  for (let i = 0; i < choices.length; i++) {
2393
2995
  console.log(
2394
- ` ${pc.cyan(String(i + 1).padStart(2))}. ${choices[i].name} ${pc.dim(choices[i].description)}`
2996
+ ` ${pc2.cyan(String(i + 1).padStart(2))}. ${choices[i].name} ${pc2.dim(choices[i].description)}`
2395
2997
  );
2396
2998
  }
2397
2999
  console.log();
2398
3000
  console.log(
2399
- pc.dim(" Non-interactive mode \u2014 use specific commands instead (e.g., tools/call <name>).")
3001
+ pc2.dim(" Non-interactive mode \u2014 use specific commands instead (e.g., tools/call <name>).")
2400
3002
  );
2401
3003
  return;
2402
3004
  }
@@ -2430,139 +3032,6 @@ import { createHash } from "crypto";
2430
3032
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2431
3033
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2432
3034
  import { z } from "zod";
2433
-
2434
- // src/config-scanner.ts
2435
- import { existsSync } from "fs";
2436
- import { readFile as readFile2 } from "fs/promises";
2437
- import { homedir } from "os";
2438
- import path from "path";
2439
- import process2 from "process";
2440
- import { input as input2, select as select2 } from "@inquirer/prompts";
2441
- function getConfigPaths() {
2442
- const home = homedir();
2443
- const cwd = process2.cwd();
2444
- const isWin = process2.platform === "win32";
2445
- const isMac = process2.platform === "darwin";
2446
- const appData = process2.env.APPDATA || path.join(home, "AppData", "Roaming");
2447
- const localAppData = process2.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
2448
- let claudeDesktopGlob;
2449
- if (isWin) {
2450
- claudeDesktopGlob = path.join(appData, "Claude", "claude_desktop_config.json");
2451
- } else if (isMac) {
2452
- claudeDesktopGlob = path.join(
2453
- home,
2454
- "Library",
2455
- "Application Support",
2456
- "Claude",
2457
- "claude_desktop_config.json"
2458
- );
2459
- } else {
2460
- claudeDesktopGlob = path.join(home, ".config", "Claude", "claude_desktop_config.json");
2461
- }
2462
- return [
2463
- { source: "Cursor (Global)", file: path.join(home, ".cursor", "mcp.json") },
2464
- { source: "Cursor (Project)", file: path.join(cwd, ".cursor", "mcp.json") },
2465
- { source: "Windsurf", file: path.join(home, ".codeium", "windsurf", "mcp_config.json") },
2466
- { source: "Claude Desktop", file: claudeDesktopGlob },
2467
- { source: "Cline", file: path.join(home, "Documents", "Cline", "MCP", "mcp.json") },
2468
- { source: "VS Code (Project)", file: path.join(cwd, ".vscode", "mcp.json") },
2469
- {
2470
- source: "VS Code (Global)",
2471
- file: path.join(
2472
- isMac ? path.join(home, "Library", "Application Support") : isWin ? appData : path.join(home, ".config"),
2473
- "Code",
2474
- "User",
2475
- "settings.json"
2476
- )
2477
- },
2478
- { source: "Copilot CLI (Global)", file: path.join(home, ".copilot", "mcp-config.json") },
2479
- { source: "Gemini CLI (Global)", file: path.join(home, ".gemini", "settings.json") },
2480
- { source: "Gemini CLI (Project)", file: path.join(cwd, ".gemini", "settings.json") },
2481
- { source: "Claude Code (Global)", file: path.join(home, ".claude.json") },
2482
- { source: "Claude Code (Project)", file: path.join(cwd, ".mcp.json") },
2483
- { source: "Antigravity", file: path.join(home, ".gemini", "antigravity", "mcp_config.json") }
2484
- ];
2485
- }
2486
- async function discoverServers() {
2487
- const servers = [];
2488
- const paths = getConfigPaths();
2489
- for (const { source, file } of paths) {
2490
- if (!existsSync(file)) continue;
2491
- try {
2492
- const content = await readFile2(file, "utf8");
2493
- const json = JSON.parse(content);
2494
- let mcpServers;
2495
- if (json.mcpServers && typeof json.mcpServers === "object") {
2496
- mcpServers = json.mcpServers;
2497
- } else if (json.mcp?.servers && typeof json.mcp.servers === "object") {
2498
- mcpServers = json.mcp.servers;
2499
- } else if (json.servers && typeof json.servers === "object") {
2500
- mcpServers = json.servers;
2501
- }
2502
- if (mcpServers) {
2503
- for (const [name, config] of Object.entries(mcpServers)) {
2504
- if (config.command) {
2505
- servers.push({ name, config, source });
2506
- }
2507
- }
2508
- }
2509
- } catch {
2510
- }
2511
- }
2512
- return servers;
2513
- }
2514
- async function pickDiscoveredServer() {
2515
- const servers = await discoverServers();
2516
- if (servers.length === 0) {
2517
- return null;
2518
- }
2519
- const uniqueServers = /* @__PURE__ */ new Map();
2520
- for (const s of servers) {
2521
- const key = `${s.name}::${s.config.command}::${(s.config.args || []).join(" ")}`;
2522
- if (!uniqueServers.has(key)) {
2523
- uniqueServers.set(key, s);
2524
- } else {
2525
- if (s.source.includes("Project")) {
2526
- uniqueServers.set(key, s);
2527
- }
2528
- }
2529
- }
2530
- const choices = Array.from(uniqueServers.values()).map((s) => {
2531
- return {
2532
- name: `${s.name} (from ${s.source})`,
2533
- value: s,
2534
- description: `${s.config.command} ${(s.config.args || []).join(" ")}`
2535
- };
2536
- });
2537
- choices.push({
2538
- name: "Enter custom server command...",
2539
- value: "CUSTOM",
2540
- description: "Manually specify a command, e.g. 'npx foo' or 'python server.py'"
2541
- });
2542
- try {
2543
- const answer = await select2({
2544
- message: "Select an MCP server to launch:",
2545
- choices,
2546
- pageSize: 15
2547
- });
2548
- if (answer === "CUSTOM") {
2549
- const customCommand = await input2({ message: "Command to spawn target MCP server:" });
2550
- if (!customCommand.trim()) return null;
2551
- const parts = customCommand.trim().match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map((p) => p.replace(/^["']|["']$/g, ""));
2552
- if (!parts || parts.length === 0) return null;
2553
- return {
2554
- name: "Custom",
2555
- config: { command: parts[0], args: parts.slice(1) },
2556
- source: "Manual"
2557
- };
2558
- }
2559
- return answer;
2560
- } catch {
2561
- return null;
2562
- }
2563
- }
2564
-
2565
- // src/server.ts
2566
3035
  function hashDefinition(obj) {
2567
3036
  return createHash("md5").update(JSON.stringify(obj)).digest("hex").slice(0, 12);
2568
3037
  }
@@ -2612,7 +3081,7 @@ async function startServer(opts) {
2612
3081
  maxTextLength: opts.maxTextLength
2613
3082
  });
2614
3083
  const mcpServer = new McpServer(
2615
- { name: "run-mcp", version: "1.4.0" },
3084
+ { name: "run-mcp", version: "1.6.0" },
2616
3085
  {
2617
3086
  capabilities: {
2618
3087
  tools: {}
@@ -2997,7 +3466,12 @@ Available: ${available}`
2997
3466
  const servers = await discoverServers();
2998
3467
  if (servers.length === 0) {
2999
3468
  return {
3000
- content: [{ type: "text", text: "No configured MCP servers discovered in common locations." }]
3469
+ content: [
3470
+ {
3471
+ type: "text",
3472
+ text: "No configured MCP servers discovered in common locations."
3473
+ }
3474
+ ]
3001
3475
  };
3002
3476
  }
3003
3477
  const lines = ["Discovered the following MCP server configurations:"];
@@ -3044,7 +3518,10 @@ Available: ${available}`
3044
3518
  env: z.record(z.string()).optional().describe("Extra environment variables for the server process"),
3045
3519
  // Lifecycle
3046
3520
  disconnect_after: z.boolean().optional().describe("Tear down the connection after this call (default: false)"),
3047
- timeout_ms: z.number().optional().describe("Timeout in ms (only applies to type: 'tool')")
3521
+ timeout_ms: z.number().optional().describe("Timeout in ms (only applies to type: 'tool')"),
3522
+ include_metadata: z.boolean().optional().describe(
3523
+ "Include a structured metadata content item with latency, interception info, and content statistics. Useful for programmatic consumption."
3524
+ )
3048
3525
  }
3049
3526
  },
3050
3527
  async ({
@@ -3055,7 +3532,8 @@ Available: ${available}`
3055
3532
  args,
3056
3533
  env,
3057
3534
  disconnect_after,
3058
- timeout_ms
3535
+ timeout_ms,
3536
+ include_metadata
3059
3537
  }) => {
3060
3538
  try {
3061
3539
  const connectError = await ensureConnected(command, args, env);
@@ -3118,20 +3596,50 @@ Available tools: ${toolNames.join(", ")}`
3118
3596
  } catch {
3119
3597
  }
3120
3598
  const startMs = Date.now();
3121
- result = await interceptor.callTool(
3122
- target,
3123
- name,
3124
- callArgs ?? {},
3125
- timeout_ms
3126
- );
3599
+ let interceptionMeta;
3600
+ if (include_metadata) {
3601
+ const { result: toolResult, metadata } = await interceptor.callToolWithMetadata(
3602
+ target,
3603
+ name,
3604
+ callArgs ?? {},
3605
+ timeout_ms
3606
+ );
3607
+ result = toolResult;
3608
+ interceptionMeta = metadata;
3609
+ } else {
3610
+ result = await interceptor.callTool(
3611
+ target,
3612
+ name,
3613
+ callArgs ?? {},
3614
+ timeout_ms
3615
+ );
3616
+ }
3127
3617
  const elapsedMs = Date.now() - startMs;
3128
3618
  const resultContent = result.content;
3129
- if (Array.isArray(resultContent) && resultContent.length > 0) {
3619
+ if (!include_metadata && Array.isArray(resultContent) && resultContent.length > 0) {
3130
3620
  const lastItem = resultContent[resultContent.length - 1];
3131
3621
  if (lastItem.type === "text") {
3132
3622
  lastItem.text += ` (${elapsedMs}ms)`;
3133
3623
  }
3134
3624
  }
3625
+ if (include_metadata && Array.isArray(resultContent)) {
3626
+ const meta = {
3627
+ latency_ms: elapsedMs,
3628
+ content_items: resultContent.length,
3629
+ is_error: result.isError === true
3630
+ };
3631
+ if (interceptionMeta) {
3632
+ meta.truncated = interceptionMeta.truncated;
3633
+ meta.images_saved = interceptionMeta.imagesSaved;
3634
+ meta.audio_saved = interceptionMeta.audioSaved;
3635
+ meta.original_size_bytes = interceptionMeta.originalSizeBytes;
3636
+ }
3637
+ resultContent.unshift({
3638
+ type: "text",
3639
+ text: `--- metadata ---
3640
+ ${JSON.stringify(meta)}`
3641
+ });
3642
+ }
3135
3643
  break;
3136
3644
  }
3137
3645
  case "resource": {
@@ -3214,7 +3722,74 @@ Available tools: ${toolNames.join(", ")}`
3214
3722
  }
3215
3723
 
3216
3724
  // src/index.ts
3217
- program.name("run-mcp").description("A smart interactive REPL and live test harness for MCP servers").version("1.4.0").passThroughOptions().allowUnknownOption().argument(
3725
+ function extractTargetCommand(targetCommand) {
3726
+ return targetCommand.filter((a) => a !== "--");
3727
+ }
3728
+ function requireTargetCommand(targetCommand, subcommandUsage) {
3729
+ const target = extractTargetCommand(targetCommand);
3730
+ if (target.length === 0) {
3731
+ process.stderr.write(`Error: No target server command provided after '--'.
3732
+ `);
3733
+ process.stderr.write(`Usage: ${subcommandUsage}
3734
+ `);
3735
+ process.exit(2);
3736
+ }
3737
+ return target;
3738
+ }
3739
+ function parseHeadlessOpts(opts) {
3740
+ return {
3741
+ outDir: opts.outDir,
3742
+ timeoutMs: opts.timeout ? Number.parseInt(opts.timeout, 10) : void 0,
3743
+ raw: opts.raw
3744
+ };
3745
+ }
3746
+ program.enablePositionalOptions();
3747
+ program.command("call").argument("<tool>", "Tool name to call").argument("[json_args]", "JSON arguments for the tool").argument("[target_command...]", "Target server command (after --)").description("Call a tool on a target MCP server and print the result as JSON").option("-o, --out-dir <path>", "Output directory for saved media").option("-t, --timeout <ms>", "Timeout in milliseconds (default: 30000)").option("--raw", "Print the full result object including metadata").allowUnknownOption().action(
3748
+ async (tool, jsonArgs, targetCommand, opts) => {
3749
+ const target = requireTargetCommand(
3750
+ targetCommand,
3751
+ "run-mcp call <tool> [json_args] -- <server_command...>"
3752
+ );
3753
+ await runHeadless(target, { type: "call", tool, args: jsonArgs }, parseHeadlessOpts(opts));
3754
+ }
3755
+ );
3756
+ program.command("list-tools").argument("[target_command...]", "Target server command (after --)").description("List all tools on a target MCP server as JSON").allowUnknownOption().action(async (targetCommand) => {
3757
+ const target = requireTargetCommand(targetCommand, "run-mcp list-tools -- <server_command...>");
3758
+ await runHeadless(target, { type: "list-tools" });
3759
+ });
3760
+ program.command("list-resources").argument("[target_command...]", "Target server command (after --)").description("List all resources on a target MCP server as JSON").allowUnknownOption().action(async (targetCommand) => {
3761
+ const target = requireTargetCommand(
3762
+ targetCommand,
3763
+ "run-mcp list-resources -- <server_command...>"
3764
+ );
3765
+ await runHeadless(target, { type: "list-resources" });
3766
+ });
3767
+ program.command("list-prompts").argument("[target_command...]", "Target server command (after --)").description("List all prompts on a target MCP server as JSON").allowUnknownOption().action(async (targetCommand) => {
3768
+ const target = requireTargetCommand(
3769
+ targetCommand,
3770
+ "run-mcp list-prompts -- <server_command...>"
3771
+ );
3772
+ await runHeadless(target, { type: "list-prompts" });
3773
+ });
3774
+ program.command("read").argument("<uri>", "Resource URI to read").argument("[target_command...]", "Target server command (after --)").description("Read a resource by URI from a target MCP server").allowUnknownOption().action(async (uri, targetCommand) => {
3775
+ const target = requireTargetCommand(targetCommand, "run-mcp read <uri> -- <server_command...>");
3776
+ await runHeadless(target, { type: "read", uri });
3777
+ });
3778
+ program.command("describe").argument("<tool>", "Tool name to describe").argument("[target_command...]", "Target server command (after --)").description("Print a tool's full schema as JSON").allowUnknownOption().action(async (tool, targetCommand) => {
3779
+ const target = requireTargetCommand(
3780
+ targetCommand,
3781
+ "run-mcp describe <tool> -- <server_command...>"
3782
+ );
3783
+ await runHeadless(target, { type: "describe", tool });
3784
+ });
3785
+ program.command("get-prompt").argument("<name>", "Prompt name").argument("[json_args]", "JSON arguments for the prompt").argument("[target_command...]", "Target server command (after --)").description("Get a prompt with optional arguments from a target MCP server").allowUnknownOption().action(async (name, jsonArgs, targetCommand) => {
3786
+ const target = requireTargetCommand(
3787
+ targetCommand,
3788
+ "run-mcp get-prompt <name> [json_args] -- <server_command...>"
3789
+ );
3790
+ await runHeadless(target, { type: "get-prompt", name, args: jsonArgs });
3791
+ });
3792
+ program.name("run-mcp").description("A smart interactive REPL and live test harness for MCP servers").version("1.6.0").passThroughOptions().allowUnknownOption().argument(
3218
3793
  "[target_command...]",
3219
3794
  "Command to spawn the target MCP server (starts REPL if provided, Agent server otherwise)"
3220
3795
  ).option("-o, --out-dir <path>", "Directory to save intercepted images and audio").option(
@@ -3234,6 +3809,15 @@ Examples:
3234
3809
  $ run-mcp --out-dir ./test-output # Agent mode with options
3235
3810
  $ run-mcp --out-dir ./screenshots node srv.js # REPL mode with options
3236
3811
 
3812
+ Headless Commands (pipe-friendly, JSON output):
3813
+ $ run-mcp call echo '{"text":"hi"}' -- node my-server.js
3814
+ $ run-mcp list-tools -- node my-server.js | jq '.[].name'
3815
+ $ run-mcp list-resources -- node my-server.js
3816
+ $ run-mcp list-prompts -- node my-server.js
3817
+ $ run-mcp read docs://readme -- node my-server.js
3818
+ $ run-mcp describe echo -- node my-server.js
3819
+ $ run-mcp get-prompt greeting '{"name":"Ada"}' -- node my-server.js
3820
+
3237
3821
  Agent Mode Configuration (mcp.json):
3238
3822
  {
3239
3823
  "mcpServers": {