From ff768db363017376f82f70c1e6566e0be35b65d2 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Tue, 9 Jun 2026 23:28:35 -0400 Subject: [PATCH] feat(mcp): single-connector default set + connector policy (#2219) Reduce the default .mcp.json to one connector (chrome-devtools) per the new policy in docs/MCP-CONNECTOR-POLICY.md: a default earns its slot only if it is universal AND MCP beats a CLI/API wrapped in a skill. June 2026 audit verdicts: github -> gh via github-ops skill; context7 -> REST via documentation-lookup; exa -> harness-native search (+ exa-search skill); memory -> native harness memory + instincts; playwright -> playwright CLI skills (vendor moved agent flows off MCP); sequential-thinking -> native extended thinking. All six remain opt-in in mcp-configs/mcp-servers.json. Tests updated: plugin-manifest policy assertions + install-apply Cursor expectations. Co-authored-by: ECC Test --- .codex-plugin/README.md | 9 +- .mcp.json | 24 +--- CHANGELOG.md | 6 + README.md | 4 +- docs/MCP-CONNECTOR-POLICY.md | 43 +++++++ tests/plugin-manifest.test.js | 191 +++++++--------------------- tests/scripts/install-apply.test.js | 6 +- 7 files changed, 102 insertions(+), 181 deletions(-) create mode 100644 docs/MCP-CONNECTOR-POLICY.md diff --git a/.codex-plugin/README.md b/.codex-plugin/README.md index ddf2b6e1..af7afac7 100644 --- a/.codex-plugin/README.md +++ b/.codex-plugin/README.md @@ -49,12 +49,9 @@ stay below provider length limits. | Server | Purpose | |---|---| -| `github` | GitHub API access | -| `context7` | Live documentation lookup | -| `exa` | Neural web search | -| `memory` | Persistent memory across sessions | -| `playwright` | Browser automation & E2E testing | -| `sequential-thinking` | Step-by-step reasoning | +| `chrome-devtools` | Interactive browser debugging via Chrome DevTools (CDP sessions, performance traces, console/network inspection) | + +The former defaults (`github`, `context7`, `exa`, `memory`, `playwright`, `sequential-thinking`) were retired in the June 2026 connector audit — their jobs are covered by skills wrapping CLIs/REST APIs or by harness-native features. They remain available as opt-in entries in `mcp-configs/mcp-servers.json`. See `docs/MCP-CONNECTOR-POLICY.md` for the policy and the per-connector rationale. ## Notes diff --git a/.mcp.json b/.mcp.json index e2141a2b..045baea1 100644 --- a/.mcp.json +++ b/.mcp.json @@ -1,28 +1,8 @@ { "mcpServers": { - "github": { + "chrome-devtools": { "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-github@2025.4.8"] - }, - "context7": { - "command": "npx", - "args": ["-y", "@upstash/context7-mcp@2.1.4"] - }, - "exa": { - "type": "http", - "url": "https://mcp.exa.ai/mcp" - }, - "memory": { - "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-memory@2026.1.26"] - }, - "playwright": { - "command": "npx", - "args": ["-y", "@playwright/mcp@0.0.69", "--extension"] - }, - "sequential-thinking": { - "command": "npx", - "args": ["-y", "@modelcontextprotocol/server-sequential-thinking@2025.12.18"] + "args": ["-y", "chrome-devtools-mcp@latest"] } } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 61c9f446..cd1893c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- Default MCP connector set reduced to a single connector (`chrome-devtools`) per the new connector policy (`docs/MCP-CONNECTOR-POLICY.md`). The six previous defaults (`github`, `context7`, `exa`, `memory`, `playwright`, `sequential-thinking`) were retired after the June 2026 audit: their jobs are covered by skills wrapping CLIs/REST APIs (`github-ops`, `documentation-lookup`, `exa-search`, e2e skills) or by harness-native features (memory, extended thinking, web search). All six remain opt-in via `mcp-configs/mcp-servers.json`. + ## 2.0.0 - 2026-06-09 ### Added diff --git a/README.md b/README.md index f7de06b1..f91f4279 100644 --- a/README.md +++ b/README.md @@ -982,10 +982,12 @@ Use Claude Code's `/mcp` command or CLI-managed MCP setup for live Claude Code s For repo-local MCP access, copy desired MCP server definitions from `mcp-configs/mcp-servers.json` into a project-scoped `.mcp.json`. +ECC ships exactly one default connector (`chrome-devtools`); everything else is a skill wrapping a CLI/REST API or an opt-in catalog entry. The rule and the June 2026 audit that retired the previous six defaults live in [docs/MCP-CONNECTOR-POLICY.md](docs/MCP-CONNECTOR-POLICY.md). + If you already run your own copies of ECC-bundled MCPs, set: ```bash -export ECC_DISABLED_MCPS="github,context7,exa,playwright,sequential-thinking,memory" +export ECC_DISABLED_MCPS="chrome-devtools" ``` ECC-managed install and Codex sync flows will skip or remove those bundled servers instead of re-adding duplicates. `ECC_DISABLED_MCPS` is an ECC install/sync filter, not a live Claude Code toggle. diff --git a/docs/MCP-CONNECTOR-POLICY.md b/docs/MCP-CONNECTOR-POLICY.md new file mode 100644 index 00000000..80fb5666 --- /dev/null +++ b/docs/MCP-CONNECTOR-POLICY.md @@ -0,0 +1,43 @@ +# MCP Connector Policy + +ECC ships exactly one default MCP connector. Everything else is a skill wrapping a CLI or REST API, or an opt-in entry in `mcp-configs/mcp-servers.json`. + +## The rule + +A default connector earns its slot only if both hold: + +1. **Universal** — it applies to essentially every user of a coding agent, on every harness ECC targets. +2. **MCP beats a CLI/API wrapped in a skill** — the job genuinely needs what MCP provides: interactive session state, streaming, an auth handshake, or structured browsing. Stateless request/response work is a skill, not a server. Tool schemas load into every session; each default connector taxes every user's context window whether they use it or not. + +The default set stays well under ten. In practice the 2026 field default across serious harnesses is zero to two connectors plus native built-ins. + +## Current default set + +| Server | Why it passes | +|---|---| +| `chrome-devtools` | Google's official DevTools MCP. Interactive CDP sessions — live debugging, performance traces, console and network inspection on a stateful browser. This is the textbook case where MCP beats a CLI: the value is the held-open session, not a one-shot command. Keyless. | + +## The six it replaced (June 2026 audit) + +| Former default | Verdict | Replacement | +|---|---|---| +| `github` | drop for skill | `gh` CLI via the `github-ops` skill. `gh` is in every model's training data, composes one-shot commands with minimal token overhead, and auths once via `gh auth login`. The MCP server's ~30 tool schemas taxed every session. | +| `context7` | drop for skill | The `documentation-lookup` skill targeting Context7's public REST API (`/api/v2/libs/search`, `/api/v2/context`). Two stateless calls with a bearer key — no session state to justify a server. | +| `exa` | drop for skill | Harness-native search (Claude Code WebSearch, Codex web_search, Cursor @Web) by default; the `exa-search` skill remains for API-key holders. Also required an API key, which fails the universality test for a default. | +| `memory` | drop entirely | Native harness memory (Claude Code auto-memory directories, Cursor memories, AGENTS.md conventions) plus ECC's instinct/continuous-learning system. The knowledge-graph server solved a 2024 problem harnesses have since absorbed. | +| `playwright` | drop for skill | Microsoft's own `@playwright/cli` agent surface — the vendor itself moved agent workflows off MCP because returning full accessibility trees per step burns context. ECC's e2e skills already drive the CLI. Browser *debugging* (the interactive case) is covered by `chrome-devtools`. | +| `sequential-thinking` | drop entirely | Native extended thinking in every modern harness. The server wrapped no external system — a prompting pattern dressed as a connector. | + +All six remain available as opt-in entries in `mcp-configs/mcp-servers.json` for users who want them. + +## Opt-out + +`ECC_DISABLED_MCPS` filters ECC-generated MCP configs at install/sync time: + +```bash +export ECC_DISABLED_MCPS="chrome-devtools" +``` + +## Adding a connector + +Open a PR that argues both prongs of the rule explicitly. "Popular" is not an argument; "the job is stateful and universal" is. diff --git a/tests/plugin-manifest.test.js b/tests/plugin-manifest.test.js index 1661d433..bfbaa1bf 100644 --- a/tests/plugin-manifest.test.js +++ b/tests/plugin-manifest.test.js @@ -61,10 +61,7 @@ function loadJsonObject(filePath, label) { assert.fail(`Expected ${label} to contain valid JSON: ${error.message}`); } - assert.ok( - parsed && typeof parsed === 'object' && !Array.isArray(parsed), - `Expected ${label} to contain a JSON object`, - ); + assert.ok(parsed && typeof parsed === 'object' && !Array.isArray(parsed), `Expected ${label} to contain a JSON object`); return parsed; } @@ -161,34 +158,22 @@ test('.opencode/plugins/ecc-hooks.ts active plugin banner matches package.json', test('docs/pt-BR/README.md latest release heading matches package.json', () => { const source = fs.readFileSync(ptBrReadmePath, 'utf8'); - assert.ok( - source.includes(`### v${expectedVersion} `), - 'Expected docs/pt-BR/README.md to advertise the current release heading', - ); + assert.ok(source.includes(`### v${expectedVersion} `), 'Expected docs/pt-BR/README.md to advertise the current release heading'); }); test('docs/tr/README.md latest release heading matches package.json', () => { const source = fs.readFileSync(trReadmePath, 'utf8'); - assert.ok( - source.includes(`### v${expectedVersion} `), - 'Expected docs/tr/README.md to advertise the current release heading', - ); + assert.ok(source.includes(`### v${expectedVersion} `), 'Expected docs/tr/README.md to advertise the current release heading'); }); test('README.zh-CN.md latest release heading matches package.json', () => { const source = fs.readFileSync(rootZhCnReadmePath, 'utf8'); - assert.ok( - source.includes(`### v${expectedVersion} `), - 'Expected README.zh-CN.md to advertise the current release heading', - ); + assert.ok(source.includes(`### v${expectedVersion} `), 'Expected README.zh-CN.md to advertise the current release heading'); }); test('docs/zh-CN/README.md latest release heading matches package.json', () => { const source = fs.readFileSync(zhCnReadmePath, 'utf8'); - assert.ok( - source.includes(`### v${expectedVersion} `), - 'Expected docs/zh-CN/README.md to advertise the current release heading', - ); + assert.ok(source.includes(`### v${expectedVersion} `), 'Expected docs/zh-CN/README.md to advertise the current release heading'); }); // ── Claude plugin manifest ──────────────────────────────────────────────────── @@ -216,10 +201,7 @@ test('claude plugin.json uses short plugin slug', () => { }); test('claude plugin.json does NOT have agents field (unsupported by Claude Code validator)', () => { - assert.ok( - !('agents' in claudePlugin), - 'agents field must NOT be declared — Claude Code plugin validator rejects it', - ); + assert.ok(!('agents' in claudePlugin), 'agents field must NOT be declared — Claude Code plugin validator rejects it'); }); test('claude plugin.json skills is an array', () => { @@ -234,26 +216,13 @@ test('claude plugin.json disables bundled MCP servers for provider tool-name com const legacyPluginName = 'everything-claude-code'; const reportedOverlongToolName = `mcp__plugin_${legacyPluginName}_github__create_pull_request_review`; - assert.ok( - reportedOverlongToolName.length > 64, - 'Expected the reported GitHub MCP tool name to exceed strict provider limits without the MCP opt-out', - ); - assert.ok( - Object.prototype.hasOwnProperty.call(claudePlugin, 'mcpServers'), - 'Expected mcpServers to be explicitly declared so Claude Code does not auto-load root .mcp.json', - ); - assert.deepStrictEqual( - claudePlugin.mcpServers, - {}, - 'Claude plugin installs must not auto-bundle root MCP servers; document/manual MCP install remains supported', - ); + assert.ok(reportedOverlongToolName.length > 64, 'Expected the reported GitHub MCP tool name to exceed strict provider limits without the MCP opt-out'); + assert.ok(Object.prototype.hasOwnProperty.call(claudePlugin, 'mcpServers'), 'Expected mcpServers to be explicitly declared so Claude Code does not auto-load root .mcp.json'); + assert.deepStrictEqual(claudePlugin.mcpServers, {}, 'Claude plugin installs must not auto-bundle root MCP servers; document/manual MCP install remains supported'); }); test('claude plugin.json does NOT have explicit hooks declaration', () => { - assert.ok( - !('hooks' in claudePlugin), - 'hooks field must NOT be declared — Claude Code v2.1+ auto-loads hooks/hooks.json by convention', - ); + assert.ok(!('hooks' in claudePlugin), 'hooks field must NOT be declared — Claude Code v2.1+ auto-loads hooks/hooks.json by convention'); }); console.log('\n=== .claude-plugin/marketplace.json ===\n'); @@ -267,10 +236,7 @@ const claudeMarketplace = loadJsonObject(claudeMarketplacePath, '.claude-plugin/ test('claude marketplace.json keeps only Claude-supported top-level keys', () => { const unsupportedTopLevelKeys = ['$schema', 'description']; for (const key of unsupportedTopLevelKeys) { - assert.ok( - !(key in claudeMarketplace), - `.claude-plugin/marketplace.json must not declare unsupported top-level key "${key}"`, - ); + assert.ok(!(key in claudeMarketplace), `.claude-plugin/marketplace.json must not declare unsupported top-level key "${key}"`); } }); @@ -316,39 +282,21 @@ test('codex plugin.json version matches package.json', () => { }); test('codex plugin.json skills is a string (not array) per official spec', () => { - assert.strictEqual( - typeof codexPlugin.skills, - 'string', - 'skills must be a string path per Codex official docs, not an array', - ); + assert.strictEqual(typeof codexPlugin.skills, 'string', 'skills must be a string path per Codex official docs, not an array'); }); test('codex plugin.json mcpServers is a string path (not array) per official spec', () => { - assert.strictEqual( - typeof codexPlugin.mcpServers, - 'string', - 'mcpServers must be a string path per Codex official docs', - ); + assert.strictEqual(typeof codexPlugin.mcpServers, 'string', 'mcpServers must be a string path per Codex official docs'); }); test('codex plugin.json mcpServers exactly matches "./.mcp.json"', () => { - assert.strictEqual( - codexPlugin.mcpServers, - './.mcp.json', - 'mcpServers must point exactly to "./.mcp.json" per official docs', - ); + assert.strictEqual(codexPlugin.mcpServers, './.mcp.json', 'mcpServers must point exactly to "./.mcp.json" per official docs'); const mcpPath = path.join(repoRoot, codexPlugin.mcpServers.replace(/^\.\//, '')); - assert.ok( - fs.existsSync(mcpPath), - `mcpServers file missing at plugin root: ${codexPlugin.mcpServers}`, - ); + assert.ok(fs.existsSync(mcpPath), `mcpServers file missing at plugin root: ${codexPlugin.mcpServers}`); }); test('codex plugin.json has interface.displayName', () => { - assert.ok( - codexPlugin.interface && codexPlugin.interface.displayName, - 'Expected interface.displayName for plugin directory presentation', - ); + assert.ok(codexPlugin.interface && codexPlugin.interface.displayName, 'Expected interface.displayName for plugin directory presentation'); }); test('codex plugin.json uses canonical ECC repo and display name', () => { @@ -363,20 +311,11 @@ test('codex plugin presentation assets exist and ship in npm package', () => { for (const field of ['composerIcon', 'logo']) { const assetPath = codexPlugin.interface[field]; assert.ok(assetPath, `Expected interface.${field}`); - assert.ok( - assetPath.startsWith('./assets/'), - `Expected interface.${field} to point at a root assets path, got ${assetPath}`, - ); + assert.ok(assetPath.startsWith('./assets/'), `Expected interface.${field} to point at a root assets path, got ${assetPath}`); const packagePath = assetPath.replace(/^\.\//, ''); - assert.ok( - fs.existsSync(path.join(repoRoot, packagePath)), - `Expected interface.${field} asset to exist: ${packagePath}`, - ); - assert.ok( - packageFiles.has(packagePath), - `Expected package.json files to include interface.${field} asset: ${packagePath}`, - ); + assert.ok(fs.existsSync(path.join(repoRoot, packagePath)), `Expected interface.${field} asset to exist: ${packagePath}`); + assert.ok(packageFiles.has(packagePath), `Expected package.json files to include interface.${field} asset: ${packagePath}`); } }); @@ -388,31 +327,27 @@ const mcpJsonPath = path.join(repoRoot, '.mcp.json'); test('.mcp.json exists at plugin root (not inside .codex-plugin/)', () => { assert.ok(fs.existsSync(mcpJsonPath), 'Expected .mcp.json at repo root (plugin root)'); - assert.ok( - !fs.existsSync(path.join(repoRoot, '.codex-plugin', '.mcp.json')), - '.mcp.json must NOT be inside .codex-plugin/ — only plugin.json belongs there', - ); + assert.ok(!fs.existsSync(path.join(repoRoot, '.codex-plugin', '.mcp.json')), '.mcp.json must NOT be inside .codex-plugin/ — only plugin.json belongs there'); }); const mcpConfig = loadJsonObject(mcpJsonPath, '.mcp.json'); test('.mcp.json has mcpServers object', () => { - assert.ok( - mcpConfig.mcpServers && typeof mcpConfig.mcpServers === 'object', - 'Expected mcpServers object', - ); + assert.ok(mcpConfig.mcpServers && typeof mcpConfig.mcpServers === 'object', 'Expected mcpServers object'); }); -test('.mcp.json includes at least github, context7, and exa servers', () => { +test('.mcp.json default set follows the connector policy', () => { const servers = Object.keys(mcpConfig.mcpServers); - assert.ok(servers.includes('github'), 'Expected github MCP server'); - assert.ok(servers.includes('context7'), 'Expected context7 MCP server'); - assert.ok(servers.includes('exa'), 'Expected exa MCP server'); + assert.ok(servers.includes('chrome-devtools'), 'Expected chrome-devtools as the default browser connector'); + assert.ok(servers.length <= 2, `Default connector set must stay minimal per docs/MCP-CONNECTOR-POLICY.md (found ${servers.length})`); }); -test('.mcp.json declares exa as an http MCP server', () => { - assert.strictEqual(mcpConfig.mcpServers.exa.type, 'http', 'Expected exa MCP server to declare type=http'); - assert.strictEqual(mcpConfig.mcpServers.exa.url, 'https://mcp.exa.ai/mcp', 'Expected exa MCP server URL to remain unchanged'); +test('.mcp.json does not reintroduce retired default connectors', () => { + const retired = ['github', 'context7', 'exa', 'memory', 'playwright', 'sequential-thinking']; + const servers = Object.keys(mcpConfig.mcpServers); + for (const name of retired) { + assert.ok(!servers.includes(name), `${name} was retired from the default set (June 2026 audit) — it lives in mcp-configs/mcp-servers.json as opt-in; see docs/MCP-CONNECTOR-POLICY.md`); + } }); // ── Codex marketplace file ──────────────────────────────────────────────────── @@ -422,10 +357,7 @@ console.log('\n=== .agents/plugins/marketplace.json ===\n'); const marketplacePath = path.join(repoRoot, '.agents', 'plugins', 'marketplace.json'); test('marketplace.json exists at .agents/plugins/', () => { - assert.ok( - fs.existsSync(marketplacePath), - 'Expected .agents/plugins/marketplace.json for Codex repo marketplace discovery', - ); + assert.ok(fs.existsSync(marketplacePath), 'Expected .agents/plugins/marketplace.json for Codex repo marketplace discovery'); }); const marketplace = loadJsonObject(marketplacePath, '.agents/plugins/marketplace.json'); @@ -467,24 +399,11 @@ test('marketplace local plugin path resolves to the repo-root Codex bundle', () continue; } - assert.ok( - plugin.source.path.startsWith('./'), - `Codex marketplace source.path must be ./-prefixed: ${plugin.source.path}`, - ); + assert.ok(plugin.source.path.startsWith('./'), `Codex marketplace source.path must be ./-prefixed: ${plugin.source.path}`); const resolvedRoot = path.resolve(repoRoot, plugin.source.path); - assert.strictEqual( - resolvedRoot, - repoRoot, - `Expected local marketplace path to resolve to repo root from marketplace root, got: ${plugin.source.path}`, - ); - assert.ok( - fs.existsSync(path.join(resolvedRoot, '.codex-plugin', 'plugin.json')), - `Codex plugin manifest missing under resolved marketplace root: ${plugin.source.path}`, - ); - assert.ok( - fs.existsSync(path.join(resolvedRoot, '.mcp.json')), - `Root MCP config missing under resolved marketplace root: ${plugin.source.path}`, - ); + assert.strictEqual(resolvedRoot, repoRoot, `Expected local marketplace path to resolve to repo root from marketplace root, got: ${plugin.source.path}`); + assert.ok(fs.existsSync(path.join(resolvedRoot, '.codex-plugin', 'plugin.json')), `Codex plugin manifest missing under resolved marketplace root: ${plugin.source.path}`); + assert.ok(fs.existsSync(path.join(resolvedRoot, '.mcp.json')), `Root MCP config missing under resolved marketplace root: ${plugin.source.path}`); } }); @@ -510,7 +429,7 @@ test('user-facing docs do not use overlong legacy marketplace install commands', path.join(repoRoot, 'README.md'), path.join(repoRoot, 'README.zh-CN.md'), path.join(repoRoot, 'skills', 'configure-ecc', 'SKILL.md'), - ...collectMarkdownFiles(path.join(repoRoot, 'docs')), + ...collectMarkdownFiles(path.join(repoRoot, 'docs')) ].filter(filePath => !path.relative(repoRoot, filePath).startsWith(`docs${path.sep}drafts${path.sep}`)); const offenders = []; @@ -521,19 +440,11 @@ test('user-facing docs do not use overlong legacy marketplace install commands', } } - assert.deepStrictEqual( - offenders, - [], - `Overlong legacy install commands must not appear in user-facing docs: ${offenders.join(', ')}`, - ); + assert.deepStrictEqual(offenders, [], `Overlong legacy install commands must not appear in user-facing docs: ${offenders.join(', ')}`); }); test('user-facing docs do not use the legacy non-URL marketplace add form', () => { - const markdownFiles = [ - path.join(repoRoot, 'README.md'), - path.join(repoRoot, 'README.zh-CN.md'), - ...collectMarkdownFiles(path.join(repoRoot, 'docs')), - ]; + const markdownFiles = [path.join(repoRoot, 'README.md'), path.join(repoRoot, 'README.zh-CN.md'), ...collectMarkdownFiles(path.join(repoRoot, 'docs'))]; const offenders = []; for (const filePath of markdownFiles) { @@ -543,31 +454,15 @@ test('user-facing docs do not use the legacy non-URL marketplace add form', () = } } - assert.deepStrictEqual( - offenders, - [], - `Legacy non-URL marketplace add form must not appear in user-facing docs: ${offenders.join(', ')}`, - ); + assert.deepStrictEqual(offenders, [], `Legacy non-URL marketplace add form must not appear in user-facing docs: ${offenders.join(', ')}`); }); test('.codex-plugin README uses current marketplace add flow', () => { const readme = fs.readFileSync(path.join(repoRoot, '.codex-plugin', 'README.md'), 'utf8'); - assert.ok( - readme.includes('codex plugin marketplace add'), - 'Expected .codex-plugin README to document codex plugin marketplace add', - ); - assert.ok( - readme.includes('codex plugin marketplace add affaan-m/ECC'), - 'Expected .codex-plugin README to document the canonical ECC repo marketplace source', - ); - assert.ok( - readme.includes('Official Plugin Directory publishing is coming soon'), - 'Expected .codex-plugin README to document current official directory status', - ); - assert.ok( - !/\bcodex plugin install\b/.test(readme), - 'codex plugin install is not a current Codex CLI command', - ); + assert.ok(readme.includes('codex plugin marketplace add'), 'Expected .codex-plugin README to document codex plugin marketplace add'); + assert.ok(readme.includes('codex plugin marketplace add affaan-m/ECC'), 'Expected .codex-plugin README to document the canonical ECC repo marketplace source'); + assert.ok(readme.includes('Official Plugin Directory publishing is coming soon'), 'Expected .codex-plugin README to document current official directory status'); + assert.ok(!/\bcodex plugin install\b/.test(readme), 'codex plugin install is not a current Codex CLI command'); }); test('docs/zh-CN/README.md version row matches package.json', () => { diff --git a/tests/scripts/install-apply.test.js b/tests/scripts/install-apply.test.js index d939154c..cfe8816b 100644 --- a/tests/scripts/install-apply.test.js +++ b/tests/scripts/install-apply.test.js @@ -150,8 +150,7 @@ function runTests() { const mcpConfig = readJson(path.join(projectDir, '.cursor', 'mcp.json')); assert.strictEqual(hooksConfig.version, 1); assert.ok(hooksConfig.hooks.sessionStart, 'Should keep Cursor sessionStart hooks'); - assert.ok(mcpConfig.mcpServers.github, 'Should install shared MCP servers into Cursor'); - assert.ok(mcpConfig.mcpServers.context7, 'Should include bundled documentation MCPs'); + assert.ok(mcpConfig.mcpServers['chrome-devtools'], 'Should install shared MCP servers into Cursor'); const statePath = path.join(projectDir, '.cursor', 'ecc-install-state.json'); const state = readJson(statePath); @@ -194,8 +193,7 @@ function runTests() { const mcpConfig = readJson(path.join(projectDir, '.cursor', 'mcp.json')); assert.ok(mcpConfig.mcpServers.custom, 'Should preserve existing custom Cursor MCP servers'); - assert.ok(mcpConfig.mcpServers.github, 'Should merge bundled GitHub MCP server'); - assert.ok(mcpConfig.mcpServers.playwright, 'Should merge bundled Playwright MCP server'); + assert.ok(mcpConfig.mcpServers['chrome-devtools'], 'Should merge the bundled chrome-devtools MCP server'); } finally { cleanup(homeDir); cleanup(projectDir);