fix(claude-code): catch up to v2.1.118 (mcp_tool hook type) - #767
Conversation
Closes #764. Triage of Claude Code v2.1.117 -> v2.1.118. The release added `type: "mcp_tool"` so hooks can invoke MCP tools directly. Without this fix, agnix would false-positive CC-HK-016 (unknown hook type) on every v2.1.118+ user following the release notes. Added `"mcp_tool"` to BOTH allow-lists in crates/agnix-core/src/rules/hooks/helpers.rs: - `valid_types` (lines 607, 973): used by CC-HK-016 strict check - `known_non_command` (lines 532, 974): used to flag async on non-command hook types Plus a regression test (`test_cc_hk_016_mcp_tool_type_valid`) asserting `mcp_tool` doesn't trigger CC-HK-016. Doc gap caveat: as of 2026-04-23 the docs at code.claude.com/docs/en/hooks only list 4 types (command, prompt, agent, http). The v2.1.118 release notes explicitly mention mcp_tool and are authoritative until the docs catch up. Other v2.1.118 changes need no agnix change: - Vim visual mode, /cost+/stats -> /usage, custom themes (UI/CLI) - Plugin themes/ directory: agnix's plugin.rs has no strict fields check, no false positive - DISABLE_UPDATES env var: not a config file - wslInheritsWindowsSettings policy key: settings.json field; agnix doesn't validate settings.json fields directly - autoMode.* '\$defaults' token: settings.json field - claude plugin tag (CLI), OAuth/credential bug fixes (runtime) Documentation: - Bumped verified_on to 2026-04-23 for all 25 CC-HK-* rules in knowledge-base/rules.json + synced crates/agnix-rules/rules.json - Regenerated website/docs/rules/generated/*.md Verified locally: - cargo clippy --workspace --all-targets --all-features -- -D warnings clean - cargo test -p agnix-core --lib: passes (+1 new regression test)
There was a problem hiding this comment.
Code Review
This pull request adds support for the mcp_tool hook type in Claude Code v2.1.118, updating validation logic, regression tests, and documentation verification dates. Feedback recommends refactoring duplicated hook type arrays into shared constants to ensure a single source of truth.
| let valid_types = ["command", "prompt", "agent", "http", "mcp_tool"]; | ||
| let known_non_command = ["prompt", "agent", "http", "mcp_tool"]; |
There was a problem hiding this comment.
The valid_types and known_non_command arrays are duplicated across multiple functions in this file (lines 532, 607, 973, and 974). Following the 'Single Source of Truth' general rule, these should be refactored into shared constants (e.g., at the module level) to ensure consistency and simplify future updates when new hook types are introduced.
References
- Instead of merging and deduplicating data from multiple sources, refactor the code to have a single source of truth to avoid duplication in the first place.
There was a problem hiding this comment.
Pull request overview
Updates agnix’s Claude Code hooks validation to account for Claude Code v2.1.118 introducing a new hook type: "mcp_tool", and refreshes CC-HK rule documentation metadata.
Changes:
- Add
"mcp_tool"to hook-type allow-lists used by CC-HK-016 and async-on-non-command handling. - Add a regression unit test for
mcp_toolnot triggering CC-HK-016. - Bump
verified_onfor CC-HK-* rules and regenerate generated rule docs; update changelog.
Reviewed changes
Copilot reviewed 5 out of 30 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/agnix-core/src/rules/hooks/helpers.rs | Extends raw hook-type allow-lists to include mcp_tool. |
| crates/agnix-core/src/rules/hooks/tests.rs | Adds regression test covering mcp_tool vs CC-HK-016. |
| knowledge-base/rules.json | Bumps verified_on dates for CC-HK-* rules. |
| crates/agnix-rules/rules.json | Mirrors verified_on bumps for CC-HK-* rules. |
| CHANGELOG.md | Adds release catch-up note for Claude Code v2.1.118. |
| website/docs/rules/generated/cc-hk-001.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-002.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-003.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-004.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-005.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-006.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-007.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-008.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-009.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-010.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-011.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-012.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-013.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-014.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-015.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-016.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-017.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-018.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-019.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-020.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-021.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-022.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-023.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-024.md | Regenerated doc with updated Verified On date. |
| website/docs/rules/generated/cc-hk-025.md | Regenerated doc with updated Verified On date. |
Comments suppressed due to low confidence (1)
crates/agnix-core/src/rules/hooks/helpers.rs:537
- The comment above says this rule only flags known non-command types “(prompt/agent)”, but the allow-list now includes
httpandmcp_toolas well. Please update the comment to match the actual behavior (or reword it to avoid enumerating types) so future changes don’t accidentally miss updating documentation around this list.
let known_non_command = ["prompt", "agent", "http", "mcp_tool"];
for_each_raw_hook(raw_value, |event, matcher_idx, hook_idx, hook| {
if hook.get("async").is_some() {
if let Some(hook_type) = hook.get("type").and_then(|t| t.as_str()) {
// Only flag async on known non-command types.
// Unknown/invalid types are handled by CC-HK-016.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| diagnostics: &mut Vec<Diagnostic>, | ||
| ) { | ||
| let valid_types = ["command", "prompt", "agent", "http"]; | ||
| let valid_types = ["command", "prompt", "agent", "http", "mcp_tool"]; |
There was a problem hiding this comment.
mcp_tool is added to the raw JSON allow-list, but typed parsing still uses schemas::hooks::Hook (internally-tagged enum) which currently only supports command|prompt|agent|http. That means configs containing {"type":"mcp_tool"...} will still fail serde_json::from_value and emit CC-HK-012, so this change likely shifts the failure from CC-HK-016 to CC-HK-012 rather than fully supporting v2.1.118. Consider adding an mcp_tool variant to Hook (with at least a tool field and a #[serde(flatten)] _extra map if you want to avoid strict-field coupling) or otherwise adjusting typed parsing to accept this new type.
| let valid_types = ["command", "prompt", "agent", "http", "mcp_tool"]; | |
| // Keep this list aligned with the currently supported typed hook variants. | |
| // Until `schemas::hooks::Hook` accepts `mcp_tool`, raw validation must not | |
| // allow it here or the config will fail later during typed deserialization. | |
| let valid_types = ["command", "prompt", "agent", "http"]; |
| let cc_hk_016: Vec<_> = diagnostics | ||
| .iter() | ||
| .filter(|d| d.rule == "CC-HK-016") | ||
| .collect(); | ||
|
|
||
| assert_eq!(cc_hk_016.len(), 0, "mcp_tool should be a valid hook type (Claude Code v2.1.118+)"); |
There was a problem hiding this comment.
This regression test only asserts that CC-HK-016 doesn’t fire. Since Hook’s typed schema currently doesn’t include an mcp_tool variant, validation may still emit CC-HK-012 (schema mismatch) for the same input, and this test would pass anyway. Please strengthen the test to assert there are no diagnostics (or at least that CC-HK-012 is also absent) once mcp_tool is fully supported.
| let cc_hk_016: Vec<_> = diagnostics | |
| .iter() | |
| .filter(|d| d.rule == "CC-HK-016") | |
| .collect(); | |
| assert_eq!(cc_hk_016.len(), 0, "mcp_tool should be a valid hook type (Claude Code v2.1.118+)"); | |
| assert!( | |
| diagnostics.is_empty(), | |
| "mcp_tool should be fully supported and produce no diagnostics (including CC-HK-012 schema mismatch or CC-HK-016 unknown hook type), got: {:?}", | |
| diagnostics | |
| ); |
| // CC-HK-016 (unknown hook type) on every v2.1.118+ user that follows | ||
| // the docs. |
There was a problem hiding this comment.
Minor: the test comment says “user that follows the docs”, but the PR description notes the docs page hasn’t been updated yet and the source of truth is the v2.1.118 release notes. Consider rewording this comment to avoid confusion about which upstream reference introduced mcp_tool.
| // CC-HK-016 (unknown hook type) on every v2.1.118+ user that follows | |
| // the docs. | |
| // CC-HK-016 (unknown hook type) for users on v2.1.118+ following | |
| // the release notes that introduced `mcp_tool`. |
|
Per-comment reply table for round 1
Plus the fmt drift CI caught (forgot `cargo fmt --all` after the round-1 test edit, same lesson as PR #758/#762). Pushing now. |
…view) Reviewer feedback (PR #767 round 1): - Copilot CRITICAL: typed Hook enum still rejected mcp_tool, so even with the raw-JSON allow-list fix, CC-HK-012 (schema mismatch) would fire on every mcp_tool hook. Added Hook::McpTool variant with tool, if, timeout fields (per release notes). Updated all match arms (command(), prompt(), type_name(), the per-variant typed-validation block in rules/hooks/mod.rs); added is_mcp_tool() for symmetry. - Copilot: test was vacuous (only checked CC-HK-016 absence; would have passed with CC-HK-012 still firing). Strengthened to assert diagnostics.is_empty() (zero diagnostics of any rule). Now genuinely fails without the typed-enum addition. - Copilot: comment said 'follows the docs' but docs page hasn't been updated yet; reworded to 'follows the release notes' with a caveat about the doc gap as of 2026-04-23. - Gemini MEDIUM: KNOWN_KEYS dedup -> won't fix (project-wide pattern, same answer as PR #757/#763). Plus fmt drift caught by CI (cargo fmt forgotten after round-1 edit; same lesson as PR #758/#762).
Summary
Triage of Claude Code v2.1.117 -> v2.1.118 (issue #764). The release added
type: "mcp_tool"so hooks can invoke MCP tools directly. Without this PR, agnix would false-positive CC-HK-016 on every v2.1.118+ user that follows the release notes.The fix
Added
"mcp_tool"to BOTH allow-lists incrates/agnix-core/src/rules/hooks/helpers.rs:valid_types(lines 607, 973): used by CC-HK-016 strict checkknown_non_command(lines 532, 974): used to flag async on non-command typesPlus a regression test (
test_cc_hk_016_mcp_tool_type_valid).Doc gap caveat
As of 2026-04-23 the official docs at
code.claude.com/docs/en/hooksstill only list 4 types (command,prompt,agent,http). The v2.1.118 release notes explicitly addmcp_tooland are authoritative until the docs catch up. Will revisit the structural validation (required fields for themcp_tooltype, e.g., atoolreference) in a future PR once the docs document them.Other v2.1.118 changes — NO agnix change
/cost+/stats->/usage, custom themes, pluginthemes/directoryDISABLE_UPDATESenv varwslInheritsWindowsSettingspolicy keyautoMode.*"$defaults"tokenclaude plugin tag(CLI), OAuth/credential bug fixesDoc bumps
verified_onto 2026-04-23 for all 25 CC-HK-* ruleswebsite/docs/rules/generated/*.mdTest plan
cargo clippy --workspace --all-targets --all-features -- -D warningscleancargo test -p agnix-core --lib: passes (+1 new regression test)Closes #764.