Skip to content

Test#525

Merged
plind-junior merged 5 commits into
mainfrom
test
Jul 18, 2026
Merged

Test#525
plind-junior merged 5 commits into
mainfrom
test

Conversation

@plind-junior

@plind-junior plind-junior commented Jul 18, 2026

Copy link
Copy Markdown
Member

What changed

Why

What might break

VEP

Tests

  • make check passes locally (lint + mypy + pytest)
  • New / changed behaviour has a test
  • CHANGELOG.md updated under ## [Unreleased]

Summary by CodeRabbit

  • New Features
    • vouch install-mcp claude-code now registers the MCP server locally, with options to approve or skip registration.
    • Receipt-verified claims are automatically approved by default.
    • Pending receipt-backed claims are processed at session start.
  • Bug Fixes
    • Duplicate claims are safely rejected without re-queuing or crashes.
    • Genuine claim conflicts remain pending for human review.
  • Documentation
    • Updated setup, approval, verification, and configuration guidance for Claude Code and receipt-based approvals.

the starter config now ships review.auto_approve_on_receipt: true (and
require_human_approval: false, an advisory key no code path reads): a
fresh kb auto-approves captured claims whose byte-offset receipts
verify, so recall works out of the box with no review pass. pages,
entities, relations, and claims that cannot quote their source still
wait for a human — the gate is structurally unchanged.

making the default real exposed two holes, both fixed here:

- a claim re-deriving text that is already durable crashed the drain
  ("cannot approve: claim … already exists") and, on the capture-answer
  path, was swallowed and left pending forever — every session that
  restated a known fact grew the queue. resolve_pending_receipt_claim
  now closes such duplicates mechanically (rejected, "duplicate:
  identical claim already durable"); an id held by different text is a
  real conflict and stays pending for a human. capture answer and the
  drain share this resolution.

- nothing ever drained a backlog of verifiable claims filed while the
  gate was off. finalize-all (the sessionstart hook) now runs the
  drain, so flipping the flag on an existing kb heals it on the next
  session start.

observed in the field: a fresh 1.4.0 install whose rollup and repeated
answers piled up pending despite the flag being on.
feat(review): receipt auto-approve on by default, duplicate-safe drain
…tension connects

following the readme in a fresh project did not work in the claude code
vs code extension: `install-mcp` wrote only a project-scope `.mcp.json`,
which claude code loads only after a per-user approval — and the
extension never surfaces that prompt. so the server sat at "pending
approval", the kb_* tools never appeared, and the hooks (which need no
approval) kept running, which reads as connected but isn't. reproduced
end-to-end on a fresh project with released 1.4.0.

install-mcp now also registers vouch at local scope in ~/.claude.json
(`projects[<abs>].mcpServers`), exactly what `claude mcp add` does — a
local-scope server is trusted on sight, no approval gate. declared by a
new manifest `user_mcp:` block so the quirk lives in the claude-code
adapter, not the core (host-neutral: only claude-code opts in). the
merge is idempotent, preserves every other key in ~/.claude.json, and
never clobbers a `vouch` server the user added themselves; a malformed
config is reported as failed, never crashes the install. `--no-approve`
opts out. the cli prints the registration and a reload-your-window
nudge (mcp servers load once, at window start).

verified end-to-end through the shipped cli: fresh project ->
`vouch install-mcp claude-code` -> `claude mcp list` reports
`vouch ... ✔ Connected` (was `⏸ Pending approval`), and an mcp
handshake exposes all eight kb_* tools.

tests sandbox ~/.claude.json via an autouse home fixture so the suite
(and the many existing install() calls) never touch the developer's
real config. readme + adapter readme now document the one-command flow
and the reload; changelog updated.
feat(install-mcp): register a local-scope mcp server so the vscode extension connects
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This change adds Claude Code MCP registration through ~/.claude.json and introduces receipt-based automatic claim approval. Session finalization drains pending receipt-backed claims, rejects durable duplicates, preserves conflicting claims as pending, and documents the updated defaults and installation workflow.

Changes

Claude MCP registration

Layer / File(s) Summary
MCP manifest contract
adapters/claude-code/install.yaml, src/vouch/install_adapter.py
Adapter manifests declare validated user-scope MCP registrations, and installation results report registrations separately.
User configuration registration
src/vouch/install_adapter.py, src/vouch/cli.py
install-mcp adds approval control and idempotently merges the vouch serve definition into project or user configuration.
MCP verification and documentation
tests/test_install_adapter.py, README.md, adapters/claude-code/README.md, CHANGELOG.md
Tests cover idempotency, preservation, malformed configuration, and traversal rejection; documentation describes reload and verification steps.

