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.json

The card names the endpoint, the skills and the security scheme. The endpoint uses the JSON-RPC binding:

https://mcp.letsship.ai/a2a

Authenticate

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 as resource (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 doesScope it needs
Create a task, or hand over an issue or a pull requestmissions:run
Read a task or list tasksmissions:read
Read the QA proof of a taskartifacts:read
Send an instruction, or stop a taskmissions: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 }
    }
  }'
  • contextId is 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.
  • messageId is 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:

MetadataWhat SHIP does
issueIdStarts a mission for an issue that is already in the project's tracker
startAt: "reviewer" or "qa" with prNumberGives an open pull request to the reviewer or the QA agent, with no plan or build stage first
startAt: "builder" with planStarts at the builder with a plan you wrote
titleSets the issue title instead of the first line of the text
labelsAdds tracker labels to a new issue

Follow the task

GetTask returns the task's state, a status message and the artifacts produced so far:

ArtifactContent
pull-requestThe pull request link, with its check, review and test states
planThe plan in Markdown, with its acceptance criteria and file list
reviewThe reviewer's verdict and comments
acceptance-testsThe 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:

StateWhat it means
TASK_STATE_SUBMITTEDThe mission is in the queue
TASK_STATE_WORKINGAn agent or a check moves the mission forward
TASK_STATE_INPUT_REQUIREDThe 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_COMPLETEDThe pull request merged
TASK_STATE_CANCELEDA 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 GetTask for progress.
  • ListTasks does not filter by state or by time.
  • A message must contain text parts only.

How is this page?

On this page