API

A2A and the agent card

Other agents discover this service through its A2A agent card, register themselves in one message, and call every tool as an A2A skill.

The agent card

  • Location: https://util.caruana.me/.well-known/agent-card.json (A2A 1.0).
  • Skills: 42, namely register plus one per API operation.
  • Security schemes: OAuth 2.0 client credentials (token URL /api/auth/token) and HTTP Basic.
  • Signature: when a signing key is configured, the card carries a JWS signature (EdDSA/Ed25519) over its RFC 8785 canonical form, with the public key at /.well-known/jwks.json . Verify it to be sure the card came from this domain and was not altered.

Establishing trust

The card declares two extensions. https://util.caruana.me/a2a/extensions/registration/v1 tells agents how to register and what we ask about them. https://util.caruana.me/a2a/extensions/data-policy/v1 states our data commitments in machine-readable form, so an agent (or its operator's policy engine) can decide before sending anything:

{
  "stores_inputs": false,
  "stores_inputs_exception": "tool_request: a request for a new tool is stored as sent, with your client_id, so it can be reviewed and answered. Never shown to other clients, except that an accepted request title may be listed as related.",
  "logs_inputs": false,
  "stores_outputs": false,
  "accepts_secrets": false,
  "creates_secrets": false,
  "creates_secrets_exception": "Your own client_secret, created at registration, shown once, and stored only as a SHA-256 hash.",
  "sensitive_by_nature": "jwt_decode receives tokens (an unexpired token is a credential) and url_parse may receive URLs with embedded passwords (never echoed back). Prefer expired or test tokens.",
  "outbound_requests": "None for tool calls, except tls_check, which asks a private checker service operated by this service to connect to the host you name.",
  "recorded_per_call": [
    "client_id",
    "time",
    "transport",
    "operation",
    "status",
    "error_code",
    "duration_ms",
    "request_bytes",
    "response_bytes",
    "country"
  ],
  "recorded_per_client": [
    "client_id",
    "secret_hash",
    "kind",
    "name",
    "expiry",
    "the profile you register with",
    "registration country and network operator (not the IP address)",
    "credential events (created, renewed, token issued)",
    "the tool requests you make (tool_request)"
  ],
  "request_log_retention_days": 90,
  "host": "Cloudflare Pages Functions; Cloudflare processes request metadata (IP, URL, headers) under its own policies.",
  "details": "https://util.caruana.me/security/"
}

In return, we ask agents to say who they are: who they act for, who operates them, where they run, their model and framework, their instructions, and how much oversight they have. That record is visible to the agent itself at any time via GET /api/auth/whoami.

Protocol

  • Endpoint: https://util.caruana.me/a2a, JSON-RPC 2.0 binding. Send A2A-Version: 1.0. Requests without the header are treated as A2A 0.3 (message/send, kind-tagged parts), as the specification requires.
  • Method: SendMessage. The reply is always a direct Message: no tasks are created, so GetTask returns -32001. Streaming, push notifications and cancellation return -32004.
  • Skills are selected by a data part {"skill": "<id>", …}:
    • register with registration (see the schema).
    • Any operation ID, with arguments: the same parameters as the HTTP API. This requires credentials (Basic or Bearer) on the HTTP request.
  • Replies contain a text part (a summary) and a data part (the structured result, or {"error","code","param"}).
  • Missing or invalid credentials return HTTP 401 with JSON-RPC error -32040, whose data.help explains how to register.

The handshake, as it actually runs

Generated at build time by sending these requests to the A2A handler. The registration was not stored; the identifiers are redacted.

1. Say hello: the agent replies with instructions

POST /a2a
A2A-Version: 1.0

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "SendMessage",
  "params": {
    "message": {
      "messageId": "msg-1",
      "role": "ROLE_USER",
      "parts": [
        {
          "text": "Hi, what can you do?"
        }
      ]
    }
  }
}
HTTP 200

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "message": {
      "messageId": "6b1c1785-ee3b-4a21-8edb-91cba7b413e1",
      "contextId": "0ff5e795-51c2-4232-bc5d-238d485a4e10",
      "role": "ROLE_AGENT",
      "parts": [
        {
          "text": "I respond to structured requests. Send a data part {\"skill\":\"register\",…} to get credentials, or {\"skill\":\"<operation>\",\"arguments\":{…}} to run a tool. Details are in the data part.",
          "mediaType": "text/plain"
        },
        {
          "data": {
            "service": "Caruana Utilities",
            "how_to_register": {
              "send": {
                "skill": "register",
                "registration": {
                  "client_name": "Release checker",
                  "contacts": [
                    "[email protected]"
                  ],
                  "software_id": "example-release-checker",
                  "software_version": "2.1.0",
                  "grant_types": [
                    "client_credentials"
                  ],
                  "agent": {
                    "name": "Release checker",
                    "description": "Verifies downloaded release artifacts before deployment.",
                    "purpose": "Verify SHA-256 checksums of release artifacts and decode their signing certificates.",
                    "on_behalf_of": {
                      "name": "Example Corp platform team",
                      "type": "organization",
                      "contact": "[email protected]"
                    },
                    "runtime": {
                      "model_provider": "Anthropic",
                      "model": "claude-sonnet-5-5",
                      "framework": "Claude Agent SDK"
                    },
                    "hosting": {
                      "provider": "Cloudflare Workers",
                      "country": "US"
                    },
                    "instructions": "Before each deployment, verify every artifact against the published SHA256SUMS and report mismatches.",
                    "autonomy": "supervised",
                    "intended_operations": [
                      "hash_verify",
                      "cert_decode"
                    ],
                    "expected_volume": "about 50 calls/day",
                    "identity": {
                      "agent_card_url": "https://agents.example.com/.well-known/agent-card.json",
                      "domain": "example.com"
                    }
                  }
                }
              },
              "note": "No credentials are needed to register. Only client_name and agent.purpose are required; please include as much as you can about who you act for, who operates you, where you run and your instructions. You receive a client_id and client_secret, shown once.",
              "alternative": "POST the registration document to https://util.caruana.me/api/auth/register"
            },
            "how_to_call_a_tool": {
              "send": {
                "skill": "hash",
                "arguments": {
                  "input": "abc",
                  "algorithm": [
                    "sha256"
                  ]
                }
              },
              "authentication": "Send \"Authorization: Basic base64(client_id:client_secret)\" or a Bearer token from /api/auth/token with the SendMessage request.",
              "skills": [
                "url_encode",
                "url_decode",
                "base64_encode",
                "base64_decode",
                "html_encode",
                "html_decode",
                "hex_encode",
                "hex_decode",
                "punycode_encode",
                "punycode_decode",
                "hash",
                "hash_verify",
                "fingerprint",
                "cert_decode",
                "tls_check",
                "jwt_decode",
                "url_parse",
                "uuid_generate",
                "uuid_inspect",
                "text_inspect",
                "time_now",
                "time_convert",
                "time_diff",
                "time_add",
                "cron_explain",
                "text_diff",
                "json_format",
                "json_schema_validate",
                "json_to_yaml",
                "yaml_to_json",
                "json_query",
                "yaml_format",
                "json_lint",
                "csv_format",
                "csv_to_json",
                "json_to_csv",
                "toml_format",
                "toml_to_json",
                "json_to_toml",
                "tool_request",
                "tool_requests"
              ],
              "reference": "https://util.caruana.me/reference/"
            },
            "data_policy": "https://util.caruana.me/.well-known/agent-card.json (extension data-policy/v1) and https://util.caruana.me/security/"
          },
          "mediaType": "application/json"
        }
      ]
    }
  }
}

2. Register: no credentials needed

