Skip to content

fix(mcp): register Context7 where Claude Code reads MCP servers - #1914

Closed
kevocodes wants to merge 2 commits into
Gentleman-Programming:mainfrom
kevocodes:fix/claude-code-context7-mcp-registry
Closed

fix(mcp): register Context7 where Claude Code reads MCP servers#1914
kevocodes wants to merge 2 commits into
Gentleman-Programming:mainfrom
kevocodes:fix/claude-code-context7-mcp-registry

Conversation

@kevocodes

@kevocodes kevocodes commented Jul 28, 2026

Copy link
Copy Markdown

🔗 Linked Issue

Closes #1868


🏷️ PR Type

  • type:bug — Bug fix (non-breaking change that fixes an issue)

📝 Summary

Claude Code loads user-scope MCP servers from ~/.claude.json only. gentle-ai was merging the Context7 entry into ~/.claude/settings.json under a top-level mcpServers key, which is not a recognised setting — Claude Code ignores it silently, so the registration was written, valid, error-free, and inert.

This PR points the Context7 registration at the file Claude Code actually reads, and moves the install-verification, backup, and uninstall consumers along with it.

Two details worth calling out, because ~/.claude.json is not an ordinary config file — it also holds the authenticated session and per-project settings:

  • Merging is strict. MergeJSONObjects resets an unparsable base to {}, which is the right recovery for a regenerable mcp.json but would replace a user's session here. MergeJSONObjectsStrict aborts instead. MergeJSONObjects is unchanged, so every other agent keeps the lenient recovery added for malformed Windows configs.
  • Uninstall never deletes the file. rewriteJSONFile removes a file that cleans out to an empty object, and deleteJSONPath prunes the emptied parent — so on a machine where the registry held nothing but the managed entry, removing Context7 would have deleted ~/.claude.json and signed the user out. rewriteMCPRegistryFile leaves {} behind instead.

Rather than adding another adapter.Agent() == model.AgentClaudeCode branch, the destination is expressed as an optional adapter capability (agents.MCPRegistryPathProvider), following the existing EffectiveCodeGraphWiringDetector pattern. This is the shape @Keesan12 and @acortesma argued for in #899: the injector, verifier, and uninstaller now all ask the adapter where its registry is, instead of each re-deriving a path from MCPStrategy().


📂 Changes

File / Area What Changed
internal/agents/interface.go New optional capability MCPRegistryPathProvider + agents.MCPRegistryPath(adapter, homeDir) helper
internal/agents/claude/paths.go, adapter.go MCPRegistryPath~/.claude.json; MCPConfigPath kept for legacy cleanup
internal/components/filemerge/json_merge.go New MergeJSONObjectsStrict — fails rather than resetting an unparsable base
internal/components/mcp/inject.go Claude Code Context7 now merges into the registry instead of settings.json
internal/cli/run.go Context7 verification and backup follow the registry path
internal/components/uninstall/service.go New rewriteMCPRegistryFile (never deletes); Context7 cleanup covers registry + legacy locations

🧪 Test Plan

  • Unit tests pass (go test ./...) — see the note on local environment failures below
  • Go format passes (go run ./internal/gofmtcheck)
  • E2E tests pass (cd e2e && ./docker-test.sh) — 3/3 images, 79 passed, 0 failed
  • Manually tested locally

Behaviour covered by new/updated tests:

  • Context7 lands in ~/.claude.json, is idempotent on a second run, and preserves oauthAccount / projects in the same file
  • The registry is created when absent
  • A malformed registry aborts the injection and is left byte-for-byte untouched
  • ~/.claude/settings.json and ~/.claude/mcp/context7.json are no longer written
  • Verification and backup report the registry, not settings.json
  • Uninstall clears mcpServers.context7 and leaves the file in place — this one fails with no such file or directory if rewriteMCPRegistryFile is swapped back to rewriteJSONFile, which is exactly the regression it guards

Manual check on macOS: claude mcp list reports context7 - ✔ Connected after registering through the same destination this PR writes.

On local environment failures. Running go test ./... on macOS produces 49 failures on a clean main checkout, before any of this work. I ran the full suite on main in a separate worktree and diffed the failing-test sets: this branch's set is identical — no test fails here that does not also fail on main, and none stops failing. They are macOS artifacts rather than repository breakage:

  • internal/reviewtransactionmaintenance authority component "/var" is unsafe and review store lock could not be acquired: not a directory. On macOS $TMPDIR lives under /var/folders/... and /var is a symlink to /private/var, which the path-safety check rejects.
  • internal/update./scripts/canonicalize-release-public-keys.sh: line 20: declare: -A: invalid option. macOS ships bash 3.2, which has no associative arrays.
  • internal/cli — git operations exceeding the 15s aggregate budget.

Everything in the blast radius of this change is green: internal/agents/..., internal/components/mcp, internal/components/filemerge, internal/components/uninstall, internal/components, and internal/components/engram.


✅ Contributor Checklist

  • PR is linked to an issue with status:approved
  • PR stays within 400 changed lines
  • I have added the appropriate type:* label to this PR
  • Unit tests pass (go test ./...)
  • Go format passes (go run ./internal/gofmtcheck)
  • E2E tests pass (cd e2e && ./docker-test.sh)
  • I have updated documentation if necessary
  • My commits follow Conventional Commits format
  • My commits do not include Co-Authored-By trailers

I don't have permission to apply labels on this repo — could a maintainer add type:bug?


💬 Notes for Reviewers

This is the first of two sequential PRs, and the second is deliberately not open yet. #1868 reports two halves: Context7 landing in an ignored key, and Engram still landing in the dead ~/.claude/mcp/engram.json from #899. Together they came to 497 changed lines, over the 400 budget, and the diff is ordinary code plus tests rather than the generated/vendor/migration case size:exception is meant for — so per CONTRIBUTING I split it instead of asking for the label.

I held the second PR back on purpose. In #1653/#1654/#1655 a three-part series was opened all at once and closed as a "cumulative duplicate … three copies of the same branch", with the ask being "a real reviewable stack with distinct boundaries". That outcome is unavoidable when later parts are opened against main before the earlier ones land, since each one carries its predecessors' commits — all three of those PRs measured an identical +2920/-44.

So this PR is the Context7 half plus the shared groundwork, standing alone at 317 changed lines. The Engram half is already implemented and tested locally; I'll open it against the updated main as soon as this merges, as a distinct 274-line diff with no overlap and linked to this same issue. Both halves stay under one approved issue rather than my opening a second one — #1868 already documents both defects, and splitting the tracking would put the follow-up behind a fresh status:approved gate for no benefit to you.

Merging this will auto-close #1868 while that second half is still outstanding, since CI requires the Closes keyword. I'll reopen it myself and it closes for good with the follow-up PR — no action needed on your side. Flagging it only so a closed issue isn't read as the work being finished.

Concretely, what still lands after this PR: internal/components/engram/inject.go writes ~/.claude/mcp/engram.json for Claude Code, which is the original dead path from #899 and is untouched here. Verified against main at b366f7ba with a sandboxed HOME — after an Engram install, ~/.claude.json and ~/.claude/settings.json both have no mcpServers, and claude mcp list reports only plugin:engram:engram, i.e. the plugin's own channel rather than anything gentle-ai registered. The follow-up routes that branch through the same registry capability this PR introduces.

The uninstall file-deletion behaviour is the part I'd most like a second pair of eyes on — I only noticed it because ~/.claude.json is one of the few targets where an empty result is not a disposable file.

Summary by CodeRabbit

  • Bug Fixes
    • Context7 configuration for Claude Code is now stored in the correct MCP registry file.
    • Existing registry content is preserved during installation and removal.
    • Malformed registry files are no longer overwritten.
    • Uninstalling Context7 removes its configuration while retaining the registry file and unrelated settings.
  • Tests
    • Added coverage for registry creation, preservation, validation, and cleanup.

- Add MergeJSONObjectsStrict, which fails instead of falling back to an empty base when the existing document cannot be decoded
- Keep MergeJSONObjects lenient so regenerable agent configs still recover from a malformed file
- Cover both the refusal on a malformed base and the merge that preserves unrelated keys
- Add MCPRegistryPath to the Claude adapter and an optional agents.MCPRegistryPathProvider capability, so components resolve ~/.claude.json instead of branching on the agent ID
- Merge the Context7 entry into the registry rather than into ~/.claude/settings.json, whose mcpServers key Claude Code ignores
- Use the strict merge so a registry that cannot be parsed aborts the injection instead of replacing the user's session
- Track the registry in install verification and backup, and clear it on uninstall without deleting the file
- Keep the settings and legacy per-server paths as uninstall cleanup targets for registrations written by earlier versions
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7a6c0b12-cf07-4ec4-bc50-736e4d45b3cb

📥 Commits

Reviewing files that changed from the base of the PR and between b366f7b and 9057689.

📒 Files selected for processing (11)
  • internal/agents/claude/adapter.go
  • internal/agents/claude/paths.go
  • internal/agents/interface.go
  • internal/cli/run.go
  • internal/cli/run_component_paths_test.go
  • internal/components/filemerge/json_merge.go
  • internal/components/filemerge/json_merge_test.go
  • internal/components/mcp/inject.go
  • internal/components/mcp/inject_test.go
  • internal/components/uninstall/service.go
  • internal/components/uninstall/service_test.go

📝 Walkthrough

Walkthrough

Context7 MCP handling for Claude Code now targets ~/.claude.json instead of ~/.claude/settings.json or legacy MCP files. The change adds registry path capability detection, strict JSON merging, updated verification, registry-preserving uninstall cleanup, and corresponding tests.

Changes

Claude MCP registry integration

Layer / File(s) Summary
Registry path capability and verification
internal/agents/..., internal/cli/run.go, internal/cli/run_component_paths_test.go
Adapters can provide MCP registry paths; Claude resolves this to ~/.claude.json, which is now used for Context7 path verification.
Strict registry injection
internal/components/filemerge/*, internal/components/mcp/*
Context7 is merged into the registry while preserving existing data; malformed registries cause injection to fail without rewriting them.
Registry-aware uninstall
internal/components/uninstall/*
Uninstall removes Context7 registry entries, preserves the registry file, and removes legacy managed MCP files when applicable.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: type:bug, slop

Suggested reviewers: alan-thegentleman

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: registering Context7 in the Claude Code MCP registry.
Linked Issues check ✅ Passed The PR moves Context7 to ~/.claude.json and updates install, backup, and uninstall flows to use the Claude registry path.
Out of Scope Changes check ✅ Passed The added strict merge and registry-path plumbing are directly tied to the Claude MCP registry fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(mcp): Claude Code MCP config written to ~/.claude/settings.json, a key Claude Code never reads

1 participant