claim
Claim a GitHub issue by posting a comment expressing your intent to work on it.
Instructions
Claim a GitHub issue by posting a comment expressing intent to work on it.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| issueUrl | Yes | Full GitHub issue URL to claim | |
| message | No | Custom claim message. If omitted, a default message is used. |
Implementation Reference
- The core handler for the 'claim' tool. Posts a comment on a GitHub issue expressing intent to work on it, fetches issue metadata (title, labels), tracks the issue locally with status 'claimed', and checkpoints state to Gist persistence.
export async function runClaim(options: ClaimOptions): Promise<ClaimOutput> { validateUrl(options.issueUrl); validateGitHubUrl(options.issueUrl, ISSUE_URL_PATTERN, 'issue'); const token = requireGitHubToken(); // Default claim message or custom const message = options.message || "Hi! I'd like to work on this issue. Could you assign it to me?"; validateMessage(message); // Parse URL const parsed = parseGitHubUrl(options.issueUrl); if (!parsed || parsed.type !== 'issues') { throw new ValidationError('Invalid issue URL format (must be an issue, not a PR)'); } const { owner, repo, number } = parsed; const octokit = getOctokit(token); const { data: comment } = await octokit.issues.createComment({ owner, repo, issue_number: number, body: message, }); // Fetch the real issue title + labels so the tracked entry has useful metadata // rather than a permanent "(claimed)" placeholder that never gets backfilled // (#1056 M24). Best-effort: if the fetch fails, fall back to the placeholder // so state still records the claim. let issueTitle = '(claimed)'; let issueLabels: string[] = []; let issueCreatedAt = new Date().toISOString(); try { const { data: issue } = await octokit.issues.get({ owner, repo, issue_number: number }); if (issue.title) issueTitle = issue.title; issueLabels = (issue.labels ?? []) .map((l) => (typeof l === 'string' ? l : (l.name ?? ''))) .filter((name): name is string => Boolean(name)); if (issue.created_at) issueCreatedAt = issue.created_at; } catch (error) { warn( MODULE, `Claimed ${options.issueUrl} but failed to enrich issue metadata (title/labels): ${error instanceof Error ? error.message : error}`, ); } // Add to tracked issues — non-fatal if state save fails (comment already posted) try { const stateManager = getStateManager(); stateManager.addIssue({ id: number, url: options.issueUrl, repo: `${owner}/${repo}`, number, title: issueTitle, status: 'claimed', labels: issueLabels, createdAt: issueCreatedAt, updatedAt: new Date().toISOString(), vetted: false, }); // Push state to Gist if in Gist mode. Best-effort — logs on failure // rather than silently swallowing, so operators see the degraded-sync // signal (#1036 audit H1). await maybeCheckpoint(stateManager, MODULE); } catch (error) { // Structured warning instead of bare console.error so the breadcrumb shows // up in the plugin's log pipeline (#1056 M24). warn( MODULE, `Comment posted on ${options.issueUrl} but failed to save to local state: ${error instanceof Error ? error.message : error}`, ); } return { commentUrl: comment.html_url, issueUrl: options.issueUrl, }; } - Input type definition for claim: issueUrl (string) and optional message (string).
interface ClaimOptions { issueUrl: string; message?: string; } - Zod schema and TypeScript interface for ClaimOutput: commentUrl and issueUrl strings.
export const ClaimOutputSchema = z.object({ commentUrl: z.string(), issueUrl: z.string(), }); - packages/mcp-server/src/tools.ts:260-273 (registration)MCP tool registration for 'claim'. Registers the tool with description, input schema (issueUrl + optional message), destructiveHint annotation, and wraps runClaim.
// 10. claim — Claim an issue (#1053: destructive; posts under user's identity) server.registerTool( 'claim', { description: "Claim a GitHub issue by posting a comment expressing intent to work on it. WARNING: posts a public comment under the authenticated user's identity. Irreversible. Do not call without explicit user confirmation.", inputSchema: { issueUrl: githubIssueUrlSchema.describe('Full GitHub issue URL to claim'), message: z.string().optional().describe('Custom claim message. If omitted, a default message is used.'), }, annotations: { readOnlyHint: false, destructiveHint: true }, }, wrapTool(runClaim), ); - ISSUE_URL_PATTERN regex and validateGitHubUrl helper used by runClaim to validate the issue URL before posting the comment.
export const ISSUE_URL_PATTERN = /^https:\/\/github\.com\/[^/]+\/[^/]+\/issues\/\d+$/; /** Matches GitHub issue or PR URLs: /issues/123 or /pull/123 */ export const ISSUE_OR_PR_URL_PATTERN = /^https:\/\/github\.com\/[^/]+\/[^/]+\/(issues|pull)\/\d+$/;