Workstation Setup tutorial series — Part 4: Connect the MCP server
A complete Conduction workstation has at least one MCP server wired into the project root. This short module explains what MCP is, how the .mcp.json file works, and how to set up the Playwright browser pool that the testing skills depend on. Pointers to the dedicated Conduction MCP server tutorial included. Fourth of six short modules.
MCP — Model Context Protocol — is the standard interface Claude Code uses to call out to external tools. Inside a Conduction project, that means two things in practice: a Playwright browser pool (browser-1 … browser-7) for the testing skills, and the OpenRegister MCP server for direct access to your local Conduction data layer. A workstation isn't really complete until at least the browser pool is wired into your project root via .mcp.json. This part covers that; the OpenRegister MCP server has its own tutorial — we link to it at the bottom.
In one sentence
MCP is a small open protocol that lets Claude Code call out to external services via a uniform interface. An "MCP server" is any program that speaks that protocol; Claude treats it as a tool provider. You wire servers into a project by listing them in .mcp.json at the project root.
A few servers you'll meet at Conduction:
browser-1…browser-7— Playwright browser instances Claude can use for testing skills (/test-app,/test-counsel, thehydra-gate-*family).- The OpenRegister MCP server — Claude can call OpenRegister directly: list registers and schemas, search and read objects, read the audit trail. Covered in its own tutorial.
This module deliberately stops at the Playwright pool: it's the one every Conduction developer needs and it works without any extra accounts.
Step 1: create .mcp.json at the project root
In a Conduction project, the file already exists — open it, scroll, confirm. In a fresh folder, create it. The minimum configuration that all testing skills assume is the seven-browser pool: six headless browsers for parallel agents, one headed browser for when you want to watch.
/path/to/your-project/.mcp.json:
{
"mcpServers": {
"browser-1": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"]
},
"browser-2": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"]
},
"browser-3": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"]
},
"browser-4": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"]
},
"browser-5": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"]
},
"browser-6": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--isolated"]
},
"browser-7": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest", "--browser", "chromium", "--headless", "--isolated"]
}
}
}
Notice the asymmetry: browser-6 omits --headless. That's deliberate — it's the visible browser for when you want to watch Claude work. The other six stay invisible.
No need to type this from scratch. The
.githubrepo ships an example .mcp.json you can copy directly.
Step 2: trust the servers via .claude/settings.json
Every Conduction repo ships a .claude/settings.json (project-level — separate from your global one) that pre-approves the MCP servers so background agents don't get blocked by prompts. The two relevant lines:
{
"enableAllProjectMcpServers": true,
"permissions": {
"allow": ["mcp__browser-*"]
}
}
enableAllProjectMcpServers: true— auto-approves all servers listed in.mcp.jsonwithout a per-reload prompt.mcp__browser-*— pre-approves every browser tool call so sub-agents working in parallel don't get silently denied.
In a fresh folder, create these settings yourself. In a Conduction repo, they're already there.
Step 3: reload VS Code
Ctrl+Shift+P → "Developer: Reload Window". VS Code re-reads .mcp.json and starts the browser processes.
Step 4: verify
Open the MCP servers panel in VS Code. Two equivalent ways:
- Type
/MCP serversin the Claude Code chat input, or Ctrl+Shift+P→ search "MCP servers" → open the panel.
All seven browsers should show Connected. If any show an error, open the output panel: Ctrl+Shift+P → "Output: Focus on Output" → select Claude VSCode from the dropdown — the error line spells out what went wrong.
Common cause if a browser fails to connect: the Playwright Chromium binary isn't installed. Run
npx playwright install chromiumfrom your WSL terminal (you should already have this from Part 2 Step 9). Reload the window after.
How does Claude know which browser to use?
There's no central dispatcher. The skills themselves assign browsers — by convention:
| Browser | When it's used |
|---|---|
browser-1 | The default; your main session uses this. |
browser-2…browser-5 and browser-7 | Parallel sub-agents (e.g. /test-app Full mode, /test-counsel). |
browser-6 (headed) | When you've explicitly asked Claude to "show me" — that window pops up so you can watch. |
If a browser errors out, Claude falls back to the next numbered one. You don't have to manage this manually.
Why seven browsers?
The count is sized for /test-app Full mode's worst case, with one slot reserved for human observation:
- One main agent (
browser-1) + five parallel sub-agents (browser-2–browser-5andbrowser-7) = six headless. That covers/test-appFull mode's heaviest parallel run. - One headed browser for observation — handy when you want to see what Claude is doing rather than just read the result.
Other workspaces may use fewer (or more) browsers depending on parallelism. The Nextcloud workspace uses these seven.
What about the OpenRegister MCP server?
For Conduction projects, the Playwright pool alone isn't enough. The OpenRegister MCP server — shipped as an endpoint inside the OpenRegister Nextcloud app at http://localhost:8080/index.php/apps/openregister/mcp — gives Claude direct access to the data layer of your Conduction stack: list registers and schemas, search and read objects, walk the audit trail.
It's a separate install with its own configuration, so it lives in a dedicated tutorial:
- Set up the Conduction MCP server — the dedicated tutorial for that step.
When you do that tutorial, you add another entry to the same .mcp.json and reload. The Playwright pool stays.
A note on .mcp.json vs. ~/.claude/settings.json
Two files, different scopes. Keep them straight:
| File | Scope | What it does |
|---|---|---|
~/.claude/settings.json | User-level, all projects | Your safety policy, write-approval hooks, version check (Part 3). |
<project>/.mcp.json | Project-level, this repo | Which MCP servers to launch for this project. Browsers, the Conduction MCP server, anything else specific. |
<project>/.claude/settings.json | Project-level, this repo | Project permissions, MCP allowlist, pre-approvals. |
The user-level config never lists MCP servers; the project-level config does. That separation means each project picks its own set of servers without touching your global safety policy.
Troubleshooting
The MCP servers panel shows 0 connected, no errorVS Code didn't pick up .mcp.json. Reload the window (Ctrl+Shift+P → Developer: Reload Window). If still empty, confirm the file is at the project root and is valid JSON — paste it into jsonlint.com.
One specific browser errors with 'browserType.launchPersistentContext: Executable doesn't exist'The Playwright Chromium binary isn't installed. Run npx playwright install chromium from your WSL terminal, then reload VS Code.
A background agent says 'tool call denied' for mcp__browser-3The project's .claude/settings.json doesn't have the mcp__browser-* allow rule, or enableAllProjectMcpServers is missing. Add both, reload, retry.
browser-6 starts headless even though I removed --headlessCheck there's no HEADLESS=true in your shell environment, and that no other --headless flag was added by an extension config. Reload and watch the output panel during startup to confirm the args passed in.
Test yourself
Five short questions to check that this part landed. Stuck? Click Hint. Curious about the answer? Click Answer.
1. Why does MCP need to exist as a separate protocol? Why not just give Claude direct API access to each tool?
Hint
Think about how many tools Claude might touch in one project (browser, database, GitHub, Slack, your local Nextcloud) and how that scales without a uniform interface.
Answer
A Conduction project might have Claude driving a browser (Playwright), reading from a local Nextcloud, calling GitHub's API, posting to Slack, and querying a database — all in the same session. Without MCP, each integration would be a custom adapter inside Claude: hand-written, version-locked to Claude's release schedule, impossible for third parties to add without forking.
MCP makes those adapters outside Claude: a small server speaks a standard protocol, Claude just consumes it. The trade-off is one extra hop (Claude → MCP server → tool), but the win is composability — anyone can write an MCP server in any language, drop it into .mcp.json, and Claude can use it without a Claude release.
It's the same idea as the Language Server Protocol for editors: define the wire format once, every editor + every language gets to interoperate without N×M custom integrations.
2. Why does the default Conduction pool have seven browsers, and why is one of them headed?
Hint
Six headless covers a specific parallel scenario; the seventh has a different purpose.
Answer
Six headless browsers cover the heaviest parallel scenario in /test-app Full mode: one orchestrator session plus five concurrent sub-agents, each on its own browser, plus one spare (browser-7). The Hydra pipeline and /test-counsel lean on the same pool.
The seventh browser is headed (browser-6 — note: numbered, not the last) because sometimes you want to see what Claude is doing rather than just read the result. When you ask Claude to "show me" or "watch", it switches to that visible window. Otherwise all real work happens on the headless six.
3. Without enableAllProjectMcpServers: true and the mcp__browser-* allow rule, parallel sub-agents silently fail with no error. What does each rule do, and why are both needed?
Hint
One controls which servers Claude is willing to start. The other controls which tool calls Claude is willing to make without asking. Both must be true for a background agent to use a browser non-interactively.
Answer
enableAllProjectMcpServers: trueauto-approves every server listed in.mcp.json. Without it, Claude prompts for permission each time.mcp.jsonis read (e.g. on every reload), and a background sub-agent can't respond to that prompt — it just hangs or fails.mcp__browser-*in the allow list pre-approves every browser tool call. Without it, Claude prompts before eachmcp__browser-3__navigateetc. — again, a background sub-agent can't respond.
So one rule unblocks server startup, the other unblocks per-call execution. A sub-agent that hits either gate without pre-approval fails silently because it has no UI to prompt against. The symptom is identical in both cases — "the test didn't run and there's no error" — which is why both rules need to be in place before parallel testing is attempted.
4. Where does .mcp.json live, and what's the difference between it and ~/.claude/settings.json?
Hint
Two files, two scopes. One is per-user, one is per-project.
Answer
.mcp.json lives at the project root (next to package.json or composer.json). It's per-project: which MCP servers should this project launch — browsers, the Conduction MCP server, anything else.
~/.claude/settings.json is user-level: your safety policy, write-approval hooks, version check. It applies to every project you open and never lists MCP servers itself.
The split matters because it lets each project pick its own MCP servers without touching your global safety configuration. Updating one never changes the other.
5. You run /test-app Full mode and notice that one parallel sub-agent silently fails — it just produces no test output. The MCP servers panel shows all seven browsers as Connected. What are the two most likely causes, and where do you look?
Hint
If the panel says "Connected" but the agent didn't use the browser, the breakage is between agent → tool call, not Claude → server. Two project-level things gate that interaction.
Answer
Two likely causes:
- The project's
.claude/settings.jsonis missing themcp__browser-*allow rule. Without it, the sub-agent's tool call is silently denied — no error returned, just no work done. Open the file and confirm thepermissions.allowarray includes"mcp__browser-*". - The sub-agent picked a browser that's in use by another sub-agent. Each
mcp__browser-Nis exclusive: if two agents try to grabbrowser-3, the second silently waits and may time out before the first releases. The fix is to make sure your skill assigns a unique browser per sub-agent (browser-2,browser-3, etc.) instead of all defaulting tobrowser-1.
To diagnose: open the Output panel (Ctrl+Shift+P → "Output: Focus on Output" → select Claude VSCode). The denied tool-call shows up there even when the parent agent's output looks clean.
Next step
Browsers are wired up. Now bring up a local Nextcloud so the apps you'll build actually have somewhere to run.