Skip to content

feat(copilot): sync MCP servers - #628

Merged
jeff-r2026 merged 5 commits into
Tencent:mainfrom
ousamabenyounes:fix/issue-610
Sep 19, 2026
Merged

jeff-r2026 merged 5 commits into
Tencent:mainfrom
ousamabenyounes:fix/issue-610

Conversation

@ousamabenyounes

@ousamabenyounes ousamabenyounes commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add Copilot CLI MCP synchronization for user and project scopes. TeamAI now renders Copilot's native MCP entries, honors COPILOT_HOME, preserves both wrapped user configuration and bare project server maps, rejects unmanaged collisions, and reconciles managed servers without modifying Copilot settings.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature causing existing behavior to change)
  • Documentation only
  • Refactor / internal cleanup

Test Plan

  • npx tsc --noEmit passes

  • npx vitest run passes

  • Added/updated tests for the change

  • RED: focused regression tests failed for missing Copilot targets, COPILOT_HOME path resolution, bare project-map preservation, and unmanaged collision detection before their fixes.

  • GREEN: the full ignored run-ci.sh passes (254 unit files / 3,515 tests; E2E 37 passing files / 3 skipped, 159 passing tests / 26 intentional skips).

  • After npm run build, the real built-CLI regression matrix passed all 12 combinations: Claude, Codex, CodeBuddy, and OpenCode across git, gitlab, and github providers.

  • Changed executable lines have 100% coverage (69/69).

  • Authenticated Copilot CLI E2E with pinned @github/copilot@1.0.86 invoked the TeamAI-managed MCP tool exactly once and returned the expected proof marker.

  • Verified user-owned MCP entries and Copilot settings remain unchanged, while TeamAI ownership markers remain intact.

Related Issues

Fixes #610

Notes for Reviewers

Copilot user scope resolves from COPILOT_HOME when set; project scope remains .github/mcp.json. Local servers include Copilot's required type, tools, command, and args fields, while remote HTTP/SSE servers include type, tools, and url.

@jeff-r2026 jeff-r2026 self-assigned this Sep 18, 2026
@github-actions

Copy link
Copy Markdown

Findings

  • [P1] Copilot MCP sync incorrectly requires a skills directory — src/mcp-reconcile.ts:210 combines the Copilot-specific root with the existing probe logic, causing user scope to require $COPILOT_HOME/skills and project scope to require .github/. A valid Copilot installation with only mcp-config.json, or an explicitly selected clean project, is silently skipped; mcp inject, removal, and uninstall can therefore leave MCP entries untouched. The new tests mask this by explicitly creating skills directories. Reuse isToolInstalledForConfig or treat Copilot as installed when selected or when $COPILOT_HOME exists.

Testing

  • The PR description includes both a real built-CLI lifecycle record and an authenticated Copilot CLI E2E record, so it satisfies the required testing documentation.

@jeff-r2026
jeff-r2026 self-requested a review September 18, 2026 13:01
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Honor COPILOT_HOME in local-agent MCP operations — src/types.ts:316 adds mcp-config.json relative to Copilot’s root, but installMcpServer and uninstallMcpServer still resolve user MCP paths from getUserHome(). HTTP-backed teams bypass normal reconciliation, so they write ~/mcp-config.json instead of $COPILOT_HOME/mcp-config.json and cannot manage the actual Copilot configuration. Route these operations through resolveToolBaseDir.
  • [P1 blocking] Preserve bare project MCP files in the local-agent path — src/types.ts:317 enables .github/mcp.json, but local-agent install/uninstall calls readJsonDoc without allowBare and always writes doc.data.mcpServers. An existing bare server map is therefore misread, unmanaged name collisions are missed, and the file becomes a mixed schema. Reuse the Copilot project handling added in applyJson.

The PR description includes both a test plan and a real authenticated CLI E2E record, so it satisfies the review-description testing requirement.

@github-actions

Copy link
Copy Markdown
  • [P1 blocking] The PR description does not document the mandatory real-CLI test matrix required by AGENTS.md: Claude, Codex, CodeBuddy, and OpenCode across git, gitlab, and github providers after npm run build. The Copilot lifecycle/authenticated E2E record is useful, but it does not demonstrate that the required regression matrix was actually executed and passed. Add the complete verification record before merge.

@ousamabenyounes

Copy link
Copy Markdown
Contributor Author

Addressed the remaining Copilot local-agent review findings in ca6b896: user MCP operations now honor COPILOT_HOME, and project operations preserve bare .github/mcp.json maps with unmanaged-collision protection.

RED: the three focused regressions failed for the intended pre-fix reasons. GREEN: 17/17 focused tests, 72/72 related tests, the full ignored run-ci.sh suite, 100% changed executable line coverage (69/69), the 12-case real built-CLI agent/provider matrix, and authenticated Copilot CLI MCP E2E. The PR body now contains the complete verification record. No merge performed.

@jeff-r2026
jeff-r2026 merged commit e10bbcb into Tencent:main Sep 19, 2026
10 checks passed
jeff-r2026 pushed a commit that referenced this pull request Oct 23, 2026
#1265)

* refactor(locate): return Location<R> through LOCATORS with place kinds (#1221)

locate now answers with a channel ('files', 'run-time', 'none', 'unknown')
and places that say what kind they are: a directory, a blocks file, or a
map inside a shared config file. A locator per resource sits in LOCATORS,
so a resource without one fails tsc, and each resource keeps its own
result type. Skills is still the only resource.

The helpers serve every place kind: writable (was writableDir) and ownOf
replace the 12 writableDir calls and 4 reads of .own; writesOf, cleanupOf
and unreadWrites also handle files, map entries and a read hidden by an
earlier file of a first-existing lookup ('shadowed').

No behavior change: A2's skills matrix and locate tests pass with only the
accessor adapted to places.

* refactor(locate): parse gitExclude, self mode and workspace children into Install (#1221)

installOf now also reads sharing.gitExclude, whether the business repo is
the team repo, and whether the workspace has child checkouts, once, so the
rules, blocks and MCP locators get them from Install instead of each
caller. Host gains realpath and readText, the two reads the key Claude Code
files a checkout under needs (#915). Nothing reads the new fields yet.

* refactor(locate): move Codex project trust out of doctor into its own module (#1221)

codex-project-trust.ts now holds Codex's verdict on a project and the fix
doctor prints, taking the ReadGate locate declares, so the agents and MCP
checks can share one reading. Doctor's MCP trust check builds the same gate
and prints the same lines.

* refactor(locate): rename Home.moves.partial to settingsAtDefault (#1221)

The flag now says what still goes to the default home when OpenCode, Pi or
Muse moves its home: teamai's hooks and models switch settings, until B4
moves them.

* refactor(locate): record where each tool takes a resource under places, not skillDirs (#1221)

skill-dirs.ts becomes places.ts: lastPullByWorkspace[key].places holds,
per resource, the places each tool took it from at the last full pull.
Skills is the only resource recorded; rules, blocks and agents join once
their writers go through locate. The fast path compares every recorded
resource (placesUnmoved). A record from an earlier release reads its
skillDirs as places.skills, and pull never writes skillDirs again.

* feat(tools): state every tool's rules, blocks, agents and MCP channels in the descriptor (#1221)

Each built-in tool now names, per scope, how it takes rules, instruction
blocks, agents and MCP servers: the adapter with the paths it reads, a
run-time channel with the probe that says it is installed, 'none', or
'unknown'. Leaving a resource or a scope out fails tsc
(tools.type-test.ts). The rows hold the #1221 design, including WorkBuddy's
MCP lookup (D-e), ZCode's mcp.servers (D-c) and no MCP channel for the
internal builds (D-f); no writer reads them yet.

The per-tool tables that decided the same facts are now views of the
descriptor: the rule formats' tool keys, the session-hook and extension
rule tools, detectMcpFormat's sets and the agent tool list. A test pins
them to the tables they replaced. The internal builds keep their MCP
format until the MCP writers move to locate.

* feat(tools): locate rules, blocks, agents and MCP from the descriptor (#1221)

Every configured or declared tool path now becomes absolute in rootsOf, and
every resource takes one write gate per tool (gatesOf), so the blocks file,
OpenCode's activation and the MCP file resolve once and move with a home.
No caller uses the new locators yet; their tables run through one shared
matrix runner, which the skills matrix now uses too.

* refactor(agents): read the agent tools from the descriptor's AGENT_TOOLS, not ALL_SUPPORTED_TOOLS (#1221)

ALL_SUPPORTED_TOOLS was a copy of AGENT_TOOLS read through casts. Its
readers narrow with isAgentToolId instead, and the copy is gone.

* test(locate): state where every tool takes agents, in both scopes (#1221)

One row per tool and scope, plus the moved homes: OpenCode under
XDG_CONFIG_HOME, OPENCODE_CONFIG_DIR and a recorded home, Muse under
XDG_CONFIG_HOME, Copilot under COPILOT_HOME, and Claude's toolRoots.

* test(locate): state every tool's rules and blocks in the matrix (#1221)

Every tool in both scopes, each row citing its source. The dsh workspace
cells follow #1223: dsh loads teamai's hook config, so its SessionStart
hook carries the workspace rules and blocks.

* feat(mcp): write each tool's servers where locate says it reads them (#1221)

resolveMcpTargets now takes every target from locate(tool, 'mcp'):
WorkBuddy's user servers go to the first existing of its lookup order,
its project servers to CodeBuddy's local scope under sharing.gitExclude,
and OpenCode, Muse and Pi follow a moved home (XDG_CONFIG_HOME,
OPENCODE_CONFIG_DIR, PI_CODING_AGENT_DIR).

The local-scope tools, the file readers of claude's .mcp.json and
OpenCode's plugin file come from the descriptor instead of tables here.
Copilot's MCP file still says it is installed (#628).

* feat(local-agent): install MCP servers where locate says the tool reads them (#1221)

install_mcp and uninstall_mcp take the tool's MCP file from
locate(tool, 'mcp'), as a pull does: WorkBuddy's user servers go to the
first of its files that exists, and OpenCode, Muse and Pi follow a moved
home. USER_MCP_LOOKUP and userMcpFile go; doctor's read-file check names
the target's own lookup files.

* fix(rules): write rules where locate puts them, so they follow a moved home (#1221)

Every rules directory pull, push, the pre-push sync, the built-in rules,
recall disable, the Cursor user-rules hook, doctor and the local agent's
report read now comes from locate(tool, 'rules'). OpenCode under
XDG_CONFIG_HOME or OPENCODE_CONFIG_DIR gets its rules and their
`instructions` glob in the config dir it reads, and the glob goes to the
activation file locate names instead of the MCP field. Muse under
XDG_CONFIG_HOME and Pi under PI_CODING_AGENT_DIR get the team-rules block
in the AGENTS.md they read.

The rules locator keeps honoring a team toolPaths entry written before a
tool's rules moved (#938, #946): it names a rules directory the tool never
reads, so locate returns that directory with no reads, pull still writes
there and doctor still fails it. The inline team-rules block comes from
the blocks location, so such an entry keeps its block too.

* feat(agents): deliver agents where locate says each tool takes them (#1221)

agentToolDirs, which the pull writer, doctor and the model holds read,
asks locate. OpenCode and Muse agents now follow a home that
XDG_CONFIG_HOME, OPENCODE_CONFIG_DIR or the tool-homes record moves,
as their skills already did.

* feat(agents): deploy and remove the built-in agents where locate says (#1221)

deployBuiltinAgents and recall off ask locate, so the recall agent
follows a moved OpenCode or Muse home too. deployBuiltinAgents now takes
the local config: every caller passes one, and the test that called it
without one passes the member's config instead.

* feat(locate): retire the agents directory a team maps for a tool teamai renders none for (#1221)

An earlier release copied team .md agents into any configured agents
directory, so teamai remove, the tombstone pass and uninstall still
clear its copies there.

* feat(agents): push and remove read the agents directory locate names (#1221)

The push scan and teamai remove ask locate for each tool's agents
directory, so they see the copies in a moved OpenCode or Muse home. A
custom tool, for which teamai renders no agents, is no longer scanned
or cleaned.

* feat(agents): pull's tombstone pass and target set ask locate for agents (#1221)

* feat(workflow): load a step's agent from where locate says pull wrote it (#1221)

runLocalDriver's toolPaths are now the team's as teamai.yaml states
them, and loadAgent asks locate, so a user-scope agent in a moved
OpenCode home loads.

* feat(uninstall): find agents in every directory locate names for them (#1221)

* feat(init): seed the agents directory locate names (#1221)

A custom tool gets no agents directory: teamai renders no agents for
it.

* feat(agents): record where each tool took agents, and name a directory it left (#1221)

places.agents joins the record the fast path compares, so a home that
moves at one team revision syncs agents in full, and the first pull
after it names the old directory once. A tool that did not take every
agent (a member's own file, a held model, a failed write or built-in)
keeps its old directory on record until it does.

The record of an earlier release names no agents places, so its first
pull syncs in full: the skillDirs upgrade test now expects that, and the
fast-path fixtures record agents places as this CLI does.

* feat(doctor): say where each tool takes agents, and whether Codex trusts the project that holds them (#1221)

Doctor names a tool whose agents moved since the last pull, notes a tool
whose agents follow a moved home while its other files stay in the
default one, and checks Codex trust for a team that ships agents and no
MCP servers.

* test(agents): drop the agents path assertions the locate table states (#1221)

The agents rows of locate-agents.test.ts state each of these paths with
its source: OMP's user agents, Qoder CN's user agents, WorkBuddy's
default, and Copilot's project directory. The whole default toolPaths
literals and the toolRoots helper outputs stay, since they pin every
other key too.

* docs(reference): agents follow OpenCode's and Muse's moved homes (#1221)

* feat(mcp): note each server's file in its ownership record (#1221)

Every MCP writer now records the file it wrote a server to: pull, which
`mcp inject` and `mcp remove` run, and the local agent. A record whose
file the tool no longer reads splits three ways:

- in one of teamai's own relocations (a lookup file, the mapped or
  alternate file, an earlier release's default): moved out, as before;
- still holding the server elsewhere, after a home move or a remap: kept,
  and named once, as skills are;
- anywhere else: taken as the current file's.

A record an older teamai wrote notes no file. It reads as the mapped file
for a lookup target, the default home for OpenCode, Muse and Pi, and the
target's file otherwise; one whose entry an earlier release's default
holds unchanged is moved out of that file.

locate marks the built-in path of a remapped tool 'builtin', apart from
an earlier release's 'default': a remap leaves the old file as it is.

* fix(hooks): count a session-start channel ready only once its hook is installed (#1221)

instructionHookChannel answered ready for every tool it did not know, so a
ZCode, DeepSeek Harness or Muse Code channel with no teamai hook counted as
delivering. channelReadiness takes the probe the tool's run-time channel
names and checks it: the SessionStart hook in the hooks file for Codex and
Muse Code, that hook plus hooks.enabled for ZCode, the cordis.patch.yml row
and the bridge config for DeepSeek Harness. A new probe fails the type check
until it has a check.

* feat(uninstall): find each tool's MCP places through locate (#1221)

Uninstall takes the servers of a partition that is no project out of
each local scope where locate places it, so WorkBuddy's in
.codebuddy.json go too, and CODEBUDDY_CONFIG_DIR is read through the
host. The files whose git exclusions it removes are every place
cleanupOf names for a tool: its file, OpenCode's plugin file, the other
files of a lookup and the retired ones.

* fix(rules): give Muse Code, dsh and ZCode a project's instruction blocks at session start (#1221)

In a project these three read no file teamai could write the culture,
claudemd and recall blocks to, and their session-start hook added only the
rules, so they got none of the blocks. Whether a tool takes a scope's blocks
from a hook now comes from its run-time blocks channel in the descriptor:
the hook adds the blocks before the rules, the HTTP agent's prompts ride the
same hook and are written to no file, and the hook's limit comes from the
descriptor.

project-hook-rules.test.ts pinned "rules only, no instruction blocks" for
the three: that was #945's requirement, and #1221 replaces it, so the test
asserts the blocks now. A run-time channel is probed in the member's home,
where the hook or extension lives; the instruction-targets shape test
creates ~/.pi for Pi as it already did ~/.omp.

* refactor(mcp): read and write a server map under a key path (#1221)

readJsonDoc and writeJsonDoc take the keys from a file's top to its
server map, keeping every other key on the way, and a target carries
the key locate names when it is not the format's own. No target has one
yet; ZCode's `mcp.servers` is next. The local agent writes under the
key of the place it writes.

* fix(mcp): deliver ZCode servers through its native config (#1221)

ZCode 3.16.2 reads mcp.servers in its user and project config. Move owned entries out of the former shared file, keep unrelated keys, and judge nested server maps for git exclusion. The outside-home path assertion changes because MCP now lives in the ZCode home.

* fix(rules): write instruction blocks where locate puts them (#1221)

Pull, the retire pass, uninstall, doctor, the OpenCode registration and
the local agent now take each tool's blocks file from locate(tool,
'blocks'). USER_TARGETS, PROJECT_TARGETS, GIT_EXCLUDED_PROJECT_TARGETS,
entryFor, instructionTargetFile, instructionTargetPath,
instructionTargetAt, retiredInstructionFiles, userRulesFile,
readsTeamRulesFromFile and isInstructionToolInstalled are gone; block
text and planning stay in instruction-targets.ts.

Behavior that moves with it:
- Muse, OpenCode and Pi get their user blocks in the home they read
  (XDG_CONFIG_HOME, OPENCODE_CONFIG_DIR, PI_CODING_AGENT_DIR), and
  OpenCode's own AGENTS.md is looked for there before the Claude
  fallback is used.
- OpenCode's `instructions` entry goes to the file locate names as the
  blocks activation, not the one the MCP field names.
- A tool has the teamai-recall subagent only where locate gives it an
  agents directory, so a custom tool with an `agents` path gets the
  direct recall block.
- blocksFileOf takes a fixed `dir` for CodeBuddy's and WorkBuddy's
  project file: CodeBuddy's engine reads .codebuddy/rules whatever the
  team maps, so both keep sharing one copy there.
- One install gate per tool: WorkBuddy is installed where .workbuddy is.
  The #945 shape test counted an entry with only a bare `claudemd` as
  installed; it now creates .workbuddy, since that requirement is gone.

* fix(mcp): use WorkBuddy project lookup and retire the old file (#1221)

WorkBuddy uses the same project lookup as CodeBuddy. Update the old default-path assertion to the observed .mcp.json requirement, and clean the previous .workbuddy/mcp.json entries by ownership hash in both tree and local scope.

* feat(doctor): report MCP read locations and unknown channels (#1221)

* fix(local-agent): preserve MCP homes and migrate retired config keys (#1221)

* test(mcp): cover tool locations in the shared matrix (#1221)

* docs(mcp): explain native configs and moved homes (#1221)

* fix(mcp): retain recorded config addresses during cleanup (#1221)

* fix(local-agent): name old MCP configs for TOML installs (#1221)

* fix(doctor): mark ZCode fallback loss as a warning (#1221)

* feat(rules): record where each tool took rules and blocks, and name a place it left (#1221)

A moved home has to redeliver rules and instruction blocks on an unchanged revision, and a copy the member kept has to leave the old place on record. The ledger's rules set is what makes that incomplete.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(doctor): say where each tool takes rules and instruction blocks (#1221)

A moved home and a directory the tool does not read were visible for skills only. Rules and blocks get the same lines.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(rules): take rules and CLAUDE.md paths from locate (#1221)

Uninstall scanned a rules directory it built with path.join, and the legacy rules-section strip did the same for CLAUDE.md. Both now ask locate. A retired claudemd stays in the strip. A rules directory stays in the uninstall scan.

The guard is the skills twin, and it balances parentheses. The skills regex stops at the first nested call, so it missed path.join(resolveToolBaseDir(tool, localConfig), toolPaths[tool]?.rules ?? '').

Red, before the move, from npx vitest run src/__tests__/locate.test.ts -t "rules directory or claudemd":

AssertionError: expected [ …(3) ] to deeply equal []

+ [
+   "resources/rules.ts: path.join(baseDir, toolPath.claudemd)",
+   "uninstall.ts: path.join(baseDir, toolPath.rules)",
+   "uninstall.ts: path.join(resolveToolBaseDir(tool, localConfig), toolPaths[tool]?.rules ?? '')",
+ ]

The allow-list is empty.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor(rules): take OpenCode's retired project config from the rules location (#1221)

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(rules): remove tombstoned copies from a rules directory teamai no longer writes (#1221)

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(reference): state where rules and instruction blocks follow a moved home (#1221)

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(pull): give fast-path fixtures the rules and instruction blocks a pull records (#1221)

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(pull): expect the instruction-block notice when an OpenCode agent home moves (#1221)

A moved OpenCode home also leaves teamai-context.md behind. The agent place tests still required that line to stay silent.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ci): keep the Codex team-hook command on bare bash (#1221)

Merging main failed Windows CI because the team-hook dispatcher still
substituted a Git Bash path into command, while main pins bare bash on
every platform and carries the Windows form in commandWindows. The same
merge left the doctor fixture without OpenCode's rendered mcp entry, and
the WorkBuddy teamai-only case expecting the retired project file.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(local-agent): resolve a local-scope MCP record without a file in that scope (#1221)

A record an earlier release wrote for Claude's or CodeBuddy's local scope
notes no file. It resolved against the project's file instead, so with
sharing.gitExclude on, install_mcp refused to update the server as not
managed by teamai. Pull never adds the file in HTTP mode, so the record
did not heal.

* fix(rules): retire a mapped claudemd for a tool with no blocks channel (#1221)

An earlier release wrote the instruction blocks into a claudemd the team
mapped for JoyCode, Kiro, Qoder or Trae. Pull no longer writes there, so
it now strips teamai's blocks from that file and uninstall finds them, as
the rules and agents locators already do for a configured directory.

team-culture.md named that claudemd as the workspace channel for ZCode,
DeepSeek Harness, JoyCode and Muse Code; the first three now get the
blocks from the session-start hook, and JoyCode gets none.

* fix(rules): list OpenCode's user instruction file whatever the team maps MCP to (#1221)

registerOpencodeContext took the config from the MCP mapping, so a team
mapping no opencode mcp got no instructions entry, and one mapping it
elsewhere got the entry in a file pull, doctor and uninstall do not read.
It writes the opencode.json beside the file again, as they read it.

* fix(uninstall): remove teamai's MCP servers from a file the tool left (#1221)

After a home move, uninstall dropped the records of servers an earlier
release wrote to the old file and left the servers there. It now takes
the unchanged ones out, as the local agent's uninstall_mcp does; a copy
the member changed stays.

* feat(locate): follow CodeBuddy's home when CODEBUDDY_CONFIG_DIR moves it (#1221)

CodeBuddy 2.168.0 reads its whole home from CODEBUDDY_CONFIG_DIR, not
~/.codebuddy, and keeps .codebuddy.json there too. teamai assumed the
variable moved only .codebuddy.json, so skills, rules, CODEBUDDY.md,
agents and the user MCP files went to a home CodeBuddy did not read.

The variable is now a home move, recorded as the others are, so every
shell writes the same place. Hooks stay in ~/.codebuddy/settings.json
until B4, and doctor notes it.

* fix(uninstall): remove local-scope MCP servers from a file the tool left (#1221)

With sharing.gitExclude off, uninstall took teamai's servers out of the
local scope's current file only. After CodeBuddy's home moved, it dropped
the records of servers in the old .codebuddy.json and left the servers.
It now removes the unchanged ones there, as it does for a target.

* fix(mcp): take servers from the file their record names after CodeBuddy's home moves (#1221)

Once CODEBUDDY_CONFIG_DIR moves CodeBuddy's home, a record names a file
the tool left. Three paths still used the current file:

- A record an earlier release wrote with no file was taken to own the
  moved mapped file, so a pull could overwrite a member's server there.
  It now resolves to the mapped file under the default home.
- The local agent's uninstall_mcp removed nothing from the recorded
  .codebuddy.json and dropped the record.
- Uninstall and pull took other checkouts' servers out of the current
  .codebuddy.json only, and dropped their records. They now go by the
  file each record names, as the orphan cleanup already did.

Test setup clears CODEBUDDY_CONFIG_DIR with the other home variables.

* test: clear every variable that moves a tool home, as TOOLS declares it (#1221)

The test setup listed the home variables by hand and missed
CODEBUDDY_CONFIG_DIR, so a test that stubs only HOME wrote fixtures into
a developer's real CodeBuddy home. It now takes them from the tool
descriptors, as init does: homeVariables moves to tools.ts beside the
descriptors so both read one list.

* fix(uninstall): take teamai's servers out of a Codex config.toml a moved root left (#1221)

Uninstall cleaned the file a tool left after its root moved with the JSON
writer only. A Codex config.toml there did not parse, so its servers
stayed. Every edit of an MCP file now goes through one writer that picks
the file's format, and a Codex block the member changed stays, as a JSON
entry does.

* fix(locate): follow the user config's tool roots from a project that records none (#1221)

A project config without toolRoots took the tool's default home for
everything locate reads from the tool's user config: the Codex hook gate
of rules and blocks, Codex's trust config for agents and MCP, and Claude's
local MCP scope. With Codex placed elsewhere by the user config and
~/.codex absent, a project pull skipped instruction delivery. The user
view now takes the user config's roots in that case, as
resolveMemberToolRoots and the earlier resolver did.

* fix(uninstall): clean each MCP record in the file it names, OpenCode's moved project file too (#1221)

Uninstall handed every OpenCode record outside the current file to the
alternate file, so after toolPaths.opencode.mcpProject moved, teamai's
servers stayed in the original opencode.json and their records were
dropped. Records now go to the file they name. A record whose file no
longer parses is kept for the retry instead of being read as gone.

* fix(uninstall): clean recorded MCP files when the current one cannot be read (#1221)

Uninstall now cleans each record in the file it names whatever state the current target is in, and a Codex config.toml that cannot be read keeps its records, as an unparsable JSON file does.

* fix(uninstall): clean recorded MCP files whatever state the current one is in, in the scope a project's servers left too (#1221)

* fix(mcp): tell a retired MCP key apart from the current one in one file, and keep what a kept record says of resolved values (#1221)

* fix(mcp): keep an earlier release's MCP record while the retired file it may sit in cannot be read (#1221)

* fix(uninstall): keep the data home while an MCP config holding teamai's servers does not parse (#1221)

* fix(uninstall): keep the data home while a removed worktree's MCP servers sit in a config that does not parse (#1221)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: teamai-ci <teamai-ci@noreply>
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.

[feat] Add Copilot CLI MCP synchronization

2 participants