Skip to content

Send the SessionStart context from a command hook (0.6.1) - #12

Merged
gabe4coding merged 1 commit into
mainfrom
claude/sessionstart-command-hook
Oct 10, 2026
Merged

gabe4coding merged 1 commit into
mainfrom
claude/sessionstart-command-hook

Conversation

@gabe4coding

Copy link
Copy Markdown
Owner

Problem

The model never gets the code-mode instruction at session start ("use run_code for MCP tool calls…"). This is true in normal sessions and in headless runs.

Claude Code 2.1.296 has a built-in security module, cc-plugin-sec-default. It handles classic.* events before the plugin modules. For SessionStart, it skips the user-tier modules. The debug log of every run says:

retroagent+code-mode: classic.SessionStart bypassed by cc-plugin-sec-default (tier user); beneath runs

code-mode added its line in a module handler (on('classic.SessionStart', …)), so the line was lost. Command hooks in hooks.json still run: retroagent's kb line comes from one and reaches the model.

What this PR does

  • hooks/session-start.sh: a command hook in hooks/hooks.json. It prints the line as hookSpecificOutput.additionalContext. It reads blockDirectMcp from CLAUDE_PLUGIN_OPTION_BLOCKDIRECTMCP, which Claude Code sets for an option that is set.
  • The module handler and sessionContext are removed. If a later Claude Code version runs module handlers again, the model does not get the line twice.
  • A new case in tests/runner.integration.mjs runs the real script in both option states.
  • The two fake-host SessionStart tests are removed. The fake host has no security module, so they passed and could not find this problem.
  • CODING_STANDARDS.md has the rule, and docs/development.mdx has the new file.

Evidence

Before (0.6.0):

  • 4 headless Haiku runs had no code-mode line in the SessionStart context, with every variant:
    • --restricted with --plugin-dir;
    • --plugin-dir alone;
    • the installed plugin;
    • a trusted folder.
  • All 4 debug logs have the bypass line.
  • 0 of the 132 transcripts of the projection bench have the line.

Probe: a temporary command hook printed a test line. The line reached the transcript, and the hook's environment had CLAUDE_PLUGIN_OPTION_BLOCKDIRECTMCP=true, CLAUDE_PLUGIN_ROOT and CLAUDE_PLUGIN_DATA.

After (this branch), 2 headless Haiku runs:

  • The default run has code-mode: use run_code for MCP tool calls, … in the SessionStart context (hook_additional_context).
  • The run with blockDirectMcp: true has code-mode: call MCP tools only from run_code, … Direct MCP tool calls are denied.
  • The debug log shows Hook SessionStart (sh "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh") provided additionalContext.
  • The bypass line now names only retroagent.

Checks: claude plugin test . (114 pass, 0 fail), node tests/runner.integration.mjs, node tests/docs.check.mjs, tests/version.check.mjs (0.6.0 → 0.6.1), tsc and claude plugin validate . all pass.

Open point

The projection bench blocked direct calls, so its numbers do not depend on this fix. How often the model calls MCP tools directly when it has the instruction is not measured yet.

🤖 Generated with Claude Code

The model never got the code-mode line at session start. Claude Code's
built-in security module skips a hooks module's classic.SessionStart; the
debug log says "retroagent+code-mode: classic.SessionStart bypassed by
cc-plugin-sec-default (tier user); beneath runs". Command hooks in
hooks.json still run.

- hooks/session-start.sh prints the line as hookSpecificOutput.additionalContext.
  It reads blockDirectMcp from CLAUDE_PLUGIN_OPTION_BLOCKDIRECTMCP.
- The module handler and sessionContext are removed, so the model cannot
  get the line twice if a later Claude Code runs module handlers again.
- The sandbox test runs the real script in both option states. The fake
  host has no security module, so its SessionStart tests passed and could
  not find this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gabe4coding
gabe4coding merged commit 9dd1fbf into main Oct 10, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant