Skip to content

fix: stop the Claude Code hook from approving run_tool calls - #45

Merged
steve-calvert-glean merged 2 commits into
mainfrom
fix/hook-no-allow
Oct 28, 2026
Merged

steve-calvert-glean merged 2 commits into
mainfrom
fix/hook-no-allow

Conversation

@steve-calvert-glean

@steve-calvert-glean steve-calvert-glean commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR stops our Claude Code PreToolUse hook from approving Glean run_tool calls. Anthropic's plugin directory holds v3.6.0 for review because of that approval.

Why this is needed

The directory holds v3.6.0, and it will hold every later version until the hook changes. Until a version clears review, installs from the directory stay on v3.4.2 and get none of the fixes released since.

v3.6.0 has two holds. This PR removes one of them. The other is described under "Not in this PR".

Why the hook approved calls

The local MCP server shows its own approval prompt before a run_tool call that needs approval (ENABLE_HITL). It skips the prompt for tools it knows are read-only, and for tools whose saved approval says not to ask, such as tools the user set to Always Allow. Claude Code also showed its own "allow this tool?" prompt for the same call. To avoid that double prompt, the hook answered "allow" for every Glean run_tool call, and the server's prompt was the only one the user saw.

The hook also did a second job. Before each call, it wrote Claude Code's permission mode to a per-session marker file. The server reads that marker so it can skip its own prompt when the user started Claude Code with --dangerously-skip-permissions.

Why the directory holds it

The directory's check is HOOK_GRANTS_PERMISSION. Its rule says a hook may deny a call or leave the decision to Claude Code's normal permission flow, but it may not approve a call or change the call's input. A hook that answers "allow" decides for the user whether they are asked at all.

The check reads the hook file statically. It reports a decision string even in code it can't prove runs.

The same hook shipped in v3.4.2, and the directory approved that version. The current check holds it, so the hook has to change before any new version can publish.

What this PR changes

  • The hook keeps its second job and drops the first. It still writes the permission-mode marker, so --dangerously-skip-permissions still skips the server's prompt. It prints nothing, so Claude Code makes the permission decision.
  • The file is renamed from auto-approve-run-tool.mjs to record-permission-mode.mjs, because the old name described the behavior this PR removes. hooks.json points to the new name.
  • The hook tests now expect no output. A new test fails if permissionDecision, "allow" or "approve" appears anywhere in the hook source, comments included, in any case. This mirrors the directory's static check.
  • Comments in run-tool.ts and policy/key.ts no longer say the hook approves calls.

Options considered

Option Passes the directory check Why not chosen
Print no decision (this PR) Yes Chosen
Return "ask" Yes The hook would return "ask" on every call, so Claude Code would prompt every time, in every permission mode except bypass.
Delete the hook Yes The marker would stop being written, so users who start Claude Code with --dangerously-skip-permissions would get the server's prompt again.

What users will notice

Results from Claude Code 2.1.312:

Permission mode Claude Code prompt on run_tool before After
Auto (Claude Code's default for interactive sessions) None (the hook answered) None in our tests. The auto-mode classifier let both a read and a write through.
Default (manual) None (the hook answered) A Yes/No prompt on every call. The prompt has no "don't ask again" option, so users who want it gone add an allow rule for run_tool with /permissions.
--dangerously-skip-permissions None None

The server's own approval prompt doesn't change in any mode. No call skips a check that it got before this PR. The only change is that Claude Code makes its own permission decision again.

In default mode, a write that needs approval now gets Claude Code's prompt as well as the server's, so users see one more prompt than before.

Later work moves the approval prompt from the local server to the remote server. Then the marker has no job left, and the hook can be deleted.

Not in this PR

The second directory hold is BINARIES_NOT_INSPECTED. The check can't fully inspect mcp/dist/index.js, which is 1.63 MB, compared with 983 KB in the approved v3.4.2. A follow-up PR reduces the bundle size. Until then, a reviewer must clear that hold by hand.

Testing

  • npm run test:bundle: 436 tests pass.
  • npm run typecheck:bundle: clean.
  • npm test (build plus pluginpack validate for Claude Code, Cursor and Codex): passes.
  • The built dist/claude/plugins/glean/hooks/ holds only capture-inventory.mjs, hooks.json and record-permission-mode.mjs, and none of them contains a decision string.
  • The built hook, run on a sample run_tool call, printed nothing, exited 0 and wrote the marker.
  • Live, in an isolated Claude Code config (CLAUDE_CONFIG_DIR) with only this build loaded:
    • Non-interactive (claude -p, Claude Code 2.1.284), with the same build and the hook from main swapped in as a baseline. With the old hook, run_tool ran with no permission check. With the new hook, Claude Code required permission, and -p reported a permission denial. With --allowedTools or a saved allow rule, the call ran. With --dangerously-skip-permissions, it ran and the hook recorded bypassPermissions.
    • Interactive (Claude Code 2.1.312), signed in to a live Glean instance. Default mode: a search prompted once (Yes/No) and returned results, and a Gmail draft write prompted, then hit the server's approval form and was not run. Auto mode: a search ran with no prompt, and the same write ran past the classifier, showed the server's approval form, and was declined there. No draft was created.

The PreToolUse hook answered "allow" for every Glean run_tool call while
ENABLE_HITL was on, to avoid a double prompt. Anthropic's plugin directory
now holds any plugin whose hook can decide a permission question for the
user, so v3.6.0 is held for review.

The hook now only records Claude Code's permission mode, which the server
uses to skip its own prompt under --dangerously-skip-permissions. It prints
nothing, so Claude Code's normal permission flow always runs. The file is
renamed to record-permission-mode.mjs to match what it does.

Users see Claude Code's own prompt on run_tool again until they allow the
tool permanently. The server's approval prompt still gates writes.
aditya-scio
aditya-scio previously approved these changes Oct 28, 2026

@aditya-scio aditya-scio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thiss makes sense. It's sad the current hook is getting blocked by marketplace submission.

@aditya-scio

Copy link
Copy Markdown
Contributor

Thanks for this

@aditya-scio aditya-scio reopened this Oct 28, 2026
@steve-calvert-glean
steve-calvert-glean marked this pull request as ready for review October 28, 2026 20:40
@steve-calvert-glean
steve-calvert-glean requested a review from a team as a code owner October 28, 2026 20:40
@steve-calvert-glean steve-calvert-glean added the bug Something isn't working label Oct 28, 2026
The hook comment said the server's prompt gates every write. It skips
tools the user told it to stop asking about, so say that.

The directory's check reads the hook file statically, so the test now
fails on "allow" or "approve" anywhere in the file, in any case.
@steve-calvert-glean
steve-calvert-glean merged commit e35c63c into main Oct 28, 2026
1 check passed
@steve-calvert-glean
steve-calvert-glean deleted the fix/hook-no-allow branch October 28, 2026 21:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants