ITK is 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.
The kit operates by dispatching a single, deeply nested instruction through a chain of agents, structuring the traversal as a complete verification cycle.
- Dispatch: The Test Runner initiates execution by sending the nested traversal instruction to the primary entrypoint agent (Agent 1) via JSON-RPC.
- Consistent Inter-Agent Traversal: For intermediate hops between agents within a given scenario, messaging evaluates a single, consistent transport protocol. Each receiving agent resolves the next target's agent card, maps the transport, and forwards the remaining payload.
- Cycle Completion & Trace Verification: Upon completing the final traversal hop, the execution unwinds, and Agent 1 returns a JSON-RPC response to the Test Runner across all modes.
- Standard / Streaming Verification: The Test Runner verifies the traversal trace directly from the returned response payload.
- Push Notification Verification: In scenarios evaluating asynchronous event delivery (
push_notification), participating agents asynchronously push trace updates to an isolated Mock Notification Server during traversal. The Test Runner queries this Push Notification Service (GET /notifications) to read and verify the accumulated traversal trace.
graph TD
Runner[Test Runner] -->|1. JSON-RPC Request| Ag1[Agent 1]
Ag1 -.->|2. Configured Transport| Ag2[Agent 2]
Ag2 -.->|2. Configured Transport| AgN[...Agent N]
%% Return Path (Always Executed)
AgN -.->|3. Response Unwinding| Ag1
Ag1 -->|3. Standard Verification - JSON-RPC Response| Runner
%% Push Notification Path & Verification
PNS[Push Notification Service]
Ag1 -.->|Async Push Event| PNS
Ag2 -.->|Async Push Event| PNS
AgN -.->|Async Push Event| PNS
PNS -->|4. Push Verification - GET /notifications| Runner
To achieve comprehensive verification, ITK utilizes graph-based traversal algorithms:
- Eulerian Circuits: Implements Hierholzer's Algorithm to generate a single linear nested instruction chain that covers 100% of directed edges in the agent cluster exactly once.
- Dynamic Topology: Supports complete digraphs (n-to-n) or custom edge definitions to test specific connection patterns.
- Universal Independence: Operates completely independently of any underlying A2A SDK version or language implementation.
ITK is structured to validate in-development SDK codebases against a cluster of reference stable configurations, basing on released versions of A2A SDKs. It is serving as a verification gate for Pull Requests and automated nightly runs.
- Stable Reference Baselines: Pre-packaged reference implementations for released A2A versions.
- Current Agent Mounting: Dynamically mounts a local SDK source checkout into a designated "current" agent process to evaluate compatibility against the stable cluster.
| SDK Language | Stable v0.3 | Stable v1.0 | Current Mount Support |
|---|---|---|---|
| Python | β | β | β |
| Go | β | β | β |
| TypeScript | β | β | β |
| Java | β | β | β |
| Rust | β | β | β |
| .NET | β | β |
Note
Executes standalone traversal scenarios dedicated to verifying compatibility across each primary transport protocol:
- JSON-RPC
- gRPC
- HTTP-JSON (REST)
Within these transport scenarios, the following A2A features can be tested:
- Send Message: Standard request-response messaging.
- Send Message (Streaming): Streaming message payloads across compatible transport protocols.
- Push Notification: Asynchronous event delivery and ingestion verification.
- Task Resubscription: Initiates a streaming communication lifecycle where the client extracts the active task ID, disconnects, re-subscribes to resume the stream, and finally issues a cancellation request (
cancel_task) to terminate the task.
protos/instruction.proto: The single source for the traversal instruction message. Every SDK's agent generates its stubs from this file.pyproto/: Python stubs generated fromprotos/instruction.proto(committed; regenerate with./build_protos.sh).matrix.yaml: Maps each (sdk, line) pair to the repo, ref, and transports the launcher fetches and drives it with. The single source for what a peer identifier likepython_v10orgo_v03means.test_suite/launcher/: The launcher engine β fetch, cache, build, spawn, health-check, and tear down a cluster of agents at given repo+SHA.test_suite/scenarios/: The scenario schemas, and the resolver that binds a role-based scenario to concrete agents viamatrix.yaml.test_suite/: The Eulerian traversal logic that turns a scenario into a nested instruction.scenarios/: The shared scenario sets βtraversal/pr.yaml,traversal/nightly.yaml, andtraversal/smoke.yaml, plus the legacysmoke.jsonkept as a compatibility pin.dashboard/: Static web assets (HTML, JS, CSS) for rendering compatibility matrix test results.scripts/: Auxiliary utilities β the sharedrun_itk.shdriver, result reporting, nightly metrics, and the scenario coverage diff. Seescripts/README.md.itk_runner.py: The scenario execution pipeline β plan, start a cluster, run, tear down. Shared by both front ends below.itk_service_v2.py: HTTP/runhandler, for CI. A thin wrapper overitk_runner.run_tests.py: Local CLI, for running scenarios on your own machine. Also a thin wrapper overitk_runner.notifications_app.py: Dedicated mock server for ingesting and verifying SDK push notifications.testlib.py: Scenario execution β payload construction, transport dispatch, and result verification.Dockerfile: Container environment definition for the ITK service.
- uv: Python package and project manager.
- Go 1.25+: Required for Go agent builds.
- Node.js v20: Required for certain A2A utility components.
Every peer is fetched from its own repository at the ref pinned in matrix.yaml, so
a scenario that doesn't reference current needs nothing checked out but this repo:
uv run run_tests.py # the bundled smoke set
uv run run_tests.py --scenarios path/to/x.json # any SDK's scenarios.json
uv run run_tests.py --scenarios scenarios/traversal/pr.yaml # the shared set
uv run run_tests.py --sdks python_v10,go_v10 # narrow to those peers
uv run run_tests.py --list-sdks # what matrix.yaml can resolve
uv run run_tests.py --dry-run # plan only, no networkTo test a local SDK checkout as the code under test, point current at it β the run_itk.sh
workflow without the container round-trip:
uv run run_tests.py --mount ~/Source/a2a-python/itk \
--scenarios ~/Source/a2a-python/itk/scenarios.jsonrun_tests.py and the HTTP /run handler share one pipeline (itk_runner.py), so
a scenario behaves the same locally and in CI.
Two formats are live at once, and /run accepts either β including a batch mixing both. That is
what lets each SDK move to the shared set on its own schedule instead of all five cutting over
together.
Legacy β what every SDK's itk/scenarios.json uses today. Agents are named individually and
edges are written out by hand. Unchanged, and still fully supported:
{"tests": [{
"name": "Star Topology (Full) - JSONRPC & GRPC",
"sdks": ["current", "python_v10", "go_v03"],
"edges": ["0->1", "0->2", "1->0", "2->0"],
"protocols": ["jsonrpc", "grpc"],
"behavior": "send_message"
}]}traversal/v1 β names roles instead, and resolves them against matrix.yaml at run time.
The same scenario:
schema: traversal/v1
name: Star - send message
tier: pr # pr | nightly
roles:
sut: current
peers:
- {sdk: python, line: v10}
- {sdk: go, line: v03}
topology: star # star | chain | euler β replaces hand-written edges
transports: [jsonrpc, grpc]
behavior: send_messageA file carrying a top-level schema: key is read as the new format; one without it is legacy.
Beyond roles and topology, the new format adds:
| Field | Effect |
|---|---|
peers: all |
Every line in matrix.yaml β so adding an SDK is a matrix change and nothing else |
expand: per_peer |
One SUT-plus-one scenario per peer, instead of one graph holding all of them |
include_own_lines |
Also test the SUT against its own SDK's released lines |
behaviors, streaming_variants |
Expand as a Cartesian product |
transport_sets |
Group several transports into one scenario (see below) |
test_when: {sut_sdk: [...]} |
Restrict a shared scenario to certain SUTs |
edges |
Escape hatch β an explicit edge list, overriding topology |
expected: pass | fail |
The scenario's designed outcome |
Together these collapse the 32-entry nightly set each repo maintained by hand into three
declarations (scenarios/traversal/nightly.yaml).
Each transport is its own scenario. transports: [jsonrpc, grpc] emits two scenarios, not
one carrying both. A traversal runs a separate circuit per transport and asserts the union of
their trace tokens, so a bundled scenario fails as a whole when any single transport is broken β
and the result name doesn't say which. Splitting gives each transport its own pass/fail, and is
what lets a known failure be excluded at transport granularity. Use transport_sets when several
genuinely belong in one traversal.
A peer that can't speak a transport leaves that scenario only, based on the line's transports
in matrix.yaml. That is why the shared PR set has no separate "no go_v03, http_json" variant β
the capability is stated once, in the matrix, and the resolver acts on it.
Combinations that are broken β as opposed to merely unsupported β go in
known_failures.yaml. Generated scenarios can't carry an inline marker
(with peers: all there is no entry in any file to annotate), so the exceptions live in one list
matched against resolved scenarios:
exclusions:
- sut_sdk: [java]
agents: [python_v03]
reason: java <-> python v0.3 fails on every transport, while go_v03 passes.
issue: https://github.com/a2aproject/a2a-java/issues/NNNreason is required, and every exclusion is logged on every run β an exclusion nobody can see is
indistinguishable from coverage that quietly disappeared.
v0.3 interoperability is a property of the pair, not of a version line. The compat layer that
translates v0.3 β v1.0 lives in whichever SDK drives the hop, so ts_v03 works over gRPC against
a TypeScript counterpart and fails against a Python one. sut_sdk and unless_sut_sdk are how a
rule says that. A per-line transports in matrix.yaml cannot, and would also hide the pairing
that does work β use it only when a line cannot speak a transport from anywhere, which in the
current corpus is just go_v03 and http_json.
When an exclusion names agents, the peer is removed from the graph and the scenario still
runs: a star keeps its meaning with one arm gone, which is what the hand-written
"No Go v03 - HTTP_JSON" scenarios did by omission. It is skipped outright only if that would leave
fewer than two agents, or if it carries an explicit edges list that cannot be re-indexed.
Removals are reported separately from skips, because such a scenario still covers less than the
file says.
scripts/scenarios_diff.py knows about both files: coverage may shrink only where one of them
explains why, and an unexplained drop fails the check.
Validate before running β a malformed scenario otherwise surfaces only after CI has built everything, and one that resolves to nothing would go green having tested less than it claims:
uv run python -m test_suite.scenarios.validate --resolve scenarios/Check that a shared set still covers what a repo's own file did. The comparison is at the level of
hops actually exercised β (caller, callee, transport, behavior, streaming) β so it sees through
the reshaping. Added coverage is reported, never an error; lost coverage fails:
uv run python scripts/scenarios_diff.py \
--old ../a2a-python/itk/scenarios.json \
--new scenarios/traversal/pr.yaml --sut-sdk pythonTwo things to know. Builds run on your machine with each SDK's native toolchain, so you need
whatever the selected peers require (uv, go + protoc-gen-go, cargo, mvn + JDK, npm) β the bundled
smoke set sticks to python and go for that reason. And builds are cached under $ITK_CACHE_DIR
(default ~/.cache/a2a-itk), so a cold first run is slow and repeats are fast. Add --log-dir DIR
to capture each agent's output when something won't start.
If you'd rather not install a toolchain, the same CLI runs inside the ITK image, which has all of them:
docker build -t itk_service .
docker run --rm -v "$PWD/scenarios:/scenarios" \
-v "$HOME/.cache/a2a-itk-launcher:/root/.cache/a2a-itk" \
itk_service uv run run_tests.py --scenarios /scenarios/smoke.jsonThe unit tests cover the launcher and traversal logic with no network at all: uv run pytest.
To gate Pull Requests or schedule automated nightly runs against an in-development SDK repository (e.g., a2a-python or a2a-go), consuming codebases mount their local source directly into ITK's validation container runtime.
-
Instruction Handling Agent Implementation:
- Consuming SDKs must implement an instruction handling agent capable of parsing nested traversal instructions and executing varied agent behavior modes.
- The agent lives in the SDK's own repository under
itk/, and generates its proto stubs from this repo'sprotos/instruction.proto. Add the SDK tomatrix.yamlso the launcher knows which repo and ref to fetch it from. - Implementation Reference: a2a-python/itk and a2a-go/itk are the reference implementations.
-
Custom Scenario Definitions:
- Consuming repositories supply customized scenario suites tuned to the desired depth of testing:
- PR Testing (
scenarios.json): Shorter, optimized validation paths focused on rapid compatibility verification. - Nightly Runs (
scenario_full.json): Comprehensive, multi-hop matrix configurations evaluating edge-case behavior and transport stability across protocol matrix boundaries.
- PR Testing (
- Scenario Schema & Fields: Configuration files define a root object containing a
testsarray. Each scenario object specifies:name(String, Required): Descriptive display title for the test scenario.sdks(Array of Strings, Required): Target agent identifiers participating in the cluster (e.g.,["current", "python_v10", "go_v03"]). The array index dictates node IDs for routing.protocols(Array of Strings, Required): Transport mechanisms executed under this topology ("jsonrpc","grpc","http_json").behavior(String, Required): Verification interaction mode ("send_message","push_notification","resubscribe").edges(Array of Strings, Optional): Custom directed communication edge pairs using zero-based SDK indices (e.g.,["0->1", "1->0"]). If omitted, defaults to a complete digraph (n-to-n) topology.streaming(Boolean, Optional): If set totrue, activates streaming message payload delivery. Defaults tofalse.build_subtests(Boolean, Optional): If set totrue, instructs the test runner to extract and execute targeted sub-graphs or individual edges as distinct validation subtests. Defaults tofalse.
- Consuming repositories supply customized scenario suites tuned to the desired depth of testing:
-
Automated Orchestration Wrapper:
- The target codebase maintains a runner script (e.g.,
run_itk.sh) that exportsA2A_ITK_REVISION, clones the test suite, compiles the core test container, dynamically mounts the workspace source as thecurrentagent context, and verifies execution outputs.
- The target codebase maintains a runner script (e.g.,
Review production integration structures, runner scripts, and CI workflow templates directly in the main remote repositories:
-
Python SDK (
a2a-python):- Integration Setup: Core integration layout and runner configurations (itk/).
- PR Validation Workflow: Continuous integration gating for Pull Requests (itk.yaml).
- Nightly Run Workflow: Automated scheduled test matrix verification (nightly.yaml).
-
Go SDK (
a2a-go):- Integration Setup: Core integration layout and runner configurations (itk/).
- PR Validation Workflow: Continuous integration gating for Pull Requests (itk.yaml).
- Nightly Run Workflow: Automated scheduled test matrix verification (itk-nightly.yaml).
ITK hosts a static centralized visualization dashboard to aggregate and display recurring nightly integration test matrix results.
- Public Dashboard URL: A2A ITK Dashboard
Note
The centralized dashboard does not provide real-time live monitoring. It functions as a daily integration status update reflecting completed overnight matrix executions.
The data presentation pipeline operates via a decoupled publication model:
- Metrics Artifact Generation: Consuming SDK repositories execute comprehensive multi-protocol traversal suites overnight. Upon completion, extracted run results are formatted as structured JSON metrics artifacts.
- Rolling Release Ingestion: Consuming repositories push these extracted JSON artifacts directly to a specially dedicated rolling release tag named
nightly-metricsinside their own GitHub releases environment. - Aggregated Deployment: A scheduled daily workflow within the
a2a-itkrepository fetches these static released metrics from each target SDK'snightly-metricstag and triggers a static site compilation, re-deploying the unified frontend to GitHub Pages.
When integrating automated nightly matrix runs for a newly onboarded language library, follow these steps to render its compatibility outputs globally:
- Ensure the new SDK's nightly continuous integration workflow publishes its final output JSON artifacts to a rolling release tag named
nightly-metrics. - Modify the automated dashboard deployment workflow within this repository (.github/workflows/deploy_dashboard.yaml) to fetch the metric payload from the new target SDK's release space alongside existing baseline configurations.
To further expand verification depth and ensure absolute compliance with the growing Agent2Agent protocol standard, future iterations aim to address the following roadmap items:
- Error Assertion Mapping: Verify that SDK implementations raise structurally correct exceptions under anomalous execution paths.
- Out-of-Order Processing: Assert failures when attempting to enqueue task status updates prior to task state creation.
- Terminal State Handshakes: Validate graceful rejections when initiating subscriptions against explicitly completed or failed task instances.
- Agent Card Passing Suites: Establish targeted automated subtests focused exclusively on resolving, exchanging, and validating
AgentCardpayload structures. - Payload Content Boundaries: Expand schema adherence gates ensuring message envelopes strictly align with explicit protocol schema definitions.
Incorporate traversal test strategies evaluating additional native client API contracts present in standard baseline models:
-
get_task/list_tasks -
create_task_push_notification_config/delete_task_push_notification_config -
get_extended_agent_card
- .NET SDK: Implement an instruction handling agent under
itk/, add amatrix.yamlentry, and wire up the orchestration workflow.