Skip to content

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.

  • 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 npx download does not look like a broken server.
  • Per-agent MCP profiles for parallel agents: --mcp-config with --strict-mcp-config in Claude Code, --profile in 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.

Terminal window
# In a terminal: every configured server, with a health check for approved ones
claude mcp list
claude mcp get github

Run /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.

Output is the cost, so control output at three levels: the client cap, the server’s tool list, and the prompt.

  1. Cap a single result at the client. In Claude Code, MAX_MCP_OUTPUT_TOKENS sets 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.

  2. Expose fewer tools per server. Codex filters tools on the client with enabled_tools or disabled_tools in the server’s config.toml entry. Many servers also scope themselves: GitHub’s remote server takes per-toolset and read-only URLs such as https://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
  3. Scope the question. “Show me the database schema” returns every table. “Show the users and orders tables 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.

  4. 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 through uvx, 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) and MCP_TOOL_TIMEOUT (one tool call) take milliseconds, for example MCP_TIMEOUT=20000 claude. In Codex, set startup_timeout_sec and tool_timeout_sec on 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 /mcp and 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 resourceWhat goes wrongIsolation
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 serverAgent A migrates, agent B’s server reads the changed schema or dataOne database per worktree, passed through an environment variable
API token and its rate limitFour 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 itselfA review agent can write to Jira because the build agent needed itA per-agent MCP profile with only the servers that agent’s job needs

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.

Terminal window
# In a terminal, inside the reviewer's worktree
claude --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}" }
}
}
}

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.json at the repo root (not .claude/mcp.json) with ${VAR} references. claude mcp add --scope project writes it for you. Each developer approves project servers on first use; claude mcp list shows unapproved ones as pending, and claude mcp reset-project-choices clears earlier approvals.
  • Codex: servers live in ~/.codex/config.toml. Keep a reviewed snippet in the repo (for example docs/codex-mcp.toml) that developers paste, with tokens read through bearer_token_env_var, env_http_headers or env. A headers = {…} table is silently ignored; the keys are http_headers, env_http_headers and bearer_token_env_var.
  • Cursor: commit .cursor/mcp.json with 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.

  1. 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.

  2. Record the context cost in the pull request that adds a server. Paste the /context numbers from before and after one typical task. A reviewer approves a cost, not a vibe.

  3. 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.

  4. 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-config makes the job load only the CI profile, not a .mcp.json a contributor could change:

    Terminal window
    # In CI, from a checkout of the default branch
    claude -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”
SymptomLikely causeRecovery
The agent loses track of the plan halfway throughOne 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 startTwo agents share one persistent Playwright profileAdd --isolated to the Playwright args in every profile
The database server reports a column you droppedThe server cached the schema before the migrationReconnect 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 outPin the version; check the filter with codex mcp get NAME
The first call of the day times outA first-run package download inside the startup windowWarm the cache in a terminal; raise MCP_TIMEOUT or startup_timeout_sec
The agent calls the wrong server’s toolTwo servers expose similar toolsName the server in the prompt, or remove one from this agent’s profile
GitHub calls start failing mid-run with parallel agentsThe shared PAT hit its hourly rate limitNarrow prompts, use gh for bulk reads, and give background jobs their own GitHub App token
Works locally, fails in CICI has no tokens, a different network, or loads a different configA dedicated CI profile with --strict-mcp-config and read-only secrets
A teammate’s agent sees different toolsUnapproved or unpinned project serversclaude 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.