a2a

package module
v0.0.0-...-378d80f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 22, 2026 License: MIT Imports: 20 Imported by: 0

README

A2A Plugin for Crush

The A2A (Agent-to-Agent) plugin lets Crush communicate with other AI agents using the A2A v1.0 protocol. It uses the official a2a-go SDK and has two halves:

  • Server mode — Expose Crush as an A2A agent over JSON-RPC 2.0.
  • Client mode — Give the LLM tools to discover and invoke remote A2A agents.

Both modes can run simultaneously. This plugin is mutually exclusive with the ACP plugin — use the crush-a2a build variant instead of crush-extended.


Quick Start

Expose Crush as an A2A server
CRUSH_A2A_PORT=8200 crush

Verify:

curl http://localhost:8200/.well-known/agent-card.json | jq .name
# "crush"
Call a remote A2A agent from Crush

Add server(s) to your crush.json:

{
  "options": {
    "plugins": {
      "a2a": {
        "servers": [
          {"name": "teammate", "url": "http://localhost:8201"}
        ]
      }
    }
  }
}

The LLM now has access to a2a_list_agents, a2a_send_message, a2a_get_task, and a2a_attach_file tools.


Server Mode

The a2a-server hook starts a JSON-RPC 2.0 server alongside the normal Crush TUI, implementing the full A2A v1.0 protocol.

Server Configuration
{
  "options": {
    "plugins": {
      "a2a-server": {
        "port": 8200,
        "agent_name": "crush",
        "description": "Crush AI coding assistant",
        "skills": [
          {
            "id": "code",
            "name": "Code Assistant",
            "description": "Write, review, and debug code",
            "tags": ["coding"]
          }
        ]
      }
    }
  }
}
Field Type Default Description
port int 8200 TCP port for the HTTP server
agent_name string "crush" Agent name in the agent card
description string "Crush AI coding assistant..." Description in the agent card
skills array 2 default skills Skills to advertise
Environment Variables
Variable Overrides Example
CRUSH_A2A_PORT port CRUSH_A2A_PORT=8201
CRUSH_A2A_AGENT_NAME agent_name CRUSH_A2A_AGENT_NAME=reviewer
CRUSH_A2A_DESCRIPTION description CRUSH_A2A_DESCRIPTION="Reviews PRs"
Agent Card

Served at /.well-known/agent-card.json with all A2A v1.0 required fields:

  • name, version, description
  • supportedInterfaces (JSON-RPC transport)
  • capabilities (streaming: true)
  • skills (configurable)
  • defaultInputModes, defaultOutputModes
JSON-RPC Methods
Method Description
SendMessage Send a message, returns a Task with artifacts
SendStreamingMessage Stream task status and artifact updates via SSE
GetTask Retrieve task status and artifacts by ID
CancelTask Cancel an in-progress task
CreateTaskPushNotificationConfig Returns error (not supported)
Task Lifecycle
submitted → working → completed
                 ├──→ failed
                 └──→ canceled

Client Mode

Client Configuration
{
  "options": {
    "plugins": {
      "a2a": {
        "servers": [
          {"name": "local", "url": "http://localhost:8201"},
          {"name": "remote", "url": "https://agent.example.com", "headers": {"Authorization": "Bearer sk-xxx"}},
          {"name": "disabled", "url": "http://localhost:9000", "enabled": false}
        ],
        "default_timeout_seconds": 120
      }
    }
  }
}
LLM Tools
a2a_list_agents

Discover agents on configured servers.

Parameter Type Required Description
server string No Server name. Uses first if omitted.
a2a_send_message

Send a message to a remote A2A agent.

Parameter Type Required Description
input string Yes Text message to send
server string No Server name. Uses first if omitted.
context_id string No Context ID for multi-turn conversations
a2a_get_task

Get task status and artifacts.

Parameter Type Required Description
task_id string Yes The task ID to retrieve
server string No Server name. Uses first if omitted.
a2a_attach_file

Attach a file as an artifact to the current A2A task response.

Parameter Type Required Description
file_path string Yes Path to the file
name string No Human-readable artifact name
description string No Artifact description
media_type string No MIME type (auto-detected if omitted)

This tool only works when Crush is processing an incoming A2A request.


Multi-Agent Architecture

Hub and Spoke
                    ┌──────────────┐
                    │ Orchestrator │  (client mode)
                    └──────┬───────┘
                           │
              ┌────────────┼────────────┐
              │            │            │
       ┌──────▼─────┐ ┌───▼────────┐ ┌─▼───────────┐
       │ Code Writer │ │  Reviewer  │ │  Test Runner │
       │   :8200     │ │   :8201    │ │    :8202     │
       └─────────────┘ └────────────┘ └──────────────┘
       (server mode)   (server mode)   (server mode)
