a2aproject / a2aproject/a2a-tck
Update TCK for A2A specification v1.0.1
- Dominant language
- Python
- Stars
- 50
- Forks
- 40
- Avg merge
- 7d 1h
- Merged PRs (30d)
- 1
Description
## Summary
Update the TCK to align with A2A specification v1.0.1 (commit `3303592`), up from v1.0.0 (commit `1736957`).
The v1.0.1 release is a patch with no new operations or message types. Changes fall into six categories:
1. **HTTP+JSON Content-Type** — `application/json` → `application/a2a+json`
2. **Error code mappings** — several HTTP status and gRPC status changes
3. **ErrorInfo requirement relaxation** — MUST → SHOULD for JSON-RPC
4. **Tenant field semantics** — clarified as "opaque routing identifier"
5. **New section 5.8** — Custom Binding Identification (URI-based)
6. **Editorial** — enum names consistently use proto form (`TASK_STATE_*`), "Stream Message" → "Send Streaming Message", `stateTransitionHistory` removed from capabilities list
---
## 1. Error Code Mapping Changes (spec Section 5.4)
Six error bindings have changed transport-level codes:
| Error | Field | Old Value | New Value |
|-------|-------|-----------|-----------|
| `TaskNotCancelableError` | `http_status` | `409` (Conflict) | `400` (Bad Request) |
| `ContentTypeNotSupportedError` | `http_status` | `415` (Unsupported Media Type) | `400` (Bad Request) |
| `InvalidAgentResponseError` | `http_status` | `502` (Bad Gateway) | `500` (Internal Server Error) |
| `PushNotificationNotSupportedError` | `grpc_status` | `UNIMPLEMENTED` | `FAILED_PRECONDITION` |
| `UnsupportedOperationError` | `grpc_status` | `UNIMPLEMENTED` | `FAILED_PRECONDITION` |
| `VersionNotSupportedError` | `grpc_status` | `UNIMPLEMENTED` | `FAILED_PRECONDITION` |
### Files to update
#### `tck/requirements/base.py` (source of truth — validators derive from these)
- `TASK_NOT_CANCELABLE_ERROR` — `http_status=409` → `400`
- `CONTENT_TYPE_NOT_SUPPORTED_ERROR` — `http_status=415` → `400`
- `INVALID_AGENT_RESPONSE_ERROR` — `http_status=502` → `500`
- `PUSH_NOTIFICATION_NOT_SUPPORTED_ERROR` — `grpc_status="UNIMPLEMENTED"` → `"FAILED_PRECONDITION"`
- `UNSUPPORTED_OPERATION_ERROR` — `grpc_status="UNIMPLEMENTED"` → `"FAILED_PRECONDITION"`
- `VERSION_NOT_SUPPORTED_ERROR` — `grpc_status="UNIMPLEMENTED"` → `"FAILED_PRECONDITION"`
#### `tck/requirements/binding_grpc.py`
- GRPC-ERR-002 description: Update references to `PushNotificationNotSupportedError=UNIMPLEMENTED`, `UnsupportedOperationError=UNIMPLEMENTED`, `VersionNotSupportedError=UNIMPLEMENTED` → all `FAILED_PRECONDITION`
#### `tck/requirements/binding_http_json.py`
- HTTP_JSON-STATUS-001 description: Update references to `TaskNotCancelableError→409`, `ContentTypeNotSupportedError→415`, `InvalidAgentResponseError→502`
#### `tests/compatibility/http_json/test_http_status.py`
- `test_task_not_cancelable_returns_409` — rename to `_returns_400`, update docstring, update accepted codes from `(404, 409)` → `(400, 404)`
- `test_content_type_not_supported_returns_415` — rename to `_returns_400`, update docstring
#### `tests/compatibility/grpc/test_status_codes.py`
- Three test docstrings referencing `UNIMPLEMENTED` → `FAILED_PRECONDITION`
- One assertion string referencing `UNIMPLEMENTED` → `FAILED_PRECONDITION`
#### `tests/unit/validators/http_json/test_error_validator.py`
- `test_contains_task_not_cancelable`: assert `== HTTP_CONFLICT` (409) → `HTTP_BAD_REQUEST` (400)
- `test_contains_content_type_not_supported`: assert `== HTTP_UNSUPPORTED_MEDIA_TYPE` (415) → `HTTP_BAD_REQUEST` (400)
- `test_get_possible_errors_conflict`: 409→TaskNotCancelableError mapping no longer exists — remove or update
- Remove unused `HTTP_CONFLICT` and `HTTP_UNSUPPORTED_MEDIA_TYPE` imports if no longer used
---
## 2. Content-Type Change: `application/json` → `application/a2a+json`
The HTTP+JSON binding (Section 11.1) now says:
> Content-Type: application/a2a+json **SHOULD** be used for requests and responses
This applies **only to the HTTP+JSON binding**, not JSON-RPC (which remains `application/json` per JSON-RPC 2.0 standard).
### Files to update
#### `tck/requirements/binding_http_json.py`
- HTTP_JSON-SVC-001: Change title from `"Content-Type is application/json"` → `"Content-Type SHOULD be application/a2a+json"`
- Update description and `expected_behavior` to reference `application/a2a+json`
- Change level from `MUST` → `SHOULD` (spec says SHOULD)
#### `tck/transport/http_json_client.py`
- `_request()`: `"Content-Type": "application/json"` → `"application/a2a+json"`
- `_request_streaming()`: `"Content-Type": "application/json"` → `"application/a2a+json"`
#### `tests/compatibility/core_operations/test_transport_behavior.py`
- HTTP_JSON Content-Type test — update expected value to `application/a2a+json`
- Raw request Content-Type header → `application/a2a+json`
#### `tests/compatibility/http_json/test_http_status.py`
- Raw request Content-Type → `application/a2a+json`
#### `tests/compatibility/http_json/test_problem_details.py`
- Error Content-Type validation — should accept `application/a2a+json`
#### `tests/compatibility/core_operations/test_error_handling.py`
- HTTP+JSON raw requests Content-Type → `application/a2a+json`
- Error response Content-Type validation → accept `application/a2a+json`
#### `tests/compatibility/core_operations/test_data_model.py`
- Raw requests — update if used for HTTP+JSON transport
#### `tests/unit/validators/http_json/test_error_validator.py`
- Test response dicts Content-Type → `application/a2a+json`
#### NOT changed (JSON-RPC stays `application/json`)
- `tck/transport/jsonrpc_client.py` — NO CHANGE
- `tck/requirements/binding_jsonrpc.py` JSONRPC-FMT-002 — NO CHANGE
- `tests/compatibility/jsonrpc/test_error_codes.py` — NO CHANGE
---
## 3. JSON-RPC ErrorInfo: MUST → SHOULD
The v1.0.1 spec relaxes the JSON-RPC ErrorInfo requirement:
> **Old (v1.0.0):** For A2A-specific errors, implementations **MUST** include a `google.rpc.ErrorInfo` message in the `data` array
> **New (v1.0.1):** Implementations **SHOULD** use well-known types such as `google.rpc.ErrorInfo` to refine error reporting
The HTTP+JSON binding **retains MUST** for ErrorInfo (since multiple error types share the same HTTP status code).
### Files to update
#### `tck/requirements/binding_jsonrpc.py`
- JSONRPC-ERR-003: Change level from `RequirementLevel.MUST` → `RequirementLevel.SHOULD`
- Update description: `data` is now "array of objects, each containing a `@type` key"
- Remove mention of `metadata` field
#### `tck/validators/error_info.py`
- `validate_error_info` currently returns `valid=False` when ErrorInfo is missing
- For JSON-RPC transport: soften (missing ErrorInfo = pass, not failure)
- For HTTP+JSON and gRPC transports: keep as MUST (failure)
#### `tck/requirements/binding_jsonrpc.py`
- JSONRPC-ERR-001: Update description of `data` field format
---
## 4. HTTP+JSON Error Model Reference
### Files to update
#### `tck/requirements/binding_http_json.py`
- HTTP_JSON-ERR-001: Update description from "AIP-193 format" → "google.rpc.Status JSON representation"
- Update description to reflect new language about `@type` key requirement
---
## 5. Tenant Field Semantics
Proto comment-only change. A new spec requirement was added:
> Set the `tenant` field in every request message to exactly the value declared in the selected `AgentInterface` entry (omit the field if `tenant` is not set in that entry)
### Files to update
- Review if there's an existing requirement covering tenant field propagation
- If not, consider adding a new requirement (e.g., `CARD-TENANT-001`)
---
## 6. New Section 5.8: Custom Binding Identification
> Custom protocol bindings **SHOULD** be identified by a URI. When a breaking change is introduced, a new URI **MUST** be used.
### Files to update
#### `tck/requirements/agent_card.py`
- Consider adding `CARD-BIND-001` (SHOULD) for URI-based binding identification
- Consider adding `CARD-BIND-002` (MUST) for new URI on breaking changes
**Note:** These apply to custom/non-standard bindings only and may be hard to test automatically.
---
## 7. Proto Changes (Comment-Only)
No structural changes to `a2a.proto` — only comment updates. **gRPC stubs and JSON schema do NOT need regeneration.**
---
## 8. Editorial / Non-Functional Changes
| Change | Impact |
|--------|--------|
| Title: "Release Candidate v1.0" → removed | No code impact |
| "Stream Message" → "Send Streaming Message" | Already correct in TCK |
| Terminal state names use `TASK_STATE_*` prefix | Already correct in TCK |
| `stateTransitionHistory` removed from capabilities | Grep and remove if referenced |
| Push notification examples: `pushNotificationConfig` → `taskPushNotificationConfig` | Grep and verify |
| Push auth: `token`→`credentials`, `schemes`→`scheme` | Grep and verify |
---
## 9. Validation Checklist
After implementing all changes:
- [ ] `make lint` passes
- [ ] `make unit-test` passes
- [ ] All requirement `spec_url` anchors resolve to correct headings
- [ ] Error binding values match spec Section 5.4 table exactly
- [ ] HTTP+JSON transport client sends `application/a2a+json`
- [ ] JSON-RPC transport client still sends `application/json`
- [ ] JSON-RPC ErrorInfo validator uses SHOULD semantics
- [ ] HTTP+JSON ErrorInfo validator retains MUST semantics
- [ ] No hardcoded `409`, `415`, `502`, or `UNIMPLEMENTED` remain for changed error types
---
## 10. Implementation Order
Recommended order (dependency-aware):
1. **`base.py` error bindings** — all validators cascade from this
2. **Requirement file descriptions** — `binding_http_json.py`, `binding_grpc.py`, `binding_jsonrpc.py`
3. **Transport client Content-Type** — `http_json_client.py` only
4. **ErrorInfo validator** — soften for JSON-RPC
5. **Integration tests** — update assertions, names, Content-Types
6. **Unit tests** — update assertions and fixtures
7. **New requirements** — tenant, custom binding identification (if warranted)
8. **Lint + unit test pass** — validate everything
9. **Audit `spec_url` anchors** — verify against updated specification.md
Contributor guide
Assessment
This issue has not been assessed yet.