Skip to main content
Glama

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

TableJSON Schema
NameRequiredDescriptionDefault
issueUrlYesFull GitHub issue URL to claim
messageNoCustom 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(),
    });
  • 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+$/;
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations indicate non-read-only and non-destructive. Description adds that it posts a comment, but does not elaborate on side effects, permissions, or edge cases.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence of 15 words, front-loading the purpose without extraneous information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool, the description is adequate but lacks details on return value, failure modes, or prerequisites. Gaps remain compared to ideal completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with clear descriptions for both parameters. The tool description adds little beyond the schema, so baseline 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('claim'), the resource ('GitHub issue'), and the method ('posting a comment'). It distinguishes from siblings like 'comments' and 'post' by specifying intent.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for claiming issues but does not explicitly state when to use or not use this tool, nor mention alternatives among siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/costajohnt/oss-autopilot'

If you have feedback or need assistance with the MCP directory API, please join our Discord server