What Is the Elicitation Hook in Claude Code Harness?
The elicitation hook is a two-stage interception system that captures every MCP server request for user input, logs it to a structured ledger, and prevents background automation deadlocks by automatically skipping UI prompts during Breezing sessions.
The elicitation hook in Chachamaru127/claude-code-harness provides a robust, auditable mechanism for handling interactive prompts without breaking automated workflows. This hook pair intercepts structured input requests from the Claude Code server, records them permanently, and enforces policies that distinguish between interactive sessions and background workers.
How the Elicitation Hook Works
The system operates as a two-stage hook pair registered in hooks/hooks.json. Each stage handles a distinct phase of the user-prompt lifecycle, creating a clean separation between request interception and response handling.
Stage One: The Elicitation Handler
When the MCP server asks the user for structured input, the harness triggers scripts/hook-handlers/elicitation-handler.sh. This handler receives a JSON payload from STDIN describing the prompt request and performs three critical actions:
- Records the event to
.claude/state/elicitation/events.jsonlusing theelicitation-event.v1schema defined ingo/internal/hookhandler/elicitation_ledger.go - Emits a desktop notification titled
Claude Code: elicitationto alert the operator - Checks for Breezing sessions via the
HARNESS_BREEZING_SESSION_IDenvironment variable
If a Breezing session is active, the handler immediately returns a denial JSON without showing UI, preventing background agents from hanging on interactive prompts.
// Input payload read from STDIN
{
"schema_version": "elicitation-event.v1",
"event_kind": "elicitation",
"elicitation_id": "req-001",
"message": "Please confirm the target repository URL:",
"source": "mcp",
"timestamp": "2026-05-28T12:00:00Z"
}
// Automatic denial during Breezing sessions
{
"decision": "deny",
"reason": "Breezing session: background agent cannot interact with elicitation UI"
}
Stage Two: The Elicitation Result Handler
After the user provides input (or the request is skipped), scripts/hook-handlers/elicitation-result.sh receives the response payload. This lightweight handler appends a result entry to the same ledger and returns minimal approval JSON to the MCP server. No UI is displayed during this stage, ensuring minimal overhead for logging the outcome.
// Result payload processed by elicitation-result.sh
{
"schema_version": "elicitation-event.v1",
"event_kind": "elicitation_result",
"elicitation_id": "req-001",
"result_status": "done",
"message": "https://github.com/example/repo.git",
"timestamp": "2026-05-28T12:01:13Z"
}
Key Improvements to User Prompt Handling
The elicitation hook introduces five specific enhancements that make Claude Code operations more reliable and auditable.
Structured Audit Ledger
Every request and response is persisted as an elicitation-event.v1 record in .claude/state/elicitation/events.jsonl. This creates a permanent, queryable trail for debugging, compliance audits, and post-hoc analysis. The Go implementation in go/internal/hookhandler/elicitation_ledger.go defines the schema and provides atomic write operations to prevent data corruption during concurrent access.
Two-Layer Defense Architecture
By separating the request (elicitation) from the response (elicitation-result), the system enforces policy decisions before any UI is rendered. This prevents scenarios where background agents might stall waiting for user interaction that can never occur. The architecture is implemented in the hook registration at hooks/hooks.json (lines 276-288), where each stage maps to its respective handler.
Breezing Session Protection
The handler detects Breezing sessions—a special mode for background workers that cannot display UI—by checking for the HARNESS_BREEZING_SESSION_ID environment variable. When present, elicitation-handler.sh short-circuits immediately with a denial decision, avoiding deadlocks in parallel automation runs.
# From elicitation-handler.sh lines 123-132
if [[ -n "$HARNESS_BREEZING_SESSION_ID" ]]; then
echo '{"decision":"deny","reason":"Breezing session: background agent cannot interact with elicitation UI"}'
exit 0
fi
Desktop Notification System
For interactive sessions, the hook emits a system notification titled Claude Code: elicitation (implemented at line 129-130 of elicitation-handler.sh). This alerts the operator precisely when the MCP server requires input, reducing the risk of missed prompts during long-running operations.
Weak Supervision Cues
The ledger serves as input for scripts/build-weak-supervision-cues.sh, which reads .claude/state/elicitation/events.jsonl to generate "weak-supervision cues." These cues provide automated scoring mechanisms and decision guidance for downstream agents, turning historical user interactions into training signals for future automation.
# Generate supervision cues from the elicitation ledger
./scripts/build-weak-supervision-cues.sh
# Output: Weak-supervision cues from local elicitation ledger: ...
Core Files and Their Responsibilities
| File | Purpose |
|---|---|
scripts/hook-handlers/elicitation-handler.sh |
Captures incoming prompts, manages UI notifications, and enforces Breezing policies |
scripts/hook-handlers/elicitation-result.sh |
Logs user responses and returns minimal approval JSON to the MCP server |
go/internal/hookhandler/elicitation_ledger.go |
Defines the ElicitationEvent struct and handles atomic ledger I/O operations |
hooks/hooks.json |
Registers the elicitation and elicitation-result hook triggers with the harness binary |
scripts/build-weak-supervision-cues.sh |
Transforms ledger entries into weak-supervision signals for downstream automation |
Summary
- The elicitation hook provides a two-stage interception system for MCP server prompts, consisting of
elicitation-handler.shfor requests andelicitation-result.shfor responses. - It maintains an immutable audit trail in
.claude/state/elicitation/events.jsonlusing theelicitation-event.v1schema. - Breezing session detection automatically denies UI requests during background worker execution, preventing automation deadlocks.
- Desktop notifications ensure operators never miss interactive prompts during active sessions.
- The ledger supports downstream weak-supervision cue generation, enabling automated systems to learn from historical user interactions.
Frequently Asked Questions
What triggers the elicitation hook in Claude Code Harness?
The hook triggers whenever the MCP (Claude Code server) attempts to request structured input from the user. This request flows through the harness binary, which consults hooks/hooks.json and executes the appropriate handler based on the hook name—either elicitation for new requests or elicitation-result for completed responses. Both handlers receive JSON payloads via STDIN and must return valid JSON to STDOUT for the MCP server to continue.
How does the elicitation hook prevent automation deadlocks?
The hook prevents deadlocks through Breezing session detection in elicitation-handler.sh. When the environment variable HARNESS_BREEZING_SESSION_ID is set—indicating a background worker that cannot display UI—the handler immediately returns a denial JSON without spawning any interactive interface. This ensures parallel automation tasks continue execution rather than hanging indefinitely on user prompts.
What is the elicitation-event.v1 schema?
The elicitation-event.v1 schema is a standardized JSON structure defined in go/internal/hookhandler/elicitation_ledger.go that records both prompt requests and user responses. Required fields include schema_version, event_kind (either "elicitation" or "elicitation_result"), elicitation_id for correlation, message content, and ISO 8601 timestamps. This schema ensures consistent, parseable records across the audit ledger.
Where are elicitation events stored?
All events are appended to .claude/state/elicitation/events.jsonl in the project root. This newline-delimited JSON file serves as the permanent ledger for the elicitation hook system. The Go implementation in elicitation_ledger.go handles atomic writes to this location, while scripts/build-weak-supervision-cues.sh reads from it to generate downstream automation signals.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →