Feature request: Lazy MCP server initialization (connect on demand)

Status Closed — not planned
Maintainer reply ✓ Yes — localden
Activity 6 comments · opened Mar 24, 2026 · closed May 23, 2026
💡 Likely answer: A maintainer (localden, collaborator) responded on this thread — see the highlighted reply below.

Problem

All configured MCP servers connect at chat startup, registering their full tool lists into context immediately. For users with multiple MCP servers (Gmail, Google Calendar, Notion, Figma, Kling, etc.), this consumes significant context window space before any work begins — even if most servers won't be used in that session.

The current "deferred tools" mechanism helps (only names loaded, full schemas on demand), but the server processes still spin up and the name list still grows with every added server.

Desired behavior

MCP servers should support lazy initialization — credentials stored locally (e.g., .env), but servers only connect and register tools when the user actually needs them mid-conversation.

Ideal flow:

  1. User configures MCP servers + auth as usual
  2. On chat start: no MCP servers connect, zero context used
  3. User says "check my email" → Claude detects Gmail MCP is needed → server connects on demand → tools become available
  4. Only that server's tools consume context

Possible implementation approaches

  • A "lazy" flag per MCP server in settings (e.g., "lazy": true)
  • A lightweight router/proxy MCP server that starts others on demand
  • Extending the existing deferred tools mechanism to defer the connection itself, not just the schema

Context

Users who integrate many MCP servers for different workflows (productivity, design, content generation) hit context limits quickly. The workaround today is manually enabling/disabling servers between sessions, which defeats the purpose of having an integrated toolchain.

This would make Claude Code significantly more scalable for power users with broad MCP setups.

View original on GitHub ↗

6 Comments

github-actions[bot] · 6 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/23411
  2. https://github.com/anthropics/claude-code/issues/17668
  3. https://github.com/anthropics/claude-code/issues/33759

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

fabb · 6 months ago

https://github.com/anthropics/claude-code/issues/17668 has some overlaps but is a bit different.

Twintails · 6 months ago

Adding a security dimension to this request

Problem

MCP servers are initialized at session start, which creates two issues:

1. Security: Secrets must be globally exported

MCP servers read env vars from the shell that launched Claude. If a server needs an API key (e.g., Authorization: Bearer ${MY_API_KEY}), that key must be present in the parent shell environment before Claude starts.

In practice, this means users must add export MY_API_KEY=... to their .zshenv or .bashrc — making secrets available to every process in every terminal session, not just Claude. There's no way to scope secrets to only the MCP connection that needs them.

This is especially problematic for:

  • Teams with vault/secret-manager workflows (HashiCorp Vault, 1Password CLI, flat-file vaults) where secrets are loaded on-demand
  • Users with multiple MCP servers requiring different credentials
  • Security-conscious environments where blanket env var exports are discouraged

2. Performance: Unused MCP servers waste context and resources

All configured MCP servers connect at session start, even if the user never invokes their tools. Each server's tool definitions are loaded into the deferred tool index, and the server process runs for the entire session. In conversations (and /btw) that never touch the MCP Tool that requires API Keys like PostHog, Jira, etc., this is pure overhead.

Every /btw side chat also eagerly connects all configured MCP servers; making the context/resource waste argument even stronger

Proposed Solution

Lazy MCP server initialization — servers are registered at session start (tool names are known) but the actual connection/process is deferred until a tool from that server is first invoked.

This would enable:

  1. Just-in-time secret resolution: When a server is about to connect, Claude could run a configured command (or skill?) to resolve secrets (e.g., source some-cool-decryption-booper.sh), scope them to that server's process, and never export them to the parent shell.
  1. Reduced resource usage: Server processes only start when needed. Sessions that never use {whatever} tools never start the {whatever} MCP server.
  1. Smaller effective context: Tool schemas are only fetched when relevant (this partially exists today via deferred tools, but the server connection is still eager).

Possible Configuration

// .mcp.json
{
  "mcpServers": {
    "posthog": {
      "type": "http",
      "url": "https://mcp.posthog.com/mcp",
      "headers": {
        "Authorization": "Bearer ${PERSONAL_KEY}"
      },
      "lazy": true,
      "envCommand": "source some-cool-decryption-booper.sh {maybe-what-needs-decrypt}"
    }
  }
}
  • lazy: true — don't connect until a tool from this server is invoked
  • envCommand — run this command to populate env vars just before connecting (output or exports are captured and scoped to the server process)

Current Workaround

Export all required secrets in your shell init file (.zshenv, .bashrc, etc.); so they're available in every shell session:

# ~/.zshenv — every terminal gets these, not just Claude
source some-dir/whatever-file-to-read

This works but defeats the purpose of on-demand secret management.

Additional Context

  • Claude Code already has deferred tool schemas (tool names are known but schemas are fetched on demand via ToolSearch). Extending this pattern to the server connection itself is a natural next step.
  • The SessionStart hook runs in a child process, so exported env vars don't propagate back to the Claude parent process that spawns MCP servers.
  • CLAUDE_ENV_FILE is sourced per Bash tool call but not by MCP server processes.
  • The env field in settings.json only supports static string values, not command output.
zhebil · 5 months ago

+1 for this. Adding a concrete data point from a different angle: wall-clock startup time amplification under concurrent sessions, not just context-window cost.

Symptom observed 2026-05-01: starting a new claude session in any window took ~1 minute when 3 other Claude Code sessions were active on the same machine (one of them dispatching 3 parallel sub-agents in isolation: "worktree"). Tool calls in already-running sessions also lagged. Couldn't even type into the UI.

Investigation showed the bottleneck was not hot disk, or memory pressure (24GB free, normal load). It was concurrent MCP server bootstrap. Setup:

  • 4 concurrent Claude sessions
  • ~10 MCP servers per session: 6 plugin-installed (atlassian, context7, telegram, figma, frontend-design, superpowers) + ~6 claude.ai remote MCPs
  • 40+ Node/bun processes spawning, each doing node startup + JS module load + MCP handshake
  • This collided with 3 concurrent npm installs in the agent worktrees → disk I/O saturation

Lazy MCP startup would have flattened this completely. None of the agents in those parallel sessions actually invoked any of the 6 plugin-installed MCPs - they were paying ~minute of cumulative startup cost for tools they never called.

So in addition to the context-window argument in the OP, the lazy-init request also addresses:

  1. Parallel-session amplification. N sessions × M MCPs × per-MCP-boot-cost compounds linearly; disk/CPU saturates. Lazy init makes the cost N × M_actually_used, which is usually tiny.
  2. Worktree-based sub-agent isolation in particular. When the user dispatches several Agent calls with isolation: "worktree", each worktree spawns a fresh Claude Code instance inside it with the full MCP set. Lazy init would let those agents run cheap and only pay for the one or two MCPs their narrow task actually needs.
  3. MCP server processes leaking on bad shutdown. When a session is force-killed, MCP children that fail to detect parent stdio close stay alive. Lazy init naturally caps the leak surface (the leaked process count = the number of MCPs actually invoked, not the number configured).

For maintainers' info on the scoping side: disabledMcpServers is per-project today, but there's no symmetric "enable only these MCPs in this project" mechanism, which is the cleanest current workaround for narrowing the boot set per repo. A "lazy" flag per MCP would be a much better answer - not having to think per-repo about what's needed.

Happy to provide additional repro data if useful (process traces, before/after of disabledMcpServers boot-time deltas).

localden collaborator · 4 months ago

Thanks for the request. This is the same ask as #17668 (per-server lazy MCP load / startDisabled). Consolidating tracking there.

github-actions[bot] · 3 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.