Every prior module in this course connected one MCP server at a time — GitHub alone, Jira alone, Sentry alone. Real engineering work rarely stays that clean. A production incident needs Sentry for the stack trace, GitHub for the fix, and Jira for the paper trail, all in the same sitting. This topic covers what changes once you stop connecting servers one at a time and start running four or five of them concurrently in a single agent session: how the agent decides which server's tool to call, how to configure multiple servers without one misconfigured entry breaking the rest, how to keep the combined tool-schema payload from eating your context budget, and how to actually see what happened when a five-server workflow does something wrong.
Assume Claude Code CLI throughout, with github-mcp-server, mcp-server-atlassian (Jira), Figma's Dev Mode MCP server, sentry-mcp, and Playwright MCP all connected in the same project. The same orchestration concerns apply to Cursor, Gemini CLI, and OpenCode — the client-specific mechanics (permission prompts, config file names) differ, the tool-selection and context-budget problems don't.
How AI Agents Select and Combine Tools Across Multiple MCP Servers
An MCP client doesn't route your prompt to a server — it hands the model one flat list of tool definitions, regardless of which server they came from, and lets the model pick. create_pull_request, create_issue (Jira's, not GitHub's — yes, both toolsets can define a tool by that name), get_error_details, get_design_context, browser_snapshot — all of it sits in the same namespace the model reasons over on every turn. This is the single fact that explains most multi-MCP weirdness: the model isn't consulting a router that "knows" GitHub tools handle GitHub things. It's pattern-matching your prompt against tool descriptions and picking the best-scoring one, the same way it picks between two similarly-named functions in a single codebase.
Two servers exposing tools with overlapping names is the most common failure mode. GitHub and Jira both ship a tool that creates an issue-like object. If your prompt says "create an issue for this," and both servers are connected, the model resolves it from tool description quality and recent conversation context — not from some notion of which system is "correct" for the request. Vague prompts get routed by vibes.
> create an issue for this bug
> create a Jira issue in the WEBAPP project for this bug,
with the Sentry event as the description
The fix is always the same: name the target system in the prompt, every time, when more than one connected server could plausibly claim the request. This feels redundant on a single-server session and becomes load-bearing the moment you cross two or more. In practice, teams that skip this end up with GitHub issues meant for Jira, or a "get status" query answered from the wrong system's data because a tool name matched more literally than the others.
Tool selection quality also degrades as tool count grows. Anthropic's own guidance on tool design (echoed in most MCP server READMEs) is to keep total active tool count under roughly 30–40 for reliable selection — past that, tool descriptions start competing for the model's attention and selection accuracy measurably drops. Five fully-loaded MCP servers (GitHub's default toolset alone is over 40 tools) blows past that number before you've added Jira, Figma, Sentry, and Playwright. Scoping each server's toolset down to only what the workflow needs is not an optimization, it's a correctness requirement once you're combining servers.
GITHUB_TOOLSETS=repos,issues,pull_requests github-mcp-server
Tips
- Name the target system explicitly in every prompt where two or more connected servers could plausibly own the request — "create a Jira issue," not "create an issue."
- Scope every server's toolset to the minimum the workflow needs (GITHUB_TOOLSETS, Sentry's project-scoped auth, Jira's site restriction) — tool count, not server count, is what degrades selection accuracy.
- When a tool call resolves to the wrong server, check for a name or description collision first — it's a config problem to fix at the source, not something to work around with more prompt caveats every time.
Configuration Patterns for Running Multiple MCP Servers Simultaneously
A single .claude/settings.json (or .mcp.json for project-scoped config shared via git) can declare any number of servers under mcpServers. The pattern that holds up under five servers is the same one you used for one: each server gets its own scoped credential, its own toolset restriction where the server supports it, and its own permission entries — never a shared "MCP" catch-all permission.
{
"mcpServers": {
"github": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "GITHUB_PERSONAL_ACCESS_TOKEN",
"-e", "GITHUB_TOOLSETS=repos,issues,pull_requests",
"ghcr.io/github/github-mcp-server"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PAT}"
}
},
"jira": {
"command": "npx",
"args": ["-y", "mcp-server-atlassian"],
"env": {
"ATLASSIAN_SITE_URL": "https://acme.atlassian.net",
"ATLASSIAN_API_TOKEN": "${JIRA_API_TOKEN}",
"ATLASSIAN_EMAIL": "${JIRA_EMAIL}"
}
},
"sentry": {
"command": "npx",
"args": ["-y", "@sentry/mcp-server"],
"env": {
"SENTRY_AUTH_TOKEN": "${SENTRY_AUTH_TOKEN}",
"SENTRY_ORG": "acme",
"SENTRY_PROJECT": "webapp"
}
},
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
},
"figma": {
"type": "http",
"url": "http://127.0.0.1:3845/mcp"
}
},
"permissions": {
"allow": [
"mcp__github__get_pull_request",
"mcp__github__list_issues",
"mcp__jira__get_issue",
"mcp__jira__search_issues",
"mcp__sentry__get_error_details",
"mcp__playwright__browser_snapshot"
],
"ask": [
"mcp__github__create_pull_request",
"mcp__jira__create_issue",
"mcp__jira__transition_issue"
],
"deny": [
"mcp__github__merge_pull_request",
"mcp__jira__delete_issue"
]
}
}
Notice the credential handling: every secret is an environment variable reference, never a literal token in the file — this file gets committed if it's project-scoped (.mcp.json), and a hardcoded PAT in git history is a breach, not a mistake you can quietly fix later. Figma's Dev Mode server runs as a local HTTP server started by the Figma desktop app itself rather than a subprocess the client spawns, which is why it's declared with type: "http" and a url instead of command/args — a detail that trips people up the first time they wire up a fifth server that doesn't follow the stdio pattern the first four did.
Permission naming follows mcp__<server-name>__<tool-name>, and the server name is whatever key you gave it in mcpServers — not the package name. Get that key wrong in a permission entry and the rule silently never matches, which reads as "the tool keeps asking for confirmation" with no error to point at the cause.
claude mcp list
github ✓ connected (38 tools)
jira ✓ connected (22 tools)
sentry ✓ connected (11 tools)
playwright ✓ connected (21 tools)
figma ✓ connected (9 tools)
Tips
- Reference every credential via environment variable substitution (${VAR}), never inline — a project-scoped.mcp.jsongets committed, and a leaked PAT for GitHub or Jira is a full-scope breach across whatever that token can touch.
- Runclaude mcp listafter every config change, not just the first time — a server reporting0 toolsor missing entirely usually means a bad env var, and it fails silently rather than erroring loudly.
- Match themcp__<server>__<tool>permission prefix to the exact key you used inmcpServers, not the package or binary name — a mismatch makes the permission rule a no-op with no warning.
Context Window Management: Preventing Token Overflow in Multi-MCP Sessions
Every connected server's tool schemas load into the context window before your first prompt is even sent — this is pure overhead, paid on every single turn of the conversation, not just when a tool from that server gets called. GitHub's full default toolset runs roughly 15,000–20,000 tokens of schema alone at typical verbosity. Add Jira, Sentry, Playwright, and Figma at their defaults and it's realistic to burn 40,000–60,000 tokens before any actual work happens — on a 200K context window, that's a quarter to a third of your budget spent on tool definitions the model may use zero of in a given turn.
This compounds two ways. First, the obvious one: less room for actual conversation, file contents, and tool results before you hit compaction or context limits. Second, the subtler one: a bloated tool-schema payload dilutes the model's attention across everything else in context, which is part of why tool-selection accuracy drops as combined tool count rises (see the previous section) — it's not purely a naming-collision problem, it's also a signal-to-noise problem.
The lever that matters most is toolset scoping per server, applied aggressively for combined sessions:
GITHUB_TOOLSETS=repos,pull_requests github-mcp-server
SENTRY_PROJECT=webapp npx -y @sentry/mcp-server
Playwright MCP is a special case worth calling out: its tool schemas are modest, but the accessibility snapshots it returns from browser_snapshot can be large — a complex page's DOM tree serialized as an accessibility tree easily runs several thousand tokens per call. In a workflow that calls it repeatedly (retry a flow, check state after each step), that adds up faster than the static schema overhead does. Ask for narrower snapshots where the tool supports it, and avoid calling it more times than the workflow actually needs.
> snapshot the page after each click
> click "Submit", then snapshot only if the URL didn't change
(to check for a validation error)
For sessions that run long — a multi-hour debugging workflow touching all five servers — Claude Code's /compact (or the equivalent context-summarization command in your client) becomes necessary, not optional. Run it between distinct phases of a workflow (after the Sentry investigation wraps, before the GitHub fix begins) rather than waiting until you hit a hard limit mid-task, since compaction quality is better with a natural break point than a token-count emergency.
/compact
Finally, disconnect servers you're not using for the current phase rather than leaving all five wired up "just in case." If you're deep in a GitHub + Sentry bug-fix loop and Figma and Playwright aren't going to be touched this session, don't pay their schema tax for the whole conversation.
Tips
- Scope every server's toolset down before combining — schema overhead is paid every turn regardless of whether that server's tools get used, so an unused toolset is pure waste, not a safety margin.
- Treat large tool results (Playwright snapshots, verbose Sentry stack traces) as a separate context cost from schema overhead — ask for narrower output explicitly rather than accepting the tool's most verbose default.
- Run compaction at natural phase boundaries in long multi-MCP workflows, not only when you hit a hard context limit — a deliberate compaction point preserves more useful state than an emergency one.
Debugging and Logging Multi-MCP Workflows
When a five-server workflow does the wrong thing, the failure surface is much wider than a single-server session: was it a tool-selection error (right intent, wrong server), a permission gap (the right tool got blocked silently), a stale connection (a server dropped mid-session and calls started failing), or a genuine logic error in how the agent chained results together? Debugging starts with narrowing which of those four you're looking at, not with re-reading the whole transcript.
Claude Code's --verbose flag (or /status mid-session) surfaces the actual tool calls and their raw arguments and results — this is the first thing to check, because it shows you exactly which server and tool fired, with what input, before any of the model's summarizing prose gets layered on top.
claude --verbose
[tool_use] mcp__jira__create_issue
args: {"project": "WEBAPP", "summary": "Export fails on Safari", ...}
[tool_result] {"key": "WEBAPP-142", "id": "10234", ...}
[tool_use] mcp__github__create_pull_request
args: {"title": "Fix Safari export bug", "body": "Fixes WEBAPP-142...", ...}
Reading raw tool calls catches a specific class of bug that's easy to miss in the model's natural-language summary: the model says "I created the Jira ticket and linked the PR to it," but the raw args show the PR body references the wrong ticket key, or the Jira issue landed in the wrong project because a default got applied silently. The summary is the model's account of what it did; the tool call log is what actually happened. Trust the log.
Mid-session server health also matters more with five servers connected — one dropping doesn't necessarily surface as an obvious error, especially if the model tries the call, gets a connection failure, and then reasons its way to a plausible-sounding but wrong fallback instead of surfacing the failure to you.
claude mcp list
For workflows you intend to run more than once, build a lightweight audit log outside the agent's own transcript — a webhook, a Sentry breadcrumb, or even a scratch file the agent appends to after each cross-system action. This matters most for the write actions (issue created, PR opened, ticket transitioned) since those are the ones with real-world side effects if a workflow silently does the wrong thing twice.
> after creating the Jira issue and opening the linked PR,
append one line to ./automation-log.md with a timestamp,
the Jira key, and the PR URL
When a specific server is misbehaving in isolation from the rest, disconnect the others and reproduce against just that one — five servers connected at once make it tempting to assume the failure is a cross-server interaction problem, and most of the time it's a single server's auth token, rate limit, or malformed argument, same as it would be in a single-server session.
Tips
- Read raw tool-call logs (--verbose,/status) before trusting the model's natural-language summary of what it did — the summary can describe success while the actual arguments show a wrong project, wrong ticket key, or wrong branch.
- Re-runclaude mcp listwhenever a server's tools stop appearing mid-session — a dropped connection sometimes surfaces as the model quietly reasoning around a missing capability instead of an obvious error.
- Isolate a misbehaving server by disconnecting the others and reproducing against it alone — most multi-MCP bugs are a single server's ordinary auth, rate-limit, or argument problem, not a genuine cross-server interaction.
Tips
Tips
- Name the target system explicitly whenever more than one connected server could plausibly own a request — tool selection is flat pattern-matching across every connected server's schemas, not intent-aware routing.
- Scope every server's toolset and permissions individually, with secrets injected via environment variables — never a shared "MCP" permission bucket, and never a literal token in a config file that gets committed.
- Budget context deliberately: narrow toolsets before combining servers, ask for smaller tool results (especially Playwright snapshots), and compact at phase boundaries in long sessions rather than waiting for a hard limit.
- Debug from raw tool-call logs, not the model's summary of its own actions, and isolate a single misbehaving server before assuming a cross-server interaction bug.