AshA2A
A Spark.Dsl.Extension that exposes Ash.Resource/Ash.Domain actions as
A2A protocol agent skills, on top of the
vendored :a2a Elixir SDK.
Declare an a2a do skill ... end block on a resource or domain, and AshA2A
compiles a verified capability index that:
- builds a real
A2A.AgentCard(AshA2A.Info.agent_card/2) advertising each declared skill, - fails closed at compile time (
AshA2A.Verify) if a skill names a nonexistent action (:REFUSED_ACTION_NOT_FOUND) or duplicates a skill name (:REFUSED_DUPLICATE_SKILL_NAME), - and dispatches an inbound
A2A.Messageto the right Ash action (AshA2A.Dispatcher.dispatch/3), either as a bare function call or through a real supervisedA2A.Agentprocess (AshA2A.Agent).
Installation
Add ash_a2a and the :a2a SDK to mix.exs:
def deps do
[
{:ash_a2a, "~> 26.9"},
{:a2a, "~> 0.1"}
]
end
Usage
Declare the DSL on a resource (or domain):
defmodule MyApp.Echo do
use Ash.Resource,
domain: MyApp.Domain,
data_layer: Ash.DataLayer.Ets,
extensions: [AshA2A]
attributes do
uuid_primary_key(:id)
attribute(:message, :string, public?: true)
end
actions do
defaults([:read])
end
a2a do
skill(:echo, :read)
end
end
defmodule MyApp.Domain do
use Ash.Domain, extensions: [AshA2A]
resources do
resource(MyApp.Echo)
end
end
AshA2A.Transformers.BuildCapabilityIndex compiles this into a persisted
:ash_a2a_capability_index, and AshA2A.Verify checks it (fail-closed) after
compilation. AshA2A may be used on a resource, a domain, or both.
Dispatch a message directly (no process):
message = A2A.Message.new_user([A2A.Part.Data.new(%{})])
{:reply, [%A2A.Part.Data{data: %{results: []}}]} =
AshA2A.Dispatcher.dispatch(:echo, message, MyApp.Echo)
For the full walkthrough — running a supervised A2A.Agent process, serving
it over HTTP with A2A.Plug/A2A.Client, and role-based LLM provider
resolution — see the Getting Started tutorial.
Documentation
This project follows the Diataxis documentation framework: tutorials for learning, how-to guides for specific tasks, reference for lookup, and explanation for understanding.
- Tutorials — Getting Started: a
complete, compiling, end-to-end walkthrough from DSL declaration through
direct dispatch, a supervised agent process, HTTP serving with
A2A.Plug/A2A.Client, and role-based LLM provider resolution. - How-to guides:
- Authenticate inbound A2A requests
— wire
A2A.Plug.Authin front ofA2A.Plugso a Bearer credential becomescontext.actor/context.tenantinside your Ash actions. - Observe dispatch with OCEL
— forward every
AshA2A.Dispatcherskill dispatch as an OCEL v2 event to an external process-mining ingest endpoint. - Use role-based LLM resolution — add an LLM-backed action that declares an abstract role instead of hardcoding a provider/model string.
- Authenticate inbound A2A requests
— wire
- Reference — Reference index: module and DSL lookup.
- Explanation — Architecture: how the capability index, verifier, dispatcher, and agent process fit together and why they're designed that way.
Installer
mix ash_a2a.install (an Igniter task) wires the extension into a project;
see lib/mix/tasks/ash_a2a.install.ex.
Documentation can be generated with ExDoc and published on HexDocs.