For: the same action has the same shape for a person and an agent. Setting labels (labelsSubmit in src/web/pages/thread.ts) is a form on the page today and has no MCP tool, so an agent in Claude, ChatGPT or OpenCode has to fall back on raw HTTP.
Done:
[ ] An MCP tool labels in src/http/mcp.ts, inserted at its alphabetical place in the tools list (so pulls adding other tools don't clash). It calls setIssueLabelsThrough, the same action the page calls, so the rules live in one place: only labels the repository has, by a writer.
[ ] Its description is one sentence in the page's words, and its answer is brief: what changed and the issue or pull's URL. tools/list stays under the size budget (#3366).
[ ] A test in test/mcp-labels.test.ts calls the tool over MCP as a writer (it works), as a reader (refused with the page's words), and with bad input (refused at the boundary).
[ ] docs/MCP.md lists the tool.
Files: src/http/mcp.ts, docs/MCP.md, test/mcp-labels.test.ts.
Risk: a tool that skips the page's gate. Never re-check permission in the tool; the action checks it.
For: the same action has the same shape for a person and an agent. Setting labels (
labelsSubmitinsrc/web/pages/thread.ts) is a form on the page today and has no MCP tool, so an agent in Claude, ChatGPT or OpenCode has to fall back on raw HTTP.Done:
labelsinsrc/http/mcp.ts, inserted at its alphabetical place in the tools list (so pulls adding other tools don't clash). It callssetIssueLabelsThrough, the same action the page calls, so the rules live in one place: only labels the repository has, by a writer.tools/liststays under the size budget (#3366).test/mcp-labels.test.tscalls the tool over MCP as a writer (it works), as a reader (refused with the page's words), and with bad input (refused at the boundary).docs/MCP.mdlists the tool.Files:
src/http/mcp.ts,docs/MCP.md,test/mcp-labels.test.ts. Risk: a tool that skips the page's gate. Never re-check permission in the tool; the action checks it.