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

MCP `origins`: list the caller's connected origins and their repositories, and sync one, with the same rules as the API #4828

Merged opened this

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

Merged

MCP now has an `origins` tool, placed right after `outsiders`, taking `action` (`list`, `repos` or `sync`) and `id`. The new `test/mcp-origins.test.ts` and the other MCP tests pass (101 tests). `tsc` could not check anything here: it fails with "Cannot find type definition file for 'bun'", so the forge's check is the first real type check.

shipped production is serving it deploy 78f82e5

opened this

MCP now has an origins tool, placed right after outsiders, taking action (list, repos or sync) and id. The new test/mcp-origins.test.ts and the other MCP tests pass (101 tests). tsc could not check anything here: it fails with "Cannot find type definition file for 'bun'", so the forge's check is the first real type check.

  • What it calls: the same functions the API routes call: listUserOrigins, listRemoteRepos and syncOriginNow, always for the caller. The answers go through the API's own originJson and remoteRepoJson, so they carry no token. I exported those two from src/http/api.ts; the routes themselves are untouched.
  • Token scope: like the API's origin routes, it needs an admin token, because an origin spends the person's credential on another forge. A ro or rw token gets forbidden. list answers without id; repos and sync need id or get invalid.
  • Not found: an unknown id, or another person's, answers not_found. An unreachable origin answers origin_unreachable.
  • Size: the description is one short sentence, with no description on id, to keep tools/list under the 37,000-byte headroom. I did not measure the bytes the tool adds; mcp-size.test.ts passes.
  • Docs and tests: docs/MCP.md has the row beside outsiders. test/mcp-origins.test.ts uses the fake origin from test/fake-origin.ts, with no network. It covers the owner listing the origin and its repositories and starting a sync, with no token in any answer. It also covers another person seeing nothing, unknown and missing ids, bad actions, and the scope refusal. test/mcp.test.ts now expects origins in the tool list. That test was out of date, not wrong.

I left out connecting and disconnecting origins and mirroring a repository over MCP. Those are separate verbs, and migrate (#4163) is where mirroring belongs.

To try it, call tools/call with { name: "origins", arguments: { action: "list" } } using an admin token.

Closes #4811

commented

MCP now has an origins tool, placed right after outsiders, taking action (list, repos or sync) and id. The new test/mcp-origins.test.ts and the other MCP tests pass (101 tests). tsc could not check anything here: it fails with "Cannot find type definition file for 'bun'", so the forge's check is the first real type check.

  • What it calls: the same functions the API routes call: listUserOrigins, listRemoteRepos and syncOriginNow, always for the caller. The answers go through the API's own originJson and remoteRepoJson, so they carry no token. I exported those two from src/http/api.ts; the routes themselves are untouched.
  • Token scope: like the API's origin routes, it needs an admin token, because an origin spends the person's credential on another forge. A ro or rw token gets forbidden. list answers without id; repos and sync need id or get invalid.
  • Not found: an unknown id, or another person's, answers not_found. An unreachable origin answers origin_unreachable.
  • Size: the description is one short sentence, with no description on id, to keep tools/list under the 37,000-byte headroom. I did not measure the bytes the tool adds; mcp-size.test.ts passes.
  • Docs and tests: docs/MCP.md has the row beside outsiders. test/mcp-origins.test.ts uses the fake origin from test/fake-origin.ts, with no network. It covers the owner listing the origin and its repositories and starting a sync, with no token in any answer. It also covers another person seeing nothing, unknown and missing ids, bad actions, and the scope refusal. test/mcp.test.ts now expects origins in the tool list. That test was out of date, not wrong.

I left out connecting and disconnecting origins and mirroring a repository over MCP. Those are separate verbs, and migrate (#4163) is where mirroring belongs.

To try it, call tools/call with { name: "origins", arguments: { action: "list" } } using an admin token.

Approved

Clean. The origins tool calls the same listUserOrigins / listRemoteRepos / syncOriginNow the API routes call. It is always scoped to the authenticated caller, with the user coming from the verified token and never from an argument. Answers go through the API's own originJson / remoteRepoJson, so no credential reaches a response or an error. It holds the same admin-token bar as requireAccount({ adminToken: true }), and that check runs inside run. So the readsWithout: "id" read path (list on a ro/rw token) is still refused, and the test covers exactly that. Another person's id answers not_found, the same as an unknown one, so it does not leak whether the id exists. No SQL or shell is built from input, and the API routes are unchanged apart from two exports. At head 3870ba85, bun test passes for mcp-origins, mcp and mcp-size (44 pass) and tsc --noEmit is clean. On the preview, / answers and /mcp refuses a call without a token. There is no terminal door in this repository to mirror. It does what #4811 asked and nothing more.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs