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
Check · Review · Land · Live done — Landed and live in production.
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
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 unlessdryRunisfalse. - The plan is a read, so a read-only token can ask for it. It gives the pull's
head,base,state,mergeable,conflictsandcheck, using the same reads as thepulltool. - Landing (
dryRun: false) callsmergePullthe same wayPOST …/pulls/:number/mergedoes. Its refusals keep their codes:conflict,review_required,checks_required,already_merged,forbiddenfor someone who cannot write, andmirrored. The messages are the pull page's own (mergeFailure). The tool only adds checks in front ofmergePulland never loosens its rules. - Agents are refused. An agent's token gets
forbiddenwith "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.mdand the tool index were regenerated withbun 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. 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
mergePullrefuses it again.requireWriterchecks who may write, and mirrors are refused. The rw scope is enforced by the shareddryByDefault/writesgate, and the test shows an ro token can plan but not land.strategyandnumberare validated at the boundary, and a sealed pull answersno_such_pullthroughmayView. There is no shell or SQL built from input, and no secret. - Same as the page and the API. It calls
mergePullexactly aspullMergeSubmitandPOST /api/v1/…/mergedo, and its refusals use the page's own words (mergeFailure). The terminal already has merge insrc/tui/screens/pull.ts. - Tests.
test/mcp-merge.test.tswould 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 tomergeOid, a conflict, a non-writer, and bad input. I ran it at head 10f66880 withtest/mcp-size.test.ts: 10 pass.bun x tsc --noEmitis clean. - Preview. It boots,
/answers 200, and an unauthenticatedtools/call mergeis 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.
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.src/http/mcp/tools/merge.ts):{ repo, number, strategy?: "merge"|"squash", dryRun? }. It writes, and only plans unlessdryRunisfalse.head,base,state,mergeable,conflictsandcheck, using the same reads as thepulltool.dryRun: false) callsmergePullthe same wayPOST …/pulls/:number/mergedoes. Its refusals keep their codes:conflict,review_required,checks_required,already_merged,forbiddenfor someone who cannot write, andmirrored. The messages are the pull page's own (mergeFailure). The tool only adds checks in front ofmergePulland never loosens its rules.forbiddenwith "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.mdand the tool index were regenerated withbun scripts/mcp-tools.ts.The steward's note prefers adding an
actionto an existing tool, but neither candidate fits.reviewhas no plan-first default, andpulldoesn't write. The issue asked for a separate tool, and the new one still fits under thetools/listsize limits (test/mcp-size.test.tspasses).Tests:
test/mcp-merge.test.tscovers:mergeOid;These and the other MCP tests I ran pass, and
bun x tsc --noEmitis clean.test/mcp.test.tshad a fixed list of tool names; I addedmergeto it. That test was wrong for the new tool, not the code.To try it: call
mergewith{ repo, number }to see the plan, then again withdryRun: falseto land the pull.🤖 Generated with Claude Code
Closes #6243