a2aproject / a2aproject/a2a-tck

Update TCK for A2A specification v1.0.1

Open
#240 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.