Review: Hook + MCP fix: GroupMe send_message requires name-based resolution + user approval

review-357-2026-03-25-v4 Review

review ready

Verdict: READY

Template Completeness

Assessed against consolidated spec (original issue body + Scope Expansion v1 + v2 + v3):
  • [x] Lineage -- standalone incident-fix, no plan phase
  • [x] Repo -- two repos identified: claude-custom (hook + settings) and groupme-mcp (tool interface)
  • [x] User Story -- clear operator story about preventing data exposure
  • [x] Context -- incident details, root cause analysis, fix approach all documented
  • [x] File Targets -- all files specified with change descriptions (expanded across v1/v2/v3)
  • [x] Acceptance Criteria -- 9 consolidated criteria covering all 5 tools, hook, tests, deploy order
  • [x] Test Expectations -- unit tests for MCP tools + hook shell tests specified with location
  • [x] Constraints -- patterns, live API requirement, SDK layer boundary, pagination, deploy ordering, hook test location
  • [x] Checklist -- PRs, tests, doc update all listed
  • [x] Related -- project pages and SOPs referenced

File Targets

claude-custom:
  • [x] hooks/block-groupme-send.sh -- NEW. Verified hooks/ directory exists with 39 existing hooks. Pattern reference block-mcp-merge.sh verified: reads stdin JSON, extracts fields with jq, returns permissionDecision: "ask". 19 lines, clean pattern to follow.
  • [x] settings.json -- Verified. PreToolUse section exists (lines 50-145) with pipe-delimited matcher pattern already used (e.g., line 121: mcp__forgejo__create_issue|mcp__forgejo__create_issue_and_branch). New entry for mcp__groupme__send_message|mcp__groupme__add_member|mcp__groupme__remove_member follows established convention.
  • [x] tests/ directory -- Confirmed does NOT exist yet. Scope expansion v3 specifies creating it with shell-based hook tests. Clear pattern specified.
groupme-mcp:
  • [x] src/groupme_mcp/tools/messages.py -- Verified. send_message(group_id=...) at line 13. 26-line file, straightforward param rename + resolution logic.
  • [x] src/groupme_mcp/tools/members.py -- Verified. Three tools: add_member(group_id=...) line 14, remove_member(group_id=...) line 51, list_members(group_id=...) line 67. All take group_id as first param.
  • [x] src/groupme_mcp/tools/groups.py -- Verified. get_group(group_id=...) at line 42. 5th tool with stale-ID vulnerability, added in scope expansion v2.
  • [x] src/groupme_mcp/server.py -- Verified. 60-line file with shared helpers (get_client, _error_response, _ok). Natural home for _resolve_group() helper.
  • [x] SDK layer (groupme-sdk) -- Confirmed NOT to touch. SDK's list_groups(page=1, per_page=10) and get_group(group_id=...) keep raw ID interface. MCP resolves above it.
Existing tests verified:
  • [x] tests/test_messages.py -- 3 tests, all use group_id="12345". Will need update to group_name + mock resolution.
  • [x] tests/test_members.py -- 8 tests, all use group_id="12345". Same update needed.
  • [x] tests/test_groups.py -- 8 tests. TestGetGroup uses group_id="12345". Will need update.

Repo Placement

OK. Two repos correctly identified:
  • forgejo_admin/claude-custom -- hook + settings.json (enforcement layer)
  • forgejo_admin/groupme-mcp -- tool interface change (MCP layer)
Forgejo issue filed on claude-custom which is the primary repo (hook is the enforcement gate). The groupme-mcp changes are the prerequisite dependency. Deploy ordering documented in constraints.

Dependencies

  • Deploy ordering (documented): groupme-mcp must deploy before claude-custom hook. Hook expects group_name in tool_input; deploying hook first would show empty group name in prompt.
  • SDK pagination (documented): SDK list_groups(page=1, per_page=10) defaults to 10 results. Currently at 10 groups -- already at the edge. Constraint added: _resolve_group() must use per_page=100 or paginate.
  • Board dependencies: Item #357 is in todo column. No blocking dependencies found on the board. No other GroupMe-related items exist.

Acceptance Criteria

All 9 consolidated criteria are testable by an implementing agent:
  • "All 5 group-scoped tools accept group_name" -- testable via pytest with mock resolution
  • "Shared _resolve_group() helper" -- testable via import + call
  • "Ambiguous/missing group name returns helpful error" -- testable with mock list_groups returning similar names
  • "PreToolUse hook fires on 3 write tools" -- testable via settings.json matcher inspection
  • "Read-only tools excluded from hook" -- testable by verifying matcher pattern
  • "Hook shows group name and tool-specific summary" -- testable via piping mock JSON through hook
  • "Hook unit tests for all 3 write tool input shapes" -- testable by running shell test scripts
  • "groupme-mcp tests updated for all 5 tools" -- testable via pytest tests/
  • "Deploy order: groupme-mcp first" -- documented constraint, verified by human
Test commands are real: cd ~/groupme-mcp && pytest tests/ (tests directory exists with conftest.py + 3 test files). Hook tests: bash tests/test_block_groupme_send.sh (new directory, location specified in scope expansion v3).

Blast Radius

  • No other MCP servers affected. Grep for group_id in claude-custom returned zero matches. The GroupMe stale-ID pattern is isolated to groupme-mcp.
  • create_group unaffected. Takes name (not group_id) already. Correctly excluded.
  • list_groups unaffected. Takes no parameters. Correctly excluded.
  • Downstream: pal-e-docs project page. Checklist item to update project-groupme-westside (remove stale ID table). Low risk.
  • 19 existing tests will break. All use group_id="12345". Scope correctly identifies these need updating. No silent breakage -- pytest fails immediately.

Recommendation

No action needed. The consolidated spec (original body + 3 scope expansions) is complete and agent-executable. All file targets verified against the codebase. All three prior review rounds' issues have been addressed:
  • v1: all 5 group-scoped tools included, hook handles different input shapes
  • v2: get_group added, hook test restored, deploy ordering documented, read-only exclusion rationale
  • v3: SDK pagination constraint added, hook test location specified
Points assessment: 5 points is appropriate. Two repos, 7 files modified/created, 5 tool interfaces changed, shared helper extracted, hook with 3 input shapes, new test directory, 19+ existing tests rewritten, deploy coordination.