# graftfork

> graftfork (Graft, at graftfork.com) keeps a changed copy of an open-source project (a fork, or a project with patched dependencies) on each upstream release. It moves the changes onto the release, runs the project's own tests in a sandbox, and keeps the moved branch only if every change lands and the tests pass. Otherwise the copy stays where it was and Graft says why. It also rebuilds a program blind from its duramen record (a written spec with examples and, often, an oracle): builders that never see the existing code write it in TypeScript, Python, Rust or Go, and Graft keeps a rebuild only when every case of the record passes.

Agents can also start at graftfork.ai, the agent front door: https://graftfork.ai/mcp is the same MCP server and https://graftfork.ai/api/v1 the same API, with the same tokens.

Without any token, an agent can try a public fork (see "Try without an account"). The MCP server and the HTTP API need a token, sent as `Authorization: Bearer <token>`. There are three ways to get one: a personal API token (graft_...) a person makes at https://graftfork.com/app#settings and gives to the agent; a token from OAuth, which a person allows on a consent page (see "Connecting with OAuth" below); a token an agent with a wallet buys for itself, with no person (see "Agent accounts" below).

Tokens have scopes: read: see your copies, syncs, reports, waiting conflicts and usage; sync: connect or disconnect a repository, start a sync, check, dependency upgrade or upstream build, connect, set or rebuild a spec or ask for a new spec report, start, edit, check, rebuild or discard a change order, revert a move, set a copy's desired state, add or remove a webhook, and track an upstream pull request or ask for Graft's reviewer (rebuilds and paid reports spend agent work); resolve: hand in a resolved file or answer a question (Graft still runs your tests before anything moves). Rate limit: 120 requests a minute per token, 30 of them changes.

Nothing an agent sends can move a copy. A resolution is stored, Graft syncs again with it, and the copy moves only if every change lands and the tests pass. By default Graft writes nothing to a repository: it keeps the moved branch, graft/<release>, for the owner to take.

## What costs money

- Free, with no account: trying a public fork (POST https://graftfork.com/api/try), reading this file, https://graftfork.com/pricing.json, the OpenAPI description and https://graftfork.com/api/status.
- Within the plan's monthly counts, at no extra cost: connecting copies, syncs, change checks, reading results and taking moves; a spec's free grade (GET /api/v1/regen/{id}/report) and its signed records.
- Agent work, which spends money: Graft's agent resolving a conflict, fixing a dependency upgrade that breaks the tests, a rebuild from a spec, a paid spec report and the hosted drafter. Premium includes $6 of it a month (up to $1.25 a sync, no overage); the free plan gets its first conflict resolved free once. Give each run a ceiling with max_usd: if it could cost more, the answer is price_quote and nothing is spent.
- A person's own Anthropic API key, if they save one, is billed by Anthropic, not graftfork.
- Agent accounts: 12 USD in USDC for 30 days of Premium, and top-ups at 10 USD (see "Agent accounts").

## Try without an account

- `POST https://graftfork.com/api/try` with `Content-Type: application/json` and `{"repo": "owner/name", "upstream": "owner/name (optional)", "base": "release tag (optional)"}`. No token. It runs one real sync of a public GitHub fork onto upstream's newest release in a short-lived sandbox: it never pushes and runs no agent.
- The 201 answer has `id`, `statusUrl` (also the Location header), `resultUrl` and `pollSeconds`. Poll `GET https://graftfork.com/api/try/{id}` until `status` is done, failed or error. `https://graftfork.com/t/{id}` is the result page to share.
- Limits: 10 tries a day per network address (people on one network share them), one running at a time, 10 minutes each. A result for the same fork, base and release is reused for 24 hours (`cached: true`). Results are kept for 30 days.

## MCP server

- Endpoint: https://graftfork.com/mcp (Streamable HTTP, stateless; protocol revisions 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26). Server name: graftfork.
- [Server Card](https://graftfork.com/mcp/server-card): how to connect, before connecting. Registry name: com.graftfork/graftfork.
- Add it to Claude Code: `claude mcp add --transport http graftfork https://graftfork.com/mcp` (Claude Code opens a browser for OAuth), or with a token: `claude mcp add --transport http graftfork https://graftfork.com/mcp --header "Authorization: Bearer <token>"`
- Every tool with its input schema: https://graftfork.com/llms-full.txt

Tools (a token sees the ones its scopes allow):

- `track_change` (sync): Register a GitHub repository whose changes to an open-source project Graft keeps on each upstream release.
- `list_copies` (read): Every copy, one line each, with its last sync and what waits.
- `status` (read): One copy, desired beside observed.
- `sync_now` (sync): Sync one copy onto its newest allowed release; it moves only if every change lands and the tests pass.
- `set_copy` (sync): Write a copy's desired state; only the fields you give change.
- `take_move` (read): A one-hour link to a moved branch (git bundle) and the commands that fetch it into the user's clone as graft/<release>.
- `revert_move` (sync): Undo the move a copy is on now, back to the release before it.
- `send_upstream` (read): One carried change and its reason, as pull request text.
- `build_upstream` (sync): Build graft/upstream/<change>: only this change on the copy's release, tested.
- `upstream_status` (read): One upstream build: status, test verdict, pull request text, one-hour bundle and patch links, and the tracked pull request's state.
- `track_upstream_pr` (sync): Record the pull request the user opened upstream for a built change.
- `explain_stop` (read): Why a copy's last sync didn't move it, and the next action: tools to resolve with, a question for the user, or something only the user can do.
- `list_pending_conflicts` (read): Conflicting files to resolve and questions only the user decides.
- `get_conflict` (read): One conflicting file: base, ours, theirs, and the change's recorded reason; or a question's options.
- `submit_resolution` (resolve): Hand in the WHOLE resolved file for one conflict, or delete: true.
- `answer_question` (resolve): Answer a sync's question with the option (1-based) the user picked; show them the question first.
- `check_change` (sync): Run the project's tests on a change and its base in Graft's sandbox: pass, fail (new failures by name) or could_not_run.
- `check_status` (read): One change check: outcome, why, new failures, the base's own failures, and test output tails.
- `upgrade_dependency` (sync): Upgrade one npm or Python dependency on graft/upgrade/<name>-<version>, delivered only if no test newly fails.
- `get_upgrade` (read): One dependency upgrade: status, version, tests before and after, the agent's fix, and where it went.
- `regen_connect` (sync): Connect a repository whose program is kept in a duramen record, so Graft can rebuild it blind (lang lists the languages open now).
- `regen_run` (sync): Rebuild a spec blind from its record, kept on graft/regen/<id> only if every case passes.
- `regen_set` (sync): Write a spec's desired state; only the fields you give change.
- `regen_disconnect` (sync): Disconnect a spec, as /app's Disconnect does: its queued rebuilds are cancelled and its kept rebuilds deleted; its rebuilds' signed records stay.
- `regen_status` (read): Specs and rebuilds.
- `regen_record` (read): A finished rebuild's signed record.
- `regen_rejudge` (sync): Judge a finished rebuild's stored builds again with today's runner, to confirm a verdict.
- `regen_report` (read): A spec's free grade (strong, usable or thin), its reasons, and what a blind rebuild can be judged on.
- `regen_report_refresh` (sync): Grade a spec's record again, no model.
- `regen_draft_check` (sync): For a record edited on a pushed branch of the user's repository.
- `regen_draft_rebuild` (sync): For a record edited on a pushed branch.
- `regen_draft_status` (read): Drafts from pushed branches.
- `regen_order_create` (sync): For an edit held in Graft (a change order).
- `regen_order_write` (sync): Replace change-order files, each a WHOLE new text (text null restores, delete true deletes).
- `regen_order_check` (sync): For an edit held in Graft (a change order).
- `regen_order_rebuild` (sync): For an edit held in Graft (a change order).
- `regen_order_status` (read): Change orders held in Graft.
- `regen_order_discard` (sync): End a change order the user doesn't want: its edits go, its history stays.
- `usage` (read): The plan, syncs and copies against limits, and agentWork: agent work left, spent, and the caps.
- `webhook_add` (sync): Register a public https URL Graft posts JSON to when work ends or needs someone, instead of polling.
- `webhook_list` (read): The user's webhooks with their last deliveries; with id, one webhook's last 50 (event, status, attempts, what the URL answered).
- `webhook_remove` (sync): Remove a webhook and its delivery log.
- `webhook_test` (sync): Post a signed ping now and say what the URL answered.
- `list_fork_alerts` (read): Team and Enterprise: what fork watch found new: advisory (a fix the fork lacks), behind (a new release), carry (changes that stopped carrying).
- `path_policy` (read): Paths a move you make may not change without a person, for a copy (id) or organization (org).
- `org_settings` (read): An organization's defaults for new copies and specs, its review checklist, its channels' quiet hours, and your own muted email events.
- `org_settings_set` (admin): Owners: set the organization's defaults, review checklist or quiet hours.
- `org_notify_set` (sync): The events you don't want by email from the organization's email channels (an empty list for all of them).
- `org_channels` (read): An organization's team channels: destination, events, team, on or off, and recent deliveries.
- `org_channel_add` (admin): Owners: a new team channel.
- `org_channel_set` (admin): Owners: change a channel's name, events, team (null for every team), recipients (email channels) or pause it.
- `org_channel_remove` (admin): Owners: remove a channel, its webhook and what waits to be sent to it.
- `org_channel_test` (admin): Owners: a test post (or one email to each recipient) now, whatever the channel's events and quiet hours.
- `org_audit_verify` (read): Sign and check the organization's audit log (signature, entry hashes, ledger copies): ok and any problems.
- `org_review_queue` (read): An organization's moves waiting for review, riskiest first, with what to read and skip.
- `buy_agent_work` (pay): Buy one top-up of Graft's agent work for this account, paid in USDC over x402 (version 2, scheme exact) from your own wallet: $10 adds $7, usable this month and next, on Premium.

## Claude connector directory

- graftfork is in Claude's connector directory: https://claude.ai/directory/graftfork. In Claude (web, desktop or mobile): Customize, Connectors, find graftfork, Connect, then allow it on graftfork's consent page. Act (sync) is off by default.

## Claude Code plugin

- `claude plugin marketplace add craigm26/graft-claude-code`, then `claude plugin install graftfork@graftfork` (in Claude Code: `/plugin marketplace add craigm26/graft-claude-code` and `/plugin install graftfork@graftfork`). Restart, run `/mcp`, choose `plugin:graftfork:graftfork` and sign in.
- Had the plugin under its old name, `graft` (1.5 or earlier)? Claude Code keeps the name the marketplace had when it was added, `graft-claude-code`, so `graftfork@graftfork` isn't found there: install `graftfork@graft-claude-code`, uninstall `graft@graft-claude-code`, and sign in again from `/mcp`. The old entry stays in the marketplace at 1.5.0, so it keeps loading until you switch.
- Beyond `claude mcp add`, it adds a skill that writes the reason for each change to a dependency, fork or vendored package into the commit in the form Graft reads, and three commands: `/graftfork:graft-status`, `/graftfork:graft-take` and `/graftfork:graft-upstream`.

## Connecting with OAuth

An MCP client that supports the MCP authorization spec connects without a pasted token: a 401 from https://graftfork.com/mcp names the Protected Resource Metadata (https://graftfork.com/.well-known/oauth-protected-resource), which names the authorization server (https://graftfork.com/.well-known/oauth-authorization-server). Register at https://graftfork.com/oauth/register (public clients only: no secret; redirect URIs are https or http on localhost, 127.0.0.1 or [::1]), then use the authorization code with PKCE (S256 only). The person signs in, allows the client, and sets its limits on the consent page. Access tokens last an hour; the refresh token is swapped for a new one on each use, and one used twice revokes the grant. A token is bound to https://graftfork.com/mcp, https://graftfork.com/api/v1, or both (the resource parameter, RFC 8707). Revoke at https://graftfork.com/oauth/revoke, or the person does it in /app Settings.

## HTTP API

- [OpenAPI 3.1 description](https://graftfork.com/api/v1/openapi.json): the same work as the MCP tools, at https://graftfork.com/api/v1, and Try at https://graftfork.com/api/try
- [API catalog](https://graftfork.com/.well-known/api-catalog) (RFC 9727) and the index at https://graftfork.com/api/v1
- [API docs](https://graftfork.com/docs/api)

## Command line

- `npm install -g graftfork` (Node 22.18 or newer) installs the command `graft`, a thin client for the HTTP API. It reads the token from GRAFT_TOKEN. From version 0.4.2 it is also installed as `graftfork` and reads GRAFTFORK_TOKEN too. Copies: `graft copies`, `graft sync`, `graft why`, `graft pull`. Rebuilds from a duramen record: `graft regen connect`, `graft regen report`, `graft regen run`, `graft regen status`. `graft verify-record <file or link>` checks a signed record with no token. Change orders, drafts, revert and desired state: `graft regen order`, `graft regen draft`, `graft revert`, `graft set`. Webhooks and re-judging are in /app, the API and the MCP server, not the command line.
- With npx, name the package: `npx graftfork verify-record <file>`. The npm package `graft` and the PyPI package `graft` belong to other people; don't run them for graftfork.

## End-to-end flows

1. Try a fork, no account: POST https://graftfork.com/api/try {"repo": "owner/name"}, poll statusUrl, read result.status (moved, stayed or current) and the result page.
2. Keep a fork current (token with read and sync): `track_change` or POST /api/v1/copies {"repo": "owner/name"} connects the copy (Graft's GitHub app must be on the repository; over the API you can give a git address instead); `sync_now` or POST /api/v1/copies/{id}/sync starts a sync; watch it with `status` or GET /api/v1/runs/{id}; if it stayed, `explain_stop` or GET /api/v1/copies/{id}/explain says why and what to do.
3. Resolve what stopped a sync (token with resolve): `list_pending_conflicts` or GET /api/v1/conflicts, `get_conflict`, then `submit_resolution` (a resolved file) or `answer_question` (a choice). Graft syncs again and the tests decide.
4. Take the move: `take_move` or GET /api/v1/copies/{id}/take gives a one-hour link and the commands that fetch graft/<release> into a clone; `graft pull` does it for you. A GitHub pull request is an opt-in per copy.
5. Rebuild from a spec: `regen_connect` or POST /api/v1/regen, read the free grade with `regen_report` or GET /api/v1/regen/{id}/report, rebuild with `regen_run` or POST /api/v1/regen/{id}/run (spends agent work; max_usd caps it), poll `regen_status` or GET /api/v1/regen/runs/{id}, then fetch the signed record (`regen_record` or GET /api/v1/regen/runs/{id}/record) and check it with `npx graftfork verify-record`.
6. Hear when runs end instead of polling: `webhook_add` or POST https://graftfork.com/api/v1/webhooks.
7. No person at all: make an agent account with POST https://graftfork.com/api/v1/agents (x402, below), then flows 2 to 6 with its token.

## Plans and prices

- [Machine-readable plans and prices](https://graftfork.com/pricing.json)
- Free: $0. 10 syncs a month, 5 copies, 20 change checks a month, the first conflict resolved free once (up to $1.25 of agent work, while Graft's agents are on and this month's trial budget lasts). Sign up at https://graftfork.com/#invite.
- Premium: $12 a month. 40 syncs a month, 50 change checks a month and $6 of Graft's agent work included, up to $1.25 of it a sync; no overage. Top-up: $10 adds $7.
- Team: $15 per fork a month, 1 to 20 forks, $6 of agent work per fork (up to $1.25 a sync; until the first invoice is paid, $6 for the whole organization), first 30 days free. Started by email.
- Enterprise: by contract. Graft's reviewer on every carry is $990 per fork per release.

## Agent accounts

An agent with a wallet can make its own Graft account, with no person, inbox or card. It pays 12 USD in USDC for 30 days of Premium and gets the account and a token (read, sync, resolve, pay) in the same answer.

1. `POST https://graftfork.com/api/v1/agents` with `{"name": "...", "contact": "optional email of a responsible person"}`. Graft answers 402 with x402 (version 2) payment requirements in the body and in the PAYMENT-REQUIRED header: scheme exact, network eip155:8453, asset 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, amount 12000000.
2. Sign an EIP-3009 transferWithAuthorization for exactly that and send the same request again with the base64 PaymentPayload in the PAYMENT-SIGNATURE header. Graft settles it through an x402 facilitator.
3. The 201 answer has the account, the token (shown once) and when the paid time ends. Use the token with the MCP server or the HTTP API.
4. Renew with `POST https://graftfork.com/api/v1/agents/renew` and the token, the same way. Past the paid time the account falls back to Free.
5. Buy more agent work with `POST https://graftfork.com/api/v1/topups` (or the MCP tool `buy_agent_work`), the same way: 10 USD in USDC (amount 10000000) adds 7 USD, usable this month and next. The same payment sent again returns the same credit. A person's token can do this too if they gave it the pay scope.

The token works for 90 days; each renewal or top-up paid with it moves that out to 90 days again. Its monthly cap is the account's agent work for the month. Set a lower ceiling per run with max_usd on sync_now, upgrade_dependency and regen_run, and register a webhook (webhook_add, or POST https://graftfork.com/api/v1/webhooks) to hear when runs end instead of polling.

The account works only through the API and the MCP server: no web sign-in, no email. Questions about a payment go by email to support@graftfork.com. Details: https://graftfork.com/docs/agents#accounts

## Forms an agent can post

Each takes `application/x-www-form-urlencoded` or a JSON object (`application/json`) and answers a JSON caller in JSON. Any other body type is a 415 that names the fields.

- `POST https://graftfork.com/api/waitlist`: sign up for a free account; graftfork emails the address a sign-in link (the account is a person's: it signs in from that inbox). Fields: email (required); optional: project, notes, t (the signed start token from GET /api/waitlist/start; without it a sign-up is looked at by a person first).
- `POST https://graftfork.com/api/report`: ask for the free fork report: Graft carries the fork onto upstream's newest release, builds and tests it, and a person emails the result, usually within a few business days. Fields: email, fork and upstream (all required; fork and upstream as GitHub URLs or owner/name); optional: target, notes. A person replies by email.
- `POST https://graftfork.com/api/feedback`: feedback on a carry or on graftfork. Fields: message or rating (1 to 5), at least one; optional: email, carry.

## Docs

- [Agents and Claude Code](https://graftfork.com/docs/agents)
- [Getting started](https://graftfork.com/docs)
- [How it works](https://graftfork.com/how-it-works)
- [Rebuilds from a spec](https://graftfork.com/regen): a program rebuilt blind from its duramen record, kept only when every case passes; [a real change, replayed](https://graftfork.com/regen/replay); [the guide](https://graftfork.com/docs/regen)
- [Security](https://graftfork.com/security)
- [Signed run records: the public keys](https://graftfork.com/.well-known/graft-record-key)
- [Status](https://graftfork.com/api/status) (JSON)

## Support

Email support@graftfork.com. Support is by email only.
