@evantahler/mcpx 0.20.0 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evantahler/mcpx",
3
- "version": "0.20.0",
3
+ "version": "0.20.1",
4
4
  "description": "A command-line interface for MCP servers. curl for MCP.",
5
5
  "type": "module",
6
6
  "exports": {
package/src/cli.ts CHANGED
@@ -21,6 +21,7 @@ import { registerServersCommand } from "./commands/servers.ts";
21
21
  import { registerSkillCommand } from "./commands/skill.ts";
22
22
  import { registerTaskCommand } from "./commands/task.ts";
23
23
  import { registerUpgradeCommand } from "./commands/upgrade.ts";
24
+ import { logger } from "./output/logger.ts";
24
25
  import { maybeCheckForUpdate } from "./update/background.ts";
25
26
 
26
27
  program
@@ -96,5 +97,5 @@ program.parse();
96
97
  // Print update notice after command output completes
97
98
  process.on("beforeExit", async () => {
98
99
  const notice = await updateNotice;
99
- if (notice) process.stderr.write(notice);
100
+ if (notice) logger.writeRaw(notice);
100
101
  });
@@ -1,4 +1,5 @@
1
1
  import { execFile } from "node:child_process";
2
+ import { logger } from "../output/logger.ts";
2
3
 
3
4
  /**
4
5
  * Open a URL in the default browser (macOS/Windows/Linux).
@@ -12,11 +13,11 @@ export function openBrowser(url: string): Promise<void> {
12
13
  try {
13
14
  const parsed = new URL(url);
14
15
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
15
- process.stderr.write(`Refusing to open non-HTTP URL: ${url}\n`);
16
+ logger.error(`Refusing to open non-HTTP URL: ${url}`);
16
17
  return Promise.resolve();
17
18
  }
18
19
  } catch {
19
- process.stderr.write(`Invalid URL: ${url}\n`);
20
+ logger.error(`Invalid URL: ${url}`);
20
21
  return Promise.resolve();
21
22
  }
22
23
 
@@ -37,7 +38,7 @@ export function openBrowser(url: string): Promise<void> {
37
38
  return new Promise((resolve) => {
38
39
  execFile(cmd, args, (err) => {
39
40
  if (err) {
40
- process.stderr.write(`Could not open browser. Please visit:\n ${url}\n`);
41
+ logger.warn(`Could not open browser. Please visit:\n ${url}`);
41
42
  }
42
43
  resolve();
43
44
  });
@@ -7,6 +7,7 @@ import type {
7
7
  PrimitiveSchemaDefinition,
8
8
  } from "@modelcontextprotocol/sdk/types.js";
9
9
  import ansis from "ansis";
10
+ import { logger } from "../output/logger.ts";
10
11
  import { validateElicitationResponse } from "../validation/schema.ts";
11
12
  import { openBrowser } from "./browser.ts";
12
13
 
@@ -74,7 +75,7 @@ async function handleFormInteractive(params: ElicitRequestFormParams): Promise<E
74
75
  const question = (prompt: string): Promise<string> => new Promise((resolve) => rl.question(prompt, resolve));
75
76
 
76
77
  try {
77
- process.stderr.write(`\n${ansis.bold("Server requests input:")} ${params.message}\n`);
78
+ logger.writeRaw(`\n${ansis.bold("Server requests input:")} ${params.message}\n`);
78
79
 
79
80
  const schema = params.requestedSchema;
80
81
  const properties = schema.properties ?? {};
@@ -86,7 +87,7 @@ async function handleFormInteractive(params: ElicitRequestFormParams): Promise<E
86
87
  const value = await promptField(key, fieldSchema, isRequired, question);
87
88
  if (value === undefined) {
88
89
  if (isRequired) {
89
- process.stderr.write(ansis.yellow("Cancelled.\n"));
90
+ logger.writeRaw(ansis.yellow("Cancelled.\n"));
90
91
  return { action: "cancel" };
91
92
  }
92
93
  continue;
@@ -98,7 +99,7 @@ async function handleFormInteractive(params: ElicitRequestFormParams): Promise<E
98
99
  const validation = validateElicitationResponse(schema as unknown as Record<string, unknown>, content);
99
100
  if (!validation.valid) {
100
101
  const msgs = validation.errors.map((e) => ` ${e.path}: ${e.message}`).join("\n");
101
- process.stderr.write(ansis.red(`Validation failed:\n${msgs}\n`));
102
+ logger.writeRaw(ansis.red(`Validation failed:\n${msgs}\n`));
102
103
  return { action: "cancel" };
103
104
  }
104
105
 
@@ -121,7 +122,7 @@ async function promptField(
121
122
 
122
123
  // Show description if present
123
124
  if (desc) {
124
- process.stderr.write(ansis.dim(` ${desc}\n`));
125
+ logger.writeRaw(ansis.dim(` ${desc}\n`));
125
126
  }
126
127
 
127
128
  // Enum (single-select)
@@ -178,7 +179,7 @@ async function promptNumber(
178
179
  if (!answer) return undefined;
179
180
  const num = Number(answer);
180
181
  if (Number.isNaN(num)) {
181
- process.stderr.write(ansis.red(` Invalid number: ${answer}\n`));
182
+ logger.writeRaw(ansis.red(` Invalid number: ${answer}\n`));
182
183
  return undefined;
183
184
  }
184
185
  return num;
@@ -206,10 +207,10 @@ async function promptEnum(
206
207
  ): Promise<string | undefined> {
207
208
  const values = (schema as { enum: string[] }).enum;
208
209
  const def = (schema as { default?: string }).default;
209
- process.stderr.write(` ${marker}${label}:\n`);
210
+ logger.writeRaw(` ${marker}${label}:\n`);
210
211
  values.forEach((v, i) => {
211
212
  const defMark = v === def ? ansis.dim(" (default)") : "";
212
- process.stderr.write(` [${i + 1}] ${v}${defMark}\n`);
213
+ logger.writeRaw(` [${i + 1}] ${v}${defMark}\n`);
213
214
  });
214
215
  const answer = await question(" > ");
215
216
  if (!answer && def !== undefined) return def;
@@ -228,10 +229,10 @@ async function promptOneOfEnum(
228
229
  ): Promise<string | undefined> {
229
230
  const options = (schema as { oneOf: { const: string; title: string }[] }).oneOf;
230
231
  const def = (schema as { default?: string }).default;
231
- process.stderr.write(` ${marker}${label}:\n`);
232
+ logger.writeRaw(` ${marker}${label}:\n`);
232
233
  options.forEach((opt, i) => {
233
234
  const defMark = opt.const === def ? ansis.dim(" (default)") : "";
234
- process.stderr.write(` [${i + 1}] ${opt.title} (${opt.const})${defMark}\n`);
235
+ logger.writeRaw(` [${i + 1}] ${opt.title} (${opt.const})${defMark}\n`);
235
236
  });
236
237
  const answer = await question(" > ");
237
238
  if (!answer && def !== undefined) return def;
@@ -264,10 +265,10 @@ async function promptMultiSelect(
264
265
  return undefined;
265
266
  }
266
267
 
267
- process.stderr.write(` ${marker}${label} (select multiple, comma-separated):\n`);
268
+ logger.writeRaw(` ${marker}${label} (select multiple, comma-separated):\n`);
268
269
  values.forEach((v, i) => {
269
270
  const display = titles ? `${titles[i]} (${v})` : v;
270
- process.stderr.write(` [${i + 1}] ${display}\n`);
271
+ logger.writeRaw(` [${i + 1}] ${display}\n`);
271
272
  });
272
273
  const answer = await question(" > ");
273
274
  if (!answer && def !== undefined) return def;
@@ -324,10 +325,10 @@ async function handleUrlInteractive(params: ElicitRequestURLParams): Promise<Eli
324
325
  }
325
326
  })();
326
327
 
327
- process.stderr.write(`\n${ansis.bold("Server requests URL interaction:")}\n`);
328
- process.stderr.write(` ${params.message}\n`);
329
- process.stderr.write(` ${ansis.yellow("Domain:")} ${domain}\n`);
330
- process.stderr.write(` ${ansis.yellow("URL:")} ${params.url}\n`);
328
+ logger.writeRaw(`\n${ansis.bold("Server requests URL interaction:")}\n`);
329
+ logger.writeRaw(` ${params.message}\n`);
330
+ logger.writeRaw(` ${ansis.yellow("Domain:")} ${domain}\n`);
331
+ logger.writeRaw(` ${ansis.yellow("URL:")} ${params.url}\n`);
331
332
 
332
333
  const answer = await question(` Open in browser? [y/n]: `);
333
334
  if (["y", "yes"].includes(answer.toLowerCase())) {
@@ -1,6 +1,7 @@
1
1
  import { chmod } from "node:fs/promises";
2
2
  import { join, resolve } from "node:path";
3
3
  import { DEFAULT_CONFIG_DIR, ENV } from "../constants.ts";
4
+ import { logger } from "../output/logger.ts";
4
5
  import { interpolateEnv } from "./env.ts";
5
6
  import {
6
7
  type AuthFile,
@@ -64,7 +65,7 @@ export async function loadConfig(options: LoadConfigOptions = {}): Promise<Confi
64
65
  const cwd = process.cwd();
65
66
  if (await hasServersFile(cwd)) {
66
67
  configDir = cwd;
67
- process.stderr.write(`Note: using servers.json from current directory (${cwd})\n`);
68
+ logger.info(`Note: using servers.json from current directory (${cwd})`);
68
69
  }
69
70
  }
70
71