CRUSH_A2A_PORT=8200 CRUSH_A2A_AGENT_NAME=coder crush --cwd /path/to/project &
CRUSH_A2A_PORT=8201 CRUSH_A2A_AGENT_NAME=reviewer crush --cwd /path/to/project &
CRUSH_A2A_PORT=8202 CRUSH_A2A_AGENT_NAME=tester crush --cwd /path/to/project &

Spec Compliance

This plugin targets 100% compliance with the A2A v1.0 spec-torture test suite:

  • 5 discovery tests (agent card)
  • 5 lifecycle tests (SendMessage, GetTask, CancelTask)
  • 3 messaging tests (text parts, context sharing)
  • 2 streaming tests (SSE)
  • 4 error handling tests (JSON-RPC error codes)
  • 1 push notification test (unsupported)

Testing

Unit tests
cd a2a && go test -short ./...
Build the A2A distro
task distro:a2a
E2E tests
task distro:all
cd a2a && go test -v -tags e2e -run 'A2A'

ACP vs A2A

Feature ACP A2A
Transport REST + NDJSON streaming JSON-RPC 2.0 + SSE
Discovery GET /agents GET /.well-known/agent-card.json
Task model Runs (sync/async/stream) Tasks (with artifacts)
Artifacts Message parts First-class Artifact type
SDK Custom implementation Official a2a-go/v2
Spec ACP draft A2A v1.0 (Google)
Build crush-extended (default) crush-a2a (separate)

Documentation

Overview

Package a2a provides an Agent-to-Agent Protocol (A2A) v1.0 plugin for Crush. It exposes Crush as an A2A server and provides client tools for invoking remote A2A agents. This plugin uses the official github.com/a2aproject/a2a-go/v2 SDK.

Index

Constants

View Source
const (
	CleanupInterval = 5 * time.Minute
	ArtifactTTL     = 1 * time.Hour
)
View Source
const (
	ToolListAgents  = "a2a_list_agents"
	ToolSendMessage = "a2a_send_message"
	ToolGetTask     = "a2a_get_task"
	ToolAttachFile  = "a2a_attach_file"

	DescriptionListAgents = `` /* 455-byte string literal not displayed */

	DescriptionSendMessage = `` /* 610-byte string literal not displayed */

	DescriptionGetTask = `` /* 306-byte string literal not displayed */

	DescriptionAttachFile = `` /* 571-byte string literal not displayed */

)
View Source
const (
	PluginName       = "a2a"
	HookName         = "a2a-server"
	DefaultPort      = 8200
	DefaultAgentName = "crush"
)

Variables

This section is empty.

Functions

func ExtractText

func ExtractText(msg *a2a.Message) string

func ExtractTextFromParts

func ExtractTextFromParts(parts a2a.ContentParts) string

func ResetManager

func ResetManager()

ResetManager resets the singleton manager for testing.

Types

type A2AServerConfig

type A2AServerConfig struct {
	Port        int           `json:"port,omitempty"`
	AgentName   string        `json:"agent_name,omitempty"`
	Description string        `json:"description,omitempty"`
	Skills      []SkillConfig `json:"skills,omitempty"`
}

A2AServerConfig is the server-side configuration for exposing Crush as an A2A agent. Configured in crush.json under options.plugins.a2a-server

type AttachFileParams

type AttachFileParams struct {
	FilePath    string `json:"file_path" jsonschema:"description=Path to the file to attach."`
	Name        string `json:"name,omitempty" jsonschema:"description=Human-readable name for the artifact."`
	Description string `json:"description,omitempty" jsonschema:"description=Description of the artifact."`
	MediaType   string `json:"media_type,omitempty" jsonschema:"description=MIME type. Auto-detected from extension if omitted."`
}

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client wraps the official A2A SDK client with connection caching.

func NewClient

func NewClient(baseURL string, opts ...ClientOption) *Client

NewClient creates a new A2A client for the given base URL.

func (*Client) CancelTask

func (c *Client) CancelTask(ctx context.Context, taskID a2acore.TaskID) (*a2acore.Task, error)

CancelTask cancels a task by ID.

func (*Client) Close

func (c *Client) Close() error

Close destroys the underlying SDK client.

func (*Client) FetchAgentCard

func (c *Client) FetchAgentCard(ctx context.Context) (*a2acore.AgentCard, error)

FetchAgentCard fetches the agent card from /.well-known/agent-card.json.

func (*Client) GetTask

func (c *Client) GetTask(ctx context.Context, taskID a2acore.TaskID) (*a2acore.Task, error)

GetTask retrieves a task by ID.

func (*Client) LastContextID

func (c *Client) LastContextID() string

LastContextID returns the last contextId received from this server.

func (*Client) SendMessage

func (c *Client) SendMessage(ctx context.Context, msg *a2acore.Message) (a2acore.SendMessageResult, error)

SendMessage sends a message to the remote A2A agent.

func (*Client) SendMessageWithContext

func (c *Client) SendMessageWithContext(ctx context.Context, text, contextID string) (a2acore.SendMessageResult, error)

SendMessageWithContext sends a text message with an existing contextID for multi-turn conversations.

func (*Client) SendStreamingMessage

func (c *Client) SendStreamingMessage(ctx context.Context, msg *a2acore.Message) (iter.Seq2[a2acore.Event, error], error)

SendStreamingMessage sends a message and returns a streaming iterator.

func (*Client) SetContextID

func (c *Client) SetContextID(id string)

SetContextID stores a contextId for auto-propagation in subsequent messages.

type ClientOption

type ClientOption func(*Client)

ClientOption configures a Client.

func WithHeaders

func WithHeaders(h map[string]string) ClientOption

WithHeaders sets custom headers for requests.

func WithLogger

func WithLogger(l *slog.Logger) ClientOption

WithLogger sets a custom logger.

type Config

type Config struct {
	Servers               []ServerConfig `json:"servers,omitempty"`
	DefaultTimeoutSeconds int            `json:"default_timeout_seconds,omitempty"`
}

Config is the client-side configuration for connecting to remote A2A servers. Configured in crush.json under options.plugins.a2a

type GetTaskParams

type GetTaskParams struct {
	TaskID string `json:"task_id" jsonschema:"description=The task ID to retrieve."`
	Server string `json:"server,omitempty" jsonschema:"description=Name of the A2A server. Uses the first configured server if omitted."`
}

type ListAgentsParams

type ListAgentsParams struct {
	Server string `` /* 126-byte string literal not displayed */
}

type SendMessageParams

type SendMessageParams struct {
	Input     string `json:"input" jsonschema:"description=Text message to send to the agent."`
	Server    string `json:"server,omitempty" jsonschema:"description=Name of the A2A server. Uses the first configured server if omitted."`
	ContextID string `json:"context_id,omitempty" jsonschema:"description=Context ID from a previous response for multi-turn conversations."`
	Streaming bool   `json:"streaming,omitempty" jsonschema:"description=If true use SendStreamingMessage for SSE streaming."`
}

type ServerConfig

type ServerConfig struct {
	Name    string            `json:"name"`
	URL     string            `json:"url"`
	Headers map[string]string `json:"headers,omitempty"`
	Enabled *bool             `json:"enabled,omitempty"`
}

ServerConfig defines a single remote A2A server endpoint.

func (ServerConfig) IsEnabled

func (s ServerConfig) IsEnabled() bool

type ServerHook

type ServerHook struct {
	// contains filtered or unexported fields
}

ServerHook implements plugin.Hook to run an A2A v1.0 server alongside Crush.

func NewServerHook

func NewServerHook(app *plugin.App, cfg A2AServerConfig) (*ServerHook, error)

NewServerHook creates a new A2A server hook.

func (*ServerHook) Addr

func (h *ServerHook) Addr() string

func (*ServerHook) ArtifactStore

func (h *ServerHook) ArtifactStore() *artifactStore

ArtifactStore returns the artifact store for use by the attach_file tool.

func (*ServerHook) CurrentTaskID

func (h *ServerHook) CurrentTaskID() a2acore.TaskID

CurrentTaskID returns the task ID of the currently executing task.

func (*ServerHook) Name

func (h *ServerHook) Name() string

func (*ServerHook) Ready

func (h *ServerHook) Ready() <-chan struct{}

func (*ServerHook) Start

func (h *ServerHook) Start(ctx context.Context) error

func (*ServerHook) Stop

func (h *ServerHook) Stop() error

type SkillConfig

type SkillConfig struct {
	ID          string   `json:"id"`
	Name        string   `json:"name"`
	Description string   `json:"description,omitempty"`
	Tags        []string `json:"tags,omitempty"`
	Examples    []string `json:"examples,omitempty"`
}

SkillConfig describes a skill to advertise in the agent card.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL