Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit523ddad

feat(mcp): tool annotations (readOnlyHint/destructiveHint) on all tools — Anthropic directory requirement

feat(mcp): tool annotations (readOnlyHint/destructiveHint) on all tools — Anthropic directory requirement

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ccanty labs committed on July 14, 2026Parent: 9945e2c
3 files changed+1190523ddadfad42a1c24ee121d57d32259a761d5329
3 changed files+119−0
Modifiedsrc/__tests__/mcp.test.ts+34−0View fileUnifiedSplit
143143 }
144144});
145145
146describe("tool annotations (Anthropic connector-directory requirement)", () => {
147 // Every tool must carry MCP ToolAnnotations with an explicit boolean
148 // readOnlyHint (and destructiveHint) so directory review can classify it.
149 for (const handler of Object.values(tools)) {
150 it(`${handler.tool.name} — has annotations with boolean readOnlyHint`, () => {
151 expect(handler.tool.annotations).toBeDefined();
152 expect(typeof handler.tool.annotations?.readOnlyHint).toBe("boolean");
153 expect(typeof handler.tool.annotations?.destructiveHint).toBe("boolean");
154 });
155 }
156
157 it("gluecron_repo_search is annotated read-only", () => {
158 const t = tools["gluecron_repo_search"].tool;
159 expect(t.annotations?.readOnlyHint).toBe(true);
160 });
161
162 it("gluecron_delete_repo is annotated destructive", () => {
163 const t = tools["gluecron_delete_repo"].tool;
164 expect(t.annotations?.readOnlyHint).toBe(false);
165 expect(t.annotations?.destructiveHint).toBe(true);
166 });
167
168 it("tools/list passes annotations through to the wire", async () => {
169 const r = await routeMcpRequest(
170 { jsonrpc: "2.0", id: 8, method: "tools/list" },
171 { ctx, tools }
172 );
173 if (!r || "error" in r) throw new Error("expected success");
174 const result = r.result as { tools: Array<{ name: string; annotations?: any }> };
175 const search = result.tools.find((t) => t.name === "gluecron_repo_search");
176 expect(search?.annotations?.readOnlyHint).toBe(true);
177 });
178});
179
146180describe("HTTP route — GET /mcp discovery", () => {
147181 it("returns server info + tool count", async () => {
148182 const res = await app.request("/mcp");
Modifiedsrc/lib/mcp-tools-expanded.ts+55−0View fileUnifiedSplit
127127 name: "gluecron_fork_repo",
128128 description:
129129 "Fork a repository to the authenticated caller's namespace. Mirrors POST /:owner/:repo/fork. Returns {owner, repo, url}. Requires 'repo' scope.",
130 annotations: { title: "Fork repository", readOnlyHint: false, destructiveHint: false },
130131 inputSchema: {
131132 type: "object",
132133 properties: {
231232 name: "gluecron_delete_repo",
232233 description:
233234 "Permanently delete a repository row (git data on disk is left untouched). Owner-only. Requires 'admin' scope. Returns {deleted: true}.",
235 annotations: { title: "Delete repository", readOnlyHint: false, destructiveHint: true },
234236 inputSchema: {
235237 type: "object",
236238 properties: {
263265 name: "gluecron_update_repo",
264266 description:
265267 "Update repository description / visibility / default_branch. Owner-only. Requires 'repo' scope. Returns {ok: true}.",
268 // Destructive: overwrites prior settings (old values unrecoverable) and can flip visibility.
269 annotations: { title: "Update repository settings", readOnlyHint: false, destructiveHint: true },
266270 inputSchema: {
267271 type: "object",
268272 properties: {
305309 name: "gluecron_search_repos",
306310 description:
307311 "Full-text search of public repositories by name/description. Mirrors GET /api/v2/search/repos. Returns ranked rows.",
312 annotations: { title: "Search repositories (ranked)", readOnlyHint: true, destructiveHint: false },
308313 inputSchema: {
309314 type: "object",
310315 properties: {
365370 name: "gluecron_clone_url",
366371 description:
367372 "Return the authenticated HTTPS clone URL for a repo + a credential-helper hint. Use this instead of embedding tokens in URLs. Returns {url, hint}.",
373 annotations: { title: "Get clone URL", readOnlyHint: true, destructiveHint: false },
368374 inputSchema: {
369375 type: "object",
370376 properties: {
399405 name: "gluecron_label_issue",
400406 description:
401407 "Attach one or more labels to an issue. Labels are created if they don't yet exist on the repo. Requires 'repo' scope. Returns {labels}.",
408 annotations: { title: "Label issue", readOnlyHint: false, destructiveHint: false },
402409 inputSchema: {
403410 type: "object",
404411 properties: {
461468 name: "gluecron_unlabel_issue",
462469 description:
463470 "Detach a label from an issue. Idempotent. Requires 'repo' scope. Returns {removed: boolean}.",
471 // Removal is trivially reversible (re-attach the label) — not destructive.
472 annotations: { title: "Remove issue label", readOnlyHint: false, destructiveHint: false, idempotentHint: true },
464473 inputSchema: {
465474 type: "object",
466475 properties: {
502511 name: "gluecron_assign_issue",
503512 description:
504513 "Assign an issue to a user. Gluecron does not yet have a dedicated assignee table; assignment is modelled as an `assignee:<username>` label so it integrates with existing label tooling. Requires 'repo' scope.",
514 annotations: { title: "Assign issue", readOnlyHint: false, destructiveHint: false },
505515 inputSchema: {
506516 type: "object",
507517 properties: {
530540 name: "gluecron_search_issues",
531541 description:
532542 "Search issues by title/body keyword on a single repo, filtered by state. Returns ranked rows.",
543 annotations: { title: "Search issues", readOnlyHint: true, destructiveHint: false },
533544 inputSchema: {
534545 type: "object",
535546 properties: {
594605 name: "gluecron_request_changes",
595606 description:
596607 "Post a 'changes requested' AI-review comment on a PR. The comment is tagged with isAiReview=true so the gate-checker recognises it. Requires 'repo' scope.",
608 annotations: { title: "Request PR changes", readOnlyHint: false, destructiveHint: false },
597609 inputSchema: {
598610 type: "object",
599611 properties: {
634646 tool: {
635647 name: "gluecron_search_prs",
636648 description: "Search pull requests by title/body keyword on a repo.",
649 annotations: { title: "Search pull requests", readOnlyHint: true, destructiveHint: false },
637650 inputSchema: {
638651 type: "object",
639652 properties: {
700713 name: "gluecron_open_draft_pr",
701714 description:
702715 "Open a draft pull request. Same payload as gluecron_create_pr but forces is_draft=true. Useful for AI-in-progress PRs that shouldn't run mergeability checks yet.",
716 annotations: { title: "Open draft pull request", readOnlyHint: false, destructiveHint: false },
703717 inputSchema: {
704718 type: "object",
705719 properties: {
746760 name: "gluecron_generate_pr_description",
747761 description:
748762 "Generate an AI commit-message-style description for a diff. Uses src/lib/ai-commit-message.ts under the hood; gracefully degrades to a heuristic when ANTHROPIC_API_KEY is missing. Returns {subject, body}.",
763 // Pure compute over the supplied diff — persists nothing.
764 annotations: { title: "Generate PR description", readOnlyHint: true, destructiveHint: false },
749765 inputSchema: {
750766 type: "object",
751767 properties: {
778794 name: "gluecron_read_file",
779795 description:
780796 "Read a file from a repo at a given ref. Mirrors GET /api/v2/repos/:owner/:repo/contents/:path. Returns {path, size, content, encoding}.",
797 annotations: { title: "Read file", readOnlyHint: true, destructiveHint: false },
781798 inputSchema: {
782799 type: "object",
783800 properties: {
815832 name: "gluecron_write_file",
816833 description:
817834 "Create or update a file on a branch via git plumbing. Wraps `createOrUpdateFileOnBranch`. Pass `content` as a UTF-8 string OR `content_base64` for binary. Requires 'repo' scope.",
835 // Overwrites are recoverable from git history — not destructive.
836 annotations: { title: "Write file", readOnlyHint: false, destructiveHint: false },
818837 inputSchema: {
819838 type: "object",
820839 properties: {
897916 name: "gluecron_delete_file",
898917 description:
899918 "Delete a file from a branch via git plumbing. Requires the existing blob sha (optimistic concurrency) and 'repo' scope. Mirrors DELETE /api/v2/contents.",
919 annotations: { title: "Delete file", readOnlyHint: false, destructiveHint: true },
900920 inputSchema: {
901921 type: "object",
902922 properties: {
10281048 name: "gluecron_list_tree",
10291049 description:
10301050 "List directory contents at a ref. Optionally `recursive: true` returns the full file list. Mirrors GET /api/v2/repos/.../tree/:ref.",
1051 annotations: { title: "List tree", readOnlyHint: true, destructiveHint: false },
10311052 inputSchema: {
10321053 type: "object",
10331054 properties: {
10611082 tool: {
10621083 name: "gluecron_get_commit",
10631084 description: "Fetch a single commit's metadata by SHA. Mirrors GET /api/v2/repos/.../commits/:sha.",
1085 annotations: { title: "Get commit", readOnlyHint: true, destructiveHint: false },
10641086 inputSchema: {
10651087 type: "object",
10661088 properties: {
10871109 name: "gluecron_create_branch",
10881110 description:
10891111 "Create a new branch ref pointing at an existing sha. Mirrors POST /api/v2/repos/.../git/refs. Requires 'repo' scope. Returns {ref, sha}.",
1112 annotations: { title: "Create branch", readOnlyHint: false, destructiveHint: false },
10901113 inputSchema: {
10911114 type: "object",
10921115 properties: {
11291152 name: "gluecron_atomic_multi_file_commit",
11301153 description:
11311154 "Apply a set of file writes + deletes as a single atomic commit on a branch (creates the branch if it doesn't exist). The killer agent tool: blob/tree/commit/ref-update sequence. Each change is `{path, content?, content_base64?, deleted?}`. Requires 'repo' scope.",
1155 // Commits (incl. deletes) preserve prior state in git history — not destructive.
1156 annotations: { title: "Atomic multi-file commit", readOnlyHint: false, destructiveHint: false },
11321157 inputSchema: {
11331158 type: "object",
11341159 properties: {
13381363 name: "gluecron_ship_spec",
13391364 description:
13401365 "Drop a spec file in .gluecron/specs/ with status: ready so the autopilot picks it up. Wraps voice-to-pr.shipAsSpec — handle both voice + manual specs. Requires 'repo' scope.",
1366 annotations: { title: "Ship spec", readOnlyHint: false, destructiveHint: false },
13411367 inputSchema: {
13421368 type: "object",
13431369 properties: {
13781404 name: "gluecron_voice_to_pr",
13791405 description:
13801406 "Interpret a free-form voice transcript and either ship it as a spec or create an issue (caller picks via `as`). Wraps src/lib/voice-to-pr.ts. Requires 'repo' scope.",
1407 annotations: { title: "Voice to PR", readOnlyHint: false, destructiveHint: false },
13811408 inputSchema: {
13821409 type: "object",
13831410 properties: {
14441471 name: "gluecron_refactor_across_repos",
14451472 description:
14461473 "Plan + execute a refactor that spans multiple repos owned by the caller. Wraps src/lib/multi-repo-refactor.ts. `dry_run: true` returns the plan only. Requires 'repo' scope.",
1474 // Creates branches/PRs across repos; prior state stays in git history — not destructive.
1475 annotations: { title: "Refactor across repos", readOnlyHint: false, destructiveHint: false },
14471476 inputSchema: {
14481477 type: "object",
14491478 properties: {
14901519 name: "gluecron_explain_repo",
14911520 description:
14921521 "Return the cached AI 'explain this codebase' Markdown for a repo. Pure read — never triggers a new generation (use the web UI for that).",
1522 annotations: { title: "Explain repository (cached)", readOnlyHint: true, destructiveHint: false },
14931523 inputSchema: {
14941524 type: "object",
14951525 properties: {
15291559 name: "gluecron_chat_with_repo",
15301560 description:
15311561 "Start a new chat with a repo: creates a chat row, sends the first user message, streams + persists the assistant reply. Returns {chat_id, reply}. Requires authentication.",
1562 // Persists chat rows — a write, though a benign one.
1563 annotations: { title: "Chat with repository", readOnlyHint: false, destructiveHint: false },
15321564 inputSchema: {
15331565 type: "object",
15341566 properties: {
15741606 name: "gluecron_chat_continue",
15751607 description:
15761608 "Send another message to an existing repo chat. Returns the assistant's reply.",
1609 annotations: { title: "Continue repository chat", readOnlyHint: false, destructiveHint: false },
15771610 inputSchema: {
15781611 type: "object",
15791612 properties: {
16101643 name: "gluecron_generate_tests",
16111644 description:
16121645 "Generate tests for a PR via Claude. Wraps src/lib/ai-test-generator.generateTestsForPr. Mode 'follow-up-pr' opens a new PR; 'append-commit' commits onto the head branch. Requires 'repo' scope.",
1646 annotations: { title: "Generate tests for PR", readOnlyHint: false, destructiveHint: false },
16131647 inputSchema: {
16141648 type: "object",
16151649 properties: {
16451679 name: "gluecron_generate_commit_message",
16461680 description:
16471681 "Generate a commit message for a diff. Same engine as gluecron_generate_pr_description but explicit for the commit-message use case.",
1682 annotations: { title: "Generate commit message", readOnlyHint: true, destructiveHint: false },
16481683 inputSchema: {
16491684 type: "object",
16501685 properties: {
16691704 name: "gluecron_generate_release_notes",
16701705 description:
16711706 "Generate release notes between two tags. Wraps src/lib/ai-release-notes.generateReleaseNotes. Returns the rendered Markdown + section data.",
1707 // generateReleaseNotes only computes + returns; persistence lives in the separate backfill task.
1708 annotations: { title: "Generate release notes", readOnlyHint: true, destructiveHint: false },
16721709 inputSchema: {
16731710 type: "object",
16741711 properties: {
17001737 name: "gluecron_propose_migration",
17011738 description:
17021739 "Propose a dependency-upgrade PR. Wraps src/lib/migration-assistant.proposeMajorMigration. Requires 'repo' scope.",
1740 annotations: { title: "Propose migration PR", readOnlyHint: false, destructiveHint: false },
17031741 inputSchema: {
17041742 type: "object",
17051743 properties: {
17481786 name: "gluecron_propose_doc_update",
17491787 description:
17501788 "Manual trigger for the AI doc-update flow: scans tracked sections on the default branch and opens a PR rewriting stale prose. Requires 'repo' scope.",
1789 annotations: { title: "Propose doc update PR", readOnlyHint: false, destructiveHint: false },
17511790 inputSchema: {
17521791 type: "object",
17531792 properties: {
17771816 name: "gluecron_trigger_workflow",
17781817 description:
17791818 "Dispatch a workflow_dispatch run. Mirrors POST /api/v2/repos/.../actions/workflows/:filename/dispatches. Requires 'repo' scope.",
1819 annotations: { title: "Trigger workflow", readOnlyHint: false, destructiveHint: false },
17801820 inputSchema: {
17811821 type: "object",
17821822 properties: {
19001940 tool: {
19011941 name: "gluecron_get_workflow_run",
19021942 description: "Fetch a workflow run's metadata + status. Mirrors GET /api/v2/repos/.../actions/runs/:id.",
1943 annotations: { title: "Get workflow run", readOnlyHint: true, destructiveHint: false },
19031944 inputSchema: {
19041945 type: "object",
19051946 properties: {
19321973 name: "gluecron_get_workflow_logs",
19331974 description:
19341975 "Return concatenated per-job logs for a workflow run, plus per-job metadata. JSON-friendly companion to the ZIP-download endpoint.",
1976 annotations: { title: "Get workflow logs", readOnlyHint: true, destructiveHint: false },
19351977 inputSchema: {
19361978 type: "object",
19371979 properties: {
19792021 tool: {
19802022 name: "gluecron_cancel_workflow_run",
19812023 description: "Cancel a queued/running workflow run. Requires 'repo' scope.",
2024 // Cancellation kills in-flight work that cannot be resumed — destructive.
2025 annotations: { title: "Cancel workflow run", readOnlyHint: false, destructiveHint: true },
19822026 inputSchema: {
19832027 type: "object",
19842028 properties: {
20242068 name: "gluecron_get_preview_url",
20252069 description:
20262070 "Return the branch-preview URL + status for a (repo, branch) pair. Wraps branch-previews.getPreviewForBranch.",
2071 annotations: { title: "Get preview URL", readOnlyHint: true, destructiveHint: false },
20272072 inputSchema: {
20282073 type: "object",
20292074 properties: {
20652110 name: "gluecron_provision_pr_sandbox",
20662111 description:
20672112 "Provision (or re-provision) a sandbox for a PR. Wraps pr-sandbox.provisionSandbox. Requires 'repo' scope.",
2113 // Re-provision replaces an ephemeral sandbox only — not destructive.
2114 annotations: { title: "Provision PR sandbox", readOnlyHint: false, destructiveHint: false },
20682115 inputSchema: {
20692116 type: "object",
20702117 properties: {
20992146 name: "gluecron_create_agent_session",
21002147 description:
21012148 "Mint a new agent-multiplayer session. Returns the plaintext `token` exactly once (store it). Requires 'admin' scope.",
2149 annotations: { title: "Create agent session", readOnlyHint: false, destructiveHint: false },
21022150 inputSchema: {
21032151 type: "object",
21042152 properties: {
21412189 name: "gluecron_acquire_lease",
21422190 description:
21432191 "Grab an exclusive lease on a target (e.g. a PR or branch) for an agent session. Returns null when another agent holds an active lease.",
2192 annotations: { title: "Acquire lease", readOnlyHint: false, destructiveHint: false },
21442193 inputSchema: {
21452194 type: "object",
21462195 properties: {
21702219 tool: {
21712220 name: "gluecron_release_lease",
21722221 description: "Release a lease by id. Idempotent. Returns {released}.",
2222 annotations: { title: "Release lease", readOnlyHint: false, destructiveHint: false, idempotentHint: true },
21732223 inputSchema: {
21742224 type: "object",
21752225 properties: { lease_id: { type: "string" } },
21892239 tool: {
21902240 name: "gluecron_get_agent_budget",
21912241 description: "Return spent / cap / remaining cents for an agent session.",
2242 annotations: { title: "Get agent budget", readOnlyHint: true, destructiveHint: false },
21922243 inputSchema: {
21932244 type: "object",
21942245 properties: { agent_session_id: { type: "string" } },
22122263 name: "gluecron_semantic_search",
22132264 description:
22142265 "Query the per-repo vector index (Voyage embeddings when configured, hash fallback otherwise). Wraps src/lib/semantic-search.searchRepository.",
2266 annotations: { title: "Semantic search", readOnlyHint: true, destructiveHint: false },
22152267 inputSchema: {
22162268 type: "object",
22172269 properties: {
22442296 name: "gluecron_find_symbol",
22452297 description:
22462298 "Find definitions of a symbol by name within a repo. Wraps src/lib/symbols.findDefinitions.",
2299 annotations: { title: "Find symbol", readOnlyHint: true, destructiveHint: false },
22472300 inputSchema: {
22482301 type: "object",
22492302 properties: {
22742327 name: "gluecron_pr_status_summary",
22752328 description:
22762329 "Compute a one-shot status summary for a PR: state, risk score, AI-review verdicts (trio), gate signals. Read-only.",
2330 annotations: { title: "PR status summary", readOnlyHint: true, destructiveHint: false },
22772331 inputSchema: {
22782332 type: "object",
22792333 properties: {
23212375 name: "gluecron_ai_cost_summary",
23222376 description:
23232377 "Return AI spend rollups. Scope by one of: user_id (self), repo {owner,repo}, or agent_session_id. Defaults to caller's user spend.",
2378 annotations: { title: "AI cost summary", readOnlyHint: true, destructiveHint: false },
23242379 inputSchema: {
23252380 type: "object",
23262381 properties: {
Modifiedsrc/lib/mcp-tools.ts+30−0View fileUnifiedSplit
5151} from "./pr-risk";
5252import { expandedTools } from "./mcp-tools-expanded";
5353
54/**
55 * MCP spec ToolAnnotations — behavioural hints surfaced to clients via
56 * tools/list. Required for Anthropic connector-directory review. These are
57 * hints only; they never gate execution server-side.
58 */
59export type McpToolAnnotations = {
60 title?: string;
61 readOnlyHint?: boolean;
62 destructiveHint?: boolean;
63 idempotentHint?: boolean;
64 openWorldHint?: boolean;
65};
66
5467export type McpTool = {
5568 name: string;
5669 description: string;
70 annotations?: McpToolAnnotations;
5771 inputSchema: {
5872 type: "object";
5973 properties: Record<string, { type: string; description?: string }>;
101115 name: "gluecron_repo_search",
102116 description:
103117 "Search public Gluecron repositories by keyword. Matches against name + description. Returns up to 20 results.",
118 annotations: { title: "Search repositories", readOnlyHint: true, destructiveHint: false },
104119 inputSchema: {
105120 type: "object",
106121 properties: {
158173 name: "gluecron_repo_read_file",
159174 description:
160175 "Read a single file from a public repository at a given ref (branch / tag / commit). Returns the text content (binary files rejected).",
176 annotations: { title: "Read repository file", readOnlyHint: true, destructiveHint: false },
161177 inputSchema: {
162178 type: "object",
163179 properties: {
218234 name: "gluecron_repo_list_issues",
219235 description:
220236 "List open issues for a public repository. Returns up to 50 ordered by most-recent.",
237 annotations: { title: "List open issues", readOnlyHint: true, destructiveHint: false },
221238 inputSchema: {
222239 type: "object",
223240 properties: {
281298 name: "gluecron_repo_explain_codebase",
282299 description:
283300 "Return the cached AI 'explain this codebase' Markdown for a public repo (most recent commit). Returns null when no cached explanation exists yet.",
301 annotations: { title: "Explain codebase (cached)", readOnlyHint: true, destructiveHint: false },
284302 inputSchema: {
285303 type: "object",
286304 properties: {
336354 name: "gluecron_repo_health",
337355 description:
338356 "Compute the current health report for a public repo: overall score (0-100), letter grade, per-category breakdown (security/testing/complexity/dependencies/documentation/activity), and a list of insights to fix next. Backed by computeHealthScore in src/lib/intelligence.ts.",
357 annotations: { title: "Repository health report", readOnlyHint: true, destructiveHint: false },
339358 inputSchema: {
340359 type: "object",
341360 properties: {
532551 name: "gluecron_create_issue",
533552 description:
534553 "Create a new issue on a Gluecron repository. Requires authenticated caller with write access on the target repo. Returns {number, url}.",
554 annotations: { title: "Create issue", readOnlyHint: false, destructiveHint: false },
535555 inputSchema: {
536556 type: "object",
537557 properties: {
611631 name: "gluecron_comment_issue",
612632 description:
613633 "Add a comment to an existing issue. Requires authenticated caller with write access. Returns {commentId}.",
634 annotations: { title: "Comment on issue", readOnlyHint: false, destructiveHint: false },
614635 inputSchema: {
615636 type: "object",
616637 properties: {
684705 name: "gluecron_close_issue",
685706 description:
686707 "Close an open issue. Requires authenticated caller with write access. Idempotent — closing an already-closed issue is a no-op. Returns {state}.",
708 annotations: { title: "Close issue", readOnlyHint: false, destructiveHint: true, idempotentHint: true },
687709 inputSchema: {
688710 type: "object",
689711 properties: {
737759 name: "gluecron_reopen_issue",
738760 description:
739761 "Reopen a previously closed issue. Requires authenticated caller with write access. Idempotent. Returns {state}.",
762 // Reopen merely undoes a close (trivially reversible) — not destructive.
763 annotations: { title: "Reopen issue", readOnlyHint: false, destructiveHint: false, idempotentHint: true },
740764 inputSchema: {
741765 type: "object",
742766 properties: {
790814 name: "gluecron_create_pr",
791815 description:
792816 "Open a new pull request. `head_branch` is required; `base_branch` defaults to the repo default branch. Requires authenticated caller with write access. Returns {number, url}.",
817 annotations: { title: "Create pull request", readOnlyHint: false, destructiveHint: false },
793818 inputSchema: {
794819 type: "object",
795820 properties: {
871896 name: "gluecron_get_pr",
872897 description:
873898 "Fetch the full detail record of a pull request (title, body, state, branches, draft, author, timestamps). Authenticated callers only (the read tool surface still works anonymously).",
899 annotations: { title: "Get pull request", readOnlyHint: true, destructiveHint: false },
874900 inputSchema: {
875901 type: "object",
876902 properties: {
940966 name: "gluecron_list_prs",
941967 description:
942968 "List pull requests on a repo, filtered by state (open|closed|merged|all). Authenticated callers only. Returns up to 50 summary rows.",
969 annotations: { title: "List pull requests", readOnlyHint: true, destructiveHint: false },
943970 inputSchema: {
944971 type: "object",
945972 properties: {
10201047 name: "gluecron_comment_pr",
10211048 description:
10221049 "Add a comment to a pull request. Requires authenticated caller with write access. Returns {commentId}.",
1050 annotations: { title: "Comment on pull request", readOnlyHint: false, destructiveHint: false },
10231051 inputSchema: {
10241052 type: "object",
10251053 properties: {
10921120 name: "gluecron_merge_pr",
10931121 description:
10941122 "Merge an open PR. Enforces the same checks as the HTTP merge flow: not a draft, head SHA resolves, GateTest+AI-review hard gates pass, branch-protection rules satisfied. M3: soft-blocks when the pre-merge risk score is `critical` unless `confirm_high_risk: true` is passed. Returns {merged, sha?, reason?, riskScore?}.",
1123 annotations: { title: "Merge pull request", readOnlyHint: false, destructiveHint: true },
10951124 inputSchema: {
10961125 type: "object",
10971126 properties: {
13621391 name: "gluecron_close_pr",
13631392 description:
13641393 "Close an open pull request without merging. Requires authenticated caller with write access. Idempotent. Returns {state}.",
1394 annotations: { title: "Close pull request", readOnlyHint: false, destructiveHint: true, idempotentHint: true },
13651395 inputSchema: {
13661396 type: "object",
13671397 properties: {
13681398