a2a

package module
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

README

a2a

a2a is Scope's thin adapter for the Agent-to-Agent protocol. It is not a second A2A SDK: the JSON-RPC envelope, SSE framing, AgentCard schema, transport, and task lifecycle all come from the official github.com/a2aproject/a2a-go/v2.

It works in both directions — a remote A2A agent becomes a local callable tool, and a Scope capability becomes an A2A endpoint.

Install

go get github.com/Tangerg/scope/a2a

Calling remote agents

OpenToolSet opens clients and resolves AgentCards in one batch, then hands back a ToolSet that owns both an immutable []tool.Tool view and an idempotent close:

toolset, err := a2a.OpenToolSet(ctx, a2a.ToolSetConfig{
    Endpoints: endpoints,
})
if err != nil {
    return err
}
defer toolset.Close()

registry, err := tool.NewRegistry(toolset.Tools()...)

The underlying SDK client is not exposed, and there is no provider, cache, or registry here. Discovery, refresh, and caching policy belong to the caller.

An A2A tool takes a single message field. A2A is a message protocol, not a typed function call, so the tool surface does not pretend otherwise.

Serving a capability

Implement the narrow Agent interface — text in, streamed text out — and mount it:

handler, err := a2a.NewHTTPHandler(a2a.HandlerConfig{Agent: myAgent, Card: card})
if err != nil {
    return err
}
http.Handle("/", handler)

The handler mounts the JSON-RPC method endpoint plus the well-known AgentCard. The executor emits a legal event sequence — submitted, working, incremental output, then completed, failed, or canceled — and skips empty increments, because the SDK rejects an empty artifact.

Transport

JSON-RPC over HTTP is the default, matching the rest of the stack. The SDK's REST and gRPC bindings are not precluded, just not wired here.

Content projection

A2A content is projected text-first into Scope's semantics, so a caller reads the same shape it gets from any other Scope capability.

See ARCHITECTURE.md for the boundaries this rests on.

Documentation

Overview

Package a2a integrates the Agent-to-Agent (A2A) protocol into the Scope agent framework, wrapping the official SDK github.com/a2aproject/a2a-go/v2 (sdka2a/a2asrv/a2aclient).

It has two sides:

  • CLIENT — OpenToolSet resolves remote AgentCards and returns a ToolSet that owns both the tool view and opened protocol clients.

  • SERVER — expose a capability AS an A2A endpoint. Implement the narrow Agent interface (text in, streamed text out); NewHTTPHandler adapts it to the SDK and mounts the JSON-RPC method endpoint plus the well-known AgentCard.

The transport default is JSON-RPC over HTTP, matching the rest of the stack; the SDK's REST/gRPC bindings are not precluded but are not wired here.

Naming convention: the SDK's core types package is imported as `sdka2a` to avoid colliding with this package's own name; the server and client SDK packages keep their names `a2asrv` / `a2aclient`.

Index

Examples

Constants

View Source
const DefaultRPCPattern = "/invoke"

DefaultRPCPattern is where NewHTTPHandler mounts the JSON-RPC method endpoint. The AgentCard's JSON-RPC interface URL must point at this path.

Variables

View Source
var (
	ErrNilCard     = errors.New("a2a: agent card must not be nil")
	ErrInvalidCard = errors.New("a2a: invalid agent card")

	ErrNilAgent = errors.New("a2a: agent must not be nil")

	ErrEmptyCardURL       = errors.New("a2a: card URL must not be empty")
	ErrInvalidCardURL     = errors.New("a2a: invalid card URL")
	ErrInvalidCardTimeout = errors.New("a2a: card timeout must not be negative")
	ErrInvalidRPCOrigin   = errors.New("a2a: invalid allowed RPC origin")
	ErrOriginNotAllowed   = errors.New("a2a: origin not allowed")

	ErrInvalidRPCPattern = errors.New("a2a: invalid RPC pattern")

	ErrInvalidResult = errors.New("a2a: invalid send-message result")
)

Functions

func NewHTTPHandler

func NewHTTPHandler(config ServerConfig) (http.Handler, error)

func NewJSONRPCInterface

func NewJSONRPCInterface(url string) *sdka2a.AgentInterface
Example
package main

import (
	"fmt"

	"github.com/Tangerg/scope/a2a"
)

func main() {
	transport := a2a.NewJSONRPCInterface("https://agent.example/invoke")

	fmt.Println(transport.URL, transport.ProtocolBinding)
}
Output:
https://agent.example/invoke JSONRPC

Types

type Agent

type Agent interface {
	// Run handles one inbound A2A message, already flattened to text, and
	// yields the reply as a sequence of text chunks. A single-shot agent
	// yields once; a streaming agent yields deltas. A yielded error ends the
	// task as failed and stops iteration.
	Run(ctx context.Context, input string) iter.Seq2[string, error]
}

Agent is the scope-side capability exposed over A2A. It is intentionally narrow — text in, streamed text out — so the consumer (an agent runtime) implements it without this package depending on those layers. The interface lives here, in the consumer, per the convention: the a2a server is what "runs an agent", so it declares the shape it needs.

type Endpoint

type Endpoint struct {
	// Name overrides the model-visible tool name. Empty derives it from the
	// resolved AgentCard.
	Name string

	// CardURL is the absolute HTTP(S) URL used to resolve the AgentCard.
	CardURL string

	// HTTPClient is the client used for both card resolution and RPC calls.
	// Nil uses http.DefaultClient. The caller retains ownership; a restricted
	// shallow copy is used internally.
	HTTPClient *http.Client

	// CardTimeout bounds AgentCard resolution only. Zero selects 30 seconds; it
	// does not impose a timeout on long-running agent RPC calls.
	CardTimeout time.Duration

	// AllowedRPCOrigins adds trusted RPC origins beyond CardURL's own origin.
	// Entries use the exact "scheme://host[:port]" form. Empty prevents an
	// AgentCard from redirecting calls to another origin.
	AllowedRPCOrigins []string
}

Endpoint describes one remote A2A agent to expose as a chat tool. Its zero policy keeps discovery and RPC traffic on CardURL's origin.

type RemoteAgentError

type RemoteAgentError struct {
	// State is the task state the remote reported.
	State sdka2a.TaskState
	// Detail is any human-readable message the remote attached, or "".
	Detail string
}

RemoteAgentError reports that a remote A2A task did not complete successfully. It lets a caller use errors.AsType to distinguish it from transport or protocol failures: the remote was reached and answered, but the work failed, was canceled or rejected, or requires unsupported continuation.

func (*RemoteAgentError) Error

func (r *RemoteAgentError) Error() string

type ServerConfig

type ServerConfig struct {
	// Agent is the capability served over A2A. Required.
	Agent Agent

	// Card is the AgentCard served at the well-known path. Required and
	// snapshotted during construction — its SupportedInterfaces should advertise
	// a JSON-RPC interface whose URL ends in RPCPattern. Build it with
	// [NewJSONRPCInterface] for the transport entry.
	Card *sdka2a.AgentCard

	// RPCPattern overrides where the JSON-RPC endpoint is mounted. Empty
	// uses [DefaultRPCPattern].
	RPCPattern string
}

ServerConfig wires a Agent into an HTTP A2A endpoint.

type ToolSet

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

ToolSet owns the clients behind an immutable view of remote-agent tools. Tools remain usable until Close; the value must not be copied after first use.

func OpenToolSet

func OpenToolSet(ctx context.Context, endpoints ...Endpoint) (*ToolSet, error)

OpenToolSet closes every client opened before a later endpoint fails, so a failed construction never transfers partial lifecycle ownership to callers.

func (*ToolSet) Close

func (t *ToolSet) Close() error

Close releases every remote-agent client in reverse acquisition order. It is nil-safe and idempotent so multiple shutdown paths can share the owner.

func (*ToolSet) Tools

func (t *ToolSet) Tools() []toolcontract.Tool

Tools returns a snapshot so callers cannot mutate the set's ordered view.

Jump to

Keyboard shortcuts

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