Skip to content

feat(install-mcp): register a local-scope mcp server so the vscode extension connects#523

Merged
plind-junior merged 1 commit into
testfrom
feat/install-mcp-user-scope
Jul 18, 2026
Merged

feat(install-mcp): register a local-scope mcp server so the vscode extension connects#523
plind-junior merged 1 commit into
testfrom
feat/install-mcp-user-scope

Conversation

@plind-junior

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

Copy link
Copy Markdown
Member

the bug, reproduced

following the readme in a fresh project does not work in the claude code vs code extension. install-mcp writes only a project-scope .mcp.json, and claude code loads a project-scope server only after a per-user approval — which the extension never surfaces a prompt for. so the server sits at ⏸ pending approval, the kb_* tools never appear, and the capture hooks (which need no approval) keep running — which reads as "connected" but isn't.

reproduced end-to-end on a clean /tmp project with released 1.4.0:

$ vouch init && vouch install-mcp claude-code   # writes .mcp.json + hooks
$ claude mcp list
vouch: vouch serve - ⏸ Pending approval          # extension shows nothing

proof this is the whole story: in the vouch repo itself the .mcp.json server is unapproved (enabledMcpjsonServers: []) yet vouch works in the extension — because it also has a local-scope mcpServers entry in ~/.claude.json. that entry, not .mcp.json, is what the extension loads.

the fix

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, so a fresh install just connects.

  • 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.
  • idempotent; preserves every other key in ~/.claude.json; never clobbers a vouch server you added yourself; a malformed config is reported as failed, never crashes the install.
  • --no-approve opts out (keeps only .mcp.json).
  • 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

$ vouch install-mcp claude-code
  ⚑ .claude.json:vouch (mcp)  (registered in ~/.claude.json)
  Reload your editor window ...
$ claude mcp list
vouch: vouch serve - ✔ Connected                 # was ⏸ Pending approval

and an mcp initialize + tools/list handshake against the registered server returns all eight kb_* tools.

tests

new test_install_adapter.py cases pin: fresh registration writes the byte-exact working spec, idempotent (no dup, file untouched), --no-approve writes nothing, existing ~/.claude.json content preserved, a user's own vouch server never clobbered, malformed config reported not raised, and a manifest user_mcp.config traversal rejected. an autouse home-sandbox fixture makes the whole module (and the many existing install() calls, now approve=True by default) never touch the developer's real ~/.claude.json.

make check green (pytest, mypy, ruff). readme + adapter readme document the one-command flow and the reload; changelog updated.

note on the older docs PRs

this supersedes the manual-workaround guidance in #521 (which documented a .claude/settings.local.json approval that i only verified against the terminal cli, not the extension). recommend closing #521 in favour of this; the readme change here is the correct, extension-verified story.

Summary by CodeRabbit

  • New Features

    • vouch install-mcp claude-code now automatically registers the MCP server for the current project, helping Claude Code tools appear without manual approval.
    • Added --no-approve to skip automatic registration.
    • Registration is safe to repeat, preserves existing configuration, and does not overwrite user-added servers.
    • Installation reports registration results and provides reload guidance.
  • Documentation

    • Updated setup instructions and troubleshooting guidance for Claude Code and the VS Code extension.

…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.
@github-actions github-actions Bot added docs documentation, specs, examples, and repo guidance cli command line interface adapters agent host adapters and install manifests tests tests and fixtures size: M 200-499 changed non-doc lines ci: passing ci is green labels Jul 17, 2026
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

install-mcp now optionally registers the Claude Code MCP server in ~/.claude.json, with validated manifest data, idempotent atomic updates, failure reporting, CLI approval controls, tests, and updated setup documentation.

Changes

Claude Code MCP registration

Layer / File(s) Summary
Manifest registration contract
adapters/claude-code/install.yaml, src/vouch/install_adapter.py
The adapter declares a project-scoped vouch MCP server, while manifest parsing validates its path, scope, name, and spec.
User configuration registration
src/vouch/install_adapter.py, tests/test_install_adapter.py
Approved installs merge MCP entries into ~/.claude.json without overwriting existing servers, use atomic writes, report failures, and test idempotency, preservation, opt-out, malformed JSON, and traversal rejection.
CLI reporting and setup guidance
src/vouch/cli.py, README.md, adapters/claude-code/README.md, CHANGELOG.md
The CLI adds --approve/--no-approve, reports registrations, and documents editor reload and pending-approval handling.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant vouch_install_mcp
  participant install_adapter
  participant claude_json
  User->>vouch_install_mcp: install-mcp claude-code
  vouch_install_mcp->>install_adapter: install(approve)
  install_adapter->>claude_json: register project MCP server
  claude_json-->>install_adapter: registration result
  install_adapter-->>vouch_install_mcp: InstallResult
  vouch_install_mcp-->>User: counts and reload guidance
