Skip to content

fix: scope PreToolUse hooks to their own tools - #155

Open
HadiHassan22 wants to merge 1 commit into
VeryGoodOpenSource:mainfrom
HadiHassan22:fix/scope-hooks-to-their-own-tools
Open

HadiHassan22 wants to merge 1 commit into
VeryGoodOpenSource:mainfrom
HadiHassan22:fix/scope-hooks-to-their-own-tools

Conversation

@HadiHassan22

Copy link
Copy Markdown

Description

Fixes #154.

check-vgv-cli.sh never read its stdin payload. It ran check_vgv_cli and emitted a
deny/allow decision for whatever tool invoked it, trusting the hooks.json matcher to
scope it. On a host where that matcher does not select correctly, a Dart tool-version check
denied unrelated MCP tool calls — reported from a TypeScript/Next.js project with no Dart in it.
The inverse is worse: when the CLI is present, the same blind path returned allow, silently
auto-approving unrelated MCP calls and bypassing the permission prompt.

What changed

Both PreToolUse hooks now confirm from the payload that the call is their business.
A matcher is a filter, not a guarantee, so each hook checks tool_name itself and exits 0 when
the tool is not its own.

block-cli-workarounds.sh had the same fault with a narrower trigger. It filtered on
.tool_input.command — a field name rather than the caller's identity — so any MCP tool carrying
a command argument was inspected and could be denied:

# before
$ echo '{"tool_name":"MCP:run_terminal_cmd","tool_input":{"command":"flutter test"}}' | bash hooks/scripts/block-cli-workarounds.sh
{"hookSpecificOutput":{"permissionDecision":"deny", ...}}

# after — stands aside

It now scopes on tool_name instead, accepting the shell tool under either name hosts give it.
When a host sends no tool_name at all it falls through to the command check, so enforcement is
never silently dropped.

The CLI lookup is fixed too, and this half is host-independent. very_good installs to
~/.pub-cache/bin, which an interactive shell adds to PATH but a hook subprocess does not
necessarily inherit — it is not in /etc/paths. A present CLI was reported missing, producing
both a spurious deny on genuine Very Good CLI calls and a false warning at every session start.
Resolution now falls back to $PUB_CACHE/bin.

A pub-cache fallback alone is not enough: the installed very_good is a sh shim that execs
dart, so a PATH without dart makes the check inconclusive rather than negative.

$ env -i HOME=$HOME PATH=/usr/bin:/bin:/usr/sbin:/sbin ~/.pub-cache/bin/very_good --version
~/.pub-cache/bin/very_good: line 8: dart: command not found   (exit 127)

check_vgv_cli now returns unverifiable for that case. Callers stand aside on an inconclusive
check instead of denying a genuine call or warning that the CLI is missing. A genuinely absent
CLI still denies and still warns, unchanged.

Tests

Adds hooks/scripts/check-vgv-cli_test.sh (14 cases) and tool-scoping cases to
block-cli-workarounds_test.sh. Every case runs against a stubbed very_good on a PATH
containing nothing else, so results do not depend on what is installed on the machine running
them. Registered in the Script Tests CI job. All three suites pass: 18, 32, 14.

Scope

This does not add Cursor support (#48), and deliberately does not touch the matcher — making
mcp__.*very-good-cli__.* match another host's naming is that issue's business. The change is
that the scripts no longer depend on the matcher being correct, plus a PATH fix that affects
Claude Code identically. #126 workstream 4 factors these hooks for multi-host adaptation; scoping
on tool_name is a prerequisite for it.

One cspell word (Anson) was added: it is pre-existing in ci.yaml and this PR touches that
file, which brings it into the spell check's changed-file set.

Known limitation, not addressed here

Cursor's preToolUse payload carries no server segment — a Very Good CLI test call arrives as
MCP:test, indistinguishable from any other server's test. The guard therefore stands aside for
genuine Very Good CLI calls on that host, so the auto-approve is lost there. That is fail-safe: no
false denials, and the tools still work through normal permission handling. Restoring it needs
either matching bare tool names (create, test, ...), which collide across MCP servers, or
Cursor's beforeMCPExecution event. Called out in #154 as a follow-up rather than widened here.

Type of Change

  • New feature (feat)
  • Bug fix (fix)
  • Code refactor (refactor)
  • Documentation (docs)
  • CI change (ci)
  • Chore (chore)

🤖 Generated with Claude Code

check-vgv-cli.sh never read its payload. It emitted a deny/allow decision for
whatever tool invoked it, trusting the hooks.json matcher to scope it, so on a
host where that matcher does not select correctly a Dart tool-version check
denied unrelated MCP tool calls — and, when the CLI was present, silently
auto-approved them.

Both PreToolUse hooks now confirm from the payload that the call is their
business before deciding. block-cli-workarounds.sh had the same fault with a
narrower trigger: it filtered on .tool_input.command, a field name rather than
the caller's identity, so any MCP tool carrying a command argument could be
denied.

Also fixes the CLI lookup, which is host-independent. very_good is installed to
~/.pub-cache/bin, which is not on a hook subprocess's PATH, so a present CLI was
reported missing. Resolution now falls back to $PUB_CACHE/bin, and because the
installed very_good is a shim that execs dart, a binary found but unreadable is
reported as "unverifiable" rather than "not installed" — callers stand aside on
an inconclusive check instead of denying a genuine call or warning at session
start.

Adds check-vgv-cli_test.sh and tool-scoping cases to the existing suite.

Closes VeryGoodOpenSource#154

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@HadiHassan22
HadiHassan22 requested a review from a team as a code owner September 13, 2026 17:08
@ryzizub ryzizub self-assigned this Sep 14, 2026
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.

fix: check-vgv-cli decides for every tool, not just Very Good CLI ones

2 participants