Public: anyone can read this; the crew can change it.

MCP `merge`: a person lands a pull from any MCP client, as the pull page's Merge button does #7253

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

This adds a new MCP tool, `merge`, so a person can land a pull from any MCP client, the same way the Merge button on the pull page does.

shipped production is serving it deploy 7b21267

opened this

This adds a new MCP tool, merge, so a person can land a pull from any MCP client, the same way the Merge button on the pull page does.

  • Arguments (src/http/mcp/tools/merge.ts): { repo, number, strategy?: "merge"|"squash", dryRun? }. It writes, and only plans unless dryRun is false.
  • The plan is a read, so a read-only token can ask for it. It gives the pull's head, base, state, mergeable, conflicts and check, using the same reads as the pull tool.
  • Landing (dryRun: false) calls mergePull the same way POST …/pulls/:number/merge does. Its refusals keep their codes: conflict, review_required, checks_required, already_merged, forbidden for someone who cannot write, and mirrored. The messages are the pull page's own (mergeFailure). The tool only adds checks in front of mergePull and never loosens its rules.
  • Agents are refused. An agent's token gets forbidden with "a person merges", before anything is read, whether or not it is a dry run. That holds even for an agent on the repository's crew that can write to it.
  • docs/MCP.md and the tool index were regenerated with bun scripts/mcp-tools.ts.

The steward's note prefers adding an action to an existing tool, but neither candidate fits. review has no plan-first default, and pull doesn't write. The issue asked for a separate tool, and the new one still fits under the tools/list size limits (test/mcp-size.test.ts passes).

Tests: test/mcp-merge.test.ts covers:

  • an agent refused, both planning and landing;
  • a plan that changes nothing, with a read-only token allowed to plan but not land;
  • a person's read-write token merging a clean pull, so the base's tip becomes the returned mergeOid;
  • a conflict refused with nothing landed;
  • a person who cannot write refused;
  • bad input refused.

These and the other MCP tests I ran pass, and bun x tsc --noEmit is clean. test/mcp.test.ts had a fixed list of tool names; I added merge to it. That test was wrong for the new tool, not the code.

To try it: call merge with { repo, number } to see the plan, then again with dryRun: false to land the pull.

🤖 Generated with Claude Code

Closes #6243

commented

This adds a new MCP tool, merge, so a person can land a pull from any MCP client, the same way the Merge button on the pull page does.

  • Arguments (src/http/mcp/tools/merge.ts): { repo, number, strategy?: "merge"|"squash", dryRun? }. It writes, and only plans unless dryRun is false.
  • The plan is a read, so a read-only token can ask for it. It gives the pull's head, base, state, mergeable, conflicts and check, using the same reads as the pull tool.
  • Landing (dryRun: false) calls mergePull the same way POST …/pulls/:number/merge does. Its refusals keep their codes: conflict, review_required, checks_required, already_merged, forbidden for someone who cannot write, and mirrored. The messages are the pull page's own (mergeFailure). The tool only adds checks in front of mergePull and never loosens its rules.
  • Agents are refused. An agent's token gets forbidden with "a person merges", before anything is read, whether or not it is a dry run. That holds even for an agent on the repository's crew that can write to it.
  • docs/MCP.md and the tool index were regenerated with bun scripts/mcp-tools.ts.

The steward's note prefers adding an action to an existing tool, but neither candidate fits. review has no plan-first default, and pull doesn't write. The issue asked for a separate tool, and the new one still fits under the tools/list size limits (test/mcp-size.test.ts passes).

Tests: test/mcp-merge.test.ts covers:

  • an agent refused, both planning and landing;
  • a plan that changes nothing, with a read-only token allowed to plan but not land;
  • a person's read-write token merging a clean pull, so the base's tip becomes the returned mergeOid;
  • a conflict refused with nothing landed;
  • a person who cannot write refused;
  • bad input refused.

These and the other MCP tests I ran pass, and bun x tsc --noEmit is clean. test/mcp.test.ts had a fixed list of tool names; I added merge to it. That test was wrong for the new tool, not the code.

To try it: call merge with { repo, number } to see the plan, then again with dryRun: false to land the pull.

🤖 Generated with Claude Code

Approved

Approved. This is a second door to mergePull and no looser than the first.

  • Security. Identity comes from the bearer token through the MCP layer. An agent is refused before anything is read, and mergePull refuses it again. requireWriter checks who may write, and mirrors are refused. The rw scope is enforced by the shared dryByDefault/writes gate, and the test shows an ro token can plan but not land. strategy and number are validated at the boundary, and a sealed pull answers no_such_pull through mayView. There is no shell or SQL built from input, and no secret.
  • Same as the page and the API. It calls mergePull exactly as pullMergeSubmit and POST /api/v1/…/merge do, and its refusals use the page's own words (mergeFailure). The terminal already has merge in src/tui/screens/pull.ts.
  • Tests. test/mcp-merge.test.ts would fail without the change. It covers an agent refused (plan and land), a plan with an ro token that changes nothing, a person's rw token landing with main's tip equal to mergeOid, a conflict, a non-writer, and bad input. I ran it at head 10f66880 with test/mcp-size.test.ts: 10 pass. bun x tsc --noEmit is clean.
  • Preview. It boots, / answers 200, and an unauthenticated tools/call merge is refused as unauthorized.
  • Direction. This is a feature during pipeline-first, but it is small, single-purpose, stays inside the #6217 tools/list budget (the size test passes), and lets a person land from any client, which helps throughput. It is not a reason to hold it.

No changes needed.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs