Repository navigation
feat: migrate .copilot/mcp-config.json -> .mcp.json (Copilot CLI auto-load path) - #1208
Closed
tamirdresher wants to merge 6 commits into
Closed
tamirdresher wants to merge 6 commits into
tamirdresher wants to merge 6 commits into
Conversation
…g.json (Phase 1) Phase 1 of the MCP config path migration. `squad init` now writes the per-repo MCP server config to `.mcp.json` at the repo root — the workspace path that GitHub Copilot CLI auto-loads per `copilot mcp --help`. The legacy `.copilot/mcp-config.json` path was never auto-loaded by the CLI; users had to pass `--additional-mcp-config .copilot/mcp-config.json` on every invocation, and many teams never noticed their MCP servers weren't loading. See github/copilot-cli#3642. Scope (all AUDIT-MATCH, no collateral, no unrelated changes): Code (4) - packages/squad-sdk/src/config/init.ts (write target → .mcp.json) - packages/squad-cli/src/cli/core/init.ts (option doc comment) - packages/squad-cli/src/cli/commands/state-mcp.ts (help text) - index.cjs (legacy inline writer removed; SDK init owns it now) Tests (1) - test/cli/init.test.ts (new .mcp.json assertion + legacy negative) Templates — twin-file mirrors (8) - .squad-templates/mcp-config.md - .squad-templates/squad.agent.md - .github/agents/squad.agent.md - packages/squad-cli/templates/mcp-config.md - packages/squad-cli/templates/squad.agent.md.template - packages/squad-sdk/templates/mcp-config.md - packages/squad-sdk/templates/squad.agent.md.template - templates/mcp-config.md - templates/squad.agent.md.template Docs (5) - docs/src/content/docs/features/mcp.md (full rewrite + migration section) - docs/src/content/docs/concepts/portability.md - docs/src/content/docs/features/enterprise-platforms.md - docs/src/content/docs/features/notifications.md - docs/src/content/docs/reference/config.md Changesets (2) - .changeset/mcp-json-migration.md (NEW — minor bump cli+sdk) - .changeset/mcp-frontmatter-init.md (1-line rationale update) Phase 2 (deferred, not in this commit): - `squad upgrade` merge helper for existing `.copilot/mcp-config.json` - `ensureSquadStateMcpPinned` dual-write window - Worf review pass before PR Tests: test/cli/init.test.ts 15/15 pass; test/mcp-config.test.cjs 10/10 pass. Refs: github/copilot-cli#3642 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds SDK helper migrateMcpConfig() that folds legacy .copilot/mcp-config.json into root .mcp.json with atomic temp+rename writes. Conflict policy: keep .mcp.json, warn on diff. Wired into runUpgrade() early so downstream steps see merged servers. Legacy file preserved for one deprecation cycle. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…fig.json (Phase 2b) If a legacy .copilot/mcp-config.json already exists, init now keeps its squad_state entry in sync with the .mcp.json spec via ensureMcpServerPinned with createIfMissing=false. Fresh inits still skip the legacy file entirely — design contract enforced by test/cli/init.test.ts:101-104. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
13 new vitest cases under test/upgrade-mcp-merge.test.ts exercise migrateMcpConfig, ensureMcpServerPinned, and atomicWriteJson against real tempdirs (the helpers use raw node:fs). Covers: no-legacy, fresh migration, disjoint merge, equivalent skip, conflict policy, malformed-target refusal, idempotence, empty-legacy, absent-file no-op, stale-pin overwrite, conflict-without-overwrite, and no .tmp leftovers on success. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- init.ts: wrap legacy ensureMcpServerPinned in try/catch (Worf Condition A) -- prevents squad init crash on EACCES/EISDIR of .copilot/mcp-config.json - atomicWriteJson: round-trip JSON.parse before write (Worf Condition B) -- defense-in-depth against silent-fallback hazard (Seven section c) - tests: cover both new failure modes Reviewed by: Worf Closes blocking conditions on feat/mcp-json-migration before upstream PR. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR migrates Squad’s per-repo MCP server configuration from the legacy .copilot/mcp-config.json location to the Copilot CLI auto-loaded workspace path .mcp.json (repo root), and adds an upgrade-time merge helper to preserve and reconcile existing legacy configs.
Changes:
- Update
squad init(SDK-driven) to write.mcp.jsonat repo root, with best-effort dual-write pinning ofsquad_stateinto an existing legacy file. - Add
migrateMcpConfig()+ supporting helpers to fold legacy.copilot/mcp-config.jsoninto.mcp.jsonduringsquad upgrade, with conflict detection and atomic writes. - Refresh templates/docs/tests to reflect the new config location and migration behavior.
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
test/upgrade-mcp-merge.test.ts |
Adds vitest coverage for merge/conflict/malformed/atomic-write behavior. |
test/cli/init.test.ts |
Updates init assertions for .mcp.json + adds legacy-write hardening test. |
templates/squad.agent.md.template |
Updates template guidance to reference .mcp.json (repo root). |
templates/mcp-config.md |
Updates MCP config location guidance (migration note needs follow-up per comments). |
packages/squad-sdk/templates/squad.agent.md.template |
Mirrors the .mcp.json guidance change for SDK templates. |
packages/squad-sdk/templates/mcp-config.md |
Mirrors MCP config location guidance for SDK templates. |
packages/squad-sdk/src/upgrade/migrate-mcp-config.ts |
New SDK helper implementing merge policy + atomic JSON writes. |
packages/squad-sdk/src/upgrade/index.ts |
New upgrade barrel export for SDK consumers. |
packages/squad-sdk/src/index.ts |
Exposes the new upgrade/* exports from the SDK root. |
packages/squad-sdk/src/config/init.ts |
Switches init output to .mcp.json and adds legacy pin dual-write. |
packages/squad-cli/templates/squad.agent.md.template |
Mirrors .mcp.json guidance change for CLI templates. |
packages/squad-cli/templates/mcp-config.md |
Mirrors MCP config location guidance for CLI templates. |
packages/squad-cli/src/cli/core/upgrade.ts |
Runs MCP merge early in squad upgrade and reports warnings. |
packages/squad-cli/src/cli/core/init.ts |
Updates option docstring to reference .mcp.json instead of legacy path. |
packages/squad-cli/src/cli/commands/state-mcp.ts |
Updates help text to reference .mcp.json auto-load behavior. |
index.cjs |
Removes legacy inline MCP writer; documents SDK init as source of truth. |
docs/src/content/docs/reference/config.md |
Updates optional file list to .mcp.json (needs rule text alignment per comments). |
docs/src/content/docs/features/notifications.md |
Updates examples text to reference .mcp.json (repo root). |
docs/src/content/docs/features/mcp.md |
Rewrites config scope guidance and adds migration section (needs “upgrade now ships merge” wording fix per comments). |
docs/src/content/docs/features/enterprise-platforms.md |
Updates ADO MCP instructions to reference .mcp.json. |
docs/src/content/docs/concepts/portability.md |
Updates portability table to reference .mcp.json. |
.squad-templates/squad.agent.md |
Mirrors .mcp.json guidance change in squad templates. |
.squad-templates/mcp-config.md |
Mirrors MCP config location guidance in squad templates. |
.github/agents/squad.agent.md |
Mirrors .mcp.json guidance in the committed agent file. |
.changeset/mcp-json-migration.md |
Adds changeset documenting the path migration + upgrade merge behavior. |
.changeset/mcp-frontmatter-init.md |
Updates changeset text to reference .mcp.json as the non-frontmatter default. |
Comment on lines
+115
to
117
| - **`.mcp.json`** — MCP server config at repo root (opt-in: `--include-mcp-config`); auto-loaded by Copilot CLI | ||
|
|
||
| > ⚠️ **Hard rule:** Squad NEVER writes temp files, logs, or memory to your repo root. All team state lives in `.squad/` only. Your project tree stays clean. |
| 3. Delete `.copilot/mcp-config.json` once you've verified the merged `.mcp.json` works (`copilot mcp` should list the merged servers). | ||
| 4. Commit `.mcp.json`. You can stop passing `--additional-mcp-config`. | ||
|
|
||
| A `squad upgrade` guided merge will ship in a follow-up release; until then, the migration is manual. |
| 3. **User-level:** `~/.copilot/mcp-config.json` (personal, user-wide). | ||
| 4. **CLI override:** `--additional-mcp-config <path>` (session-specific). | ||
|
|
||
| > **Migration note:** Older Squad versions wrote the per-repo config to `.copilot/mcp-config.json`. That path was not auto-loaded by Copilot CLI and required `--additional-mcp-config`. New `squad init` writes `.mcp.json` instead. If your repo still has a `.copilot/mcp-config.json`, it is preserved as-is — see `docs/features/mcp.md` for a manual merge recipe until `squad upgrade` ships a guided merge. |
| 3. **User-level:** `~/.copilot/mcp-config.json` (personal, user-wide). | ||
| 4. **CLI override:** `--additional-mcp-config <path>` (session-specific). | ||
|
|
||
| > **Migration note:** Older Squad versions wrote the per-repo config to `.copilot/mcp-config.json`. That path was not auto-loaded by Copilot CLI and required `--additional-mcp-config`. New `squad init` writes `.mcp.json` instead. If your repo still has a `.copilot/mcp-config.json`, it is preserved as-is — see `docs/features/mcp.md` for a manual merge recipe until `squad upgrade` ships a guided merge. |
| 3. **User-level:** `~/.copilot/mcp-config.json` (personal, user-wide). | ||
| 4. **CLI override:** `--additional-mcp-config <path>` (session-specific). | ||
|
|
||
| > **Migration note:** Older Squad versions wrote the per-repo config to `.copilot/mcp-config.json`. That path was not auto-loaded by Copilot CLI and required `--additional-mcp-config`. New `squad init` writes `.mcp.json` instead. If your repo still has a `.copilot/mcp-config.json`, it is preserved as-is — see `docs/features/mcp.md` for a manual merge recipe until `squad upgrade` ships a guided merge. |
| 3. **User-level:** `~/.copilot/mcp-config.json` (personal, user-wide). | ||
| 4. **CLI override:** `--additional-mcp-config <path>` (session-specific). | ||
|
|
||
| > **Migration note:** Older Squad versions wrote the per-repo config to `.copilot/mcp-config.json`. That path was not auto-loaded by Copilot CLI and required `--additional-mcp-config`. New `squad init` writes `.mcp.json` instead. If your repo still has a `.copilot/mcp-config.json`, it is preserved as-is — see `docs/features/mcp.md` for a manual merge recipe until `squad upgrade` ships a guided merge. |
Comment on lines
+1361
to
+1363
| `Updated legacy .copilot/mcp-config.json with the latest squad_state pin. ` + | ||
| `Run \`squad upgrade\` to fold this file into .mcp.json and delete the legacy copy.`, | ||
| ); |
Comment on lines
+365
to
+366
| writeFileSync(tempPath, serialized, 'utf-8'); | ||
| renameSync(tempPath, filePath); |
Merged
7 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
GitHub Copilot CLI auto-loads MCP server configuration from
.mcp.jsonat the repo root, not from.copilot/mcp-config.json. This was clarified by @caarlos0 in github/copilot-cli#3642 (now closed) and verified empirically againstcopilot mcp --helpon CLI 1.0.58:squad-cli has been writing project-local MCP config to a path the CLI was never auto-loading. Users had to pass
--additional-mcp-config @./.copilot/mcp-config.jsonon every invocation or they'd silently miss their project's MCP servers — many teams never noticed.What
Additive migration from
.copilot/mcp-config.json→.mcp.json(repo root). No breaking changes — existing repos keep working, andsquad upgrademerges seamlessly.6 commits organized in two phases:
Phase 1 — path rename (commit
892b2da2)packages/squad-sdk/src/config/init.ts:squad initnow writes.mcp.jsonat repo rootsquad.agent.md.templateupdates (twin-file invariant preserved viascripts/sync-templates.mjs --sync)docs/.../features/mcp.mdwith a migration troubleshooting sectiontest/cli/init.test.ts: 15/15 (new.mcp.jsonassertion + legacy negative)test/mcp-config.test.cjs: 10/10 (legacy preservation verified)index.cjs: legacy inline writer removed; SDK init owns it nowPhase 2 —
squad upgrademerge helper + dual-write (commits3207f075,4b635463,c264e57b,92e3a394)packages/squad-sdk/src/upgrade/migrate-mcp-config.ts(401 lines)migrateMcpConfig(repoRoot, opts)— merges legacymcpServersinto.mcp.json, preserves the legacy file (no silent delete)ensureMcpServerPinned(path, name, entry, opts)— used forsquad_statepinatomicWriteJson(path, value)— temp-file + intra-directory rename, never leaves a half-written.mcp.jsoncommand/args/env, sort-key-normalized) → no-op.mcp.jsonentry (never silently overwrite).mcp.jsonwith zero diagnostic; the helper refuses to clobber a malformed file and refuses to write oneensureSquadStateMcpPinneddual-write — during the additive window,squad_stateis pinned to BOTH.mcp.json(auto-loaded) and.copilot/mcp-config.jsonIF it already exists (never creates the legacy file fresh)runUpgradebefore any other state migration so MCP servers are discoverable for the rest of the sessiontest/upgrade-mcp-merge.test.ts: migration, merge, conflict (both branches), malformed input refusal, idempotence, atomic-write cleanlinessPhase 2 hardening (commit
77186501)init.ts: legacy dual-write call wrapped in try/catch — EACCES/EISDIR on the legacy file downgrades to a warning instead of crashingsquad initatomicWriteJson: round-tripJSON.parse(serialized)before disk I/O — defense-in-depth against any future serialization regressionit.skipIf)Quick Check
Then exercise the helper:
Readiness Checklist
npm run build -w packages/squad-sdk✅scripts/sync-templates.mjs --sync.changeset/mcp-json-migration.md(minor for@bradygaster/squad-cli+@bradygaster/squad-sdk).changeset/mcp-frontmatter-init.mdpath reference updated.copilot/mcp-config.jsonpreserved on upgrade;--additional-mcp-configescape hatch still works for users who want overlaysOut of scope (deferred)
/root/.copilot/mcp-config.json— user-level path remains auto-loaded by Copilot CLI, so the sandbox keeps working unchangedCloses
Closes the round-trip on github/copilot-cli#3642.
PR queued behind #1200, #1202, #1207 — happy to defer review until you've caught up on those.