claude-code - 💡(How to fix) Fix Feature Request: Session-Scoped State Persistence for MCP-Tool Hooks [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#55093Fetched 2026-05-01 05:46:25
View on GitHub
Comments
0
Participants
1
Timeline
3
Reactions
0
Participants
Timeline (top)
labeled ×3

The type: "mcp_tool" hook option enables hooks to invoke MCP tools in response to events (PostToolUse, PreToolUse, SubagentStop, etc.). However, the current hook execution model is completely stateless — each hook invocation is independent with no ability to maintain or access state across events within a session. This fundamentally limits the utility of MCP-tool hooks for any workflow that requires correlating multiple discrete events with shared session context.

Root Cause

The MCP-tool hooks feature is a powerful primitive — it allows MCP servers to serve as hook action handlers, bridging Claude Code events to external systems. But without session state, the feature is limited to truly stateless operations (sending notifications, firing webhooks with no context dependency).

Any workflow that requires correlating events with shared context is currently impossible to implement correctly. The work-logging example is one instance, but the pattern is general:

  • Tracking multi-step operations across a session
  • Implementing circuit breakers that count failures across invocations
  • Building session-level feature flags for experimental hook behaviors
  • Correlating agent actions with the user's original intent

Fix Action

Fix / Workaround

The feature request is to make MCP-tool hooks first-class stateful event processors rather than stateless per-event action dispatchers.

Code Example

Hook fires: PostToolUse (Bash, "npm run test")
Hook has: toolName="Bash", input="npm run test", duration_ms=2341
Hook needs: taskId="T-456" (the task the agent is working on)
Hook cannot determine this because:
  - taskId was set by a prior board_claim_task call (result not accessible)
  - taskId was mentioned in a user message 10 turns ago (conversation not accessible)
  - No session-level state store exists for hooks to read/write

---

{
  "hooks": [{
    "name": "work-logger",
    "events": ["PostToolUse"],
    "state": {
      "activeTaskId": null,
      "workLog": []
    },
    "if": "${toolName} != 'board_log_work'",
    "postToolUse": {
      "type": "mcp_tool",
      "tool": "log_work_entry",
      "args": {
        "taskId": "${state.activeTaskId}",
        "tool": "${toolName}",
        "duration": "${duration_ms}"
      },
      "setState": {
        "activeTaskId": "${lastResult.taskId}"
      }
    }
  }]
}

---

{
  "postToolUse": {
    "type": "mcp_tool",
    "tool": "log_work_entry",
    "args": {
      "taskId": "${lastResultOf('board_claim_task').taskId}"
    }
  }
}

---

{
  "postToolUse": {
    "type": "mcp_tool",
    "tool": "log_work_entry",
    "args": {
      "taskId": "${extractTaskIdFromRecentMessages}"
    }
  }
}
RAW_BUFFERClick to expand / collapse

Feature Request: Session-Scoped State Persistence for MCP-Tool Hooks

Summary

The type: "mcp_tool" hook option enables hooks to invoke MCP tools in response to events (PostToolUse, PreToolUse, SubagentStop, etc.). However, the current hook execution model is completely stateless — each hook invocation is independent with no ability to maintain or access state across events within a session. This fundamentally limits the utility of MCP-tool hooks for any workflow that requires correlating multiple discrete events with shared session context.

Problem Statement

When using MCP-tool hooks to perform work-logging or audit-trail operations (e.g., "log every file edit to the task the agent is currently working on"), the hook system has no mechanism to determine which task the agent is working on at any given moment. The hook receives only the immediate event (tool name, inputs, outputs, duration) but has no access to:

  1. Session-scoped state — values that persist across hook invocations within the same session
  2. Prior tool results — outputs from earlier tool calls in the session that might establish context (e.g., which task was claimed)
  3. Conversation context — the user's messages or agent's reasoning that would indicate intent

This makes it impossible to implement patterns like:

  • "Log all file modifications to the task ID the agent claimed earlier in this session"
  • "Track work progress against a specific feature or work item"
  • "Correlate a series of tool calls with a single unit of work"

Concrete Example

Consider implementing a hook that logs an agent's work to a task management system:

Hook fires: PostToolUse (Bash, "npm run test")
Hook has: toolName="Bash", input="npm run test", duration_ms=2341
Hook needs: taskId="T-456" (the task the agent is working on)
Hook cannot determine this because:
  - taskId was set by a prior board_claim_task call (result not accessible)
  - taskId was mentioned in a user message 10 turns ago (conversation not accessible)
  - No session-level state store exists for hooks to read/write

Proposed Solutions

Any of the following would address the core issue:

Option A: Hook-Level Session State

Allow hooks to maintain a state object that persists across invocations within a session:

{
  "hooks": [{
    "name": "work-logger",
    "events": ["PostToolUse"],
    "state": {
      "activeTaskId": null,
      "workLog": []
    },
    "if": "${toolName} != 'board_log_work'",
    "postToolUse": {
      "type": "mcp_tool",
      "tool": "log_work_entry",
      "args": {
        "taskId": "${state.activeTaskId}",
        "tool": "${toolName}",
        "duration": "${duration_ms}"
      },
      "setState": {
        "activeTaskId": "${lastResult.taskId}"
      }
    }
  }]
}

Option B: Prior Result References

Allow hook expressions to reference the result of the most recent matching tool call:

{
  "postToolUse": {
    "type": "mcp_tool",
    "tool": "log_work_entry",
    "args": {
      "taskId": "${lastResultOf('board_claim_task').taskId}"
    }
  }
}

Option C: Conversation Context Access

Allow hooks to access recent conversation messages for pattern matching:

{
  "postToolUse": {
    "type": "mcp_tool",
    "tool": "log_work_entry",
    "args": {
      "taskId": "${extractTaskIdFromRecentMessages}"
    }
  }
}

Why This Matters

The MCP-tool hooks feature is a powerful primitive — it allows MCP servers to serve as hook action handlers, bridging Claude Code events to external systems. But without session state, the feature is limited to truly stateless operations (sending notifications, firing webhooks with no context dependency).

Any workflow that requires correlating events with shared context is currently impossible to implement correctly. The work-logging example is one instance, but the pattern is general:

  • Tracking multi-step operations across a session
  • Implementing circuit breakers that count failures across invocations
  • Building session-level feature flags for experimental hook behaviors
  • Correlating agent actions with the user's original intent

Priority Assessment

This is a hard blocker for any stateful MCP-tool hook use case. Without session state, the feature works only for stateless notification-style hooks, which could already be handled by simpler mechanisms (Bash hooks calling curl, existing webhook systems, etc.).

The feature request is to make MCP-tool hooks first-class stateful event processors rather than stateless per-event action dispatchers.


Submitted via Claude Code CLI

extent analysis

TL;DR

Implementing session-scoped state persistence for MCP-tool hooks is necessary to enable stateful event processing.

Guidance

  • Introduce a state object that persists across hook invocations within a session, as proposed in Option A: Hook-Level Session State.
  • Allow hook expressions to reference the result of the most recent matching tool call, as shown in Option B: Prior Result References.
  • Consider providing access to recent conversation messages for pattern matching, as suggested in Option C: Conversation Context Access.
  • Evaluate the trade-offs between these options, considering factors such as complexity, performance, and security.

Example

{
  "hooks": [{
    "name": "work-logger",
    "events": ["PostToolUse"],
    "state": {
      "activeTaskId": null,
      "workLog": []
    },
    "if": "${toolName} != 'board_log_work'",
    "postToolUse": {
      "type": "mcp_tool",
      "tool": "log_work_entry",
      "args": {
        "taskId": "${state.activeTaskId}",
        "tool": "${toolName}",
        "duration": "${duration_ms}"
      },
      "setState": {
        "activeTaskId": "${lastResult.taskId}"
      }
    }
  }]
}

Notes

The implementation of session-scoped state persistence will require careful consideration of issues such as data storage, serialization, and security.

Recommendation

Apply a workaround by introducing a state object that persists across hook invocations within a session, as this is the most straightforward solution that addresses the core issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix Feature Request: Session-Scoped State Persistence for MCP-Tool Hooks [1 participants]