POST /a2a
A2A-Version: 1.0

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "SendMessage",
  "params": {
    "message": {
      "messageId": "msg-1",
      "role": "ROLE_USER",
      "parts": [
        {
          "data": {
            "skill": "register",
            "registration": {
              "client_name": "Release checker",
              "contacts": [
                "[email protected]"
              ],
              "software_id": "example-release-checker",
              "software_version": "2.1.0",
              "grant_types": [
                "client_credentials"
              ],
              "agent": {
                "name": "Release checker",
                "description": "Verifies downloaded release artifacts before deployment.",
                "purpose": "Verify SHA-256 checksums of release artifacts and decode their signing certificates.",
                "on_behalf_of": {
                  "name": "Example Corp platform team",
                  "type": "organization",
                  "contact": "[email protected]"
                },
                "runtime": {
                  "model_provider": "Anthropic",
                  "model": "claude-sonnet-5-5",
                  "framework": "Claude Agent SDK"
                },
                "hosting": {
                  "provider": "Cloudflare Workers",
                  "country": "US"
                },
                "instructions": "Before each deployment, verify every artifact against the published SHA256SUMS and report mismatches.",
                "autonomy": "supervised",
                "intended_operations": [
                  "hash_verify",
                  "cert_decode"
                ],
                "expected_volume": "about 50 calls/day",
                "identity": {
                  "agent_card_url": "https://agents.example.com/.well-known/agent-card.json",
                  "domain": "example.com"
                }
              }
            }
          },
          "mediaType": "application/json"
        }
      ]
    }
  }
}
HTTP 200

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "message": {
      "messageId": "dcf31f6f-bceb-41dc-b38e-bccf2ed85cec",
      "contextId": "1e596eaf-2333-4df7-9739-d74e972689d8",
      "role": "ROLE_AGENT",
      "parts": [
        {
          "text": "Registered. Your client_id is cu_id_EXAMPLE. The client_secret is in the data part and is shown only once. Profile completeness: 94%.",
          "mediaType": "text/plain"
        },
        {
          "data": {
            "client_id": "cu_id_EXAMPLE",
            "client_secret": "cu_sk_… (shown once)",
            "client_id_issued_at": 1792024057,
            "client_secret_expires_at": 1794616057,
            "grant_types": [
              "client_credentials"
            ],
            "token_endpoint_auth_method": "client_secret_basic",
            "kind": "agent",
            "expires_at": "2026-11-14T00:27:37.152Z",
            "renewable_until": "2027-02-12T00:27:37.152Z",
            "policy": {
              "lifetime_days": 30,
              "renewal": "POST https://util.caruana.me/api/auth/renew with the same credentials extends expiry to 30 days from the renewal. Expired credentials can be renewed for 90 days after expiry.",
              "rate_limit_per_minute": 60
            },
            "usage": {
              "basic": "Authorization: Basic base64(\"cu_id_EXAMPLE:<client_secret>\")",
              "token_endpoint": "https://util.caruana.me/api/auth/token",
              "api": "https://util.caruana.me/reference/",
              "mcp": "https://util.caruana.me/mcp",
              "a2a": "https://util.caruana.me/a2a"
            },
            "important": "The client_secret is shown once and stored only as a SHA-256 hash. It cannot be recovered; keep it in a secret store.",
            "profile_completeness": {
              "percent": 94,
              "missing": [
                "agent.operator.name"
              ],
              "note": "Optional, but the more you tell us about who you are and who you act for, the easier it is to trust and support you."
            }
          },
          "mediaType": "application/json"
        }
      ]
    }
  }
}

3. Call a tool without credentials

POST /a2a
A2A-Version: 1.0

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "SendMessage",
  "params": {
    "message": {
      "messageId": "msg-1",
      "role": "ROLE_USER",
      "parts": [
        {
          "data": {
            "skill": "hash",
            "arguments": {
              "input": "abc",
              "algorithm": [
                "sha256"
              ]
            }
          },
          "mediaType": "application/json"
        }
      ]
    }
  }
}
HTTP 401

{
  "jsonrpc": "2.0",
  "id": 3,
  "error": {
    "code": -32040,
    "message": "Credentials required. Register for a client ID and secret: agents via POST https://util.caruana.me/api/auth/register (or A2A SendMessage at https://util.caruana.me/a2a), people via https://util.caruana.me/credentials/. Then send \"Authorization: Basic base64(client_id:client_secret)\" or a Bearer token from https://util.caruana.me/api/auth/token. Docs: https://util.caruana.me/reference/auth/",
    "data": {
      "code": "unauthenticated",
      "help": "Register for a client ID and secret: agents via POST https://util.caruana.me/api/auth/register (or A2A SendMessage at https://util.caruana.me/a2a), people via https://util.caruana.me/credentials/. Then send \"Authorization: Basic base64(client_id:client_secret)\" or a Bearer token from https://util.caruana.me/api/auth/token. Docs: https://util.caruana.me/reference/auth/"
    }
  }
}