feat: name-based group resolution on all group-scoped tools #4
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "3-name-based-group-resolution"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Replaces raw
group_idparameters withgroup_nameon all 5 group-scoped MCP tools, eliminating the class of error where stale or wrong IDs route messages to the wrong group. A shared_resolve_group()helper resolves names against the live GroupMe API with pagination support.Changes
src/groupme_mcp/server.py: Added_resolve_group(group_name)helper with paginatedlist_groups(per_page=100), case-insensitive exact matching, and clear error messages for no-match/ambiguous casessrc/groupme_mcp/tools/messages.py:send_messagenow acceptsgroup_name; response includes resolved group name, member names, and member countsrc/groupme_mcp/tools/members.py:add_member,remove_member,list_membersnow acceptgroup_nameinstead ofgroup_idsrc/groupme_mcp/tools/groups.py:get_groupnow acceptsgroup_nameinstead ofgroup_idtests/conftest.py: Updated fixture to patch_resolve_groupin all tool modulestests/test_resolve_group.py: New test file with 8 tests covering exact match, case-insensitive match, no-match error, ambiguous error, and paginationtests/test_messages.py,tests/test_members.py,tests/test_groups.py: Updated all calls to usegroup_nameTest Plan
pytest tests/ -v)ruff formatandruff checkcleansend_message(group_name="Westside Stakeholders", ...)resolves correctlyReview Checklist
Related
claude-custom#160-- companion PreToolUse hook (depends on this deploying first)PR Review -- forgejo_admin/groupme-mcp#4
Acceptance Criteria Verification
group_name_resolve_group()inserver.py_resolve_group()paginates withper_page=100send_messageresponse includes group name + member names + countTest Expectations Verification
send_messagewith exact group name resolves correctlyadd_memberwith exact group name resolves correctly_resolve_grouphandles pagination (>10 groups)Code Quality
_resolve_groupin server.py, consumed by all tool modules_resolve_groupto avoid live API;test_resolve_group.pypatchesget_clientat server level to test the helper directlyFindings
No issues found.
VERDICT: APPROVED
PR #4 Review
DOMAIN REVIEW
Tech stack: Python 3.12+ / FastMCP / Pydantic / pytest / groupme-sdk. Ruff for linting. Hatchling build. No CI pipeline defined in-repo (no
.woodpecker.yml).Python / MCP quality:
_resolve_group()is a well-designed shared helper with proper pagination, case-insensitive matching, and clear error messages for zero-match and ambiguous-match cases. Good use ofValueErrorfor domain errors.get_group,send_message,add_member,remove_member,list_members) correctly resolve by name via the shared helper -- no DRY violation.list[dict],dict,str | None,Annotated)._resolve_grouppattern of resolve-then-use-id is clean: resolution happens once at the top of each tool function, the SDK call still uses the rawgroup_idunderneath.from __future__ import annotationsused consistently across all modules.try/except Exceptionand delegates to_error_response(), which produces structured JSON for bothGroupMeHTTPErrorand generic exceptions.Response enrichment on
send_message: The enriched response now returnsresolved_group.name,resolved_group.id,resolved_group.member_count, andresolved_group.member_namesalongside the message data. This is a good UX improvement for agent consumers -- it provides confirmation of which group received the message and who is in it.Pagination correctness: The pagination loop in
_resolve_group(lines 41-48 ofserver.py) correctly handles three termination conditions: empty batch, batch smaller than page size, and implicit continuation when batch is exactly 100. All three paths are tested.BLOCKERS
None.
_resolve_group(exact match, case-insensitive, no-match error, no-match lists available, ambiguous, pagination beyond page 1, stops on incomplete page, stops on empty page). All existing tool tests updated to usegroup_name. 28 total tests reported passing. Coverage is thorough across happy path, error handling, and edge cases.group_nameis a requiredstrparameter on all tools (enforced by Pydantic/FastMCP). The_resolve_grouphelper validates against live API data. No raw user input reaches SQL or shell.GroupMeClient()readsGROUPME_ACCESS_TOKENfrom environment (handled by the SDK)._resolve_grouphelper, no duplicated resolution logic across tools.NITS
conftest.py_make_resolve_groupignoresgroup_nameargument: The stub returned by_make_resolve_groupacceptsgroup_namebut always returns the sameFAKE_GROUPregardless. This is fine for current tests (they all use "Westside Stakeholders" which matches the fixture), but it means the tests do not verify that the correct group name is being passed from the tool to_resolve_group. A more precise stub could assert on the receivedgroup_name. Low priority -- thetest_resolve_group.pytests cover the real function behavior thoroughly.per_page=100magic number: The pagination page size100appears twice in_resolve_group(lines 42 and 46 ofserver.py). Could be extracted to a module-level constant (e.g.,_GROUPS_PAGE_SIZE = 100) for clarity. Very minor.get_groupdoes a double-fetch:get_groupcalls_resolve_group(which callslist_groupsto find the group), then callsget_client().get_group(group_id=...)to fetch it again. The first call already returned the group dict. For the other tools this pattern is necessary (they need thegroup_idto perform a different operation), butget_groupcould just return the resolved dict directly. The extra API call is not wrong --get_groupmay return fresher/more-detailed data -- but it is worth noting as a minor inefficiency.No CI pipeline: There is no
.woodpecker.ymlor equivalent CI config in this repo. Tests are run manually. Not a blocker for this PR, but worth tracking as a future item.SOP COMPLIANCE
3-name-based-group-resolutionreferences issue #3Closes #3claude-custom#160PROCESS OBSERVATIONS
.woodpecker.ymlwould improve DF and reduce change failure risk.claude-custom#160that depends on this deploying first. The sequencing is documented.test_resolve_group.pywith its ownmock_clientfixture) and integration-style tests for tool functions (sharedconftest.pyfixture). The conftest properly patches_resolve_groupin each tool module's namespace, preventing live API calls.VERDICT: APPROVED