Loading

Possibly related PRs

  • vouchdev/vouch#151: Earlier installer workflow and install_adapter.py/CLI plumbing used by this registration change.
  • vouchdev/vouch#516: Both modify install-mcp CLI behavior and its installation flow.
  • vouchdev/vouch#521: Related Claude Code MCP approval and pending-approval documentation.

Suggested labels: mcp

Suggested reviewers: dripsmvcp

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding local-scope MCP registration so the VS Code extension can connect.
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 feat/install-mcp-user-scope

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.

@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: 2

🤖 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 `@src/vouch/install_adapter.py`:
- Around line 415-419: In src/vouch/install_adapter.py lines 415-419, update the
existing-server handling to compare the registered specification: return the
normal empty result only for an exact match, and report differing entries
through the established conflict or failure mechanism. In src/vouch/cli.py lines
4050-4055, show the registered reminder only for exact matches and surface
conflicts explicitly, using the installer result to distinguish these states.
- Around line 422-427: Update the config replacement flow around cfg_path and
tmp to use an exclusive temporary file created in the same directory via mkstemp
or NamedTemporaryFile(delete=False), rather than the fixed .vouch-tmp name. Set
the temporary file mode before writing config contents, replace cfg_path only
after a successful write, and remove the temporary file in a finally block on
all paths.
🪄 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: bc5a4cdd-346d-4e5b-9fda-ebd50dc69723

📥 Commits

Reviewing files that changed from the base of the PR and between dec9e2b and cc4de5d.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • README.md
  • adapters/claude-code/README.md
  • adapters/claude-code/install.yaml
  • src/vouch/cli.py
  • src/vouch/install_adapter.py
  • tests/test_install_adapter.py

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

distinguish an identical registration from a name conflict.

the installer currently emits the same empty result for both states, causing the cli to claim success when a user-owned server blocks vouch.

  • src/vouch/install_adapter.py#L415-L419: compare the existing specification; preserve exact matches, but report differing entries as conflicts or failures.
  • src/vouch/cli.py#L4050-L4055: show the registered reminder only for an exact existing match, and surface conflicts explicitly.
📍 Affects 2 files
  • src/vouch/install_adapter.py#L415-L419 (this comment)
  • src/vouch/cli.py#L4050-L4055
🤖 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, In
src/vouch/install_adapter.py lines 415-419, update the existing-server handling
to compare the registered specification: return the normal empty result only for
an exact match, and report differing entries through the established conflict or
failure mechanism. In src/vouch/cli.py lines 4050-4055, show the registered
reminder only for exact matches and surface conflicts explicitly, using the
installer result to distinguish these states.

Comment on lines +422 to +427
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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C3 'NamedTemporaryFile|mkstemp|copymode|\.vouch-tmp' src tests

Repository: vouchdev/vouch

Length of output: 1437


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '390,460p' src/vouch/install_adapter.py
printf '\n---\n'
sed -n '1,80p' src/vouch/migrations/rewriter.py

Repository: vouchdev/vouch

Length of output: 5474


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C4 'json\.dumps\(data|servers\[reg\.name\]|cfg_path|install-mcp|registered' src/vouch/install_adapter.py

Repository: vouchdev/vouch

Length of output: 4211


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '420,455p' src/vouch/install_adapter.py

Repository: vouchdev/vouch

Length of output: 1720


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n 'tmp\.unlink|unlink\(|replace\(cfg_path\)|except \(OSError, ValueError\)' src/vouch/install_adapter.py

Repository: vouchdev/vouch

Length of output: 224


use an exclusive tempfile here. src/vouch/install_adapter.py:422-427 writes a fixed .vouch-tmp before copying the mode, so a failed run can leave the full config behind and the temp file can exist with broader permissions than the final config. use mkstemp/NamedTemporaryFile(delete=False) in the same directory, set the mode before writing, and clean it up in finally.

🧰 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 422 - 427, Update the config
replacement flow around cfg_path and tmp to use an exclusive temporary file
created in the same directory via mkstemp or NamedTemporaryFile(delete=False),
rather than the fixed .vouch-tmp name. Set the temporary file mode before
writing config contents, replace cfg_path only after a successful write, and
remove the temporary file in a finally block on all paths.

@plind-junior
plind-junior merged commit 0e84e09 into test Jul 18, 2026
16 of 20 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jul 18, 2026
3 tasks
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: M 200-499 changed non-doc lines tests tests and fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant