Feature request: let PostToolUse hooks replace tool output (updatedToolOutput)

Open 💬 2 comments Opened Aug 12, 2026 by kevintseng
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Motivation

We build toonify-mcp, a context-compression tool: it converts large JSON/YAML tool output to a compact tabular format and collapses repetitive logs (60%+ token reduction on realistic payloads), while passing through anything it can't reproduce losslessly.

On Claude Code this works as a PostToolUse hook that returns hookSpecificOutput.updatedToolOutput, which replaces what the model sees — that replacement is what actually shrinks context.

Current Codex behavior

PostToolUseOutcome supports block / additionalContext / feedback, but no output replacement. The output parser already recognizes a Claude-style updatedMCPToolOutput field — and explicitly rejects it as unsupported (codex-rs/hooks/src/events/post_tool_use.rs, unsupported_updated_mcp_tool_output_fails_open), so the concept appears to be on your radar already.

additionalContext can't serve this use case: appending a compressed copy on top of the original output makes context larger, the exact opposite of the goal.

Request

Support an updatedToolOutput (and/or the already-parsed updatedMCPToolOutput) in PostToolUse hook output that replaces the tool result before it enters model context — schema-checked against the tool's output type, with fail-open on mismatch, like the current unsupported-field handling.

This would let context-optimization tools (compression, redaction, truncation policies) work across both ecosystems with the same hook contract.

View original on GitHub ↗

2 Comments

github-actions[bot] contributor · 16 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #36940

Powered by Codex Action

jdcodes1 · 9 days ago

Verified against main (1f41cc5d92), and the symmetric precedent makes this a well-bounded ask: PreToolUse already does replacement. The registry's dispatch loop handles PreToolUseHookResult::Continue { updated_input: Some(_) } by rebuilding the invocation via with_updated_hook_input before the tool runs (https://github.com/openai/codex/blob/1f41cc5d92/codex-rs/core/src/tools/registry.rs#L590-L608). So the architecture already accepts "hook rewrites the payload, fail closed on malformed rewrite" — the request is the mirror image on the result side, applied between handle_any_tool returning and the output entering history.

And as noted, the parser half-exists: updatedMCPToolOutput is recognized and deliberately failed-open with "returned unsupported updatedMCPToolOutput" (hooks/src/events/post_tool_use.rs#L410-L431), so the wire contract is already parsed — only the application step is missing.

Design details from the input-side precedent worth copying: schema-check the replacement against the tool's output shape and treat mismatch like the current unsupported-field fail-open (never lose the original output); and record both original and replaced output in the rollout so /feedback and debugging keep ground truth while only the replacement reaches model context.