SessionStart hooks with mcp_tool type fire before MCP servers connect

Status Closed — not planned
Maintainer reply None cached
Activity 6 comments · opened May 11, 2026 · closed May 11, 2026

Description

Plugin SessionStart hooks that use type: "mcp_tool" always fail because they fire before the MCP server has connected. The hooks execute synchronously during startup, and MCP connection begins ~290ms later. Every mcp_tool hook gets skipped with "MCP server not connected."

Steps to Reproduce

  1. Create a plugin with hooks/hooks.json containing SessionStart hooks that call MCP tools:
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "concurai-ememory",
            "tool": "e_property",
            "input": { "action": "list" },
            "timeout": 30
          }
        ]
      }
    ]
  }
}
  1. Plugin has .mcp.json configuring an HTTP MCP server
  2. Launch Claude Code with --plugin-dir

Expected Behavior

SessionStart hooks with type: "mcp_tool" should execute after the MCP server they reference has connected, or Claude Code should provide an McpReady hook event that fires after MCP connections are established.

Actual Behavior

All mcp_tool hooks are skipped with warning: Hooks: mcp_tool hook skipped — MCP server 'concurai-ememory' not connected

Debug log shows the timing gap clearly:

2026-05-11T01:45:08.907Z [WARN] Hooks: mcp_tool hook skipped — MCP server 'concurai-ememory' not connected
2026-05-11T01:45:08.907Z [WARN] Hooks: mcp_tool hook skipped — MCP server 'concurai-ememory' not connected
2026-05-11T01:45:08.907Z [WARN] Hooks: mcp_tool hook skipped — MCP server 'concurai-ememory' not connected
2026-05-11T01:45:08.907Z [WARN] Hooks: mcp_tool hook skipped — MCP server 'concurai-ememory' not connected
2026-05-11T01:45:08.921Z [DEBUG] MCP server "plugin:concurai-ememory:concurai-ememory": Starting connection...
2026-05-11T01:45:09.197Z [DEBUG] MCP server "plugin:concurai-ememory:concurai-ememory": Successfully connected (transport: http) in 280ms

Hooks fire at .907, MCP connection starts at .921 and completes at .197 — hooks never had a chance.

Use Case

Session memory continuity. Our plugin uses SessionStart hooks to restore context from a knowledge store on startup (orient), on context compaction (restore session state), and on resume (cross-session context). These are the primary use cases for mcp_tool hooks — they're useless if they can't call the MCP server they depend on.

Suggested Fix

Either:

  1. Wait for MCP connections before firing SessionStart hooks that reference MCP tools
  2. Add an McpReady hook event that fires after all MCP servers have connected
  3. Retry mcp_tool hooks that fail due to server not connected (with backoff)

Environment

  • Claude Code v2.1.138
  • macOS (Darwin 25.3.0)
  • MCP transport: HTTP (Streamable HTTP)
  • Plugin loaded via --plugin-dir

View original on GitHub ↗

6 Comments

github-actions[bot] · 5 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/36692
  2. https://github.com/anthropics/claude-code/issues/35899
  3. https://github.com/anthropics/claude-code/issues/41778

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

carldowns · 5 months ago

Update: PostCompact hooks also affected

Same MCP disconnection issue affects PostCompact hooks, not just SessionStart.

Reproduction

hooks.json with PostCompact event:

{
  "hooks": {
    "PostCompact": [
      {
        "matcher": "manual",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "concurai-ememory",
            "tool": "e_property",
            "input": { "action": "list" },
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Run /compact in a session with the plugin loaded.

Debug log evidence

2026-05-11T13:57:13.484Z [DEBUG] Forked agent [compact] finished: 1 messages
2026-05-11T13:57:13.489Z [WARN] Hooks: mcp_tool hook skipped — MCP server 'concurai-ememory' not connected

Hook fires 5ms after compaction completes, MCP still disconnected.

User-visible error

PostCompact [concurai-ememory/e_property] failed: MCP server 'concurai-ememory' not connected

Impact

This makes mcp_tool hooks unusable for all lifecycle events — SessionStart, PostCompact, and likely PreCompact and SessionEnd too. MCP disconnects during lifecycle transitions and doesn't reconnect before hooks fire.

The primary use case for PostCompact + mcp_tool is session rehydration: restoring context from a knowledge store after compaction destroys it. Without this, plugins that provide long-term memory have no automated recovery path after compaction.

Environment

  • Claude Code v2.1.138
  • macOS Darwin 25.3.0
  • MCP transport: Streamable HTTP
carldowns · 5 months ago

Resolution: Not a race condition — plugin-scoped MCP limitation

After further testing, this is not a timing race. The root cause:

  • Plugin-scoped MCP servers (defined in plugin .mcp.json) are torn down during all lifecycle transitions (SessionStart, PostCompact, PreCompact). Hooks firing during these events find the server disconnected.
  • Connector-level MCP servers (account-level, claudeai-proxy transport) stay connected through all lifecycle transitions. Hooks targeting connectors work perfectly — PostCompact hook via connector executed successfully.

The fix (for plugin authors)

Don't put MCP server definitions in the plugin's .mcp.json. Configure the MCP connection as a connector at the account level. Plugin provides skills, CLAUDE.md, and hooks — hooks reference the connector server name. This works today with no changes to Claude Code.

Still potentially worth fixing

Plugin-scoped MCP servers could survive lifecycle transitions like connectors do, but it's not a blocker — the connector approach is architecturally cleaner anyway (plugin = logic, connector = infrastructure).

Closing as resolved via workaround.

dnoggle · 5 months ago

Your workaround doesn't fix SessionStart, right? Feels like this should be reopened.

carldowns · 4 months ago

Correct. we just are avoiding the problem by setting up MCP outside of the plugin.

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.