consult-mcp 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,7 @@ npx -y consult-mcp@latest review --base main
13
13
 
14
14
  With [live sessions](#live-sessions) on, agents can also ask the sessions you already have running and message them, and with [task dispatch](#task-dispatch) on, hand a coding task to a Codex or Grok worker and follow its progress.
15
15
 
16
- [consult.n3el.dev](https://consult.n3el.dev) · [npm](https://www.npmjs.com/package/consult-mcp)
16
+ [getconsult.dev](https://getconsult.dev) · [npm](https://www.npmjs.com/package/consult-mcp)
17
17
 
18
18
  ## Install
19
19
 
@@ -239,7 +239,7 @@ node dist/cli.js install # register this checkout with your agents
239
239
 
240
240
  Releases: bump `version` in `package.json`, then push a matching tag (`git tag v0.3.0 && git push --tags`). The [release workflow](.github/workflows/release.yml) publishes to npm with provenance through npm trusted publishing, then creates the GitHub release. Everyone using `@latest` picks it up on their next agent session. Tags with a pre-release suffix (`v0.3.0-rc.1`) publish to the `next` dist-tag instead of `latest`.
241
241
 
242
- The landing page is in [`site/`](site) and deploys to [consult.n3el.dev](https://consult.n3el.dev) on Vercel.
242
+ The landing page is in [`site/`](site) and deploys to [getconsult.dev](https://getconsult.dev) on Vercel. The old address, consult.n3el.dev, redirects there.
243
243
 
244
244
  ## License
245
245
 
@@ -13,13 +13,42 @@ const WORKSPACE_WRITE = [
13
13
  // Codex runs MCP tools outside its shell sandbox, so advisors and workers get none: plugins are switched off
14
14
  // (they bring their own MCP servers) and every server still enabled is disabled by name.
15
15
  // That includes consult itself, which is what stops agents from consulting each other in a loop.
16
+ // Names are interpolated into `-c mcp_servers.<name>.enabled=false`. Codex splits that key only on ".", so a
17
+ // name with spaces is disabled as is, but one holding ".", "=", quotes, control characters or edge whitespace
18
+ // would not reliably name the server, and is refused. The JSON list also includes each server's env, so a parse
19
+ // failure must not echo stdout.
20
+ const SAFE_MCP_NAME = /^[^\s.="'\x00-\x1f](?:[^.="'\x00-\x1f]*[^\s.="'\x00-\x1f])?$/;
21
+ const MCP_LIST_UNREADABLE = "could not parse `codex mcp list --json`; refusing to run codex with its MCP servers still enabled";
16
22
  async function listEnabledMcpServers(run, cwd, signal) {
17
23
  const res = await run("codex", ["mcp", "list", "--json", ...PLUGINS_OFF], { cwd, timeoutMs: 30_000, signal });
18
- if (res.code !== 0) {
19
- throw new AdvisorError(`could not list codex MCP servers to disable them: ${res.stderr.trim().slice(-500)}`);
24
+ if (res.stopped || res.code !== 0) {
25
+ const detail = res.stderr.trim().slice(-500) || res.stopped || "exited unsuccessfully";
26
+ throw new AdvisorError(`could not list codex MCP servers to disable them: ${detail}`);
20
27
  }
21
- const servers = JSON.parse(res.stdout);
22
- return servers.filter((s) => s.enabled).map((s) => s.name);
28
+ let parsed;
29
+ try {
30
+ parsed = JSON.parse(res.stdout);
31
+ }
32
+ catch {
33
+ throw new AdvisorError(MCP_LIST_UNREADABLE);
34
+ }
35
+ if (!Array.isArray(parsed))
36
+ throw new AdvisorError(MCP_LIST_UNREADABLE);
37
+ const names = [];
38
+ for (const entry of parsed) {
39
+ if (typeof entry !== "object" || entry === null || typeof entry.name !== "string") {
40
+ throw new AdvisorError(MCP_LIST_UNREADABLE);
41
+ }
42
+ const { name, enabled } = entry;
43
+ if (enabled !== true)
44
+ continue;
45
+ if (!SAFE_MCP_NAME.test(name)) {
46
+ const shown = name.length > 80 ? `${name.slice(0, 80)}…` : name;
47
+ throw new AdvisorError(`cannot safely disable codex MCP server ${JSON.stringify(shown)}; disable or rename it before consulting codex`);
48
+ }
49
+ names.push(name);
50
+ }
51
+ return names;
23
52
  }
24
53
  const prepare = async (run, cwd, signal) => {
25
54
  const names = await listEnabledMcpServers(run, cwd, signal);
@@ -56,9 +56,17 @@ function withoutConsult(config) {
56
56
  return Object.keys(otherServers).length > 0 ? { ...rest, mcp_servers: otherServers } : rest;
57
57
  }
58
58
  const hasConsult = (config) => CODEX_SERVER in (config.mcp_servers ?? {});
59
- /** Whether the codex config has a consult entry. A missing file means not registered. */
59
+ /**
60
+ * Whether the codex config has a consult entry. A missing, unreadable, or unparseable file means not
61
+ * registered: `consult settings` checks this for every installed CLI, and a broken config must not abort it.
62
+ */
60
63
  export function isCodexRegistered(path) {
61
- return existsSync(path) && hasConsult(parseConfig(readFileSync(path, "utf8"), path));
64
+ try {
65
+ return existsSync(path) && hasConsult(parseConfig(readFileSync(path, "utf8"), path));
66
+ }
67
+ catch {
68
+ return false;
69
+ }
62
70
  }
63
71
  function writeAtomically(path, text) {
64
72
  mkdirSync(dirname(path), { recursive: true });
package/dist/tui.js CHANGED
@@ -68,7 +68,17 @@ export async function runSettings(io) {
68
68
  const current = await loadOrReset(io);
69
69
  if (!current)
70
70
  return cancel("Left settings unchanged.");
71
- const registered = (await Promise.all(io.installed.map(async (agent) => ((await io.isRegistered(agent)) ? [agent] : [])))).flat();
71
+ const registered = [];
72
+ for (const agent of io.installed) {
73
+ try {
74
+ if (await io.isRegistered(agent))
75
+ registered.push(agent);
76
+ }
77
+ catch (err) {
78
+ // One CLI failing its probe must not abort the wizard; the user can still change the other settings.
79
+ log.warn(`${agent}: ${errorMessage(err)}`);
80
+ }
81
+ }
72
82
  const supported = io.sessionsSupported ?? true;
73
83
  if (!supported)
74
84
  log.info(`Note: ${SESSIONS_UNSUPPORTED}.`);
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "consult-mcp",
3
- "version": "1.0.0",
4
- "description": "MCP server that lets Claude Code, Codex and Grok ask each other for a second opinion through the CLIs already signed in on your machine",
3
+ "version": "1.0.2",
4
+ "mcpName": "io.github.neelsatyavolu/consult",
5
+ "description": "Get a second opinion on your code from every coding agent you have. Claude Code, Codex and Grok review each other's diffs and answer each other's questions, even inside a session that's already running. Uses the CLIs you're signed in to, no API keys.",
5
6
  "keywords": [
6
7
  "mcp",
7
8
  "mcp-server",
@@ -13,7 +14,7 @@
13
14
  "second-opinion",
14
15
  "code-review"
15
16
  ],
16
- "homepage": "https://consult.n3el.dev",
17
+ "homepage": "https://getconsult.dev",
17
18
  "bugs": {
18
19
  "url": "https://github.com/neelsatyavolu/consult/issues"
19
20
  },