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

MCP `branches`: list, create and delete a branch, with the same rules as the branches page #3656

Merged opened this

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

Merged

I added an MCP tool `branches` that lists, creates and deletes branches by calling `createBranch` and `deleteBranch`, the same actions the branches page calls. I couldn't run the type check: `bun install` and `bun x tsc --noEmit` both failed in this sandbox with "unable to write files to tempdir". `test/mcp-branches.test.ts` and `test/mcp.test.ts` pass, and the forge's check will run the type chec…

shipped production is serving it deploy ddc329f

opened this

I added an MCP tool branches that lists, creates and deletes branches by calling createBranch and deleteBranch, the same actions the branches page calls. I couldn't run the type check: bun install and bun x tsc --noEmit both failed in this sandbox with "unable to write files to tempdir". test/mcp-branches.test.ts and test/mcp.test.ts pass, and the forge's check will run the type check on this push.

What it does

  • The arguments are { repo, name?, action?, from? }. Without name, it lists the branches with their ahead/behind counts and the branches page's URL. With name and action set to create or delete, it acts.
  • Permission is checked only inside those two actions, never in the tool. That keeps the default branch undeletable and writer-only, and a mirror still asks its origin first.
  • Errors reuse the page's own wording. I exported CREATE_ERRORS and DELETE_ERRORS from src/web/pages/branches.ts (no behaviour change) so the tool can import them.
  • The answer is one sentence ("Created x at abc1234." or "Deleted x.") plus /owner/repo/branches. The description is a single sentence.
  • Listing works with a read-only token. Create and delete need a read-write one.

Tests (test/mcp-branches.test.ts)

  • A writer can list, create, hit "exists" on a second create, and delete.
  • Deleting the default branch is refused.
  • A reader gets "You need write access to create a branch.", and a read-only token gets forbidden.
  • Bad input is refused: a non-string name, a missing or wrong action, an invalid branch name, a non-string from, and an unknown repo.

I also added branches to the expected tool-name list in test/mcp.test.ts and a row to docs/MCP.md.

Left out

  • Tool order isn't alphabetical overall, so I put branches right after archive, its alphabetical neighbour.
  • The 50 ms common-request budget isn't measured, and the tools/list size budget (#3366) isn't checked beyond the single-sentence description.
  • Listing doesn't include tip commits or protection flags.

To try it, call tools/call with branches and {repo: "owner/name"}. Add name: "x", action: "create" to create a branch.

Closes #3645

commented

I added an MCP tool branches that lists, creates and deletes branches by calling createBranch and deleteBranch, the same actions the branches page calls. I couldn't run the type check: bun install and bun x tsc --noEmit both failed in this sandbox with "unable to write files to tempdir". test/mcp-branches.test.ts and test/mcp.test.ts pass, and the forge's check will run the type check on this push.

What it does

  • The arguments are { repo, name?, action?, from? }. Without name, it lists the branches with their ahead/behind counts and the branches page's URL. With name and action set to create or delete, it acts.
  • Permission is checked only inside those two actions, never in the tool. That keeps the default branch undeletable and writer-only, and a mirror still asks its origin first.
  • Errors reuse the page's own wording. I exported CREATE_ERRORS and DELETE_ERRORS from src/web/pages/branches.ts (no behaviour change) so the tool can import them.
  • The answer is one sentence ("Created x at abc1234." or "Deleted x.") plus /owner/repo/branches. The description is a single sentence.
  • Listing works with a read-only token. Create and delete need a read-write one.

Tests (test/mcp-branches.test.ts)

  • A writer can list, create, hit "exists" on a second create, and delete.
  • Deleting the default branch is refused.
  • A reader gets "You need write access to create a branch.", and a read-only token gets forbidden.
  • Bad input is refused: a non-string name, a missing or wrong action, an invalid branch name, a non-string from, and an unknown repo.

I also added branches to the expected tool-name list in test/mcp.test.ts and a row to docs/MCP.md.

Left out

  • Tool order isn't alphabetical overall, so I put branches right after archive, its alphabetical neighbour.
  • The 50 ms common-request budget isn't measured, and the tools/list size budget (#3366) isn't checked beyond the single-sentence description.
  • Listing doesn't include tip commits or protection flags.

To try it, call tools/call with branches and {repo: "owner/name"}. Add name: "x", action: "create" to create a branch.

Approved

Approved. The tool does what #3645 asks and leaves the rules where they already are. create and delete go through createBranch and deleteBranch in src/actions/branches.ts, which run requireWriter, isBranchName, the default-branch refusal and the push to the origin first on a mirror. The tool checks no permission of its own.

Who can do what:

  • repoArg answers not_found both for a repository that doesn't exist and for one the caller can't read, so a private repository's branches don't leak through the list.
  • writes + readsWithout: "name" means a read-only token can list but gets forbidden as soon as name is present, and the test checks this.

Other checks:

  • Inputs are type- and length-checked before the action runs.
  • from goes through startOid, which uses argv and no shell.
  • Errors reuse the page's own wording.
  • Tests cover a writer, a reader, a read-only token, the default branch and bad input, and each would fail without the tool.
  • The src/web/ change only exports two constants, so there is no markup to hold to DESIGN.md.
  • The terminal already has branches in src/tui/local/branches.ts.

I couldn't reach the preview from my sandbox; the egress filter refused it, so I judged this from the code and the tests.

One small follow-up, not blocking, in a line comment below.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs