# Conformance and debugging tools for A2A v1.0 Surveyed 2026-10-06 against the manual's pinned specification (A2A v1.0.1, 2026-05-28). Clones live under `research/repos/`; each tool was checked out at its latest release tag, with a second clone of `main` where the tag is far behind. All claims link to the checked-out revision. Nothing was executed; the commands below are what the repositories document. | Tool | Repository | Checked out | Date | Protocol version it targets | |---|---|---|---|---| | a2a-tck | https://github.com/a2aproject/a2a-tck | tag `1.0.0.alpha2` = `29063fe95e903cddac5d8ff811ab94df1ad6ef86` (plus `main` = `263b9cfaf16a554bdfb166a7ba5b67716e946349`, 2026-09-01, in `a2a-tck-main`) | 2026-05-27 | A2A v1.0.0 (specification pinned to the A2A repository's `v1.0.0` branch at commit `1736957`) | | a2a-inspector | https://github.com/a2aproject/a2a-inspector | tag `v0.1.0` = `0792c21ca592f5a760fcdbcf0ba24e94ccca45be` (plus `main` = `8aa064639af106ff771d60428ef6d460f5454743`, 2026-08-08, in `a2a-inspector-main`) | 2025-06-18 | the tag is a v0.2-era tool; `main` accepts both v0.3 and v1.0 payloads | | a2a-cli | https://github.com/a2aproject/a2a-cli | tag `v0.3.0` = `6286ed6a9a1f8c7360c03b5e832811fe0896414a` | 2026-09-24 | A2A v1.0 (built on a2a-go v2.5.0; `--protocol 0.3` server compat mode) | | a2a-itk | https://github.com/a2aproject/a2a-itk | no tags or releases; `main` = `b57c5332aa883b27c1e5c915fe61cac76d2a1de9` | 2026-09-30 | v1.0 SDK lines plus v0.3 lines for Python, Go and TypeScript | Release facts come from `gh release list` and `git tag`: a2a-tck has tags but no GitHub Releases (`1.0.0.alpha2`, `1.0.0.alpha1` 2026-05-13, and a `0.3.0.beta*` line ending at `0.3.0.beta5` 2026-05-14); a2a-inspector has exactly one release, `v0.1.0`; a2a-cli has `v0.3.0` (latest) and `v0.2.0` (2026-09-09); a2a-itk has none. ## a2a-tck: the Technology Compatibility Kit ### What it is "A compatibility test suite that validates A2A (Agent-to-Agent) Protocol implementations across three transports: gRPC, JSON-RPC, and HTTP+JSON" ([README.md:3](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/README.md#L3-L3)). It is a pytest project: `tck/` holds requirement specs, transport clients, validators and reporting; `tests/compatibility/` holds the conformance tests that need a running System Under Test (SUT); `tests/unit/` needs none ([AGENTS.md:7-35](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/AGENTS.md#L7-L35)). Package version `1.0.0`, license metadata `MIT` at the tag ([pyproject.toml:5-14](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/pyproject.toml#L5-L14)); `main` later aligned the license metadata (`cd34e1f`, "fix: align package license metadata (#199)"). ### Protocol version The bundled specification is a snapshot of the A2A repository's `v1.0.0` branch at commit `173695755607e884aa9acf8ce4feed90e32727a1`, downloaded 2026-03-13 ([specification/version.json:1-12](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/specification/version.json#L1-L12)); `main` carries the same pin. The TCK therefore targets v1.0.0, not the v1.0.1 patch the manual uses. JSON Schema (`a2a.json`) and generated proto stubs are derived from that snapshot (`specification/` directory; `make spec` and `make proto` regenerate them, [README.md:131-137](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/README.md#L131-L137)). ### How to run it against a server Clone and check out the tag, install, run: ```bash git clone --filter=blob:none https://github.com/a2aproject/a2a-tck.git cd a2a-tck git checkout 1.0.0.alpha2 uv venv && source .venv/bin/activate uv pip install -e . ./run_tck.py --sut-host http://localhost:9999 ``` The install and quick-start lines are [README.md:12-34](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/README.md#L12-L34); the bundled `run-tck` skill says to prefix the runner with `uv run` and to run from the TCK root ([.agents/skills/run-tck/SKILL.md:61-80](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/.agents/skills/run-tck/SKILL.md#L61-L80)). Python 3.11+ and `uv` are required ([README.md:7-8](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/README.md#L7-L8)). `run_tck.py` is a thin wrapper that builds one pytest command ([run_tck.py:34-72](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/run_tck.py#L34-L72)): ``` python -m pytest tests/compatibility/ --sut-host= --tb=short [--transport=grpc,jsonrpc,http_json] [-m must|should|may] [-q|-v|-v -s --log-cli-level=INFO] --compatibility-report=reports/compatibility --html=reports/tck_report.html --self-contained-html --junitxml=reports/junitreport.xml [extra pytest args after --] ``` Flags ([README.md:40-51](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/README.md#L40-L51)): `--sut-host URL` (required), `--transport LIST`, `--level must|should|may`, `-v`, `--verbose-log`, and `-- ` (for example `-- -x --pdb`). One more pytest option is not in the README: `--webhook-host` names the host the SUT uses to reach the TCK's own webhook receiver for the push-delivery tests (default `localhost`) ([tests/compatibility/conftest.py:70-78](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/conftest.py#L70-L78)); the receiver is a small `http.server` started once per session ([tck/webhook/server.py:1-5](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/webhook/server.py#L1-L5), [conftest.py:187-190](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/conftest.py#L187-L190)). Transport discovery: the TCK fetches `{sut-host}/.well-known/agent-card.json` and builds one client per `supportedInterfaces[]` entry, keyed by `protocolBinding` (`JSONRPC`, `GRPC`, `HTTP+JSON`) at that interface's `url`; with no interfaces it fails outright ([conftest.py:85-140](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/conftest.py#L85-L140), [README.md:84-94](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/README.md#L84-L94)). All three bindings are covered: gRPC, JSON-RPC and HTTP+JSON, each with its own test package ([tests/compatibility/grpc](https://github.com/a2aproject/a2a-tck/tree/1.0.0.alpha2/tests/compatibility/grpc), [jsonrpc](https://github.com/a2aproject/a2a-tck/tree/1.0.0.alpha2/tests/compatibility/jsonrpc), [http_json](https://github.com/a2aproject/a2a-tck/tree/1.0.0.alpha2/tests/compatibility/http_json)) and the cross-transport tests parametrized over `ALL_TRANSPORTS`. SUT generation: Gherkin scenarios in `scenarios/` (`core_operations.feature`, `streaming.feature`) feed a code generator that emits a Java/Quarkus SUT into the a2a-java repository's `tck` module (`A2A_JAVA_DIR=... make codegen-a2a-java-sut`, [README.md:107-127](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/README.md#L107-L127)); the tag also ships generated `sut/a2a-python` and `sut/a2a-jakarta` trees, and `main` documents both as targets (`make codegen-a2a-python-sut`, `uv run python sut_agent.py` on `:9999` and gRPC `:10000`; `make codegen-a2a-jakarta-sut`, `mvn wildfly:run`) ([main README.md:129-161](https://github.com/a2aproject/a2a-tck/blob/263b9cfaf16a554bdfb166a7ba5b67716e946349/README.md#L129-L161)). ### Compliance levels and categories Levels follow RFC 2119 ([README.md:66-82](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/README.md#L66-L82)): | Level | Meaning | Test behavior | |---|---|---| | MUST | absolute requirement | hard failure | | SHOULD | expected unless there is a reason to differ | `xfail`, does not block compatibility | | MAY | optional | skipped when the agent card does not declare the capability | Markers: `core`, `must`, `should`, `may`, `grpc`, `jsonrpc`, `http_json`, `streaming`, `integration` ([pyproject.toml:52-62](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/pyproject.toml#L52-L62), [tests/compatibility/markers.py:17-25](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/markers.py#L17-L25)). Requirement families are identified by prefix ([AGENTS.md:46-52](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/AGENTS.md#L46-L52)); the registry at the tag defines: `CORE-SEND/STREAM/GET/LIST/CANCEL/EXECUTION-MODE/ERR/CAP/MULTI/HIST-*` ([tck/requirements/core_operations.py](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/core_operations.py)), `STREAM-*` (streaming.py), `PUSH-CREATE/GET/LIST/DEL/DELIVER-*` ([push_notifications.py](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/push_notifications.py)), `CARD-DISC/PROTO/STRUCT/EXT/SIGN/CACHE-*` ([agent_card.py](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/agent_card.py)), `AUTH-TLS/SERVER/INTASK/SCOPE-*` ([auth.py](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/auth.py)), `VER-CLIENT/SERVER-*` ([versioning.py](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/versioning.py)), `DM-*` (data_model.py), `BIND-*` (interop.py), `JSONRPC-FMT/SVC/ERR/SSE-*`, `HTTP_JSON-URL/SVC/QP/ERR/STATUS/SSE-*`, `GRPC-SVC/META/ERR-*`. Having a requirement ID does not mean having a test: requirements with an `operation` and no `multi-operation` tag are run generically (send the `sample_input`, validate the response against the JSON schema or proto type, run any custom validators) ([tests/compatibility/core_operations/test_requirements.py:68-83](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_requirements.py#L68-L83), [89-121](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_requirements.py#L89-L121), [149-195](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_requirements.py#L149-L195)); multi-operation ones need a dedicated test, and families such as `AUTH-SCOPE-*` and `CARD-SIGN-*` have no test at the tag (a grep of `tests/compatibility` finds no reference to them). The tag has 129 test functions; `main` has 135. ### What the report looks like Four files land in `reports/` after every run ([README.md:96-105](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/README.md#L96-L105)): `compatibility.json`, `compatibility.html` (self-contained, with an executive summary, agent-card details and per-test results), `tck_report.html` (pytest-html) and `junitreport.xml`. The JSON has three parts ([tck/reporting/json_formatter.py:37-68](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/reporting/json_formatter.py#L37-L68)): ```json { "summary": {"timestamp": ..., "sut_url": ..., "spec_version": ..., "overall_compatibility": "97.3%", "must_compatibility": ..., "should_compatibility": ..., "may_compatibility": ...}, "per_requirement": {"CORE-SEND-002": {"level": "MUST", "status": "PASS", "transports": {"jsonrpc": "PASS", "grpc": "FAIL"}, "errors": [...], "test_ids": [...]}}, "per_transport": {"jsonrpc": {"total": n, "passed": n, "failed": n, "skipped": n}}, "agent_card": {...} } ``` Per-requirement status is `PASS`, `FAIL` or `SKIPPED`, computed per transport and then combined (any failure fails the requirement) ([tck/reporting/aggregator.py:105-125](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/reporting/aggregator.py#L105-L125)); requirements in the registry that no test touched are listed as `NOT TESTED` ([aggregator.py:164](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/reporting/aggregator.py#L164-L164)). The percentages exclude `SKIPPED` and `NOT TESTED` rows ([aggregator.py:200-203](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/reporting/aggregator.py#L200-L203)); `main` fixed a bug where `NOT TESTED` counted as compatible (`cf4985b`, "#218"). The console prints a header, SUT and spec version, the overall percentage (green at 100%, yellow at 80% and above), a level table, a per-transport summary and the failed requirements ([tck/reporting/console_formatter.py:45-55](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/reporting/console_formatter.py#L45-L55), [71-80](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/reporting/console_formatter.py#L71-L80)). The HTML report links each requirement to the pinned specification source ([tck/reporting/html_formatter.py:22-32](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/reporting/html_formatter.py#L22-L32)). ### Which of the fourteen behaviors it tests | # | Behavior | Covered? | What the TCK does | Where | |---|---|---|---|---| | B1 | REST subscribe verb | partly, by construction | The TCK's own binding table makes subscribe `POST /tasks/{id}:subscribe`, so its HTTP+JSON client always sends POST; `test_http_methods_correct` checks the TCK's constants, not the server, and omits subscribe. A GET-only server fails every REST streaming test; a server that accepts both is never told it is lenient. | [tck/requirements/base.py:147-152](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/base.py#L147-L152); [tests/compatibility/core_operations/test_transport_behavior.py:352-376](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_transport_behavior.py#L352-L376) | | B2 | When the stream closes | terminal only | `STREAM-SUB-002`: subscribe to a working task, complete it, expect the stream to end; `STREAM-SUB-003`: subscribe to a terminal task, expect an error (immediately or on first iteration). Nothing tests `INPUT_REQUIRED` or `AUTH_REQUIRED`; stream-ordering tests only rank those states as "active". | [test_task_lifecycle.py:363-420](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_task_lifecycle.py#L363-L420), [422-462](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_task_lifecycle.py#L422-L462); [test_stream_ordering.py:58-67](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_stream_ordering.py#L58-L67) | | B3 | Push notification request | partly | `PUSH-DELIVER-001` expects `Authorization: Bearer ` on the webhook call; `PUSH-DELIVER-002` expects at least one delivery; `PUSH-DELIVER-003` validates the body against the `StreamResponse` JSON schema. `Content-Type`, the token header and retries are not checked. All three skip when the card lacks `pushNotifications`. | [test_push_notifications.py:416-449](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_push_notifications.py#L416-L449), [451-479](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_push_notifications.py#L451-L479), [481-515](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_push_notifications.py#L481-L515); config helper [60-84](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_push_notifications.py#L60-L84) | | B4 | `historyLength` unset | the set cases only | `CORE-HIST-001` (SHOULD): `historyLength=0` on GetTask returns no history; `CORE-HIST-002` (MUST): `historyLength=1` returns at most one message; `CORE-HIST-003` (SHOULD): `historyLength=0` on SendMessage. The unset case and negative values are untested. | [test_task_history.py:55-88](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_task_history.py#L55-L88), [90-120](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_task_history.py#L90-L120), [128-177](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_task_history.py#L128-L177); specs [core_operations.py:686-737](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/core_operations.py#L686-L737) | | B5 | ListTasks pagination | schema only | `CORE-LIST-001..005` exist (authorized tasks only, cursor pagination, sort order, `nextPageToken == ""` on the last page, artifacts omitted) but run through the generic runner: one `ListTasks` call with the sample input, validated against the `ListTasksResponse` schema. Token format, `pageSize` bounds, ordering and the empty-string rule are not asserted. | [core_operations.py:236-327](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/core_operations.py#L236-L327); runner [test_requirements.py:149-195](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_requirements.py#L149-L195) | | B6 | Message to a terminal task | yes, error only | `CORE-SEND-002`: complete a task, send a follow-up with its `taskId`, pass if any error comes back (the code is not checked here). The -32004 / 400 / gRPC checks exist only for the "streaming not supported" trigger and skip when the card declares streaming. The TCK's gRPC expectation for `UnsupportedOperationError` is `UNIMPLEMENTED`, not the `FAILED_PRECONDITION` of spec §5.4. | [test_task_lifecycle.py:254-288](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_task_lifecycle.py#L254-L288); [jsonrpc/test_error_codes.py:162-195](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/jsonrpc/test_error_codes.py#L162-L195); [http_json/test_http_status.py:106-136](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/http_json/test_http_status.py#L106-L136); [grpc/test_status_codes.py:127-155](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/grpc/test_status_codes.py#L127-L155); [base.py:285-291](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/base.py#L285-L291) | | B7 | `A2A-Version` header | partly | `VER-SERVER-002`: `A2A-Version: 99.0` must produce an error on JSON-RPC and REST; the JSON-RPC error is validated as `VersionNotSupportedError` (-32009) and the REST one as HTTP 400; gRPC metadata `a2a-version: 99.0` is expected to give `UNIMPLEMENTED` (spec: `FAILED_PRECONDITION`). `VER-SERVER-003` sends an empty header and accepts either a result or an error ("never crash"), so a server that ignores a missing header passes. The query parameter is untested. | [test_error_handling.py:392-416](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_error_handling.py#L392-L416), [418-442](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_error_handling.py#L418-L442), [444-470](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_error_handling.py#L444-L470); [jsonrpc/test_error_codes.py:245-288](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/jsonrpc/test_error_codes.py#L245-L288); [http_json/test_http_status.py:189-230](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/http_json/test_http_status.py#L189-L230); [grpc/test_status_codes.py:183-245](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/grpc/test_status_codes.py#L183-L245); [base.py:325-331](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/base.py#L325-L331) | | B8 | Content type of responses | yes, but against `application/json` | `JSONRPC-FMT-002`: JSON-RPC success `Content-Type` must contain `application/json`; `HTTP_JSON-SVC-001`: REST success must contain `application/json`; `HTTP_JSON-ERR-001`: REST error must contain `application/json`; streaming must be `text/event-stream`. The checks are substring tests, and `application/a2a+json` does not contain `application/json`, so a REST server that follows §11.1 (Python 1.2.2 on success, JS 1.3.0, Rust) fails `HTTP_JSON-SVC-001`; `main` has not changed these assertions (no `a2a+json` anywhere in its tests). | [test_transport_behavior.py:100-112](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_transport_behavior.py#L100-L112), [207-232](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_transport_behavior.py#L207-L232), [249-261](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_transport_behavior.py#L249-L261); [http_json/test_problem_details.py:84-116](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/http_json/test_problem_details.py#L84-L116) | | B9 | Message with only `taskId` | weakly | `CORE-MULTI-005`: create a working task, send a follow-up with only `taskId`, pass if the call succeeds. Which `contextId` the server inferred, and what it stored, are not inspected. | [test_task_lifecycle.py:290-319](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_task_lifecycle.py#L290-L319); spec [core_operations.py:654-668](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/core_operations.py#L654-L668) | | B10 | Mismatched `taskId` and `contextId` | yes, error only | `CORE-MULTI-006`: send `taskId` of a completed task with a wrong `contextId`, pass on any error. Because the task is terminal, a server that checks the terminal state first (JS, Rust) passes for the wrong reason, and the error code is not checked. | [test_task_lifecycle.py:321-350](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_task_lifecycle.py#L321-L350); spec [core_operations.py:669-684](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/core_operations.py#L669-L684) | | B11 | `append: true` to an unknown artifact | no | No test or requirement sends an artifact update; `append` does not occur in `tests/compatibility` outside list operations. | [tests/compatibility/core_operations/test_artifacts.py](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_artifacts.py) only checks artifact part types | | B12 | Another caller's task | no | `CORE-LIST-001` ("only authorized tasks") runs as a single-identity schema check; `AUTH-SCOPE-001..003` are defined but have no tests at the tag or on `main`. | [core_operations.py:237-253](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/core_operations.py#L237-L253); [auth.py:168-206](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/auth.py#L168-L206) | | B13 | Extended agent card | capability errors only | `CORE-CAP-003`: when the card does not declare `extendedAgentCard`, `GetExtendedAgentCard` must fail (JSON-RPC only); when it does declare it, the test skips. Authentication is never exercised; `CARD-EXT-001/002` are defined, and `main` added a probe-and-route precondition for them (`263b9cf`, "#188/#205") and skips `CARD-EXT-002` when a card is configured (`5996b79`). | [test_error_handling.py:357-379](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/core_operations/test_error_handling.py#L357-L379); [agent_card.py:94-129](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/agent_card.py#L94-L129) | | B14 | Duplicate `messageId` | no | Every test mints a session-unique id (`tck_id`); nothing resends one. | [base.py:21-32](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/base.py#L21-L32) | Where the TCK's own error table departs from spec §5.4 ([tck/requirements/base.py:269-331](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tck/requirements/base.py#L269-L331)): `TaskNotCancelableError` HTTP 409 (spec 400), `PushNotificationNotSupportedError` gRPC `UNIMPLEMENTED` (spec `FAILED_PRECONDITION`), `UnsupportedOperationError` gRPC `UNIMPLEMENTED` (spec `FAILED_PRECONDITION`), `ContentTypeNotSupportedError` HTTP 415 (spec 400), `InvalidAgentResponseError` HTTP 502 (spec 500), `VersionNotSupportedError` gRPC `UNIMPLEMENTED` (spec `FAILED_PRECONDITION`). The HTTP+JSON status test for cancel also expects 409 ([http_json/test_http_status.py:78](https://github.com/a2aproject/a2a-tck/blob/1.0.0.alpha2/tests/compatibility/http_json/test_http_status.py#L78-L78)). Java's `UNIMPLEMENTED` for `UnsupportedOperationError` matches the TCK; Go, JS, Rust and .NET (main) match the specification and would fail the gRPC status tests on those paths. ### Differences on `main` (14 commits after the tag, to 2026-09-01) Fixes that change results: assert the specific spec-mandated error rather than any error (`de6af18`, "#207"); read a streamed error body before close so non-2xx streaming errors are recognised (`38ab89e`); send a valid JSON body in the wrong-Content-Type test (`37d4944`); follow redirects in the harness HTTP clients (`c478be0`); stop counting `NOT TESTED` as compatible (`cf4985b`); recover the requirement and transport in the safety-net hook when a test crashes (`14b3761`); probe-and-route the `CARD-EXT` precondition (`263b9cf`); update the a2a-python reference SUT for the current SDK routing API (`107a5fd`). The specification pin and the content-type substring checks are unchanged. ## a2a-inspector: the browser debugger ### What it is "A web-based tool designed to help developers inspect, debug, and validate servers that implement the A2A (Agent2Agent) protocol": connect to an agent by base URL, view its card, run "basic validation on the agent card", chat with the agent, and watch the raw messages in a debug console ([main README.md:3-13](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/README.md#L3-L13)). FastAPI backend plus a TypeScript frontend, joined by socket.io ([main README.md:5](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/README.md#L5-L5), [backend/app.py:15](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/backend/app.py#L15-L15)). ### Which protocol version The only release, `v0.1.0` (2025-06-18), predates v1.0: it depends on `a2a-sdk>=0.2.6` ([pyproject.toml:7](https://github.com/a2aproject/a2a-inspector/blob/v0.1.0/pyproject.toml#L7-L7)), requires a top-level `url` on the card ([backend/validators.py:9-20](https://github.com/a2aproject/a2a-inspector/blob/v0.1.0/backend/validators.py#L9-L20)), expects a `kind` discriminator and lowercase `role: "agent"` on every event ([validators.py:97-120](https://github.com/a2aproject/a2a-inspector/blob/v0.1.0/backend/validators.py#L97-L120)), and talks JSON-RPC only through `A2AClient` ([backend/app.py:221-223](https://github.com/a2aproject/a2a-inspector/blob/v0.1.0/backend/app.py#L221-L223)). It cannot inspect a v1.0 agent. `main` (66 commits later, 2026-08-08) is the usable version: it depends on `a2a-sdk[all]>=1.1.2` ([pyproject.toml:7](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/pyproject.toml#L7-L7)), its validators accept "both v0.3 (Pydantic-based, camelCase JSON) and v1.0 (protobuf-based ...)" and list the differences they tolerate (no `final`, `TASK_STATE_*` names, flat parts, `supportedInterfaces`) ([backend/validators.py:1-15](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/backend/validators.py#L1-L15)), and it builds the client with `ClientFactory` over JSON-RPC, HTTP+JSON and gRPC, falling back to the v0.3 constructor signature when the installed SDK is old ([backend/app.py:340-366](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/backend/app.py#L340-L366), [586-592](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/backend/app.py#L586-L592)). The transport actually used is the card's first interface ([app.py:131-148](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/backend/app.py#L131-L148)). It reports the version it is speaking only indirectly, through the SDK it imports. ### What it checks Agent card ([main validators.py:61-135](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/backend/validators.py#L61-L135)): `name`, `description`, `version`, `skills` present; either `url` (absolute `http(s)`) or a non-empty `supportedInterfaces` whose entries have `url` and `protocolBinding` (or legacy `transport`); `capabilities` is an object; `defaultInputModes`/`defaultOutputModes` are arrays of strings; `skills` non-empty. Results arrive as `validation_errors` next to the card ([app.py:517-521](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/backend/app.py#L517-L521)). Each streamed event ([validators.py:138-214](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/backend/validators.py#L138-L214)): a task has `id` and a recognised `status.state`; a status update has a recognised state; an artifact update has a non-empty `parts` array; a message has non-empty parts and role `agent`, `ROLE_AGENT` or `2`. The backend adds a `kind` (`task`, `message`, `status-update`, `artifact-update`) from the SDK type name and lowercases v1.0 state names before validation and display ([app.py:228-300](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/backend/app.py#L228-L300)). It does not check the `A2A-Version` header, error codes, content types, pagination or any of the fourteen behaviors above; "spec compliance" here means the structural checks listed. ### How to run it From `main` ([README.md:34-131](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/README.md#L34-L131)); prerequisites Python 3.10+, `uv`, Node.js and npm ([README.md:15-19](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/README.md#L15-L19)): ```bash git clone https://github.com/a2aproject/a2a-inspector.git && cd a2a-inspector uv sync (cd frontend && npm install) bash scripts/run.sh # frontend watch build + backend together # or, in two terminals: (cd frontend && npm run build -- --watch) (cd backend && uv run -- uvicorn app:app --host 127.0.0.1 --port 5001 --reload) # or Docker: docker build -t a2a-inspector . && docker run -d -p 8080:8080 a2a-inspector ``` Then open `http://127.0.0.1:5001` (or `:8080` for Docker) and enter the agent's base URL. Custom request headers typed into the UI are forwarded to the agent, which is how a bearer token reaches an authenticated agent ([app.py:490-495](https://github.com/a2aproject/a2a-inspector/blob/8aa064639af106ff771d60428ef6d460f5454743/backend/app.py#L490-L495)). ## a2a-cli: the official command-line client ### What it is The blog post of 2026-10-01 ("Meet the A2A CLI", `research/v2/data/main/docs/blog/posts/introducing-a2a-cli.md`) introduces `a2a` as "the official command-line client: one standard command surface for every A2A agent", "built on the CLI from the A2A Go SDK" and speaking "A2A Protocol v1.0". The repository is https://github.com/a2aproject/a2a-cli ([README.md:22-34](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/README.md#L22-L34)), Apache-2.0, latest release `v0.3.0` (2026-09-24), with a `CHANGELOG.md` whose first entries are the 0.2.0 import of the Go CLI and a reconciliation against the project's own `specification/SPEC.md` ([CHANGELOG.md:1-30](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/CHANGELOG.md#L1-L30)). The internal reference still carries an "alpha stage" warning ([internal/README.md:3-4](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/internal/README.md#L3-L4)). Not to be confused with the Rust workspace's crate of the same name: a2a-rs publishes a crate `a2a-cli` (binary `a2acli`, tags `a2a-cli-v0.3.1`), "Standalone A2A CLI client built on top of `a2a-client`" ([a2a-rs a2acli/Cargo.toml:1-16](https://github.com/a2aproject/a2a-rs/blob/a2a-cli-v0.3.1/a2acli/Cargo.toml#L1-L16), [a2acli/README.md:1-16](https://github.com/a2aproject/a2a-rs/blob/a2a-cli-v0.3.1/a2acli/README.md#L1-L16)). The blog post's tool is the Go one. ### Protocol version `go.mod` requires `github.com/a2aproject/a2a-go/v2 v2.5.0` (the 1.0 SDK) and `github.com/a2aproject/a2a-go v0.3.15` (the 0.3 SDK, for the compat server mode) ([go.mod:5-7](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/go.mod#L5-L7)). The specification it implements says "Applies to: A2A Protocol v1.0 — built against A2A v1.0.0 as its baseline" ([specification/SPEC.md:6](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/specification/SPEC.md#L6-L6)). The client sends whatever a2a-go's client sends (`A2A-Version: 1.0`, see B7); `--protocol 0.3` switches the built-in server to the 0.3 compat transports ([internal/README.md:279](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/internal/README.md#L279-L279); [internal/cli/client.go:160](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/internal/cli/client.go#L160-L160)). The Homebrew formula in the tag still pins 0.2.0 ([Formula/a2a.rb:8-13](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/Formula/a2a.rb#L8-L13)). ### Install ```bash brew tap a2aproject/a2a-cli https://github.com/a2aproject/a2a-cli && brew install a2a # macOS, Linux winget install a2aproject.a2acli # Windows go install github.com/a2aproject/a2a-cli@latest # names the binary a2a-cli; rename to a2a ``` ([README.md:36-62](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/README.md#L36-L62)). Prebuilt archives with `checksums.txt` are on the releases page ([internal/README.md:20-26](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/internal/README.md#L20-L26)). ### Commands Global flags ([internal/README.md:28-43](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/internal/README.md#L28-L43)): `-a, --agent-card `, `-e, --endpoint ` (with exactly one `--transport`), `--transport rest|jsonrpc|grpc` (repeatable, preference order), `-o text|json|jsonl`, `--svc-param k=v`, `--auth ` (shorthand for `Authorization`), `--tenant`, `--timeout` (default `30s`), `-v`, `--config `. Configuration also comes from `A2ACLI_*` environment variables, `.env` files and `~/.config/a2a-cli/config.yaml`, with flag > env > local file > user config > global `.env` > default precedence ([internal/README.md:47-124](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/internal/README.md#L47-L124)); `a2a config show` prints each effective value and its source ([126-135](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/internal/README.md#L126-L135)). Client commands ([internal/README.md:139-260](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/internal/README.md#L139-L260)): | Command | Does | Notable flags | |---|---|---| | `a2a card get ` | fetch the agent card | `--extended --auth "Bearer "` fetches the extended card | | `a2a send -a "text"` | SendMessage, waiting for a terminal or interrupted state | `--stream` (SendStreamingMessage, falls back to polling), `--async` (return at once), `--task-id`, `--context-id`, `--history `, `--text-part`, `--file-part `, `--data-part `, `--media-type`, `--request-payload ` | | `a2a task get -a ` | GetTask | `--history `, `--wait` (poll until terminal, returning early on `input-required`/`auth-required`), `--poll-interval` | | `a2a task list -a ` | ListTasks | `--context`, `--status `, `--limit`, `--page-token`, `--history`, `--since `, `--with-artifacts` | | `a2a task cancel -a ` | CancelTask | | | `a2a task subscribe -a ` | SubscribeToTask, streams until terminal | output matches `send --stream` | | `a2a version` | prints version (`-o json` supported) | [internal/cli/version.go:34-43](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/internal/cli/version.go#L34-L43) | Server mode ([internal/README.md:264-360](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/internal/README.md#L264-L360)): `a2a server --echo` (returns the user's text), `a2a server --proxy -a ` (forwards every operation, injects `--svc-param` headers, logs traffic), `a2a server --exec "" [--chunk=]` (stdin gets the first text part, stdout becomes the artifact, exit 0 means `completed`, non-zero `failed`; with `--chunk` each piece streams as an artifact chunk with `append: true` and the last one `lastChunk: true`). Common server flags: `--port 8080`, `--host 127.0.0.1`, `--serve-transport rest|jsonrpc|grpc` (default `rest`), `--protocol latest|0.3`, `--card `, `--card-compat` (dual v0.3/v1.0 card), `--name`, `--description`, `--advertise-address`, `--insecure`, `--quiet`. Output and exit codes ([SPEC.md:390-463](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/specification/SPEC.md#L390-L463)): `text` is one `Label: value` per line; `-o json` is one document (the final Task or Message) or JSON Lines under `--stream`; stdout carries only the payload in machine mode. Exit `0` means the CLI did its job even if the task ended `FAILED` or paused at `INPUT_REQUIRED`; `1` failure; `2` usage error; reserved `3` unreachable agent, `4` authentication, `5` timeout. Extensibility: command plugins `a2a-` on `PATH`, off by default (`a2a plugin set-enabled true`, `a2a plugin list`), and transport plugins `a2a-transport-` (`a2a transport list`, `--transport `) built with `devkit/clitransport` ([internal/README.md:372-410](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/internal/README.md#L372-L410)). An Agent Skill for coding assistants ships at [skills/a2a-cli/SKILL.md](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/skills/a2a-cli/SKILL.md) (`npx skills add https://github.com/a2aproject/a2a-cli --skill a2a-cli`, [README.md:87-98](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/README.md#L87-L98)), with a cookbook under [examples/](https://github.com/a2aproject/a2a-cli/tree/v0.3.0/examples). What it is not: a conformance tool. Its own specification notes that "The Technology Compatibility Kit validates agents, not clients" and that "the Inspector is a browser application with no headless or CI path" ([SPEC.md:35](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/specification/SPEC.md#L35-L35), in an HTML comment); a `conformance` command is listed as a Tier 2 feature in the roadmap, not as something implemented ([SPEC.md:94](https://github.com/a2aproject/a2a-cli/blob/v0.3.0/specification/SPEC.md#L94-L94)). ## a2a-itk: the Integration Test Kit It exists: https://github.com/a2aproject/a2a-itk, "Integration Testing Kit for A2A SDKs" (GitHub description), Apache-2.0, no tags or releases, `main` at `b57c5332` (2026-09-30). It is not a conformance checker for one server; it is a cross-SDK interoperability harness: "a technical toolkit designed to verify compatibility across different A2A SDK implementations and versions. It uses a multi-hop traversal model to ensure that messages can be routed across a cluster of agents using varied transport protocols (JSON-RPC, gRPC, and HTTP-JSON/REST), including support for streaming" ([README.md:7](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/README.md#L7-L7)). How it works ([README.md:11-41](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/README.md#L11-L41)): a test runner sends one nested traversal instruction to Agent 1 over JSON-RPC; each agent resolves the next peer's card and forwards the rest over the scenario's transport; the trace comes back in the response (or, for push scenarios, is collected by a mock notification server at `GET /notifications`). Topologies are generated as Eulerian circuits (Hierholzer's algorithm) so every directed edge is exercised once ([README.md:42-49](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/README.md#L42-L49)). Behaviors: `send_message`, streaming, `push_notification`, and `resubscribe` (stream, disconnect, `SubscribeToTask`, then `CancelTask`) ([README.md:73-84](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/README.md#L73-L84)). Peers are defined in `matrix.yaml`: Python, Go and TypeScript in both `v10` (repo `main`) and `v03` (`+itk` overlay tags) lines, Java, Rust and .NET in `v10` only, with per-line transport ceilings (`go_v03` has no `http_json`; `dotnet` has no `grpc`) ([matrix.yaml:25-52](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/matrix.yaml#L25-L52), [README.md:62-71](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/README.md#L62-L71)). Each SDK repository carries its own agent under `itk/` built from `protos/instruction.proto` ([README.md:277-281](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/README.md#L277-L281)). Broken pairs are listed in `known_failures.yaml`; two of them document SDK splits from this manual's matrix: a2a-rs has no v0.3 client at all, and a2a-rs's REST client sends `SubscribeToTask` as `GET`, which POST-only servers (the .NET SUT) reject ([known_failures.yaml:24-40](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/known_failures.yaml#L24-L40)). Running it ([README.md:114-128](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/README.md#L114-L128)): `uv run run_tests.py` (bundled smoke set), `--scenarios scenarios/traversal/pr.yaml`, `--sdks python_v10,go_v10`, `--list-sdks`, `--dry-run`, and `--mount ~/Source/a2a-python/itk --scenarios ~/Source/a2a-python/itk/scenarios.json` to put a local checkout in the `current` seat; peers are fetched and built with their native toolchains (uv, Go, cargo, Maven, npm) and cached under `~/.cache/a2a-itk`, or the same CLI runs in the `itk_service` container ([README.md:258-270](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/README.md#L258-L270)). The HTTP service exposes `/health`, `/run` (traversal scenarios in the legacy or `traversal/v1` format) and `/run-acts` ([itk_service_v2.py:136-229](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/itk_service_v2.py#L136-L229)). ACTS: the ITK also runs the "A2A Conformance Test Suite" corpus (`scenarios/acts/*.acts.yaml`, 15 files, 111 tests, `acts_version: "1.0"`, `spec_version: "1.0"`) against a mounted SDK: `./run_acts.py --mount ../a2a-python/itk --sdk a2a-python --transport all` ([run_acts.py:1-17](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/run_acts.py#L1-L17), [scenarios/acts/suite.acts.yaml:1-28](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/scenarios/acts/suite.acts.yaml#L1-L28)). The corpus is a byte-identical mirror of `tests/acts/` in the A2A repository at commit `82c277f342a2fd7ea47548e2de94f97ea578a7ed` (branch `conformance-spec-adjustments`, PRs #1882 and #2227), which means the ACTS suite itself is still unmerged upstream ([scenarios/acts/PROVENANCE.md:1-13](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/scenarios/acts/PROVENANCE.md#L1-L13)). Its requirement IDs reuse the TCK scheme (`CORE-SEND-002`, `CORE-LIST-001`, ...) ([core-operations.acts.yaml:14-391](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/scenarios/acts/core-operations.acts.yaml#L14-L391)); the SDK notes cite each SDK's `itk/` ACTS executor as evidence of how its authors expect streams to end (Go, Rust, JS, .NET). Results of the nightly matrix are published at https://a2aproject.github.io/a2a-itk/dashboard, rebuilt daily from `nightly-metrics` release artifacts that each SDK repository pushes ([README.md:318-340](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/README.md#L318-L340)). The backlog says what the traversal scenarios do not yet cover: terminal-state subscription rejections, `get_task`/`list_tasks`, push-config CRUD and `get_extended_agent_card` ([README.md:344-362](https://github.com/a2aproject/a2a-itk/blob/b57c5332aa883b27c1e5c915fe61cac76d2a1de9/README.md#L344-L362)). ## Reading the four together - The TCK is the only tool that grades a single server, and only against v1.0.0; of the fourteen split behaviors it pins down B6 and B10 (error present, code unchecked), B2 and B3 in part, and tests nothing about B1, B4-unset, B5 token rules, B9's inferred value, B11, B12 and B14; its content-type and gRPC-status expectations disagree with the specification in ways that penalise the SDKs that follow §11.1 and §5.4. - The inspector shows traffic and catches malformed cards and events; it decides nothing about protocol conformance. - The CLI is the fastest way to reproduce a split by hand (`-o jsonl --stream`, `--svc-param A2A-Version=...`, `task subscribe`, `server --exec`), but it is a client, with a2a-go's behaviors on the wire. - The ITK answers the question the TCK cannot: whether two SDKs talk to each other, and its known-failures file is where the B1 verb split first shows up as a recorded incompatibility.