[FEATURE] Pre-MCP-init hook so plugins can prepare shared infrastructure before .mcp.json is read

Resolved 💬 1 comment Opened Apr 20, 2026 by mcmanusj-reninc Closed May 27, 2026

Preflight Checklist

  • [x] I have searched existing requests — closest is #24057 (MCP config hot-reload); this is a different, complementary ask
  • [x] This is a single feature request

Problem Statement

.mcp.json is read once at session startup, before any plugin hook (including SessionStart) fires. This ordering makes it impossible for a plugin to prepare shared MCP infrastructure in time for the first session after install or update.

Concrete case: a plugin wants to run an MCP server as a shared, long-lived Docker container that all Claude Code windows connect to (via streamable-http on a fixed port), instead of each window spawning its own docker run --rm stdio container. Shared stack = lower RAM, faster startup, one place to debug.

The only way to ship this today is a two-file shuffle:

  1. Ship .mcp.json as stdio (safe default — docker run --rm is self-contained).
  2. SessionStart hook runs docker compose up -d in the background.
  3. Once the stack is healthy, the hook overwrites .mcp.json with a streamable-http config.
  4. The current session still uses the stdio containers it already spawned; the next session picks up streamable-http.

Net effect: every fresh install and every plugin update costs one "bad" session with random-name containers before the shared stack takes over. The ordering — not the plugin design — is what forces this.

Proposed Solution

Add a new hook event that fires before .mcp.json is read:

{
  "hooks": {
    "PreMcpInit": [
      {
        "hooks": [
          { "type": "command", "command": "bash start-mcp-stack.sh", "timeout": 30 }
        ]
      }
    ]
  }
}

Behavior:

  • Claude blocks on the hook up to a bounded timeout (configurable, default ~10s).
  • If the hook exits 0 within the timeout, Claude proceeds to read .mcp.json normally. The hook has had the chance to bring up whatever infrastructure the config points to.
  • If the hook times out or fails, Claude falls through to .mcp.json as today — no hang, graceful degrade.
  • Optionally: allow the hook to emit a replacement config path on stdout (e.g., MCP_CONFIG=/path/to/streamable-http.json) so plugins can pick the right config without mutating .mcp.json on disk.

Alternative Solutions

  1. #24057 (MCP / hooks / plugins hot-reload on config change). If .mcp.json changes were picked up mid-session, the SessionStart hook could swap the config and the same session would pick it up. This solves the use case reactively rather than preventatively and would also be great to have.
  2. Pre-warm infrastructure at plugin install. Post-install script runs docker compose up -d. Invasive (runs outside Claude's context), doesn't help if Docker isn't running when Claude eventually opens, still needs a fallback.
  3. Current workaround — two-launch UX. Ship stdio as the default; swap to streamable-http on disk via SessionStart for the next session. Works, but every install and update costs one throwaway session.

Priority

Medium — Would be very helpful

Feature Category

MCP server integration

Use Case Example

  1. Team ships an internal plugin bundling ~12 MCP servers (Jira, Oracle, Datadog, etc.) via a Docker compose stack.
  2. User runs /update-ai-tools. Plugin reinstalls, .mcp.json returns to the stdio default.
  3. User opens Claude Code. Claude reads stdio .mcp.json → each of the 12 MCP servers spawns its own docker run --rm container. SessionStart hook runs docker compose up -d in the background.
  4. User closes, reopens Claude. Now .mcp.json has been swapped to streamable-http by the hook. Claude connects to the shared mcp-* stack. From here, every window shares one stack.

With PreMcpInit, steps 3 and 4 collapse: the hook brings up the shared stack before Claude reads .mcp.json, so the first session already uses streamable-http and no per-session containers are ever spawned.

Additional Context

Claude Code version: 2.1.x (all recent)
Platform: cross-platform (Windows/macOS/Linux) — the ordering constraint is platform-independent
Related: #24057

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