Use AIP through A2A Use this guide to connect an A2A 1.0 client to getaip-server, discover the local Agent Card, invoke one native AIP capability, and read the projected task. The A2A profile translates JSON-RPC messages into native actions. AIP remains the owner of validation, authorization, policy, execution, and durable lifecycle state. The walkthrough uses one bearer identity on a loopback listener and invokes the built-in health capability. Later sections explain streaming, push notifications, and the current tenant boundary. The walkthrough does not qualify a production deployment or an independent A2A client. The examples target source revision d7cce13d1d555644d04a4d73c66c95b113737635. This authoring pass checked the source and snippet syntax without building the workspace or starting a service. Choose the A2A boundary deliberately Use A2A when an existing agent already implements the A2A task model and needs to call a capability in the local getaip-server manifest. Use native AIP when the client needs the complete envelope, verified tenant context, operational read model, or a protocol feature that the A2A projection does not expose. | Client need | Preferred path | | Discover local skills through an Agent Card | A2A | | Send a user message and follow a projected task | A2A | | Invoke a tenant-routed remote connector fleet | Native AIP or the documented MCP facade | | Preserve complete AIP identity, approval, transaction, and receipt semantics | Native AIP | | Launch a command-based compatibility client | MCP over stdio | An Agent Card is discovery data, not authorization. A visible skill can still require scopes, approval, idempotency, or other AIP policy. Know the current routes and names The preferred public routes are: | Purpose | Route | | Agent Card | GET /.well-known/agent-card.json | | A2A 1.0 JSON-RPC | POST /a2a/v1 | The Agent Card aliases /.well-known/agent.json and /a2a/agent-card return the same local projection. The JSON-RPC aliases /a2a and /aip/v1/a2a reach the same handler. Current operation names are SendMessage, SendStreamingMessage, GetTask, ListTasks, CancelTask, SubscribeToTask, the four task push-notification configuration operations, and GetExtendedAgentCard. Explicit v0.3 method aliases remain accepted for migration. New clients should send current names and should not infer a separate legacy task store. The A2A-Version request header is optional. When present, its value must be 1.0 at the reviewed revision. Prerequisites For the loopback walkthrough, you need: • the reviewed source checkout and its locked Rust dependencies; • curl and jq; • local TCP port 18080, or another unused loopback port used consistently; • permission to create local guide state and an owner-only token file; • two terminals, one for getaip-server and one for client commands. The example token is intentionally local and disposable. Do not reuse it on a shared or public endpoint. 1. Create the local bearer credential Create one owner-readable token file from the repository root: umask 077 printf '%s\n' 'a2a-guide-token' > .getaip-server-a2a-token getaip-server trims a conventional trailing line ending when it reads the secret file. Keep the file out of version control. 2. Start the authenticated A2A edge Start the daemon in the first terminal: cargo run --locked -p getaip-server -- \ --bind 127.0.0.1:18080 \ --service-id agent:getaip:server:a2a-guide \ --storage-dir .getaip-server-a2a-guide \ --native-bearer-token-file .getaip-server-a2a-token \ --native-principal service:a2a:guide \ --native-principal-scope action:read \ --native-principal-scope action:write Keep the process running. In the second terminal, verify readiness: curl -fsS http://127.0.0.1:18080/ready \ | jq -e '.status == "ready"' A successful check prints true. This result covers the local process and its required workers. It does not prove external connectivity, connector qualification, or production readiness. 3. Inspect the local Agent Card Agent Card discovery is public. Fetch it without the bearer token: curl -fsS http://127.0.0.1:18080/.well-known/agent-card.json \ | tee .getaip-server-a2a-card.json \ | jq -e ' .supportedInterfaces[0].url == "http://127.0.0.1:18080/a2a/v1" and .supportedInterfaces[0].protocolVersion == "1.0" and .capabilities.streaming == true and .capabilities.pushNotifications == false and .securitySchemes.aipBearer.httpAuthSecurityScheme.scheme == "Bearer" ' A successful check prints true. Push notification support is false in this walkthrough because no callback signer or credential-encryption key is configured. The card projects capabilities from the local AIP manifest into A2A skills. It declares content modes, the preferred JSON-RPC interface, and its bearer scheme. It does not expose secrets or grant task access. 4. Send one health message Create .getaip-server-a2a-send.json: { "jsonrpc": "2.0", "id": "guide-send-1", "method": "SendMessage", "params": { "message": { "messageId": "message-a2a-health-1", "taskId": "task-a2a-health-1", "contextId": "context-a2a-guide", "role": "ROLEUSER", "parts": [ { "data": {}, "mediaType": "application/json" } ] }, "metadata": { "aip": { "capabilityid": "cap:aip:server:health", "input": {} } } } } The current send mapping requires a non-empty messageId, the ROLEUSER role, at least one part, and metadata.aip.capabilityid. The profile maps messageId to the native idempotency key a2a-message:message-a2a-health-1. A skill label alone is not stable routing authority. Send the request and retain the response: A2ATOKEN="$(sed -n '1p' .getaip-server-a2a-token)" curl -fsS \ -H "Authorization: Bearer ${A2ATOKEN}" \ -H 'A2A-Version: 1.0' \ -H 'Content-Type: application/json' \ --data-binary @.getaip-server-a2a-send.json \ http://127.0.0.1:18080/a2a/v1 \ | tee .getaip-server-a2a-send-response.json \ | jq -e ' .error == null and .result.task.id == "task-a2a-health-1" and .result.task.status.state == "TASKSTATECOMPLETED" and any( .result.task.artifacts[]?.parts[]?; .text == "getaip-server is healthy" ) ' A successful check prints true. The response is an A2A task projection. Its metadata retains the native action identity needed for later lifecycle reads. When metadata.aip.input is absent, the profile derives input from the A2A message and its first text part. Supply explicit input when the native capability expects a structured contract. 5. Read the task again Extract the returned task ID and issue GetTask: A2ATOKEN="$(sed -n '1p' .getaip-server-a2a-token)" TASKID="$(jq -er '.result.task.id' .getaip-server-a2a-send-response.json)" curl -fsS \ -H "Authorization: Bearer ${A2ATOKEN}" \ -H 'A2A-Version: 1.0' \ -H 'Content-Type: application/json' \ --data-binary "$( jq -nc --arg id "${TASKID}" '{ jsonrpc: "2.0", id: "guide-get-1", method: "GetTask", params: {id: $id, historyLength: 5} }' )" \ http://127.0.0.1:18080/a2a/v1 \ | jq -e --arg id "${TASKID}" ' .error == null and .result.id == $id and .result.status.state == "TASKSTATECOMPLETED" ' A successful check prints true. historyLength keeps the newest requested history entries while preserving their native sequence order. Choose synchronous, asynchronous, or streaming work The send configuration changes response behavior, not the underlying AIP lifecycle: | A2A operation or option | Current behavior | | SendMessage | Dispatches through the gateway and returns a completed, submitted, or input-required task according to the native outcome | | SendMessage with configuration.returnImmediately: true | Requests native asynchronous mode and returns the durable task handle | | SendStreamingMessage | Forces asynchronous mode and emits task status and artifact updates as SSE until a terminal state | | SubscribeToTask | Opens a new SSE view for an existing nonterminal task | | GetTask | Reads one native status projection and optionally limits history | | ListTasks | Lists visible A2A-bound actions with filters and page sizes from 1 through 100 | | CancelTask | Persists native cancellation intent unless the task is already terminal | The streaming handler polls durable native action status and retained chunks. Its SSE events do not contain event IDs, and it does not implement Last-Event-ID. Reconnect a nonterminal task with SubscribeToTask and the task ID. Read a terminal task with GetTask; terminal-task subscription is rejected. A transport disconnect does not prove that a mutation failed. Read the task or native action before retrying the same logical work, and reuse the original messageId when idempotency should return the original result. Understand identity and tenant limits Every A2A JSON-RPC operation requires the configured native bearer token. The daemon compares the token in constant time, binds the configured principal and scopes, and constructs trusted gateway context. JSON-RPC metadata cannot choose the authenticated actor. Public Agent Card discovery remains unauthenticated. The current command-line A2A edge maps one static token to one principal. It does not perform OAuth introspection or map different external users to different AIP principals. A trusted edge can protect and rate-limit the route, but requests forwarded with the daemon token still share that one daemon-side identity. At this revision, A2A JSON-RPC dispatch calls the verified gateway without a VerifiedTenant. Treat a request tenant field as profile data or a list filter, not as tenant authority. When connector fleet services are enabled, the Agent Card advertises an optional tenant-capability-discovery extension that points to a separate authenticated native endpoint. It does not materialize the remote fleet catalog as Agent Card skills. Use native AIP or the stable MCP fleet facade when verified tenant routing is a requirement. Do not describe A2A task success as connector-fleet qualification. Enable push notifications safely getaip-server advertises A2A push notifications only when both controls are present: • an Ed25519 callback signer from --callback-signing-seed-file, its inline alternative, or the corresponding environment variable; • a 32-byte encryption key from GETAIPSERVERA2APUSHENCRYPTIONKEYHEX or the inline --a2a-push-encryption-key-hex option. Prefer a file for the signing seed and secret-manager injection for the encryption-key environment variable. Inline hexadecimal values can persist in shell history or process inspection. For a non-loopback deployment, the A2A-specific controls fit into a command such as this after the encryption-key environment variable is injected: cargo run --locked -p getaip-server -- \ --bind 0.0.0.0:18080 \ --public-base-url https://aip.example.com \ --service-id agent:getaip:server:a2a-production \ --postgres-url-file /run/secrets/getaip-server-postgres-url \ --native-bearer-token-file /run/secrets/getaip-server-native-token \ --native-principal service:a2a:trusted-edge \ --native-principal-scope action:read \ --native-principal-scope action:write \ --callback-signing-seed-file /run/secrets/aip-callback-signing-seed \ --callback-allowed-host callbacks.example.com Terminate TLS at a trusted edge and route the public HTTPS origin to the private listener. The external base URL must be an HTTPS origin without a path, query, fragment, or embedded credentials. Before accepting a push configuration, verify that: • the callback URL uses HTTPS and its exact host is allowlisted; • private and loopback destinations remain blocked unless an owned isolated network requires an explicit exception; • stored callback token and authorization credentials use the configured encryption key; • the receiver verifies the signed callback and treats deliveries as idempotent; • durable profile state, callback delivery state, recovery, backup, and key rotation are part of the deployment design. The delivery worker uses a durable cursor and fencing to coordinate recovery. That control suppresses competing recovery work but does not justify an exactly-once provider claim. Resolve common failures | Symptom | Likely boundary | Safe next check | | 401 Unauthorized | A2A bearer identity | Confirm --native-bearer-token-file, the exact Authorization header, and token-file permissions | | VERSIONNOTSUPPORTED | A2A header | Send A2A-Version: 1.0 or omit the optional header | | INVALIDPARAMS for send | Message mapping | Check messageId, ROLEUSER, at least one part, and metadata.aip.capabilityid | | Agent Card omits a fleet capability | Discovery boundary | Use the advertised tenant discovery extension, native AIP, or the stable MCP facade | | TASKNOTFOUND | Identity, task binding, or retention | Reuse the authenticated principal and exact returned task ID; then inspect native action state | | Streaming ended before the result was read | Connection boundary | Call GetTask; use SubscribeToTask only while the task is nonterminal | | Push support is false | Callback controls | Configure both the signer and A2A credential-encryption key, then restart the daemon | | Push configuration is rejected | Destination or ownership policy | Check task access, HTTPS, the exact host allowlist, private-network policy, and configuration ID conflicts | JSON-RPC application errors normally arrive in a successful HTTP response. Inspect the JSON error field even when curl reports HTTP success. Stop and clean up the walkthrough Stop getaip-server with Control-C. Inspect and remove only the local guide files: du -sh .getaip-server-a2a-guide 2>/dev/null || true rm -rf -- .getaip-server-a2a-guide rm -f -- \ .getaip-server-a2a-token \ .getaip-server-a2a-card.json \ .getaip-server-a2a-send.json \ .getaip-server-a2a-send-response.json Do not apply this cleanup to a production state path. Durable task, push, callback, and recovery records can reside there. Related documentation • Use native AIP (use-native-aip.md) • Profiles and connectors (../concepts/profiles-and-connectors.md) • Actions and sessions (../concepts/actions-and-sessions.md) • Identity and trust (../concepts/identity-and-trust.md) • Approvals and policy (../concepts/approvals-and-policy.md)