Skip to content

[RFC] autoConnect: false — Deferred MCP Server Registration + On-Demand Lifecycle Init #1938

@blkout-hd

Description

@blkout-hd

Summary

Spec-level RFC proposing autoConnect: false for mcp-config.json / .mcp.json to enable true lazy/deferred server registration — resolving context window token exhaustion, 400 Bad Request errors under Sonnet 4.6, and the inability to hot-reload .mcp.json in active sessions.

Consolidates upstream issues: anthropics/claude-code#6638, #11364, #11370, #14879, #16826, #18497


Problem Statement

MCP server lifecycle has one mode: eager connect at session init. All configured servers run initialize → tools/list before the first prompt regardless of need. Three bugs compound this:

  1. /mcp disable does not evict schemas — tools hidden from selector but schemas remain serialized in context. Token cost paid regardless. ([Bug] Disabled MCP servers still load tool definitions into Claude's context anthropics/claude-code#11370)
  2. /mcp add permanently mutates mcp-config.json — no session-scoped lazy injection without global config mutation. (Add dynamic loading/unloading of MCP servers during active sessions anthropics/claude-code#6638)
  3. .mcp.json disk changes invisible to active session — no reload, sync, or watch mechanism. (Add dynamic loading/unloading of MCP servers during active sessions anthropics/claude-code#6638, #14879)

Measured impact: 10-server unoptimized setup = ~75,000 tokens consumed at session start before any user prompt. Directly causes 400 Bad Request at Sonnet 4.6 serialization ceiling.


Proposed Config Schema

Add autoConnect (boolean, default true) to the server entry spec in mcp-config.json / .mcp.json:

{
  "mcpServers": {
    "playwright": {
      "type": "local",
      "command": "npx",
      "args": ["@playwright/mcp@latest"],
      "tools": ["browser_navigate", "browser_screenshot"],
      "autoConnect": false
    },
    "linear": {
      "type": "http",
      "url": "https://mcp.linear.app/sse",
      "tools": ["create_issue", "list_issues", "update_issue"],
      "autoConnect": false
    },
    "github": {
      "type": "local",
      "command": "gh",
      "args": ["mcp", "serve"],
      "autoConnect": true
    }
  }
}
  • autoConnect: true (default) — current behavior, zero regression
  • autoConnect: false — registered but dormant: no process spawn, no stdio pipe, no handshake, zero schema tokens until explicitly activated

Lifecycle State Machine

┌─────────────────────────────────────────────────────────┐
│  REGISTERED (dormant)                                   │
│  autoConnect: false at config parse                     │
│  No process. No pipe. No tokens.                        │
└────────────────────────┬────────────────────────────────┘
                         │  /mcp enable  OR  first tool-call trigger
                         ▼
┌─────────────────────────────────────────────────────────┐
│  CONNECTING                                             │
│  Full initialize handshake per MCP spec 2025-11-25      │
│  Identical path to /mcp add mid-session                 │
└────────────────────────┬────────────────────────────────┘
                         │  initialized + tools/list complete
                         ▼
┌─────────────────────────────────────────────────────────┐
│  ACTIVE                                                 │
│  Schemas injected at next turn boundary only            │
└────────────────────────┬────────────────────────────────┘
                         │  /mcp disable
                         ▼
┌─────────────────────────────────────────────────────────┐
│  SUSPENDED                                              │
│  Process kept alive (fast re-enable)                    │
│  Schemas EVICTED from context at next turn  ← NEW       │
└────────────────────────┬────────────────────────────────┘
                         │  /mcp enable
                         ▼
                      ACTIVE  (re-announce tools at next turn)

Required Behavior Changes

# Change Upstream Ref
1 /mcp disable MUST evict schemas at next turn boundary — not just hide from selector anthropics/claude-code#11370
2 autoConnect: false MUST NOT spawn process, pipe, or handshake at session init anthropics/claude-code#18497
3 /mcp enable on dormant server MUST execute full initialize handshake (already works for /mcp add — formalize as documented contract) anthropics/claude-code#6638
4 New /mcp sync command — re-parses .mcp.json from cwd in-session, registers/deregisters without session restart anthropics/claude-code#6638, #14879

MCP Protocol Spec Extension

For modelcontextprotocol/specification RFC — add to initialize handshake:

// ClientCapabilities
lazyRegistration?: {
  supported: boolean; // client can handle deferred tool injection
};

// ServerCapabilities (InitializeResult)
supportsLazyRegistration?: boolean; // server can defer tools/list until client requests

Enables bidirectional lazy-connect signaling at protocol layer.


Token Budget Impact

Configuration Schema Tokens at Session Start
Eager wildcard * (current default) ~75,000
autoConnect: false (this RFC) 0 until /mcp enable
autoConnect: true + defer loading: true ~1,100/server (BM25 meta-tools, v2.1.9+)
Dormant + defer loading on activation 0 → ~1,100 strictly on demand

Current Workaround (Windows 11 / Copilot CLI)

# Launch: register all, connect none
copilot `
  --disable-mcp-server playwright `
  --disable-mcp-server linear `
  --disable-builtin-mcps

# Mid-session — activate on demand:
/mcp enable linear      # Full initialize handshake fires NOW
                        # Schemas available at NEXT prompt turn
/mcp disable linear     # Suspend, evict schemas next turn
/mcp enable playwright

Structurally equivalent to autoConnect: false using existing primitives. This RFC formalizes it as a first-class config flag and closes the schema-eviction gap in /mcp disable.


/mcp sync Command Spec

/mcp sync [--file PATH]
  • Re-reads .mcp.json from cwd (or --file) and ~/.copilot/mcp-config.json
  • New entries: register per their autoConnect value
  • Removed entries: kill process, deregister, evict schemas
  • Modified entries: full disconnect + reconnect
  • No session restart required
  • Read-only from CLI perspective — writes nothing to disk

Handshake Path Comparison

Property Session Init /mcp enable (post-RFC)
Trigger CLI launch In-session command or first tool call
Phase 4 harvest Eager, ALL servers, blocking Single server, on-demand
Context impact Immediate, all schemas Next turn boundary only
Config write Read-only Read-only (no mutation)

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions