Read several files from one exact branch snapshot
| repo_idrequired | string | repo id to act on |
| pathsrequired | string[] | Repository-relative file paths. At most 12. |
| ref | string | Branch name. Defaults to the trunk branch. |
27 tools over protocol 2025-06-18, on HTTP or stdio. Each tool is one operation of the HTTP API under an agent-facing name — not a second implementation of it.
A JSON-RPC dispatcher in front of the operation registry. A tool call resolves to the operation the tool was generated from and runs it as the authenticated actor, so an agent and the web UI cannot see different behaviour.
initialize answers with instructions written for whoever authenticated the session — your handle, your model, and the reminder to check your scopes before proposing. Tool schemas are the operation's schemas: path parameters, query and body flattened into one input object.
Use the same /mcp endpoint whether the client authenticates with a long-lived agent key or a delegated OAuth connection.
claude mcp add --transport http trunk https://app.trunk.dev/mcp \ --header "Authorization: Bearer $AGENT_KEY"
[mcp_servers.trunk] url = "https://app.trunk.dev/mcp" bearer_token_env_var = "TRUNK_API_KEY"
Codex reads the variable at connect time and sends it as the bearer token, so the key never sits in the file. codex mcp add trunk --url https://app.trunk.dev/mcp --bearer-token-env-var TRUNK_API_KEY writes the same entry.
{ "$schema": "https://opencode.ai/config.json", "mcp": { "trunk": { "type": "remote", "url": "https://app.trunk.dev/mcp", "enabled": true, "headers": { "Authorization": "Bearer {env:TRUNK_API_KEY}" } } } }
{env:TRUNK_API_KEY} is opencode's own substitution, resolved when the config is read — write the key itself here and it is committed the moment the file is.
OAuth-capable remote MCP clients, including ChatGPT, should be pointed at https://app.trunk.dev/mcp and let MCP/OAuth discovery drive registration and authorization. They do not need a pre-created Trunk agent key; see Remote OAuth clients below.
Every message is a POST to https://app.trunk.dev/mcp. A notification — a message with no id — is answered 202 with no body, as JSON-RPC requires. Both transports run the same dispatcher; mcp.json carries the stdio command for a checkout you run yourself.
HTTP MCP accepts either an existing Trunk bearer credential or a delegated OAuth bearer issued specifically for the /mcp protected resource.
/mcp resource, not as general /v1 API authority.tools/call fails until a credential is present.For remote clients that support MCP OAuth, Trunk uses discovery plus Authorization Code with PKCE and explicit human consent instead of asking the user to copy an agent key into the client.
https://app.trunk.dev/mcp. Trunk publishes OAuth protected-resource and authorization-server metadata, so a compatible client can discover the authorization, token, registration and revocation endpoints. Public clients may use dynamic client registration.trunk:mcp:read is the required base OAuth scope. Its complete audited tool surface is trunk_whoami, trunk_list_repos, trunk_get_repo, trunk_log, trunk_list_branches, trunk_list_source, trunk_read_source, trunk_read_sources, trunk_search_source, trunk_preflight_patch and trunk_stats. It does not imply access to the rest of the generic MCP read catalog. Delegated OAuth also exposes only the trunk://me MCP resource; trunk://queue and trunk://policy remain generic API-key/session MCP resources.trunk:mcp:changes:read adds exactly trunk_list_changes and trunk_get_change. Delegated list results contain only id, repo_id, branch, revision, base_commit and status. Delegated get accepts the Change id without the generic include selector and adds only the exact stored diff. Generic Change risk, reported results, author presentation, evidence, trace, decisions and track data are not delegated by this scope.trunk:mcp:propose adds exactly trunk_propose_change and trunk_mark_change_ready; it does not inherit the consenting human's owner/admin authority. The delegated integration agent still needs its own current Trunk repository/service/path proposal scope for either authoring mutation. It may propose Draft or non-Draft work, and only the exact author may mark its exact current Draft revision Ready. Ready removes only that author-controlled hold; it does not grant direct landing authority. An authorized proposal may still automatically land through the normal Change lifecycle when the integration agent's scope and budget, repository policy and required checks allow it.trunk_land_change, force-land, revert, verification, scope-granting or other human/provider control-plane operations. Automatic landing after trunk_propose_change or exact-author Ready is a policy outcome, not a delegated land operation. A new OAuth scope added by Trunk also does not silently widen an already-issued token; additional authority requires fresh consent.Request offline_access only when the client needs refresh tokens. Trunk rotates refresh tokens: a successful refresh immediately makes the presented refresh token unusable and returns one successor. Reusing that predecessor while it is still within its accepted lifetime is treated as replay and retires only that refresh-token family; after the predecessor expires it is simply invalid. For signed credentials, revoking a family-bound access token or a refresh token likewise retires that family, while other independently issued token families on the same active delegation grant survive. Public-client revocation requests include their client_id; a mismatched client or unknown token remains an idempotent, non-disclosing revocation result. An access-only token has no refresh family, so revoking it retires the grant generation instead. Losing the delegating human's organization membership also retires the delegation grant, so credentials derived from that grant no longer authenticate.
What the dispatcher answers. Anything else comes back as -32601.
| initialize | Opens the session. Returns the negotiated protocol version, the server capabilities and instructions written for the authenticated agent. |
| notifications/initialized | The client saying it is ready. Carries no id, so it gets no reply. |
| ping | Liveness. Answers with an empty result. |
| tools/list | Every tool, with input schema and, when negotiated, structured output schema generated from its operation contract. |
| tools/call | Runs one tool as the authenticated actor. Requires a credential; a failure comes back as an error result, not a transport error. |
| resources/list | The read-only resources below. |
| resources/read | Reads one resource by URI. |
A tool result is one text block holding the operation's JSON response. Two things are worth knowing before you parse it.
trunk_propose_change returns the routing decision rather than the whole evidence bundle: the change id and status, whether it landed, the risk score and band, the rule that decided it, the findings above info with their file and line, and what to do next. Whentrunk_get_change is available, it reads back the Change view allowed for the current credential; delegated OAuth Change-read authority intentionally returns only recovery fields plus the exact stored proposal diff.isError: true and the message, so the session survives it.The catalog below is the full generic MCP tool surface, with each operation and input shape. It is not an OAuth grant manifest: delegated OAuth clients see only the audited base-read tools above plus separately consented Change-readback or proposal tools.
| repo_idrequired | string | repo id to act on |
| pathsrequired | string[] | Repository-relative file paths. At most 12. |
| ref | string | Branch name. Defaults to the trunk branch. |
| repo_idrequired | string | repo id to act on |
| queryrequired | string | Literal text to find; regular expressions are not evaluated. |
| ref | string | Branch name. Defaults to the trunk branch. |
| path | string | Optional repository-relative subtree to search. |
| case_sensitive | boolean | |
| max_results | integer |
| repo_idrequired | string | repo id to act on |
| diffrequired | string |
| repo_idrequired | string | repo id to act on |
| commitrequired | string | Pinned Git SHA-1 commit to import. |
| sourcerequired | string | Audit label for the source, without credentials. Never fetched by the server. |
| pack_digestrequired | string | Lowercase SHA-256 digest of the exact raw Git pack. |
| pack_bytesrequired | integer | Exact raw Git pack byte count. |
| repo_idrequired | string | repo id to act on |
Takes no arguments.
Takes no arguments.
| repo_idrequired | string | repo id to act on |
| repo_idrequired | string | Repository id or name. |
| titlerequired | string | Imperative one-liner, as it will read on trunk. |
| intentrequired | string | Why this change exists, in your own words: the problem, the evidence it is real, and what you decided not to do. This is what a human reads first. |
| diffrequired | string | Unified diff (git format). Trunk derives its own evidence from this — it does not take your word for what changed. |
| service | string | Owning service. Inferred from the changed paths when omitted. |
| branch | string | The branch this change lives on. Naming one that still carries an open change revises that change; omit it and Trunk opens a new change on a free branch derived from your handle and the title. |
| spec | object | |
| goal | string | |
| non_goals | string[] | |
| acceptance_criteria | string[] | |
| constraints | string[] | |
| base_commit | string | Trunk commit you worked from. Defaults to current head. |
| reported | object | Results you observed. Recorded as self-reported and weighted as such. |
| suites | object[] | |
| namerequired | string | |
| passedrequired | integer | |
| failedrequired | integer | |
| skipped | integer | |
| duration_ms | integer | |
| coverage_delta | number | |
| benchmarks | object[] | |
| namerequired | string | |
| before_msrequired | number | |
| after_msrequired | number | |
| trace | object[] | Your session trace: how you got here. |
| kindrequired | string | What the agent was doing at this step. |
| summaryrequired | string | One line, written for a human skimming the trace. |
| detail | string | Optional longer output: command, result, reasoning. |
| ts | string | ISO timestamp. Defaults to now. |
| tokens | integer | Tokens consumed by this step. |
| usd | number | Cost of this step in USD. |
| cost_usd | number | What this change cost to produce. Counts against your budget. |
| draft | boolean | When true, all normal verification/CI/Preview work continues but this Change enters explicit revision-bound Draft/Ready landing control. |
| repo_id | string | |
| service | string | |
| branch | string | Only changes on this branch. |
| author | string | Actor handle or id. |
| status | string | Comma-separated: verifying, awaiting_decision, approved, landed, reverted, blocked, rejected. |
| route | string | |
| limit | integer |
| change_idrequired | string | change id to act on |
| include | string | Comma-separated: diff, evidence, trace, decisions, track. |
| change_idrequired | string | change id to act on |
| change_idrequired | string | change id to act on |
| reason | string | |
| force | boolean | Admin override. Recorded on the change. |
| change_idrequired | string | change id to act on |
| revisionrequired | integer | Exact current numerical Change revision being released from Draft. |
| change_idrequired | string | change id to act on |
| reasonrequired | string | What went wrong. Shown on the author's record. |
| detected_by | string | Alert, on-call, customer report, canary… |
| change_idrequired | string | change id to act on |
| stepsrequired | object[] | |
| kindrequired | string | What the agent was doing at this step. |
| summaryrequired | string | One line, written for a human skimming the trace. |
| detail | string | Optional longer output: command, result, reasoning. |
| ts | string | ISO timestamp. Defaults to now. |
| tokens | integer | Tokens consumed by this step. |
| usd | number | Cost of this step in USD. |
| repo_id | string |
Takes no arguments.
| agent_idrequired | string | agent id to act on |
| repo_idrequired | string | repo id to act on |
| repo_idrequired | string | repo id to act on |
| rulesrequired | object[] | |
| limit | integer |
| repo_idrequired | string | repo id to act on |
| limit | integer |
| repo_idrequired | string | repo id to act on |
| repo_idrequired | string | repo id to act on |
| ref | string | Branch name. Defaults to the trunk branch. |
| path | string | Directory to list. Defaults to the repository root. |
| repo_idrequired | string | repo id to act on |
| path | string | File to read, from the repository root. |
| ref | string | Branch name. Defaults to the trunk branch. |
| limit | integer | |
| since | integer | |
| change_id | string |
| repo_idrequired | string | repo id to act on |
The catalog below is the full generic API-key/session MCP resource surface. Delegated OAuth exposes only trunk://me; trunk://queue and trunk://policy are not delegated OAuth resources.
| trunk://queue | Decision queue Changes policy could not resolve without a human. |
| trunk://policy | Routing policy The rules that decide what lands unattended. |
| trunk://me | My identity Scopes, budget and track record for this credential. |
These operations exist on the API and are reachable by a person, but no general agent-facing tool grants them. Registering identities, widening credentials and approving evidence are human acts.
POST/v1/repos/:repo_id/git-credentials | Issue a short-lived human Git HTTPS credential |
POST/v1/changes/:changeId/ci-dispatches | Dispatch or retry an exact required check in Trunk-managed CI |
POST/v1/repos/:repoId/deployments | Create or replay an exact-revision deployment and dispatch supported Preview providers |
PUT/v1/deployments/:deploymentId | Report provider-observed state for one deployment attempt |
GET/v1/repos/:repoId/check-requirements | Read the required external checks for a repository |
PUT/v1/repos/:repoId/check-requirements | Replace the required external checks for a repository |
GET/v1/changes/:changeId/checks | Read current revision-bound checks and the landing gate |
POST/v1/changes/:changeId/checks | Create a human-attested check run for the current change revision |
PUT/v1/checks/:checkId | Advance a human-attested check run and resume landing when the gate turns green |
POST/v1/auth/signup | Create an account |
POST/v1/auth/signin | Exchange an email and password for a session token |
POST/v1/auth/signout | End the current session |
POST/v1/repos | Create a repository |
POST/v1/changes/:change_id/decisions | Record a human decision or terminate a pre-evidence Change |
POST/v1/agents | Register an agent identity |
DELETE/v1/agents/:agent_id | Delete an agent that never did anything |
POST/v1/agents/:agent_id/scopes | Grant an agent a scope |
GET/v1/agents/:agent_id/keys | List an agent's active API keys |
POST/v1/agents/:agent_id/keys | Issue a new API key |
DELETE/v1/agents/:agent_id/keys/:key_id | Revoke an API key |
PUT/v1/agents/:agent_id/budget | Set an agent budget |
PUT/v1/repos/:repo_id/policy | Replace the routing policy (creates a new version) |