Native postToolUse hooks accept and log additional_context successfully, but the injected context is not surfaced to the model

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

A native postToolUse hook configured in .cursor/hooks.json runs successfully, returns valid JSON with an additional_context field, and Cursor’s Hooks log shows that output correctly. However, the model does not behave as if it received the injected context.

I verified this with a minimal smoke-test hook that removes normal hook logic as a variable. The hook only fires for one dedicated test file path and returns a single unmistakable additional_context string telling the model to say exactly POST_TOOL_SMOKE_TEST_RECEIVED if the context is visible. Cursor logs the hook as successful and shows the exact payload, but the model never acknowledges or reacts to it.

This suggests the issue is not invalid JSON, matcher configuration, or hook script logic. The problem appears to be that postToolUse additional_context is accepted by the hook runner but is not actually surfaced to the model.

Steps to Reproduce

  1. Create a native Cursor hook in .cursor/hooks.json:

{
“version”: 1,
“hooks”: {
“postToolUse”: [
{
“matcher”: “Write”,
“command”: “bash .cursor/hooks/posttooluse-smoke-test.sh”
}
]
}
}

  1. Create the hook script at .cursor/hooks/posttooluse-smoke-test.sh:

#!/bin/bash
set -euo pipefail

INPUT_JSON=$(cat)
FILE_PATH=$(printf ‘%s’ “$INPUT_JSON” | python3 -c ’
import json, sys
try:
payload = json.load(sys.stdin)
tool_input = payload.get(“tool_input”, {})
print(tool_input.get(“file_path”, “”) or tool_input.get(“path”, “”))
except Exception:
print(“”)
')

case “$FILE_PATH” in
*/.cursor-posttooluse-smoke-test.txt)
python3 - <<‘PY’
import json

print(json.dumps({
“additional_context”: (
"CURSOR_POSTTOOLUSE_SMOKE_TEST_ACTIVE. "
"If you can read this injected context, explicitly say exactly: "
“‘POST_TOOL_SMOKE_TEST_RECEIVED’.”
)
}))
PY
;;
*)
echo ‘{}’
;;
esac

  1. Make the script executable.
  2. In Cursor Agent, create a file named .cursor-posttooluse-smoke-test.txt.
  3. Open the Hooks log and confirm the postToolUse hook ran.
  4. Check whether the model responds with POST_TOOL_SMOKE_TEST_RECEIVED.

Expected Behavior

The model should receive the additional_context string injected by the native postToolUse hook and react to it on the next continuation. In this repro, it should explicitly say POST_TOOL_SMOKE_TEST_RECEIVED.

Operating System

MacOS

Version Information

Version: 2.6.20
VSCode Version: 1.105.1
Commit: b29eb4ee5f9f6d1cb2afbc09070198d3ea6ad760
Date: 2026-03-17T01:50:02.404Z
Build Type: Stable
Release Track: Default
Electron: 39.8.1
Chromium: 142.0.7444.265
Node.js: 22.22.1
V8: 14.2.231.22-electron.0
OS: Darwin arm64 24.6.0

For AI issues: which model did you use?

Model name: gpt-5.4-high

Additional Information

Relevant observations:

  • This is a native Cursor hook in .cursor/hooks.json, not a third-party Claude compatibility hook.
  • The hook runner appears to work correctly.
  • The additional_context payload is logged exactly as expected.
  • preToolUse deny behavior works correctly in the same environment.
    stop hooks with followup_message also work correctly in the same environment.
  • The issue appears specific to postToolUse additional_context.

Example of the logged hook output:

{
“additional_context”: “CURSOR_POSTTOOLUSE_SMOKE_TEST_ACTIVE. If you can read this injected context, explicitly say exactly: ‘POST_TOOL_SMOKE_TEST_RECEIVED’.”
}

But the model never outputs POST_TOOL_SMOKE_TEST_RECEIVED, which strongly suggests the injected context is not actually reaching the model.

Does this stop you from using Cursor

Sometimes - I can sometimes use Cursor

Hi @Jim_Beno

Thank you for this incredibly well-documented report – the methodical testing with your hooks output really helps.

I can confirm this is a bug. The additional_context field in postToolUse responses is accepted and validated by the hook runner (which is why your logs show it working), but the value is never actually injected into the model’s conversation context. The plumbing on the consumer side was started but not completed.

Specifically:

  • For non-MCP tools, the postToolUse hook runs as fire-and-forget – the response (including additional_context) is discarded

  • For MCP tools, the response is awaited but only updated_mcp_tool_output is consumed; additional_context is still ignored

  • The only hook where additional_context currently works end-to-end is sessionStart

I’ve flagged this with our engineering team for tracking. Your observation that sessionStart works correctly while postToolUse does not is consistent with the code – they have different execution paths and only sessionStart has the full plumbing connected.

In the meantime, a potential workaround depending on your use case: if the context you need to inject is relatively static or can be determined at session start, you could use sessionStart with additional_context to pre-load it. I understand this won’t cover the dynamic per-tool-use case you’re building for, but it may help for some scenarios.