A2A.JSON (A2A v0.3.0)

Copy Markdown View Source

Codec for converting between Elixir structs and the A2A v1.0 camelCase JSON wire format.

Produces intermediate maps (not JSON strings) suitable for composing with JSON-RPC envelopes. Use Jason.encode!/1 on the result when you need a string.

Encoding emits the v1.0 flat shape (no kind discriminator, flat Part with text/data/raw/url + mediaType/filename). Decoding accepts both v1.0 and the legacy v0.3 shape (nested file: {bytes|uri, mimeType} and kind-tagged parts) so v0.3 clients keep working.

Encoding

iex> part = A2A.Part.Text.new("hello")
iex> {:ok, map} = A2A.JSON.encode(part)
iex> map
%{"text" => "hello"}

Decoding

iex> {:ok, part} = A2A.JSON.decode(%{"text" => "hello"}, :part)
iex> part
%A2A.Part.Text{text: "hello", metadata: %{}}

Summary

Functions

Decodes a JSON map into an Elixir struct of the given type.

Decodes a JSON map into an Elixir struct, raising on failure.

Decodes a JSON map into an %A2A.AgentCard{} struct.

Decodes a wire-format state string to an atom.

Encodes an Elixir struct to a JSON-ready map.

Encodes an Elixir struct to a JSON-ready map, raising on failure.

Encodes an agent card into the AgentCard JSON format.

Converts an Elixir map with atom keys to a camelCase JSON map.

Encodes a streaming event into the v1.0 StreamResponse wrapper.

Returns the list of valid v0.3 wire-format state strings.

Types

decode_type()

@type decode_type() ::
  :task
  | :status
  | :message
  | :artifact
  | :part
  | :file_content
  | :event
  | :status_update_event
  | :artifact_update_event
  | :push_notification_config

encode_result()

@type encode_result() :: {:ok, map()} | {:error, term()}

Functions

decode(map, atom)

@spec decode(map(), decode_type()) :: {:ok, struct()} | {:error, term()}

Decodes a JSON map into an Elixir struct of the given type.

Returns {:ok, struct} on success or {:error, reason} on failure.

The :part type dispatches on the "kind" field, or infers the type from content fields ("text", "file", "data") when "kind" is absent (v0.3 format). The :event type dispatches on the v1.0 StreamResponse wrapper key — "task", "message", "statusUpdate" or "artifactUpdate" — falling back to the v0.3 "kind" discriminator.

decode!(map, type)

@spec decode!(map(), decode_type()) :: struct()

Decodes a JSON map into an Elixir struct, raising on failure.

decode_agent_card(map)

@spec decode_agent_card(map()) :: {:ok, A2A.AgentCard.t()} | {:error, term()}

Decodes a JSON map into an %A2A.AgentCard{} struct.

Returns {:ok, agent_card} on success or {:error, reason} on failure.

Example

iex> map = %{
...>   "name" => "test",
...>   "description" => "A test agent",
...>   "url" => "https://example.com",
...>   "version" => "1.0.0",
...>   "skills" => [
...>     %{"id" => "s1", "name" => "Skill", "description" => "Does things", "tags" => []}
...>   ]
...> }
iex> {:ok, card} = A2A.JSON.decode_agent_card(map)
iex> card.name
"test"

decode_state(str)

@spec decode_state(String.t()) ::
  {:ok, atom()} | {:error, {:invalid_state, String.t()}}

Decodes a wire-format state string to an atom.

Accepts both v0.3 ("TASK_STATE_WORKING") and legacy ("working") formats.

encode(task)

@spec encode(struct()) :: encode_result()

Encodes an Elixir struct to a JSON-ready map.

Returns {:ok, map} on success or {:error, reason} on failure. Optional nil fields and empty collections are omitted from the output.

encode!(struct)

@spec encode!(struct()) :: map()

Encodes an Elixir struct to a JSON-ready map, raising on failure.

encode_agent_card(card, opts \\ [])

@spec encode_agent_card(A2A.AgentCard.t() | A2A.Agent.card(), keyword()) :: map()

Encodes an agent card into the AgentCard JSON format.

Accepts either a plain map (as returned by A2A.Agent.agent_card/0) or a fully-populated %A2A.AgentCard{} struct. When a struct is passed, fields like capabilities, provider, documentation_url, etc. are read from the struct and used as defaults. Options in opts always take precedence over struct fields.

In v1.0 the top-level url and protocolVersion fields are gone from the wire format — both are per-interface. The :url option is still required because it seeds the default supportedInterfaces entry; pass :supported_interfaces directly to override.

Options

All options override the corresponding struct field when a %A2A.AgentCard{} is passed as card.

  • :url — agent endpoint URL, used as the default supportedInterfaces[0].url
  • :capabilities — AgentCapabilities map (default: %{})
  • :default_input_modes — list of MIME types (default: ["text/plain"])
  • :default_output_modes — list of MIME types (default: ["text/plain"])
  • :provider — %{organization: ..., url: ...} map
  • :documentation_url — URL string
  • :icon_url — URL string
  • :supported_interfaces — list of %{url: ..., protocol_binding: ..., protocol_version: ...} maps. Defaults to a single JSON-RPC interface derived from :url.
  • :security_schemes — %{name => %SecurityScheme.X{}} map
  • :security — list of %{name => scopes} maps (OpenAPI-style)
  • :signatures — list of JWS signature maps (each %{"protected" => ..., "signature" => ..., "header" => ...})

encode_known_keys(source, mappings)

@spec encode_known_keys(map(), [{String.t(), atom()}]) :: map()

Converts an Elixir map with atom keys to a camelCase JSON map.

Each mapping is a {json_key, atom_key} pair. Keys whose values are nil (or absent) are omitted from the result. Looks up both the atom key and the JSON-string key so the function works with either representation.

encode_stream_response(task)

@spec encode_stream_response(struct()) :: encode_result()

Encodes a streaming event into the v1.0 StreamResponse wrapper.

Streaming operations and push notification payloads carry exactly one of task, message, statusUpdate or artifactUpdate. The wrapper key is the discriminator, which is why the wrapped objects carry no kind of their own — the schema rejects unknown properties at both levels.

valid_state_strings()

@spec valid_state_strings() :: [String.t()]

Returns the list of valid v0.3 wire-format state strings.