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

MCP `explore` gains a `tree` view: an app's fork chain in the page's own words, no new tool #11405

Merged opened this

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

Merged

MCP `explore` now takes `{ kind: "tree", app: "owner/name" }` and returns that app's fork chain as plain rows. There is no new tool. One part of the spec is not met: "last taken" is not a real take time, because the forge doesn't record one. It is the time the fork's default branch last moved, which the forks page labels "pushed".

shipped production is serving it deploy abf00b0

opened this

MCP explore now takes { kind: "tree", app: "owner/name" } and returns that app's fork chain as plain rows. There is no new tool. One part of the spec is not met: "last taken" is not a real take time, because the forge doesn't record one. It is the time the fork's default branch last moved, which the forks page labels "pushed".

The new tests, mcp-size, mcp-table and the other tests scripts/fast.ts picked all pass, and tsc and the gate are clean. I did not run bun scripts/design-lint.ts, since nothing under src/web changed. The forks page and the answer share the "follows" and "standing" wording but not the "pushed" time, which the page prints as a relative time and the answer as an ISO timestamp.

What it returns. The answer is { app, upstream, forks, truncated }. Each row is { repo, follows, standing, updatedAt }.

  • upstream is the chain above the app, nearest first, ending at the highest ancestor the caller can read.
  • forks are the app's direct forks, at most 60. truncated is true when more exist.
  • follows uses the Following card's wording ("Follows x automatically", "Proposes updates from x", "Does not follow x"). The top of a chain says "This repository follows nothing."
  • standing uses the forks page's drift wording ("2 ahead · 0 behind", or "standing unknown").
  • updatedAt is the fork's last-moved time described above.

Privacy. A fork the caller can't read is not a row. An app the caller can't read, or one that doesn't exist, answers not_found.

Where it lives.

  • The reader is src/actions/fork_tree_rows.ts.
  • The tool change is in src/http/mcp/tools/explore.ts.
  • docs/MCP.md names the view, and I regenerated its tool table with scripts/mcp-table.ts.
  • test/mcp-tree.test.ts covers the chain, the wording, a private fork hidden from a stranger, and an unreadable app. Its parity check fetches /alice/lib-mid/forks and confirms the page carries the same "follows" and "standing" text.

Size. The description change stays inside the existing ceilings; I raised none. Most of the new text is in the tool's doc line, which clients never receive.

Left alone. The existing fork_tree and forks tools, which answer in other shapes (whole tree, and drift pages), are unchanged.

Try it. Call explore with { "kind": "tree", "app": "alice/lib-mid" } and compare the rows with /alice/lib-mid/forks.

Closes #11377

commented

MCP explore now takes { kind: "tree", app: "owner/name" } and returns that app's fork chain as plain rows. There is no new tool. One part of the spec is not met: "last taken" is not a real take time, because the forge doesn't record one. It is the time the fork's default branch last moved, which the forks page labels "pushed".

The new tests, mcp-size, mcp-table and the other tests scripts/fast.ts picked all pass, and tsc and the gate are clean. I did not run bun scripts/design-lint.ts, since nothing under src/web changed. The forks page and the answer share the "follows" and "standing" wording but not the "pushed" time, which the page prints as a relative time and the answer as an ISO timestamp.

What it returns. The answer is { app, upstream, forks, truncated }. Each row is { repo, follows, standing, updatedAt }.

  • upstream is the chain above the app, nearest first, ending at the highest ancestor the caller can read.
  • forks are the app's direct forks, at most 60. truncated is true when more exist.
  • follows uses the Following card's wording ("Follows x automatically", "Proposes updates from x", "Does not follow x"). The top of a chain says "This repository follows nothing."
  • standing uses the forks page's drift wording ("2 ahead · 0 behind", or "standing unknown").
  • updatedAt is the fork's last-moved time described above.

Privacy. A fork the caller can't read is not a row. An app the caller can't read, or one that doesn't exist, answers not_found.

Where it lives.

  • The reader is src/actions/fork_tree_rows.ts.
  • The tool change is in src/http/mcp/tools/explore.ts.
  • docs/MCP.md names the view, and I regenerated its tool table with scripts/mcp-table.ts.
  • test/mcp-tree.test.ts covers the chain, the wording, a private fork hidden from a stranger, and an unreadable app. Its parity check fetches /alice/lib-mid/forks and confirms the page carries the same "follows" and "standing" text.

Size. The description change stays inside the existing ceilings; I raised none. Most of the new text is in the tool's doc line, which clients never receive.

Left alone. The existing fork_tree and forks tools, which answer in other shapes (whole tree, and drift pages), are unchanged.

Try it. Call explore with { "kind": "tree", "app": "alice/lib-mid" } and compare the rows with /alice/lib-mid/forks.

Approved

Clean: reuses explore (no new tool), reads gated by repoArg/canRead/visibleForks, unreadable ancestors cut the chain, forks capped at 60, input length-capped. Tests cover chain, wording parity with the forks page, private-fork omission, not_found. Note: updatedAt is last push, not a true take time (disclosed, no record exists). The terminal already has fork screens; this is an MCP-only view as the issue asked.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs