acquire is the front door. Your agent calls it with a connection, a goal in a sentence or two, and any hints it has, and Graft’s model does the rest: it reads the vendor’s documentation, writes the smallest module that makes the call, checks it, proves it with reads, publishes it with a test input, dry-runs it, fixes and republishes until the dry run passes, then promotes it into the agent’s working set.
The code is written by a model inside Graft, not by the model driving your harness. Your agent decides what is needed; Graft’s model writes and proves it; you enter secrets and answer approvals.
The call
connectionId must be in the agent’s scope; a connection outside it is refused and the answer points at request_connection. goal is what the tool must do, in your terms. hints is anything the agent already knows: an endpoint, a field name, and above all a documentation URL, which is the single most useful hint.
The answer comes back at once:
Before it starts: the build approval
The first time an agent acquires against a connection, you are asked once: may this agent build against this connection? That is the moment Graft’s model starts reading your data there through dry-run reads. The answer holds for that agent and that connection; later acquires against it ask nothing. In Claude and ChatGPT the ask is a card in the chat with Allow and Decline; in Hermes it is Hermes’s own approval card with allow and deny buttons. Elsewhere the agent relays a link and you answer under Pending actions. A decline records nothing, so the nextacquire asks again.
While it runs: acquire_status
acquire itself waits for the job for as long as the harness allows and answers with where it stands. Unfinished, the agent calls acquire_status { jobId }, which waits up to twenty seconds for a new line before answering, and gets { status, progress, attempts, result? }. status is queued, running, succeeded or failed; progress is the list of lines Graft’s model has reported so far, one per step, and the agent relays the newest to you in a sentence when it changes.
The steps, in order:
- Read the documentation. Pages are fetched server-side and read as text. A cheap triage model decides whether to open the documentation URLs the goal and hints name, and condenses long pages; it never writes code.
- Write the module. TypeScript, one call, the fields the goal needs.
- Check it. A static pass: types against the input schema, no credential or host in the code, no import outside the module, any SDK bound to the proxy. The check also derives the tool’s read-only and destructive annotations from the HTTP methods it uses.
- Prove it with reads. The module runs against the live vendor with reads only, until a read returns what the documentation said it would.
- Publish with a test input. The version is written to your toolbox, its packages installed under the package policy, and dry-run.
- Dry run. Reads reach the vendor for real. Every other method stops at the proxy, which answers with a preview of the request that would have left. The model compares each preview against the documentation. A dry run never asks you anything and is never shown to you; it exists for the model.
- Fix and republish, when the dry run fails, with a changed module. Each new draft is an attempt.
- Promote and notify. The tool joins the agent’s working set and
tools/list_changedfires.
When it ends
Onsucceeded, result names the tool:
run_tool { vendor, name, input } calls it by name. The first real use is your agent’s, through the published tool, and that is where a write asks you.
On failed, result.failure is one word and result.message says why, with what was tried: attempt_budget, token_ceiling or turn_budget when a bound was reached, model_gave_up or model_failed, connection_unavailable, build_approval_missing, sandbox_unavailable, or job_failed. The agent says what happened in your words and what it will try next, usually a documentation URL as a hint or a different connection.
The bounds
A job may draft at most four modules and spend at most 400,000 tokens across every model turn, and a job that reaches either bound ends with a result naming it. Every draft is an attempt, a draft the check refuses as much as one whose dry run fails. Self-hosted deployments set the two bounds withGRAFT_ACQUIRE_MAX_ATTEMPTS and GRAFT_ACQUIRE_TOKEN_CEILING.
Which model
On Graft Cloud,acquire runs on a fixed model Graft chooses, with a cheap model for triage. Bring your own key under Settings and your jobs run on your provider instead, and the vendor documentation those jobs read goes to your provider rather than Graft’s. Self-hosted Graft always authors with your own key: GRAFT_MODEL_BACKEND=provider, GRAFT_MODEL_PROVIDER (anthropic or openai) and GRAFT_MODEL_API_KEY, and it refuses to start without them.
How approvals work
The build approval, and what the published tool asks on first use.
Bring your own model key
Route your acquire jobs through your own provider on Graft Cloud.