Receipt-gated approval

Layer / File(s) Summary
Receipt policy and claim resolution
src/vouch/storage.py, src/vouch/proposals.py
Starter configuration enables receipt auto-approval, while claim resolution handles eligible claims, duplicates, and conflicting identities.
Capture and session-start draining
src/vouch/capture.py, src/vouch/cli.py
Captured claims use the shared resolver, and session finalization drains eligible pending receipts while returning an auto_approved count.
Receipt behavior validation
tests/test_*.py, README.md, docs/getting-started.md, adapters/claude-code/.claude/settings.json, CHANGELOG.md
Tests cover default and disabled gates, backlog draining, recapture duplicates, and conflicts; related approval guidance is updated.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ClaudeCode
  participant capture_answer
  participant resolve_pending_receipt_claim
  participant KBStore
  ClaudeCode->>capture_answer: capture answer
  capture_answer->>resolve_pending_receipt_claim: resolve receipt-backed claim
  resolve_pending_receipt_claim->>KBStore: check durable claim
  KBStore-->>resolve_pending_receipt_claim: claim identity result
  resolve_pending_receipt_claim-->>capture_answer: approved, rejected duplicate, or pending
Loading
sequenceDiagram
  participant User
  participant install_mcp
  participant install
  participant claude_json
  User->>install_mcp: run install-mcp
  install_mcp->>install: pass approve option
  install->>claude_json: register vouch MCP server
  claude_json-->>install: registration result
  install-->>install_mcp: report registered or failed
Loading

Possibly related PRs

  • vouchdev/vouch#522 — Closely overlaps the receipt auto-approval, duplicate handling, backlog draining, and configuration changes.
  • vouchdev/vouch#523 — Directly overlaps the Claude Code local-scope MCP registration implementation and tests.
  • vouchdev/vouch#516 — Modifies the same install-mcp bootstrap flow extended here with approval control.

Suggested labels: mcp, ci: passing

Suggested reviewers: dripsmvcp

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too generic and does not describe the actual changes in the pull request. Replace it with a concise, specific summary of the main change, such as receipt auto-approval and Claude Code MCP registration.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added docs documentation, specs, examples, and repo guidance cli command line interface adapters agent host adapters and install manifests storage kb storage, migrations, schemas, and proposals tests tests and fixtures size: L 500-999 changed non-doc lines labels Jul 18, 2026
@github-actions github-actions Bot added the ci: passing ci is green label Jul 18, 2026
@plind-junior
plind-junior merged commit d1a0dee into main Jul 18, 2026
16 of 20 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
src/vouch/proposals.py (3)

504-510: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Avoid evaluating None to "None".

If proposal.payload.get("id") explicitly returns None, str(None) will result in the string "None", which could theoretically match a claim actually named "None". It is safer to check for truthiness before casting to string.

🛠️ Proposed refactor
-    claim_id = str(proposal.payload.get("id", ""))
+    claim_id = proposal.payload.get("id")
     existing: Claim | None = None
     if claim_id:
         try:
-            existing = store.get_claim(claim_id)
+            existing = store.get_claim(str(claim_id))
         except ArtifactNotFoundError:
             existing = None
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/proposals.py` around lines 504 - 510, Update the claim ID
initialization in the proposal lookup flow to check the payload’s raw id value
for truthiness before converting it to a string, ensuring an explicit None
produces an empty claim_id rather than "None". Preserve the existing get_claim
and ArtifactNotFoundError handling for valid IDs.

481-484: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Cache _review_config to avoid repeated disk I/O.

resolve_pending_receipt_claim is called inside a loop over all pending proposals in auto_approve_receipts, and inside a loop over extracted claims in capture_answer. Since _review_config(store) reads and parses config.yaml from disk on every call, this introduces unnecessary I/O overhead. Consider accepting review_cfg as an optional parameter to reuse the configuration across loop iterations.

⚡ Proposed refactor
 def resolve_pending_receipt_claim(
-    store: KBStore, proposal: Proposal, *, actor: str, reason: str
+    store: KBStore, proposal: Proposal, *, actor: str, reason: str,
+    review_cfg: dict[str, Any] | None = None
 ) -> Claim | None:
     """Mechanically decide one pending CLAIM proposal, honouring the gate.

Inside the function:

-    review_cfg = _review_config(store)
+    review_cfg = review_cfg if review_cfg is not None else _review_config(store)

And update the caller in auto_approve_receipts:

-    if not _review_config(store).get("auto_approve_on_receipt"):
+    review_cfg = _review_config(store)
+    if not review_cfg.get("auto_approve_on_receipt"):
         return []
     approved: list[Claim] = []
     for proposal in store.list_proposals(ProposalStatus.PENDING):
         claim = resolve_pending_receipt_claim(
             store, proposal,
             actor=actor or proposal.proposed_by,
             reason="receipt verified — auto-approved",
+            review_cfg=review_cfg
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/proposals.py` around lines 481 - 484, Update
resolve_pending_receipt_claim to accept an optional review_cfg parameter and
reuse it when provided, only calling _review_config(store) when absent. In
auto_approve_receipts and capture_answer, load the review configuration once per
loop operation and pass it into each resolve_pending_receipt_claim call.

