@lannguyensi/harness 0.41.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +9 -0
- package/dist/cli/adopt/derive.d.ts +15 -0
- package/dist/cli/adopt/derive.js +34 -7
- package/dist/cli/adopt/derive.js.map +1 -1
- package/dist/cli/adopt/index.d.ts +30 -0
- package/dist/cli/adopt/index.js +23 -3
- package/dist/cli/adopt/index.js.map +1 -1
- package/dist/cli/apply/generate-settings.d.ts +14 -1
- package/dist/cli/apply/generate-settings.js +7 -3
- package/dist/cli/apply/generate-settings.js.map +1 -1
- package/dist/cli/approve/stdin-report.js +2 -0
- package/dist/cli/approve/stdin-report.js.map +1 -1
- package/dist/cli/doctor/claude-mcp.d.ts +50 -0
- package/dist/cli/doctor/claude-mcp.js +170 -0
- package/dist/cli/doctor/claude-mcp.js.map +1 -0
- package/dist/cli/doctor/format.js +37 -0
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.d.ts +10 -0
- package/dist/cli/doctor/index.js +24 -0
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/npm-bin-path.js +42 -0
- package/dist/cli/doctor/npm-bin-path.js.map +1 -1
- package/dist/cli/doctor/types.d.ts +11 -0
- package/dist/cli/doctor/types.js.map +1 -1
- package/dist/cli/index.js +253 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/agent-tasks-auth.js +32 -0
- package/dist/cli/init/agent-tasks-auth.js.map +1 -1
- package/dist/cli/init/composer.js +3 -9
- package/dist/cli/init/composer.js.map +1 -1
- package/dist/cli/init/dependencies.js +21 -0
- package/dist/cli/init/dependencies.js.map +1 -1
- package/dist/cli/init/detect.d.ts +20 -3
- package/dist/cli/init/detect.js +34 -36
- package/dist/cli/init/detect.js.map +1 -1
- package/dist/cli/init/interactive.d.ts +33 -0
- package/dist/cli/init/interactive.js +399 -5
- package/dist/cli/init/interactive.js.map +1 -1
- package/dist/cli/init/profiles.d.ts +1 -1
- package/dist/cli/init/profiles.js +1 -1
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +64 -5
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/main.js +10 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/pack/hook-post-merge-gate-record.d.ts +30 -0
- package/dist/cli/pack/hook-post-merge-gate-record.js +153 -0
- package/dist/cli/pack/hook-post-merge-gate-record.js.map +1 -0
- package/dist/cli/pack/hook-post-merge-gate.d.ts +28 -0
- package/dist/cli/pack/hook-post-merge-gate.js +244 -0
- package/dist/cli/pack/hook-post-merge-gate.js.map +1 -0
- package/dist/cli/pack/hook-solution-acceptance-writeguard.js +148 -2
- package/dist/cli/pack/hook-solution-acceptance-writeguard.js.map +1 -1
- package/dist/cli/pack/understanding-report-schema-hint.js +1 -1
- package/dist/cli/pack/understanding-report-schema-hint.js.map +1 -1
- package/dist/cli/record/index.d.ts +64 -0
- package/dist/cli/record/index.js +211 -0
- package/dist/cli/record/index.js.map +1 -0
- package/dist/cli/session-start/index.d.ts +7 -0
- package/dist/cli/session-start/index.js +133 -38
- package/dist/cli/session-start/index.js.map +1 -1
- package/dist/cli/session-start/toolchain-parity.d.ts +149 -0
- package/dist/cli/session-start/toolchain-parity.js +618 -0
- package/dist/cli/session-start/toolchain-parity.js.map +1 -0
- package/dist/cli/uninstall/index.d.ts +73 -1
- package/dist/cli/uninstall/index.js +134 -2
- package/dist/cli/uninstall/index.js.map +1 -1
- package/dist/io/claude-mcp.d.ts +258 -0
- package/dist/io/claude-mcp.js +544 -0
- package/dist/io/claude-mcp.js.map +1 -0
- package/dist/policies/ledger-client.d.ts +34 -1
- package/dist/policies/ledger-client.js +21 -16
- package/dist/policies/ledger-client.js.map +1 -1
- package/dist/policy-packs/builtin/post-merge-gate-runtime.d.ts +195 -0
- package/dist/policy-packs/builtin/post-merge-gate-runtime.js +290 -0
- package/dist/policy-packs/builtin/post-merge-gate-runtime.js.map +1 -0
- package/dist/policy-packs/builtin/post-merge-gate.d.ts +56 -0
- package/dist/policy-packs/builtin/post-merge-gate.js +291 -0
- package/dist/policy-packs/builtin/post-merge-gate.js.map +1 -0
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js +28 -13
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +1 -1
- package/dist/policy-packs/registry.d.ts +1 -1
- package/dist/policy-packs/registry.js +12 -0
- package/dist/policy-packs/registry.js.map +1 -1
- package/dist/runtime/git-context.d.ts +50 -0
- package/dist/runtime/git-context.js +108 -1
- package/dist/runtime/git-context.js.map +1 -1
- package/dist/runtime/hermetic-spawn-guard.d.ts +25 -0
- package/dist/runtime/hermetic-spawn-guard.js +136 -0
- package/dist/runtime/hermetic-spawn-guard.js.map +1 -0
- package/dist/runtime/ledger-writer.d.ts +27 -0
- package/dist/runtime/ledger-writer.js +46 -0
- package/dist/runtime/ledger-writer.js.map +1 -0
- package/dist/runtime/read-only-bash.js +118 -0
- package/dist/runtime/read-only-bash.js.map +1 -1
- package/dist/schema/index.d.ts +41 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/toolchain-parity.d.ts +18 -0
- package/dist/schema/toolchain-parity.js +35 -0
- package/dist/schema/toolchain-parity.js.map +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
// All interaction with Claude Code's user-scope MCP registration lives here.
|
|
2
|
+
// harness never writes `~/.claude.json` (or `$CLAUDE_CONFIG_DIR/.claude.json`)
|
|
3
|
+
// itself — mutations go exclusively through the `claude mcp` CLI, which is
|
|
4
|
+
// the only thing Claude Code actually reads at runtime for user-scope
|
|
5
|
+
// servers (unlike the `mcpServers` block in `~/.claude/settings.json`,
|
|
6
|
+
// which Claude Code does not consume — see
|
|
7
|
+
// .ai/runs/2026-07-18-init-mcp-wiring-claude-code/01-plan.md).
|
|
8
|
+
//
|
|
9
|
+
// Three layers, task T-001 scope only (no init/doctor wiring — that's
|
|
10
|
+
// T-002/T-003):
|
|
11
|
+
// 1. A thin, injectable-exec wrapper around `claude mcp get/add-json/
|
|
12
|
+
// remove/list`, ENOENT-tolerant and timeout-bounded, that turns raw
|
|
13
|
+
// exit codes/stderr into structured result objects (never throws for
|
|
14
|
+
// "CLI missing" or "timed out" — those are just another outcome).
|
|
15
|
+
// 2. `ensureMcpServers`: reconciles a desired per-server state against
|
|
16
|
+
// the live registry (add/replace-on-drift/no-op), and — opt-in via
|
|
17
|
+
// `opts.gc` (task 363a6de0, MCP-removal GC) — also `claude mcp
|
|
18
|
+
// remove`s any caller-designated "owned" name that's registered but
|
|
19
|
+
// no longer in `desired` (a manifest entry the operator removed or
|
|
20
|
+
// disabled). Ownership is entirely the caller's call: this module
|
|
21
|
+
// never guesses which names harness owns, so a foreign/manually
|
|
22
|
+
// registered server is never touched, no matter how it drifts from
|
|
23
|
+
// `desired`. The ONLY read of the registry file this module performs
|
|
24
|
+
// serves both the drift comparison and the GC candidate scan, and it
|
|
25
|
+
// reads strictly the top-level `mcpServers` key — never
|
|
26
|
+
// `projects.<path>.mcpServers` (that's project-local Claude Code
|
|
27
|
+
// state; `uninstall`'s `probeProjectLocalClaudeJson` already surfaces
|
|
28
|
+
// it separately, read-only). All writes still go through the CLI
|
|
29
|
+
// wrapper above.
|
|
30
|
+
// 3. `stripOwnedMcpServers`: a pure function that removes owned names
|
|
31
|
+
// from a parsed settings.json object (the dead `mcpServers` block
|
|
32
|
+
// the old write path left behind). No file I/O — callers own reading
|
|
33
|
+
// and writing the file.
|
|
34
|
+
//
|
|
35
|
+
// Verified CLI contract (empirical probe, 2026-07-18, disposable
|
|
36
|
+
// CLAUDE_CONFIG_DIR):
|
|
37
|
+
// `claude mcp add-json --scope user <name> <json>`
|
|
38
|
+
// new name → exit 0, stdout "Added stdio MCP server <name> to user config"
|
|
39
|
+
// name exists → exit 1, stderr "MCP server <name> already exists in user config" (no overwrite, no prompt)
|
|
40
|
+
// malformed → exit 1, stderr "Invalid configuration"
|
|
41
|
+
// `claude mcp remove --scope user <name>`
|
|
42
|
+
// present → exit 0
|
|
43
|
+
// absent → exit 1, stderr 'No MCP server named "<name>" in user scope'
|
|
44
|
+
// `claude mcp get <name>`
|
|
45
|
+
// present → exit 0; absent → exit 1
|
|
46
|
+
// `claude mcp list`
|
|
47
|
+
// exit 0 even with dead servers. Line format:
|
|
48
|
+
// `<name>: <command> <args> - <marker> <statusText>` with marker one
|
|
49
|
+
// of "✔ Connected" | "✘ Failed to connect" | "! Needs authentication".
|
|
50
|
+
// Respects CLAUDE_CONFIG_DIR: the user-scope registry then lives at
|
|
51
|
+
// $CLAUDE_CONFIG_DIR/.claude.json instead of ~/.claude.json.
|
|
52
|
+
import { spawn } from "node:child_process";
|
|
53
|
+
import * as fs from "node:fs";
|
|
54
|
+
import * as os from "node:os";
|
|
55
|
+
import * as path from "node:path";
|
|
56
|
+
import { assertNoRealSpawnInTests } from "../runtime/hermetic-spawn-guard.js";
|
|
57
|
+
export const DEFAULT_TIMEOUT_MS = 30_000;
|
|
58
|
+
/**
|
|
59
|
+
* Hermetic guard (task 54739002 primitive, applied here per task
|
|
60
|
+
* 0d80e969): asserts BEFORE touching `child_process` that we are not
|
|
61
|
+
* running under vitest without a test having injected `opts.exec`. This
|
|
62
|
+
* is the ONLY spawn point behind every `claude mcp <verb>` call in this
|
|
63
|
+
* module (add-json/remove/get/list all default to `opts.exec ??
|
|
64
|
+
* realClaudeMcpExec`). An accidental real spawn here talks to the
|
|
65
|
+
* OPERATOR'S REAL Claude Code user-scope MCP registry (`~/.claude.json`
|
|
66
|
+
* or `$CLAUDE_CONFIG_DIR/.claude.json`) — `list`/`get` only read it, but
|
|
67
|
+
* `add-json`/`remove` actually mutate it. See
|
|
68
|
+
* src/runtime/hermetic-spawn-guard.ts for why and the env signal used.
|
|
69
|
+
*
|
|
70
|
+
* `realClaudeMcpExec` has no try/catch around this call, so the thrown
|
|
71
|
+
* `HermeticSpawnViolationError` propagates directly to whichever
|
|
72
|
+
* exported wrapper (addJsonMcpServer/removeMcpServer/getMcpServer/
|
|
73
|
+
* listMcpServers) called it, and from there to every one of THEIR
|
|
74
|
+
* callers (including `ensureMcpServers`). Local "no try/catch here" is
|
|
75
|
+
* not the actual guarantee, though: the OW guard
|
|
76
|
+
* (src/cli/init/interactive.ts) proved a local absence-of-catch
|
|
77
|
+
* argument isn't enough on its own — that violation had to survive a
|
|
78
|
+
* catch further up the call chain. The backstops verified for THIS
|
|
79
|
+
* module's call sites:
|
|
80
|
+
* - init's wire-now path: `wireRuntime`'s own catch
|
|
81
|
+
* (src/cli/init/interactive.ts, the "Failed to wire ..." handler)
|
|
82
|
+
* re-throws a `HermeticSpawnViolationError` before its normal
|
|
83
|
+
* degrade-to-warning-and-retry handling, and runInteractive's outer
|
|
84
|
+
* catch (src/cli/init/interactive.ts, the top-level handler that
|
|
85
|
+
* otherwise treats a caught error as either an `isAbortError`
|
|
86
|
+
* Ctrl-C or a rethrow) re-throws it again past every remaining
|
|
87
|
+
* intermediate handler.
|
|
88
|
+
* - doctor's `buildClaudeMcpRegistration` (src/cli/doctor/claude-mcp.ts)
|
|
89
|
+
* and `doctor()` (src/cli/doctor/index.ts) have no try/catch at all
|
|
90
|
+
* around the `listMcpServers` call, so it propagates unmodified.
|
|
91
|
+
* - uninstall's `removeRegisteredMcpServers` (src/cli/uninstall/
|
|
92
|
+
* index.ts) and `uninstall()` likewise have no try/catch around the
|
|
93
|
+
* `removeMcpServer` call.
|
|
94
|
+
*/
|
|
95
|
+
function realClaudeMcpExec(args, timeoutMs) {
|
|
96
|
+
// Every caller in this module passes >=2 elements (["mcp", <verb>, ...]),
|
|
97
|
+
// so the fallback below is unreached today — kept anyway so a future
|
|
98
|
+
// call site that passes fewer never degrades to a blank/truncated
|
|
99
|
+
// "claude " label in the guard's own error message.
|
|
100
|
+
const verb = args.slice(0, 2).join(" ") || "(no args)";
|
|
101
|
+
assertNoRealSpawnInTests(`claude ${verb}`, "Inject a fake `exec` (ClaudeMcpExec) via opts.exec instead of exercising the real spawn path.");
|
|
102
|
+
return new Promise((resolve) => {
|
|
103
|
+
let child;
|
|
104
|
+
try {
|
|
105
|
+
child = spawn("claude", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const e = err;
|
|
109
|
+
resolve({
|
|
110
|
+
code: 127,
|
|
111
|
+
stdout: "",
|
|
112
|
+
stderr: `spawn failed: ${e.message}`,
|
|
113
|
+
enoent: e.code === "ENOENT",
|
|
114
|
+
timedOut: false,
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
let stdout = "";
|
|
119
|
+
let stderr = "";
|
|
120
|
+
let settled = false;
|
|
121
|
+
const timer = setTimeout(() => {
|
|
122
|
+
if (settled)
|
|
123
|
+
return;
|
|
124
|
+
settled = true;
|
|
125
|
+
child.kill("SIGTERM");
|
|
126
|
+
resolve({ code: -1, stdout, stderr, enoent: false, timedOut: true });
|
|
127
|
+
}, timeoutMs);
|
|
128
|
+
child.stdout?.on("data", (c) => {
|
|
129
|
+
stdout += c.toString("utf8");
|
|
130
|
+
});
|
|
131
|
+
child.stderr?.on("data", (c) => {
|
|
132
|
+
stderr += c.toString("utf8");
|
|
133
|
+
});
|
|
134
|
+
child.on("error", (err) => {
|
|
135
|
+
if (settled)
|
|
136
|
+
return;
|
|
137
|
+
settled = true;
|
|
138
|
+
clearTimeout(timer);
|
|
139
|
+
const e = err;
|
|
140
|
+
resolve({ code: 127, stdout, stderr, enoent: e.code === "ENOENT", timedOut: false });
|
|
141
|
+
});
|
|
142
|
+
child.on("exit", (code) => {
|
|
143
|
+
if (settled)
|
|
144
|
+
return;
|
|
145
|
+
settled = true;
|
|
146
|
+
clearTimeout(timer);
|
|
147
|
+
resolve({ code: code ?? 1, stdout, stderr, enoent: false, timedOut: false });
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// Omit empty args/env so the JSON handed to `add-json` stays minimal
|
|
152
|
+
// (mirrors buildMcpServers in generate-settings.ts).
|
|
153
|
+
function compactSpec(spec) {
|
|
154
|
+
const out = { command: spec.command };
|
|
155
|
+
if (spec.args && spec.args.length > 0)
|
|
156
|
+
out.args = spec.args;
|
|
157
|
+
if (spec.env && Object.keys(spec.env).length > 0)
|
|
158
|
+
out.env = spec.env;
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Wrap a value as a single POSIX shell single-quoted token, escaping any
|
|
163
|
+
* embedded single quote as `'\''`. Used to build the copy-pasteable
|
|
164
|
+
* `claude mcp add-json ... <json>` fallback commands harness prints when the
|
|
165
|
+
* CLI is missing or a server is unregistered, so the operator can paste them
|
|
166
|
+
* verbatim even when a value (e.g. a home path containing an apostrophe, as
|
|
167
|
+
* projected into grounding-mcp's EVIDENCE_LEDGER_DB) has a single quote.
|
|
168
|
+
* Harness never executes these strings itself.
|
|
169
|
+
*/
|
|
170
|
+
export function posixSingleQuote(value) {
|
|
171
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
172
|
+
}
|
|
173
|
+
export async function addJsonMcpServer(name, spec, opts = {}) {
|
|
174
|
+
const exec = opts.exec ?? realClaudeMcpExec;
|
|
175
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
176
|
+
const json = JSON.stringify(compactSpec(spec));
|
|
177
|
+
const r = await exec(["mcp", "add-json", "--scope", "user", name, json], timeoutMs);
|
|
178
|
+
if (r.timedOut) {
|
|
179
|
+
return { status: "timeout", message: `claude mcp add-json timed out after ${timeoutMs}ms`, code: r.code };
|
|
180
|
+
}
|
|
181
|
+
if (r.enoent) {
|
|
182
|
+
return { status: "cli-missing", message: "claude CLI not found on PATH", code: r.code };
|
|
183
|
+
}
|
|
184
|
+
if (r.code === 0) {
|
|
185
|
+
return { status: "added", message: r.stdout.trim(), code: r.code };
|
|
186
|
+
}
|
|
187
|
+
const stderr = r.stderr.trim();
|
|
188
|
+
if (/already exists/i.test(stderr)) {
|
|
189
|
+
return { status: "already-exists", message: stderr, code: r.code };
|
|
190
|
+
}
|
|
191
|
+
if (/invalid configuration/i.test(stderr)) {
|
|
192
|
+
return { status: "invalid-config", message: stderr, code: r.code };
|
|
193
|
+
}
|
|
194
|
+
return { status: "error", message: stderr || `claude mcp add-json exited ${r.code}`, code: r.code };
|
|
195
|
+
}
|
|
196
|
+
export async function removeMcpServer(name, opts = {}) {
|
|
197
|
+
const exec = opts.exec ?? realClaudeMcpExec;
|
|
198
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
199
|
+
const r = await exec(["mcp", "remove", "--scope", "user", name], timeoutMs);
|
|
200
|
+
if (r.timedOut) {
|
|
201
|
+
return { status: "timeout", message: `claude mcp remove timed out after ${timeoutMs}ms`, code: r.code };
|
|
202
|
+
}
|
|
203
|
+
if (r.enoent) {
|
|
204
|
+
return { status: "cli-missing", message: "claude CLI not found on PATH", code: r.code };
|
|
205
|
+
}
|
|
206
|
+
if (r.code === 0) {
|
|
207
|
+
return { status: "removed", message: r.stdout.trim(), code: r.code };
|
|
208
|
+
}
|
|
209
|
+
const stderr = r.stderr.trim();
|
|
210
|
+
if (/No MCP server named/i.test(stderr)) {
|
|
211
|
+
return { status: "not-found", message: stderr, code: r.code };
|
|
212
|
+
}
|
|
213
|
+
return { status: "error", message: stderr || `claude mcp remove exited ${r.code}`, code: r.code };
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* One `claude mcp remove --scope user <name>` line per name, sorted.
|
|
217
|
+
* Sibling of `manualAddJsonLines` in `cli/init/interactive.ts` — the
|
|
218
|
+
* copy-pasteable fallback GC (task 363a6de0) prints when it can't reach
|
|
219
|
+
* the `claude` CLI (or hits some other non-"removed"/"not-found" outcome)
|
|
220
|
+
* to deregister a stale, harness-owned server itself. Uses
|
|
221
|
+
* {@link posixSingleQuote} on the name for the same reason
|
|
222
|
+
* `manualAddJsonLines` quotes its JSON payload: a server name is an
|
|
223
|
+
* ordinary token today, but quoting it costs nothing and keeps the line
|
|
224
|
+
* safe to paste verbatim regardless. Harness never executes these strings
|
|
225
|
+
* itself.
|
|
226
|
+
*/
|
|
227
|
+
export function manualRemoveLines(names) {
|
|
228
|
+
return [...names].sort().map((name) => `claude mcp remove --scope user ${posixSingleQuote(name)}`);
|
|
229
|
+
}
|
|
230
|
+
export async function getMcpServer(name, opts = {}) {
|
|
231
|
+
const exec = opts.exec ?? realClaudeMcpExec;
|
|
232
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
233
|
+
const r = await exec(["mcp", "get", name], timeoutMs);
|
|
234
|
+
if (r.timedOut) {
|
|
235
|
+
return { status: "timeout", raw: "", code: r.code };
|
|
236
|
+
}
|
|
237
|
+
if (r.enoent) {
|
|
238
|
+
return { status: "cli-missing", raw: "", code: r.code };
|
|
239
|
+
}
|
|
240
|
+
if (r.code === 0) {
|
|
241
|
+
return { status: "found", raw: r.stdout, code: r.code };
|
|
242
|
+
}
|
|
243
|
+
// Per the verified contract `get` only ever exits 0 (found) or 1
|
|
244
|
+
// (absent); any other non-zero, non-enoent exit still reads as
|
|
245
|
+
// "not-found" rather than inventing an "error" bucket the contract
|
|
246
|
+
// never demonstrated.
|
|
247
|
+
return { status: "not-found", raw: r.stderr.trim(), code: r.code };
|
|
248
|
+
}
|
|
249
|
+
function markerToStatus(marker) {
|
|
250
|
+
switch (marker) {
|
|
251
|
+
case "✔":
|
|
252
|
+
return "connected";
|
|
253
|
+
case "✘":
|
|
254
|
+
return "failed";
|
|
255
|
+
case "!":
|
|
256
|
+
return "needs-authentication";
|
|
257
|
+
default:
|
|
258
|
+
return "unknown";
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// `<name>: <command> <args...> - <marker> <statusText>`. Greedy `.+` for
|
|
262
|
+
// the command segment finds the RIGHTMOST " - <marker> " separator,
|
|
263
|
+
// which is what we want since the command/args portion (e.g. a URL like
|
|
264
|
+
// `https://...`) may itself contain " - ". The name segment stops at the
|
|
265
|
+
// first ": " (not just ":") so URLs like "https://..." — colon with no
|
|
266
|
+
// following space — never get mistaken for the name/command boundary.
|
|
267
|
+
const LIST_LINE_RE = /^(.+?): (.+) - (✔|✘|!) (.+)$/;
|
|
268
|
+
/**
|
|
269
|
+
* Parse `claude mcp list` stdout. Lines that don't match the expected
|
|
270
|
+
* shape (blank lines, any future banner/preamble) are skipped rather
|
|
271
|
+
* than throwing — the exit code alone tells us the command succeeded.
|
|
272
|
+
*
|
|
273
|
+
* Caveat (same one `buildMcpServers` in generate-settings.ts documents
|
|
274
|
+
* for the inverse direction): splitting the command+args segment on
|
|
275
|
+
* whitespace mis-splits a command path with embedded spaces. Foreign
|
|
276
|
+
* entries whose "command" is a URL (e.g. `claude.ai Gmail: https://... -
|
|
277
|
+
* ! Needs authentication`) still parse correctly since URLs have no
|
|
278
|
+
* embedded whitespace.
|
|
279
|
+
*/
|
|
280
|
+
export function parseClaudeMcpListOutput(stdout) {
|
|
281
|
+
const entries = [];
|
|
282
|
+
for (const rawLine of stdout.split("\n")) {
|
|
283
|
+
const line = rawLine.trim();
|
|
284
|
+
if (line === "")
|
|
285
|
+
continue;
|
|
286
|
+
const m = LIST_LINE_RE.exec(line);
|
|
287
|
+
if (!m)
|
|
288
|
+
continue;
|
|
289
|
+
const name = (m[1] ?? "").trim();
|
|
290
|
+
const commandLine = (m[2] ?? "").trim();
|
|
291
|
+
const marker = m[3] ?? "";
|
|
292
|
+
const statusText = (m[4] ?? "").trim();
|
|
293
|
+
const tokens = commandLine.split(/\s+/).filter((t) => t.length > 0);
|
|
294
|
+
entries.push({
|
|
295
|
+
name,
|
|
296
|
+
command: tokens[0] ?? "",
|
|
297
|
+
args: tokens.slice(1),
|
|
298
|
+
status: markerToStatus(marker),
|
|
299
|
+
statusText,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return entries;
|
|
303
|
+
}
|
|
304
|
+
export async function listMcpServers(opts = {}) {
|
|
305
|
+
const exec = opts.exec ?? realClaudeMcpExec;
|
|
306
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
307
|
+
const r = await exec(["mcp", "list"], timeoutMs);
|
|
308
|
+
if (r.timedOut) {
|
|
309
|
+
return { status: "timeout", servers: [], message: `claude mcp list timed out after ${timeoutMs}ms` };
|
|
310
|
+
}
|
|
311
|
+
if (r.enoent) {
|
|
312
|
+
return { status: "cli-missing", servers: [], message: "claude CLI not found on PATH" };
|
|
313
|
+
}
|
|
314
|
+
if (r.code !== 0) {
|
|
315
|
+
return { status: "error", servers: [], message: r.stderr.trim() || `claude mcp list exited ${r.code}` };
|
|
316
|
+
}
|
|
317
|
+
return { status: "ok", servers: parseClaudeMcpListOutput(r.stdout) };
|
|
318
|
+
}
|
|
319
|
+
// ---------------------------------------------------------------------
|
|
320
|
+
// Layer 2: ensure routine (desired-state reconciliation + opt-in GC)
|
|
321
|
+
// ---------------------------------------------------------------------
|
|
322
|
+
function isRecord(x) {
|
|
323
|
+
return typeof x === "object" && x !== null && !Array.isArray(x);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Resolve the path to the user-scope registry file the `claude` CLI
|
|
327
|
+
* itself reads/writes — the same precedence the CLI applies: an explicit
|
|
328
|
+
* `CLAUDE_CONFIG_DIR` wins (`$CLAUDE_CONFIG_DIR/.claude.json`); otherwise
|
|
329
|
+
* it's `~/.claude.json`, derived here as `path.join(path.dirname(homeDir),
|
|
330
|
+
* ".claude.json")` to match the existing resolution in
|
|
331
|
+
* `src/cli/uninstall/index.ts` (`probeProjectLocalClaudeJson`).
|
|
332
|
+
*/
|
|
333
|
+
export function resolveClaudeUserRegistryPath(opts = {}) {
|
|
334
|
+
const env = opts.env ?? process.env;
|
|
335
|
+
const configDir = env["CLAUDE_CONFIG_DIR"];
|
|
336
|
+
if (typeof configDir === "string" && configDir.length > 0) {
|
|
337
|
+
return path.join(configDir, ".claude.json");
|
|
338
|
+
}
|
|
339
|
+
const homeDir = opts.homeDir ?? path.join(os.homedir(), ".claude");
|
|
340
|
+
return path.join(path.dirname(homeDir), ".claude.json");
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Read strictly the top-level `mcpServers` key of the registry file. Never
|
|
344
|
+
* reads/interprets `projects.<path>.mcpServers` — that's project-local
|
|
345
|
+
* Claude Code state, out of scope here (see `probeProjectLocalClaudeJson`
|
|
346
|
+
* in uninstall for the existing, separate, read-only handling of it).
|
|
347
|
+
*
|
|
348
|
+
* Exported (task 83d8d03a) as the single allowed primitive for reading the
|
|
349
|
+
* effective Claude Code user-scope MCP registration from outside this
|
|
350
|
+
* module — `adopt` (MCP-drift comparison) and `detect` (team/full
|
|
351
|
+
* recognition) both call this directly instead of duplicating a registry
|
|
352
|
+
* reader or spawning `claude mcp list`. Read-only; never writes.
|
|
353
|
+
*/
|
|
354
|
+
export function readTopLevelMcpServers(registryPath) {
|
|
355
|
+
let raw;
|
|
356
|
+
try {
|
|
357
|
+
raw = fs.readFileSync(registryPath, "utf8");
|
|
358
|
+
}
|
|
359
|
+
catch (err) {
|
|
360
|
+
const e = err;
|
|
361
|
+
if (e.code === "ENOENT")
|
|
362
|
+
return { servers: {}, error: null };
|
|
363
|
+
return { servers: {}, error: `cannot read ${registryPath}: ${e.message}` };
|
|
364
|
+
}
|
|
365
|
+
let parsed;
|
|
366
|
+
try {
|
|
367
|
+
parsed = JSON.parse(raw);
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
return { servers: {}, error: `${registryPath} is not valid JSON: ${err.message}` };
|
|
371
|
+
}
|
|
372
|
+
if (!isRecord(parsed)) {
|
|
373
|
+
return { servers: {}, error: `${registryPath} is not a JSON object` };
|
|
374
|
+
}
|
|
375
|
+
const mcp = parsed["mcpServers"];
|
|
376
|
+
if (mcp === undefined)
|
|
377
|
+
return { servers: {}, error: null };
|
|
378
|
+
if (!isRecord(mcp)) {
|
|
379
|
+
return { servers: {}, error: `${registryPath} \`mcpServers\` is not a JSON object` };
|
|
380
|
+
}
|
|
381
|
+
return { servers: mcp, error: null };
|
|
382
|
+
}
|
|
383
|
+
// Equality tolerates a missing `args` on either side as `[]` and a
|
|
384
|
+
// missing `env` on either side as `{}` (constraint: "args-Default []
|
|
385
|
+
// tolerieren"; env gets the same treatment for consistency).
|
|
386
|
+
function specsEqual(existing, desired) {
|
|
387
|
+
if (!isRecord(existing))
|
|
388
|
+
return false;
|
|
389
|
+
if (existing["command"] !== desired.command)
|
|
390
|
+
return false;
|
|
391
|
+
const existingArgs = Array.isArray(existing["args"]) ? existing["args"] : [];
|
|
392
|
+
const desiredArgs = desired.args ?? [];
|
|
393
|
+
if (existingArgs.length !== desiredArgs.length)
|
|
394
|
+
return false;
|
|
395
|
+
for (let i = 0; i < existingArgs.length; i++) {
|
|
396
|
+
if (existingArgs[i] !== desiredArgs[i])
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
const existingEnv = isRecord(existing["env"]) ? existing["env"] : {};
|
|
400
|
+
const desiredEnv = desired.env ?? {};
|
|
401
|
+
const existingEnvKeys = Object.keys(existingEnv).sort();
|
|
402
|
+
const desiredEnvKeys = Object.keys(desiredEnv).sort();
|
|
403
|
+
if (existingEnvKeys.length !== desiredEnvKeys.length)
|
|
404
|
+
return false;
|
|
405
|
+
for (let i = 0; i < existingEnvKeys.length; i++) {
|
|
406
|
+
const k = existingEnvKeys[i];
|
|
407
|
+
if (k !== desiredEnvKeys[i])
|
|
408
|
+
return false;
|
|
409
|
+
if (existingEnv[k] !== desiredEnv[k])
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Reconcile `desired` against the live user-scope registry:
|
|
416
|
+
* - name absent from the registry → `add-json`
|
|
417
|
+
* - name present, spec identical → no-op, NO exec call at all
|
|
418
|
+
* - name present, spec differs (drift) → `remove` then `add-json`
|
|
419
|
+
* The registry is only ever read (for the comparison); every write goes
|
|
420
|
+
* through the CLI wrapper. If the registry file can't be read safely
|
|
421
|
+
* (malformed JSON, `mcpServers` not an object, non-ENOENT read error),
|
|
422
|
+
* every desired server is reported `skipped` with a reason instead of
|
|
423
|
+
* guessing — a missing file (ENOENT, i.e. no registry yet) is NOT an
|
|
424
|
+
* error and is treated as an empty registry.
|
|
425
|
+
*
|
|
426
|
+
* `opts.gc` (task 363a6de0) opts into a second pass over the SAME
|
|
427
|
+
* registry snapshot: every name in `opts.gc.ownedNames` that is currently
|
|
428
|
+
* registered but absent from `desired` gets `claude mcp remove`d — this is
|
|
429
|
+
* how a manifest edit that deletes or disables a `tools.mcp[]` entry
|
|
430
|
+
* propagates into a deregistration on the next ensure run. A name that's
|
|
431
|
+
* owned but ALSO still in `desired` (e.g. an operator hand-registered a
|
|
432
|
+
* drifted copy of a still-active entry) is never a GC candidate — the
|
|
433
|
+
* `desired` membership check excludes it before ownership is even
|
|
434
|
+
* considered, so it's protected automatically, not via special-cased
|
|
435
|
+
* content comparison. Without `opts.gc`, behavior is exactly the pre-GC
|
|
436
|
+
* contract: every registry entry outside `desired` is left alone. If the
|
|
437
|
+
* registry couldn't be read safely, GC is skipped too, mirroring the main
|
|
438
|
+
* loop (`gc.registryReadError` set, `gc.results` empty) — same "never
|
|
439
|
+
* guess" rule.
|
|
440
|
+
*/
|
|
441
|
+
export async function ensureMcpServers(opts) {
|
|
442
|
+
const registryPath = opts.registryPath ?? resolveClaudeUserRegistryPath({ homeDir: opts.homeDir, env: opts.env });
|
|
443
|
+
const { servers: existing, error: readError } = readTopLevelMcpServers(registryPath);
|
|
444
|
+
const callOpts = { exec: opts.exec, timeoutMs: opts.timeoutMs };
|
|
445
|
+
const results = [];
|
|
446
|
+
for (const name of Object.keys(opts.desired).sort()) {
|
|
447
|
+
const spec = opts.desired[name];
|
|
448
|
+
if (readError !== null) {
|
|
449
|
+
results.push({ name, action: "skipped", reason: readError });
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
const current = existing[name];
|
|
453
|
+
if (current === undefined) {
|
|
454
|
+
const add = await addJsonMcpServer(name, spec, callOpts);
|
|
455
|
+
results.push({ name, action: "add", add });
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (specsEqual(current, spec)) {
|
|
459
|
+
results.push({ name, action: "noop" });
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
const remove = await removeMcpServer(name, callOpts);
|
|
463
|
+
if (remove.status !== "removed" && remove.status !== "not-found") {
|
|
464
|
+
// remove failed for a reason unrelated to "already gone" (cli-missing,
|
|
465
|
+
// timeout, or a genuine error) — a follow-up add-json would just fail
|
|
466
|
+
// the same way (or worse, collide), so stop here and report it.
|
|
467
|
+
results.push({ name, action: "replace", remove });
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
const add = await addJsonMcpServer(name, spec, callOpts);
|
|
471
|
+
results.push({ name, action: "replace", remove, add });
|
|
472
|
+
}
|
|
473
|
+
let gc;
|
|
474
|
+
if (opts.gc) {
|
|
475
|
+
if (readError !== null) {
|
|
476
|
+
gc = { results: [], registryReadError: readError };
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
const desiredNames = new Set(Object.keys(opts.desired));
|
|
480
|
+
const owned = new Set(opts.gc.ownedNames);
|
|
481
|
+
// Owned ∧ registered ∧ NOT desired (D-103/D-107 — this module is
|
|
482
|
+
// agnostic to how the caller built `ownedNames`). `desired`
|
|
483
|
+
// membership is checked first, so a still-active manifest entry is
|
|
484
|
+
// protected regardless of whether its registered content has
|
|
485
|
+
// drifted.
|
|
486
|
+
const candidates = Object.keys(existing)
|
|
487
|
+
.filter((name) => owned.has(name) && !desiredNames.has(name))
|
|
488
|
+
.sort();
|
|
489
|
+
const gcResults = [];
|
|
490
|
+
for (const name of candidates) {
|
|
491
|
+
const remove = await removeMcpServer(name, callOpts);
|
|
492
|
+
if (remove.status === "removed" || remove.status === "not-found") {
|
|
493
|
+
gcResults.push({ name, action: "removed", remove });
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
gcResults.push({ name, action: "skipped", remove, reason: remove.message });
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
gc = { results: gcResults };
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return { registryPath, results, ...(gc !== undefined ? { gc } : {}) };
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Remove exactly the given owned names from `settings.mcpServers`,
|
|
506
|
+
* preserve every foreign entry, drop the `mcpServers` key entirely when
|
|
507
|
+
* it becomes empty, and leave every other top-level key byte-identical
|
|
508
|
+
* (same values, same insertion order). Pure — no file I/O; callers own
|
|
509
|
+
* reading/writing settings.json.
|
|
510
|
+
*/
|
|
511
|
+
export function stripOwnedMcpServers(settings, ownedNames) {
|
|
512
|
+
const mcpServers = settings["mcpServers"];
|
|
513
|
+
if (!isRecord(mcpServers)) {
|
|
514
|
+
// Absent or malformed `mcpServers`: nothing this function owns to
|
|
515
|
+
// strip. Returning the same reference keeps the "byte-identical"
|
|
516
|
+
// guarantee trivially true.
|
|
517
|
+
return { settings, removedNames: [] };
|
|
518
|
+
}
|
|
519
|
+
const owned = new Set(ownedNames);
|
|
520
|
+
const kept = {};
|
|
521
|
+
const removed = [];
|
|
522
|
+
for (const [name, value] of Object.entries(mcpServers)) {
|
|
523
|
+
if (owned.has(name)) {
|
|
524
|
+
removed.push(name);
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
kept[name] = value;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (removed.length === 0) {
|
|
531
|
+
return { settings, removedNames: [] };
|
|
532
|
+
}
|
|
533
|
+
// Spread preserves existing key order; reassigning `mcpServers` in
|
|
534
|
+
// place keeps its original position instead of moving it to the end.
|
|
535
|
+
const out = { ...settings };
|
|
536
|
+
if (Object.keys(kept).length === 0) {
|
|
537
|
+
delete out["mcpServers"];
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
out["mcpServers"] = kept;
|
|
541
|
+
}
|
|
542
|
+
return { settings: out, removedNames: removed.sort() };
|
|
543
|
+
}
|
|
544
|
+
//# sourceMappingURL=claude-mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-mcp.js","sourceRoot":"","sources":["../../src/io/claude-mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -42,6 +42,19 @@ export interface LedgerSessionQuery {
|
|
|
42
42
|
sessionId: string;
|
|
43
43
|
sinceIso?: string;
|
|
44
44
|
contentPrefix?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Optional per-call override of the session's `timeoutMs` (task
|
|
47
|
+
* 2026-07-18 subprocess-test-deflake, T-003). Applies only to this
|
|
48
|
+
* call's `ledger_summary` round-trip (the `tools/list` capability
|
|
49
|
+
* probe, only run when `sinceIso`/`contentPrefix` is set, still uses
|
|
50
|
+
* the session default). Omitted, behaviour is byte-for-byte identical
|
|
51
|
+
* to before this option existed: the session's own `timeoutMs` is
|
|
52
|
+
* used. A timeout on an overridden call still sets the session-wide
|
|
53
|
+
* latch exactly like a timeout on the session default would — see
|
|
54
|
+
* `LedgerSession.callTool`'s `options.timeoutMs` doc for the shared
|
|
55
|
+
* latch contract and the enforcement-path cost-bound caveat.
|
|
56
|
+
*/
|
|
57
|
+
timeoutMs?: number;
|
|
45
58
|
}
|
|
46
59
|
/**
|
|
47
60
|
* Result of a generic `LedgerSession.callTool` round-trip. Three-way so a
|
|
@@ -73,7 +86,27 @@ export type LedgerSessionCallResult = {
|
|
|
73
86
|
*/
|
|
74
87
|
export interface LedgerSession {
|
|
75
88
|
querySummary(query: LedgerSessionQuery): Promise<LedgerQueryResult>;
|
|
76
|
-
|
|
89
|
+
/**
|
|
90
|
+
* `options.timeoutMs` (task 2026-07-18 subprocess-test-deflake, T-003)
|
|
91
|
+
* is an optional per-call override of the session's `timeoutMs` for
|
|
92
|
+
* THIS call only; every other call on the session keeps using the
|
|
93
|
+
* session default. Omitted, behaviour is byte-for-byte identical to
|
|
94
|
+
* before this option existed. Fail-open/degrade semantics and the
|
|
95
|
+
* session-wide timeout latch are unchanged either way: a timeout on an
|
|
96
|
+
* overridden call latches the session exactly like a timeout on the
|
|
97
|
+
* session default does — every subsequent call (on this session, with
|
|
98
|
+
* or without its own override) short-circuits to `degraded` without a
|
|
99
|
+
* new round-trip.
|
|
100
|
+
*
|
|
101
|
+
* Cost-bound caveat (OKF gate-fail-posture-matrix): the latch's promise
|
|
102
|
+
* that a dead ledger costs a session at most ~1× its `timeoutMs` holds
|
|
103
|
+
* only while per-call overrides on the enforcement critical path stay
|
|
104
|
+
* ≤ the session default. The override exists for tests and non-critical
|
|
105
|
+
* callers; do not pass a LARGER override on the PreToolUse gate path.
|
|
106
|
+
*/
|
|
107
|
+
callTool(name: string, args: Record<string, unknown>, options?: {
|
|
108
|
+
timeoutMs?: number;
|
|
109
|
+
}): Promise<LedgerSessionCallResult>;
|
|
77
110
|
dispose(): void;
|
|
78
111
|
}
|
|
79
112
|
export declare function openLedgerSession(opts: LedgerClientOptions): LedgerSession;
|