Skip to content

feat(mcp): add unclaim_issue, queue, issue-list, pr-fix, and groomer tools#644

Merged
joryirving merged 2 commits into
mainfrom
feat/mcp-unclaim-tool
Jul 21, 2026
Merged

feat(mcp): add unclaim_issue, queue, issue-list, pr-fix, and groomer tools#644
joryirving merged 2 commits into
mainfrom
feat/mcp-unclaim-tool

Conversation

@joryirving

@joryirving joryirving commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • unclaim_issue — exposes /api/issues/unclaim: agent label off, lease released (AgentWork on operator path), in-progressready, lane preserved, audit row. Same DISPATCH_AGENT_NAME fallback contract as claim_issue.
  • get_queue — the agent's ranked queue (PR-fix items first), lane-filterable.
  • list_issues — filtered issue queries (repo/status/lane/agent/priority, optional closed).
  • list_pr_fixes + mark_pr_fix — the PR-fix worker contract (list queued items; mark fixed/blocked/stale/ignored with note).
  • run_groomer — trigger a hosted groomer run immediately, optionally targeting one issue.

All thin wrappers over existing authorized API routes — no route changes. Together with the existing six tools this covers the full agent workspace contract, so agent docs no longer need raw-curl instructions.

Why

Unclaiming from an agent session previously required raw label edits that miss the lease release and status transition (see the 17-issue strand reconciled by hand 2026-07-21; context foreman-dispatch-bridge#46). The queue/pr-fix/groomer verbs are what agent workspace docs currently teach via curl.

Verification

  • vitest run: full suite green, incl. new handler + client tests (URL/body assertions, env-fallback, error paths).
  • tsc --noEmit + eslint clean.

Jory Irving added 2 commits July 21, 2026 11:50
The unclaim operation existed only as the HTTP route
(/api/issues/unclaim); the MCP server exposed claim/status/sync but no
release, so unclaiming from an agent session meant raw gh label surgery
that misses the lease release and the in-progress -> ready transition.

Add unclaimIssue to mc-client (resolve -> POST /api/issues/unclaim,
mirroring claimIssue) and register unclaim_issue with the same
DISPATCH_AGENT_NAME fallback contract as claim_issue. The route already
owns the semantics: agent label off, lease released (AgentWork on the
operator path), status/in-progress -> status/ready, lane preserved,
audit row written.

AI assistance: authored with Claude Code.

Signed-off-by: Jory Irving <jory.irving@users.noreply.github.com>
Round out the MCP surface with the read/act verbs agents currently reach
via raw curl per the workspace contracts:

- get_queue: the agent's ranked queue (pr-fix first), lane-filterable,
  DISPATCH_AGENT_NAME fallback like claim_issue
- list_issues: filtered issue queries (repo/status/lane/agent/priority)
- list_pr_fixes + mark_pr_fix: the PR-fix worker contract
  (list queued items, mark fixed/blocked/stale/ignored)
- run_groomer: trigger a hosted groomer run now (optionally targeting a
  specific issue) instead of waiting for the cron

All thin wrappers over existing authorized API routes; no route changes.

AI assistance: authored with Claude Code.

Signed-off-by: Jory Irving <jory.irving@users.noreply.github.com>
@joryirving joryirving changed the title feat(mcp): add unclaim_issue tool feat(mcp): add unclaim_issue, queue, issue-list, pr-fix, and groomer tools Jul 21, 2026

@its-saffron its-saffron Bot 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.

AI Automated Review

Full PR review.

Analysis engine: MiniMax-M2.7@https://litellm.jory.dev/v1 (anthropic) — escalated (fast_low_confidence)

Recommendation: Approve

This PR adds six new MCP tools (unclaim_issue, get_queue, list_issues, list_pr_fixes, mark_pr_fix, run_groomer) as thin wrappers over existing Dispatch API routes. The implementation is consistent with established codebase patterns and passes all CI checks.


Change-by-Change Findings

src/lib/mc-client.ts (+101 lines)

Adds the following client functions:

  • unclaimIssue — resolves the issue CUID first, then POSTs to /api/issues/unclaim with the same body shape as claimIssue (issueId, repoFullName, issueNumber, agentName). Returns UnclaimIssueResult { success, labels }.
  • getQueue — GET to /api/agents/{agentName}/queue with optional lane, exclude_decomposed, includeClaimed, includeRenovate query params.
  • listIssues — GET to /api/issues with optional repo, status, lane, agent, priority, includeClosed query params.
  • listPrFixes — GET to /api/pr-fix-queue/queued with optional lane, include_blocked query params.
  • markPrFix — POST to /api/pr-fix-queue/mark with { repo, pr, status, note }.
  • runGroomer — POST to /api/groomer/run with optional { repoFullName, issueNumber }.

Observations:

  • includeClosed stays as camelCase when building the query string (line 277), while includeBlocked → include_blocked is snake_cased (line 291). This matches the underlying API routes (/api/issues vs /api/pr-fix-queue/queued) and is internally consistent.
  • Return types use unknown[] for list operations. This is looser than typed interfaces like UnclaimIssueResult, but consistent with the PR scope of thin API wrappers, and matches the existing getClaimableLanes pattern in this module.

src/mcp/server.ts (+203 lines)

Adds six MCP tool handlers and registers them via server.registerTool():

  • unclaimIssueHandler — validates agentName via resolveAgentName, then delegates to unclaimIssue. Error message follows the established "Do not use generic identities like 'Dispatch MCP'" convention.
  • getQueueHandler — same resolveAgentName validation and error pattern.
  • listIssuesHandler — no auth/env dependency; passes filter args directly.
  • listPrFixesHandler — no auth/env dependency.
  • markPrFixHandler — validates required repo, pr, status via Zod schema; note is optional.
  • runGroomerHandler — optional targeting via Zod schema; POSTs empty body when no target given.

Observations:

  • The agentName validation pattern (require explicit arg or fall back to DISPATCH_AGENT_NAME) is correctly applied to unclaimIssueHandler and getQueueHandler. This is consistent with claimIssueHandler.
  • The "Do not use generic identities like 'Dispatch MCP'" error suffix is present in both new handlers and matches the existing convention.
  • markPrFixHandler accepts status: string without pre-validating against fixed|blocked|stale|ignored; the API validates this. This matches the pattern used by setIssueStatus which also accepts status: string and relies on API-level validation.

src/lib/mc-client.test.ts (+109 lines)

  • unclaimIssue: mocks resolve → unclaim fetch chain; asserts POST body; tests "not found" rejection.
  • getQueue: asserts URL contains /api/agents/test-agent/queue and query params.
  • listIssues: asserts URL building with repo, status, lane params.
  • listPrFixes: asserts URL with lane and include_blocked params.
  • markPrFix: asserts POST body with all fields including optional note.
  • runGroomer: asserts empty body default and targeted body variant.

src/mcp/server.test.ts (+155 lines)

  • unclaimIssueHandler: mocks resolve → unclaim; checks isError is undefined on success; asserts status/ready in returned labels; tests "agentName required" error; tests API rejection surfaces as isError: true.
  • getQueueHandler: tests env var fallback (DISPATCH_AGENT_NAME); tests error when both arg and env are missing.
  • listIssuesHandler: tests JSON return path.
  • listPrFixesHandler: tests includeBlocked pass-through.
  • markPrFixHandler: tests 404 error surfaces as isError: true.
  • runGroomerHandler: tests candidateNumber in response.

Standards Compliance

The PR conforms to the following repository conventions:

Convention Status
DISPATCH_AGENT_NAME fallback for agent identity (AGENTS.md §Agent Workflow Contract) ✅ Consistent with claimIssueHandler
No generic agent names in error messages ✅ Both new handlers include the "Do not use generic identities like 'Dispatch MCP'" message
Thin wrappers over existing authorized API routes ✅ Confirmed — no new routes added
Bearer auth (DISPATCH_AGENT_TOKEN) for agent API calls ✅ All functions use mcJson which sets bearer auth header
Audit trail for mutation operations unclaimIssue hits /api/issues/unclaim (already audited), markPrFix hits /api/pr-fix-queue/mark (already audited)
Label pattern agent/* for ownership ✅ Tool descriptions reference agent label semantics correctly
Test coverage for error paths ✅ Covers: missing agentName, API rejection, not-found, env fallback

Tool Harness Findings

The gh_api call for repos/misospace/miso-gallery/compare/... returned 404 — this is unrelated to the PR (the repo misospace/miso-gallery is not involved in this change). All other tool harness calls succeeded.


Unknowns / Needs Verification

None. The implementation is fully visible in the diff, tests are comprehensive, CI passes, and the PR description provides clear motivation traced to real-world context (the 17-issue strand from 2026-07-21, referenced in foreman-dispatch-bridge#46). No blocking gaps remain.

@joryirving
joryirving merged commit c86474c into main Jul 21, 2026
6 checks passed
@joryirving
joryirving deleted the feat/mcp-unclaim-tool branch July 21, 2026 18:07
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.

1 participant