497-503: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Optimize evaluation for trusted agents.

Currently, _claim_receipts_verify is evaluated even when trusted is true. Since this verification reads from the disk and performs string matching, it should be skipped if trusted is already true.

⚡ Proposed refactor
     trusted = review_cfg.get("approver_role") == "trusted-agent"
-    receipted = bool(
-        review_cfg.get("auto_approve_on_receipt")
-    ) and _claim_receipts_verify(store, proposal)
-    if not (trusted or receipted):
-        return None
+    if not trusted:
+        receipted = bool(review_cfg.get("auto_approve_on_receipt")) and _claim_receipts_verify(store, proposal)
+        if not receipted:
+            return None
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/proposals.py` around lines 497 - 503, Update the approval condition
in the proposal flow to short-circuit receipt verification when trusted is true:
evaluate _claim_receipts_verify only if the approver is not trusted, while
preserving the existing auto_approve_on_receipt requirement for non-trusted
agents.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 151-152: Qualify the approval behavior in README.md lines 151-152
by stating that setting review.auto_approve_on_receipt to false disables
receipt-based auto-approval, while trusted-agent policy may still approve
eligible proposals. Update CHANGELOG.md lines 26-35 to replace any “fully human
gate” wording with “disable receipt-based auto-approval” or explicitly include
the trusted-agent exception.
- Around line 126-128: Update the README approval instructions to apply only to
manual wiring or installations using --no-approve. Clarify that the default
install-mcp flow already registers the trusted local-scope server, and remove
any wording implying install-mcp cannot perform that registration; retain
approval steps for users who skip automatic registration.

In `@src/vouch/install_adapter.py`:
- Around line 249-260: Update the `_UserMcp` manifest-loading validation to
require `scope` to be a string before checking it against the allowed values,
preserving `AdapterError` for invalid YAML types. Validate `spec` for JSON
serializability during loading and convert serialization failures into
`AdapterError`, so invalid values cannot reach registration; apply the same
validation to the additional user MCP parsing path.
- Around line 390-427: Protect the complete configuration read-modify-write flow
in the installer method containing the reg.scope and reg.name checks with an
interprocess lock held from reading cfg_path through tmp.replace(cfg_path). Use
a unique temporary file created in cfg_path.parent for each write, preserving
same-directory atomic replacement and cleaning it up appropriately, so
concurrent installers or Claude Code updates cannot be overwritten.
- Around line 415-419: Update the registration logic around the servers check in
the install-mcp flow to compare the existing mcpServers.vouch specification with
the expected server configuration. Return normally only for an identical
specification; record a differing existing specification as a conflict while
preserving the user’s entry and avoiding result.skipped, so the CLI does not
report it as successfully registered.

---

Nitpick comments:
In `@src/vouch/proposals.py`:
- Around line 504-510: Update the claim ID initialization in the proposal lookup
flow to check the payload’s raw id value for truthiness before converting it to
a string, ensuring an explicit None produces an empty claim_id rather than
"None". Preserve the existing get_claim and ArtifactNotFoundError handling for
valid IDs.
- Around line 481-484: Update resolve_pending_receipt_claim to accept an
optional review_cfg parameter and reuse it when provided, only calling
_review_config(store) when absent. In auto_approve_receipts and capture_answer,
load the review configuration once per loop operation and pass it into each
resolve_pending_receipt_claim call.
- Around line 497-503: Update the approval condition in the proposal flow to
short-circuit receipt verification when trusted is true: evaluate
_claim_receipts_verify only if the approver is not trusted, while preserving the
existing auto_approve_on_receipt requirement for non-trusted agents.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b402f358-a941-4dc4-9258-08ebd55384fb

📥 Commits

Reviewing files that changed from the base of the PR and between 5730cca and 86a90f7.

📒 Files selected for processing (16)
  • CHANGELOG.md
  • README.md
  • adapters/claude-code/.claude/settings.json
  • adapters/claude-code/README.md
  • adapters/claude-code/install.yaml
  • docs/getting-started.md
  • src/vouch/capture.py
  • src/vouch/cli.py
  • src/vouch/install_adapter.py
  • src/vouch/proposals.py
  • src/vouch/storage.py
  • tests/test_capture.py
  • tests/test_capture_answer.py
  • tests/test_extract.py
  • tests/test_install_adapter.py
  • tests/test_receipt_auto_approve.py

Comment thread README.md
Comment on lines +126 to +128
`install-mcp` initialises the KB when no `.vouch/` is discoverable (pass `--no-init` to skip; `vouch init` still exists for KB-only setup), then writes `.mcp.json` (the `kb.*` MCP tools), the `/vouch-*` slash commands, and five hooks — `SessionStart` recall, `UserPromptSubmit` per-prompt recall, `PostToolUse` capture, `Stop` answer capture, `SessionEnd` rollup. It also registers vouch as a local-scope MCP server in `~/.claude.json` (the `⚑` line in the output). **Reload your editor window** (VS Code: *Developer: Reload Window*) so it loads.

> **Why the extra registration?** A committed `.mcp.json` is a *project*-scope server, and Claude Code only loads one after a per-user approval — which the **VS Code extension never prompts for**, so `.mcp.json` alone leaves the `kb_*` tools invisible in the extension (they sit at "pending approval", while the hooks quietly work — easy to misread as "connected"). The local-scope entry `install-mcp` writes is trusted on sight, so a fresh install just connects. Verify with `claude mcp list` (`vouch … ✔ Connected`). Pass `--no-approve` to skip it and approve `.mcp.json` yourself.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

scope the following approval instructions to manual installs.

this paragraph says the default command registers a trusted local server, but the next “one-time approval” block still instructs every reader to approve .mcp.json and says install-mcp cannot perform the step. Mark that block as applying only to --no-approve or manual wiring.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 126 - 128, Update the README approval instructions to
apply only to manual wiring or installations using --no-approve. Clarify that
the default install-mcp flow already registers the trusted local-scope server,
and remove any wording implying install-mcp cannot perform that registration;
retain approval steps for users who skip automatic registration.

Comment thread README.md
Comment on lines +151 to +152
Receipt-verified claims skip the queue by default (`review.auto_approve_on_receipt: true` in the starter config): each session's captured answers become recallable memory with no review pass. What lands in `vouch review` is everything the mechanical check can't vouch for — session-summary pages, entities, relations, and claims that can't quote their source. Set the flag to `false` in `.vouch/config.yaml` to put every write behind the gate.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

qualify the “fully human gate” claim.

disabling review.auto_approve_on_receipt does not guarantee that every write requires human approval when the opt-in trusted-agent shortcut remains enabled.

  • README.md#L151-L152: say the flag disables receipt-based approval, while trusted-agent policy may still approve eligible proposals.
  • CHANGELOG.md#L26-L35: replace “fully human gate” with “disable receipt-based auto-approval,” or include the trusted-agent exception.
📍 Affects 2 files
  • README.md#L151-L152 (this comment)
  • CHANGELOG.md#L26-L35
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 151 - 152, Qualify the approval behavior in README.md
lines 151-152 by stating that setting review.auto_approve_on_receipt to false
disables receipt-based auto-approval, while trusted-agent policy may still
approve eligible proposals. Update CHANGELOG.md lines 26-35 to replace any
“fully human gate” wording with “disable receipt-based auto-approval” or
explicitly include the trusted-agent exception.

Comment on lines +249 to +260
scope = raw.get("scope", "project")
if scope not in {"project", "user"}:
raise AdapterError(
f"{host}: install.yaml user_mcp `scope` must be 'project' or 'user', got {scope!r}"
)
name = raw.get("name")
if not isinstance(name, str) or not name.strip():
raise AdapterError(f"{host}: install.yaml user_mcp needs a non-empty `name`")
spec = raw.get("spec")
if not isinstance(spec, dict):
raise AdapterError(f"{host}: install.yaml user_mcp `spec` must be a mapping")
return _UserMcp(config=config, scope=scope, name=name, spec=spec)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

validate every yaml value before constructing _UserMcp.

a list or mapping supplied as scope raises TypeError at set membership instead of AdapterError. similarly, yaml permits values in spec that json.dumps() cannot serialize, and that TypeError is not caught during registration. validate scope as a string and verify that spec is json-serializable while loading the manifest.

as per coding guidelines, “install.yaml files for MCP host adapters must use strict YAML manifest validation.”

proposed fix
     scope = raw.get("scope", "project")
-    if scope not in {"project", "user"}:
+    if not isinstance(scope, str) or scope not in {"project", "user"}:
         raise AdapterError(
             f"{host}: install.yaml user_mcp `scope` must be 'project' or 'user', got {scope!r}"
         )
...
     if not isinstance(spec, dict):
         raise AdapterError(f"{host}: install.yaml user_mcp `spec` must be a mapping")
+    try:
+        json.dumps(spec)
+    except (TypeError, ValueError) as e:
+        raise AdapterError(
+            f"{host}: install.yaml user_mcp `spec` must contain JSON-compatible values"
+        ) from e

Also applies to: 420-432

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/install_adapter.py` around lines 249 - 260, Update the `_UserMcp`
manifest-loading validation to require `scope` to be a string before checking it
against the allowed values, preserving `AdapterError` for invalid YAML types.
Validate `spec` for JSON serializability during loading and convert
serialization failures into `AdapterError`, so invalid values cannot reach
registration; apply the same validation to the additional user MCP parsing path.

