Repository navigation
fix(compat): send v0.3 REST request bodies with json_name keys - #1455
Conversation
The compat REST client serialized send, stream and push config create bodies with preserving_proto_field_name=True, so it sent "request" and "message_id" instead of "message" and "messageId". The v0.3 spec requires json_name keys for HTTP+JSON, and a2a-sdk 0.3.x sent them. Servers that only read json_name keys, like a2a-go's v0.3 REST server, reject every send with 400 "message is required". Use the default MessageToDict, which a2a-python's own v0.3 REST server already accepts.
🧪 Code Coverage (vs
|
| Base | PR | Delta | |
|---|---|---|---|
| src/a2a/server/cluster/database_event_stream.py | 97.27% | 93.64% | 🔴 -3.64% |
| Total | 93.63% | 93.59% | 🔴 -0.04% |
Generated by coverage-comment.yml
1AbdullahButt
left a comment
There was a problem hiding this comment.
Looks correct. Here's what I checked.
- Older a2a-python v0.3 servers still accept the new bodies.
CompatRestHandler.on_message_sendandset_push_notification(compat/v0_3/rest_handler.py:72and:214) read the body withparse_lenientinto the v0.3 proto. The protobuf JSON parser accepts both thejson_nameand the original field name (ProtoJSON format: "Protobuf JSON parsers are required to accept both the converted lowerCamelCase name and the proto field name"). So switching fromrequest/message_idtomessage/messageIddoesn't break this SDK's own server. The cross-version tests in the description are consistent with that. SendMessageRequest.requestis renamed tomessage. That comes fromjson_nameina2a_v0_3.proto. The serialized descriptor hasR\x07messageon field 1, so'request' not in sentis the right assertion.- There are no other spots to change. The only other request-building paths in
rest_transport.pyalready used camelCase by hand (params['historyLength']at:162) or send no body. Responses are read withParseDict(..., ignore_unknown_fields=True), which accepts either naming. git merge-treeagainstmainis clean.
One question for a follow-up, not a blocker. The v0.3 CreateTaskPushNotificationConfig HTTP rule has body: "config" (visible in the serialized descriptor in a2a_v0_3_pb2.py). Under standard gRPC-HTTP transcoding, the POST body is then only the TaskPushNotificationConfig, not the whole request with parent/configId. This client sends the whole request, and this SDK's server parses the whole request, so the two agree with each other. A server that transcodes strictly from the proto (a2a-go may be one) would look for pushNotificationConfig at the top level, so push config create could still fail there after this PR. I haven't checked a2a-go's handler. If you have the a2a-go setup from the description handy, a push-config create against it would settle the question.
I didn't run the tests.
Reviewed with help from Claude Code.
Fixes #1454
Drops
preserving_proto_field_name=Truefrom the threeMessageToDictcalls (send, stream, push config create), so the body usesjson_namekeys like the spec requires.Against a2a-go's v0.3 server, a send went from
400 "message is required"to200with the agent's reply.The three new tests in
test_rest_transport.pyfail on main. The cross-version tests against a2a-sdk 0.3.24 still pass.