A2A agent
SHIP as an A2A agent. How another agent finds SHIP, hands it a task, follows the task to a verified pull request and stops it.
SHIP is an agent that other agents can call over the A2A protocol, version 1.0. Another agent can give SHIP a task description, an issue or an open pull request. SHIP's agents then plan, build, review and test the change in your repository, and the other agent follows the work to a verified pull request.
Each A2A task is one SHIP mission. The A2A server is a thin layer over the API, so a task obeys the same scopes, billing and project limits as any other mission.
Find the agent
The Agent Card is at the usual well-known path:
https://letsship.ai/.well-known/agent-card.jsonThe card names the endpoint, the skills and the security scheme. The endpoint uses the JSON-RPC binding:
https://mcp.letsship.ai/a2aAuthenticate
Each request carries a bearer token. The Agent Card offers two kinds, and either one works:
- An OAuth access token for this endpoint. The A2A endpoint is its own OAuth resource,
https://mcp.letsship.ai/a2a. Sign the person in with the authorization code flow and PKCE, and send that URL asresource(RFC 8707). A person approves the request in the SHIP console. A token for the API or the MCP server does not work here. - A SHIP personal access token. It is the same token that the API, the CLI and the MCP server take. A person creates it in the console.
A request without a token gets 401 with a WWW-Authenticate header. The header points to the metadata of this resource at https://mcp.letsship.ai/.well-known/oauth-protected-resource/a2a, and the metadata names the authorization server. A token that SHIP refuses gets 401 with error="invalid_token". Then refresh the token, or sign in again.
The scopes of the token limit what the client agent can do. See Authentication for scopes and project limits, and auth.md for the OAuth steps and the steps an agent gives its user.
| What the agent does | Scope it needs |
|---|---|
| Create a task, or hand over an issue or a pull request | missions:run |
| Read a task or list tasks | missions:read |
| Read the QA proof of a task | artifacts:read |
| Send an instruction, or stop a task | missions:write |
Each request also sends the header A2A-Version: 1.0. A request without it means version 0.3 to an A2A server, and SHIP answers it with VersionNotSupportedError.
Send a task
A mission runs for minutes to hours. So send each message with configuration.returnImmediately set to true, then poll GetTask. SHIP refuses a message in the default mode, which waits for a final state.
curl https://mcp.letsship.ai/a2a \
-H "Authorization: Bearer $SHIP_API_TOKEN" \
-H "A2A-Version: 1.0" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"messageId": "7f9c2ba4-e88f-11ee-9a1a-0242ac120002",
"role": "ROLE_USER",
"contextId": "proj_9b8e2a3f3c4d4e5f9a8b",
"parts": [{ "text": "Add a rate limit to the search endpoint\nReturn 429 with a Retry-After header over 60 requests a minute." }]
},
"configuration": { "returnImmediately": true }
}
}'contextIdis the SHIP project to work in. A token that is limited to one project can leave it out. An org-wide token must set it.- The first line of the text becomes the issue title. The full text becomes the issue body, so acceptance criteria written there apply as they do on any issue.
messageIdis the idempotency key. If you send the same message again, SHIP charges it once and starts the work once.
The answer is a task in the TASK_STATE_SUBMITTED state. Its id has the form <projectId>:<issueId>.
Hand over an issue or a pull request
Message metadata selects another way in:
| Metadata | What SHIP does |
|---|---|
issueId | Starts a mission for an issue that is already in the project's tracker |
startAt: "reviewer" or "qa" with prNumber | Gives an open pull request to the reviewer or the QA agent, with no plan or build stage first |
startAt: "builder" with plan | Starts at the builder with a plan you wrote |
title | Sets the issue title instead of the first line of the text |
labels | Adds tracker labels to a new issue |
Follow the task
GetTask returns the task's state, a status message and the artifacts produced so far:
| Artifact | Content |
|---|---|
pull-request | The pull request link, with its check, review and test states |
plan | The plan in Markdown, with its acceptance criteria and file list |
review | The reviewer's verdict and comments |
acceptance-tests | The QA verdict for each acceptance criterion |
proof-<id> | A screenshot, a video or a trace that QA captured as proof |
The state tells the client agent what to do next:
| State | What it means |
|---|---|
TASK_STATE_SUBMITTED | The mission is in the queue |
TASK_STATE_WORKING | An agent or a check moves the mission forward |
TASK_STATE_INPUT_REQUIRED | The mission waits for a person: the pull request is ready to merge, the mission is paused, it needs attention, or billing holds it. The status message says which, and what to do |
TASK_STATE_COMPLETED | The pull request merged |
TASK_STATE_CANCELED | A person stopped the mission, or closed the pull request without a merge |
ListTasks lists the tasks of a project, newest update first, 50 to a page.
Send changes
Send a message with the task's taskId to give the mission an instruction. By default the instruction asks the builder to change the open pull request. Set metadata.kind to note to record context for the next agent without a new run. The answer carries a receipt that says what the instruction will start.
Stop a task
CancelTask stops the mission. SHIP stops its agents, and the pull request stays open. The task answers TASK_STATE_CANCELED at once.
Limits
- SHIP serves the JSON-RPC binding of A2A 1.0 only.
- Streaming, push notifications and an extended Agent Card are not available. Poll
GetTaskfor progress. ListTasksdoes not filter by state or by time.- A message must contain text parts only.
How is this page?