Source: Coding guidelines

Comment on lines +390 to +427
if cfg_path.exists():
raw = cfg_path.read_text(encoding="utf-8")
data = json.loads(raw) if raw.strip() else {}
if not isinstance(data, dict):
result.failed.append(label)
return
else:
data = {}

if reg.scope == "project":
projects = data.setdefault("projects", {})
if not isinstance(projects, dict):
result.failed.append(label)
return
container = projects.setdefault(str(target), {})
else:
container = data
if not isinstance(container, dict):
result.failed.append(label)
return
servers = container.setdefault("mcpServers", {})
if not isinstance(servers, dict):
result.failed.append(label)
return

if reg.name in servers:
# already registered — never clobber the user's own edits, and stay
# out of `result.skipped` (a file-path channel; this is a config
# entry, not a file). The CLI reminds the user to reload regardless.
return
servers[reg.name] = dict(reg.spec)

cfg_path.parent.mkdir(parents=True, exist_ok=True)
tmp = cfg_path.with_name(cfg_path.name + ".vouch-tmp")
tmp.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
if cfg_path.exists():
shutil.copymode(cfg_path, tmp)
tmp.replace(cfg_path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

protect the read-modify-write from concurrent updates.

the atomic replacement prevents partial files, but it does not prevent lost updates: claude code or another installer can modify .claude.json after line 391, and line 427 then replaces those changes with the stale snapshot. lock the configuration across read, merge, and replace, and use a unique temporary file in the same directory.

🧰 Tools
🪛 ast-grep (0.44.1)

[info] 423-423: use jsonify instead of json.dumps for JSON output
Context: json.dumps(data, indent=2)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/install_adapter.py` around lines 390 - 427, Protect the complete
configuration read-modify-write flow in the installer method containing the
reg.scope and reg.name checks with an interprocess lock held from reading
cfg_path through tmp.replace(cfg_path). Use a unique temporary file created in
cfg_path.parent for each write, preserving same-directory atomic replacement and
cleaning it up appropriately, so concurrent installers or Claude Code updates
cannot be overwritten.

Comment on lines +415 to +419
if reg.name in servers:
# already registered — never clobber the user's own edits, and stay
# out of `result.skipped` (a file-path channel; this is a config
# entry, not a file). The CLI reminds the user to reload regardless.
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

report a conflicting existing server instead of treating it as registered.

any existing mcpServers.vouch entry is silently accepted, even when it points to another command. consequently, install-mcp prints “vouch is registered” although the expected server was not installed. treat an identical specification as idempotent, but record a differing specification as a conflict without overwriting it.

proposed fix
         if reg.name in servers:
-            # already registered — never clobber the user's own edits, and stay
-            # out of `result.skipped` (a file-path channel; this is a config
-            # entry, not a file). The CLI reminds the user to reload regardless.
+            if servers[reg.name] != reg.spec:
+                result.failed.append(label)
             return
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if reg.name in servers:
# already registered — never clobber the user's own edits, and stay
# out of `result.skipped` (a file-path channel; this is a config
# entry, not a file). The CLI reminds the user to reload regardless.
return
if reg.name in servers:
if servers[reg.name] != reg.spec:
result.failed.append(label)
return
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vouch/install_adapter.py` around lines 415 - 419, Update the registration
logic around the servers check in the install-mcp flow to compare the existing
mcpServers.vouch specification with the expected server configuration. Return
normally only for an identical specification; record a differing existing
specification as a conflict while preserving the user’s entry and avoiding
result.skipped, so the CLI does not report it as successfully registered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

adapters agent host adapters and install manifests ci: passing ci is green cli command line interface docs documentation, specs, examples, and repo guidance size: L 500-999 changed non-doc lines storage kb storage, migrations, schemas, and proposals tests tests and fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant