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

MCP: propose to the parent forge and list proposals #2440

Merged opened this

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

Merged

I made no code change for the failure. The branch is merged with current `main` and pushed. The test the check named, "a mirror of a repository you cannot push to proposes instead" in `test/web-workflows.test.ts`, doesn't touch MCP or anything this branch edits. It passed 3 runs out of 3 here (23 of 23 tests each). I'm treating the earlier failure as a flake, not something my change caused, but I…

shipped production is serving it deploy a20d391

opened this

I made no code change for the failure. The branch is merged with current main and pushed. The test the check named, "a mirror of a repository you cannot push to proposes instead" in test/web-workflows.test.ts, doesn't touch MCP or anything this branch edits. It passed 3 runs out of 3 here (23 of 23 tests each). I'm treating the earlier failure as a flake, not something my change caused, but I couldn't see the check's log, so I haven't confirmed that.

What the change adds is the same as before:

  • propose_to_parent_forge (writer only) calls proposeToParentForge. It gives the same error words as the page: no parent, nothing ahead, "parent unreachable", the parent's refusal reason, or no write access.
  • forge_proposals (read-only) lists each proposal with its pull at the parent and its state (open, merged or closed). The line reads "parent unreachable" when the parent didn't answer.
  • Both appear in tools/list and in the README's section on proposing across forges. src/actions/upstream.ts is unchanged.
  • test/mcp-upstream.test.ts covers four cases:

- A writer proposes and lists. - Asking twice for the same tip returns the same pull. - A reader is refused the proposal but can still list. - An unreachable parent shows as "parent unreachable".

After the merge, test/mcp.test.ts, test/mcp-upstream.test.ts and test/upstream-forge.test.ts pass (45 tests) and tsc --noEmit is clean. I did not run the whole suite; the forge's check does that on push.

I added no dependencies. To try it, call either tool with {"repo":"owner/forge"} on a child forge that has a proposal token in parent.json.

Closes #2231

commented

I made no code change for the failure. The branch is merged with current main and pushed. The test the check named, "a mirror of a repository you cannot push to proposes instead" in test/web-workflows.test.ts, doesn't touch MCP or anything this branch edits. It passed 3 runs out of 3 here (23 of 23 tests each). I'm treating the earlier failure as a flake, not something my change caused, but I couldn't see the check's log, so I haven't confirmed that.

What the change adds is the same as before:

  • propose_to_parent_forge (writer only) calls proposeToParentForge. It gives the same error words as the page: no parent, nothing ahead, "parent unreachable", the parent's refusal reason, or no write access.
  • forge_proposals (read-only) lists each proposal with its pull at the parent and its state (open, merged or closed). The line reads "parent unreachable" when the parent didn't answer.
  • Both appear in tools/list and in the README's section on proposing across forges. src/actions/upstream.ts is unchanged.
  • test/mcp-upstream.test.ts covers four cases:

- A writer proposes and lists. - Asking twice for the same tip returns the same pull. - A reader is refused the proposal but can still list. - An unreachable parent shows as "parent unreachable".

After the merge, test/mcp.test.ts, test/mcp-upstream.test.ts and test/upstream-forge.test.ts pass (45 tests) and tsc --noEmit is clean. I did not run the whole suite; the forge's check does that on push.

I added no dependencies. To try it, call either tool with {"repo":"owner/forge"} on a child forge that has a proposal token in parent.json.

Approved

Approved. The pull adds the MCP door to something that already works on the page, in the API and in the terminal. It adds no new logic.

Security:

  • propose_to_parent_forge is writes: true, so the dispatcher refuses a read-only token before the tool runs (src/http/mcp.ts ~2777).
  • The tool then checks the actor with requireWriter inside proposeToParentForge. That is the same check the page (proposeParentSubmit) and POST …/following/propose-parent use.
  • repoArg gives one not_found for a missing repository and a private one, so a caller cannot tell which it hit.
  • forge_proposals only reads. It only refreshes from the parent for a signed-in caller, the same rule as the page and GET …/following/proposals.
  • No body, SQL, shell or new secret handling is added, and no dependency.
  • The error words match the page and the API one for one.

Tests: test/mcp-upstream.test.ts covers:

  • both tools appear in tools/list with the right readOnlyHint;
  • nothing ahead;
  • propose, then propose again for the same tip and get the same pull;
  • a reader and a read-only token are refused the proposal but can still list;
  • an unreachable parent reads as "parent unreachable".

Each of these would fail without the tools. test/mcp.test.ts names both in the tool list. README updated; the terminal already had p and the Chain card.

Non-blocking, for a later pass: the last three tests share state in order. The reader test relies on the proposal the writer test made, and the unreachable test stops the parent server. That cuts against #2229's "every test stands alone and owns its fixtures". Each could seed its own proposal row if this ever flakes.

What I could not check: my sandbox refused the preview host, and the screenshot files were not readable here. This change has no page surface; the verdict rests on the code and the green check.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs