MCP best practices: context budget, speed and parallel agents
MCP best practices keep a server setup lean, fast and predictable: measure what each server costs in context, cap and scope tool results, pin server versions, give each parallel agent its own MCP profile and browser or database state, and prove every server with a read-only health check. In 2026, tool results cost more context than tool schemas.
Four agents run in four worktrees. Two of them fail at once because they fight over the same Playwright browser profile. A third migrated its database, but its Postgres server still answers with the old schema. The fourth pulled a 400-line Actions log into context and lost the plan it was following. None of these is a broken server. Each one is a setup that worked for one agent and nobody tuned for four. This page is for developers and tech leads who own that setup.
What a tuned MCP setup gives you
Section titled “What a tuned MCP setup gives you”- A way to measure each server’s context cost in Claude Code and Codex, instead of guessing.
- The switches that cap result size and tool count:
MAX_MCP_OUTPUT_TOKENS,enabled_tools, GitHub toolset URLs. - Pinned server versions and timeouts, so a slow
npxdownload does not look like a broken server. - Per-agent MCP profiles for parallel agents:
--mcp-configwith--strict-mcp-configin Claude Code,--profilein Codex, and isolated browser and database state. - A health check you can paste, run locally, and repeat in CI, plus a failure table with the fix for each symptom.
For first-time setup, start with the MCP starter stack. For servers that do not connect at all, see MCP server connection issues.
What do MCP servers cost in context in 2026?
Section titled “What do MCP servers cost in context in 2026?”The cost moved. Claude Code (checked on 2.1.283; also true on the stable channel) and Codex (0.157.1, since 0.143.0) both load MCP tool schemas through tool search, on by default: the agent sees a searchable index and pulls a tool’s full definition only when it needs it. Ten connected servers no longer mean ten full schema dumps in every request.
What still fills the context window is tool output. A database schema dump, a full CI log, a 30-item issue list or a page snapshot lands in context and stays there until the session compacts or ends. That is why the rules below are mostly about results, not about server count. Cursor’s loading behaviour was not verified for this page (cursor.com was unreachable on 2026-09-26), so in Cursor keep the server list short and turn off what the current task does not need.
The ecosystem is large enough that “install everything that looks useful” is a real temptation. As of 2026-09-26, the Official MCP Registry, still a preview with no quality gate beyond namespace verification, lists 36,176 servers (registry API count). Install for the job in front of you.
Measure a server before you keep it
Section titled “Measure a server before you keep it”# In a terminal: every configured server, with a health check for approved onesclaude mcp listclaude mcp get githubRun /context in a fresh session, then run one typical task that uses the server, then run /context again. The difference is what the server costs you per task. For a server installed as a plugin, claude plugin details PLUGIN_NAME shows the plugin’s component inventory and projected token cost. Measure the MCP servers it adds with /context before and after, as above, because tool schemas load through tool search on demand.
# In a terminal: configured servers and whether each is enabledcodex mcp listcodex mcp get githubIn the TUI, /mcp lists the configured MCP tools and /mcp verbose adds detail. Compare the token usage the session reports before and after one typical task.
Servers live in .cursor/mcp.json (project) or ~/.cursor/mcp.json (global). Cursor’s settings list each configured server and let you turn one off without deleting its entry. Check the current menu path in Cursor’s MCP docs, which could not be re-read for this page.
How do you keep MCP tool results small?
Section titled “How do you keep MCP tool results small?”Output is the cost, so control output at three levels: the client cap, the server’s tool list, and the prompt.
-
Cap a single result at the client. In Claude Code,
MAX_MCP_OUTPUT_TOKENSsets the ceiling for one MCP tool result; the 2.1.283 binary falls back to 25,000 tokens when you do not set it. Lower it for a session that must stay lean:MAX_MCP_OUTPUT_TOKENS=8000 claude. The cap is a safety net, not a filter: a result that hits it is still the wrong result, so fix the prompt or the tool as well. -
Expose fewer tools per server. Codex filters tools on the client with
enabled_toolsordisabled_toolsin the server’sconfig.tomlentry. Many servers also scope themselves: GitHub’s remote server takes per-toolset and read-only URLs such ashttps://api.githubcopilot.com/mcp/x/actions/readonly.# ~/.codex/config.toml (parsed with codex mcp get on 0.157.1;# tool names from the github/github-mcp-server README, read 2026-09-26)[mcp_servers.github]url = "https://api.githubcopilot.com/mcp/"bearer_token_env_var = "GITHUB_PAT"enabled_tools = ["issue_read", "list_pull_requests", "pull_request_read"]tool_timeout_sec = 60 -
Scope the question. “Show me the database schema” returns every table. “Show the
usersandorderstables and the foreign keys between them” returns two. “Read the failing Actions log” returns hundreds of lines; “return only the failing test names and their first error line” returns ten. -
Push large research into a subagent. A subagent spends its own context on the raw results and returns only a summary. For the pattern that turns a tool-heavy server into on-demand code, see reducing MCP token cost.
How do you make MCP servers start fast and behave the same for everyone?
Section titled “How do you make MCP servers start fast and behave the same for everyone?”Most “the server is slow” reports are a first-run download, not a slow server. An unpinned npx -y package@latest resolves and may download a new release at session start, and it can change the tool set under you between Monday and Tuesday.
- Pin versions in
args.@playwright/mcp@0.0.82(Playwright MCP) or@upstash/context7-mcp@4.1.1(Context7), not@latest. For Python servers run throughuvx, pin the same way:uvx postgres-mcp@0.3.0(Postgres MCP Pro and other database servers). Bump pins in a reviewed pull request, like any dependency. - Warm the cache once. Run the pinned command in a terminal before the first session of the day, so the download happens outside the agent’s startup window.
- Set timeouts deliberately. In Claude Code,
MCP_TIMEOUT(server startup) andMCP_TOOL_TIMEOUT(one tool call) take milliseconds, for exampleMCP_TIMEOUT=20000 claude. In Codex, setstartup_timeout_secandtool_timeout_secon the server’s[mcp_servers.NAME]entry. Asking the agent to “wait longer” in the prompt changes nothing: the client enforces the timeout. - Reload after a schema change. A database server that caches metadata keeps reporting the old schema after a migration. In Claude Code, open
/mcpand reconnect the server. In Codex, start a new session; the configuration is read at launch. In Cursor, restart the server from its MCP settings.
How do you run MCP servers safely across parallel agents?
Section titled “How do you run MCP servers safely across parallel agents?”A setup that is fine for one agent breaks with four, because some MCP state is shared per machine or per account, not per worktree. Give each agent its own profile and its own stateful resources.
| Shared resource | What goes wrong | Isolation |
|---|---|---|
| Browser profile (Playwright MCP) | A persistent profile opens in one browser at a time, so the second agent fails | --isolated (profile kept in memory) or a distinct --user-data-dir per agent |
| Database behind a DB server | Agent A migrates, agent B’s server reads the changed schema or data | One database per worktree, passed through an environment variable |
| API token and its rate limit | Four agents on one GitHub PAT share one budget: 5,000 requests per hour for a user’s personal rate limit (GitHub REST docs, read 2026-09-26) | Scoped prompts, read-only toolset URLs, and gh for bulk reads |
| The server list itself | A review agent can write to Jira because the build agent needed it | A per-agent MCP profile with only the servers that agent’s job needs |
Give each agent its own MCP profile
Section titled “Give each agent its own MCP profile”Keep one JSON file per role and start each agent with only that file. --strict-mcp-config ignores every other MCP configuration, including the project .mcp.json and your user-scope servers.
# In a terminal, inside the reviewer's worktreeclaude --mcp-config mcp-profiles/review.json --strict-mcp-config{ "mcpServers": { "github": { "type": "http", "url": "https://api.githubcopilot.com/mcp/x/pull_requests/readonly", "headers": { "Authorization": "Bearer ${GITHUB_PAT}" } } }}Start the session and run /mcp: the server should show as connected, which also confirms that ${GITHUB_PAT} was expanded. The project .mcp.json supports ${VAR} and ${VAR:-default} expansion, so one committed file can point each worktree at its own resources:
{ "mcpServers": { "playwright": { "command": "npx", "args": ["-y", "@playwright/mcp@0.0.82", "--isolated"] }, "postgres": { "command": "uvx", "args": ["postgres-mcp@0.3.0", "--access-mode=restricted"], "env": { "DATABASE_URI": "${DATABASE_URI}" } } }}-p NAME layers $CODEX_HOME/NAME.config.toml on top of the base config. Keep heavy or write-capable servers disabled in the base file and switch them on per profile:
[mcp_servers.playwright]command = "npx"args = ["-y", "@playwright/mcp@0.0.82", "--isolated"]enabled = false[mcp_servers.playwright]enabled = true# In a terminal: the UI-checking agent gets the browser, the others do notcodex -p browser# One-off override for a single runcodex -c mcp_servers.github.enabled=falseBoth forms were checked with codex mcp list on 0.157.1.
Each worktree opened as its own workspace reads its own .cursor/mcp.json, so the per-worktree file is the profile. Add --isolated to the Playwright args, and give each worktree its own database URL. Cursor’s docs describe ${env:NAME} interpolation in mcp.json (secondary source: a snippet of cursor.com/docs/mcp); confirm it on your version before you rely on it.
For the worktree setup itself, see parallel agents.
Replace wt-api-2 with the database name your worktree should use.
How should a team share one MCP configuration?
Section titled “How should a team share one MCP configuration?”Commit the configuration, never the credentials, and make one person the owner of the file.
- Claude Code: commit
.mcp.jsonat the repo root (not.claude/mcp.json) with${VAR}references.claude mcp add --scope projectwrites it for you. Each developer approves project servers on first use;claude mcp listshows unapproved ones as pending, andclaude mcp reset-project-choicesclears earlier approvals. - Codex: servers live in
~/.codex/config.toml. Keep a reviewed snippet in the repo (for exampledocs/codex-mcp.toml) that developers paste, with tokens read throughbearer_token_env_var,env_http_headersorenv. Aheaders = {…}table is silently ignored; the keys arehttp_headers,env_http_headersandbearer_token_env_var. - Cursor: commit
.cursor/mcp.jsonwith the same servers and pinned versions. - Ownership: put the config files under
CODEOWNERS, so adding a server or bumping a pin needs the tech lead’s review. Organization-wide allowlists belong in MCP registries and gateways; token scopes and prompt-injection defences belong in MCP security.
The onboarding note in your contributing guide needs four lines: the servers and pinned versions, the token scopes each one needs, the environment variables to set, and the health-check prompt below.
How do you prove the MCP setup works without reading every tool call?
Section titled “How do you prove the MCP setup works without reading every tool call?”You check evidence that a script or a person can compare, not transcripts.
-
Run the health check. The prompt below calls one read-only tool per server and returns PASS or FAIL with an identifier you can confirm, such as a PR number or a table count. A new developer’s setup is done when every row passes.
-
Record the context cost in the pull request that adds a server. Paste the
/contextnumbers from before and after one typical task. A reviewer approves a cost, not a vibe. -
Diff the pins. Version bumps arrive as a config diff under
CODEOWNERS, so a changed tool set is reviewed before it reaches anyone’s session. -
Repeat the check in CI from a trusted checkout. Run it on a schedule from the default branch, never on code from an untrusted pull request.
--strict-mcp-configmakes the job load only the CI profile, not a.mcp.jsona contributor could change:Terminal window # In CI, from a checkout of the default branchclaude -p "$(cat ci/mcp-health-prompt.txt)" \--mcp-config ci/mcp-readonly.json --strict-mcp-config \--allowedTools "mcp__github" "mcp__postgres"Codex uses the same idea with a profile:
codex -p ci-readonly exec "$(cat ci/mcp-health-prompt.txt)". Keep the CI profile read-only and give the job a token with read scopes only. The CI prompt file is the prompt below without the Browser line, because the CI profile loads only GitHub and Postgres.
When MCP tuning breaks: symptoms and fixes
Section titled “When MCP tuning breaks: symptoms and fixes”| Symptom | Likely cause | Recovery |
|---|---|---|
| The agent loses track of the plan halfway through | One large tool result filled the context | /compact or a new session; lower MAX_MCP_OUTPUT_TOKENS; rewrite the prompt to ask for filtered results |
| The second parallel agent’s browser fails to start | Two agents share one persistent Playwright profile | Add --isolated to the Playwright args in every profile |
| The database server reports a column you dropped | The server cached the schema before the migration | Reconnect the server (/mcp in Claude Code) or start a new Codex session |
| A tool the prompt names is “not found” | The server version changed under an unpinned @latest, or enabled_tools filters it out | Pin the version; check the filter with codex mcp get NAME |
| The first call of the day times out | A first-run package download inside the startup window | Warm the cache in a terminal; raise MCP_TIMEOUT or startup_timeout_sec |
| The agent calls the wrong server’s tool | Two servers expose similar tools | Name the server in the prompt, or remove one from this agent’s profile |
| GitHub calls start failing mid-run with parallel agents | The shared PAT hit its hourly rate limit | Narrow prompts, use gh for bulk reads, and give background jobs their own GitHub App token |
| Works locally, fails in CI | CI has no tokens, a different network, or loads a different config | A dedicated CI profile with --strict-mcp-config and read-only secrets |
| A teammate’s agent sees different tools | Unapproved or unpinned project servers | claude mcp list to check approval; pin versions in the committed file |
If a server does not connect at all, work through MCP server connection issues first. If a result looks like it carries instructions to the agent, treat it as a prompt-injection attempt and follow MCP security.