Documentation
¶
Overview ¶
Package a2a integrates the Agent-to-Agent protocol through a2a-go/v2.
OpenToolSet resolves remote AgentCards and returns a ToolSet that owns the opened clients. NewHTTPHandler serves an Agent at the exact JSON-RPC path advertised by its card, plus the well-known AgentCard endpoint. The Host supplies and owns the task store, so task retention and eviction are its responsibility rather than a hidden default. Discovery has independent time and decoded-body limits configured by Endpoint.CardTimeout and Endpoint.CardMaxBytes; neither limits RPC work. A served handler bounds each RPC request body by ServerConfig.MaxRequestBytes before the SDK decodes it.
Endpoint.ConcurrencyPolicy supplies Host scheduling declarations for remote tools; ToolConcurrencyPolicy describes their scope. The served handler is JSON-RPC; the client opens the JSON-RPC and HTTP+JSON transports a card advertises. Client and server spans retain error classifications without raw messages; callers receive complete protocol errors.
The SDK owns task state, history, artifact accumulation, and cancellation. Serving a continuation emits updates to that existing task rather than a replacement Task. Agent.Run receives the current message; retained protocol history is not an implicit conversational context for the Agent. An input-required remote task becomes a tool.InputRequest bound to the original task, context, and tool binding. Ordinary continuations require a non-empty text answer. External waits bind a WaitAddress and an exact JSON Schema, so answers follow the remote contract without losing numeric precision. Validated answers reuse the reply identity and continue the same task. Authentication requests require explicit Host handling. Durable Hosts set Endpoint.ContinuationIdentity from their stable principal and execution revision, and use that same revision in the interaction's ToolSet declaration. The adapter also binds discovery, remote interfaces and tool name. Reconstructing an equivalent ToolSet can then resume a saved InputRequest; changing its authority rejects the continuation before RPC dispatch. With an empty identity, continuations belong only to the opened ToolSet's lifetime.
Ordinary task output rejects structured, binary and URI parts. External waits can carry JSON in text parts to avoid the SDK's numeric Data conversion. Independently encoded JSON-RPC and HTTP+JSON peers check result semantics; raw JSON-RPC callers exercise stored-task continuation and cancellation. Raw peers must offer the interface's advertised protocol version using A2A-Version; an omitted version does not implicitly select this service.
Index ¶
- Constants
- Variables
- func NewHTTPHandler(config ServerConfig) (http.Handler, error)
- func NewProtocolHTTPHandler(config ProtocolServerConfig) (http.Handler, error)
- type Agent
- type Endpoint
- type ProtocolServerConfig
- type RemoteAgentError
- type ServerConfig
- type ToolConcurrencyPolicy
- type ToolSet
- type WaitAddress
Examples ¶
Constants ¶
const ( MediaTypePlainText = "text/plain" MediaTypeJSON = "application/json" )
const ( // WaitMetadataKey carries the WaitAddress an answering message replies to. WaitMetadataKey = "scope.wait" // AnswerSchemaMetadataKey carries the JSON Schema an answer must satisfy, // as exact text that survives SDK metadata conversion. AnswerSchemaMetadataKey = "scope.answer_schema" // ExternalWaitExtension is the A2A extension URI agents declare to // publish external waits through this metadata. ExternalWaitExtension = "urn:scope:a2a:external-wait" )
Variables ¶
var ( ErrUnsupportedContent = errors.New("a2a: content cannot be represented as plain text") ErrNilCard = errors.New("a2a: agent card must not be nil") ErrInvalidCard = errors.New("a2a: invalid agent card") ErrNilTaskStore = errors.New("a2a: task store must not be nil") ErrNilProtocolHandler = errors.New("a2a: protocol handler must not be nil") ErrInvalidMaxRequestBytes = errors.New("a2a: maximum request bytes must not be negative") 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") ErrInvalidCardMaxBytes = errors.New("a2a: card maximum bytes must not be negative") ErrInvalidRPCOrigin = errors.New("a2a: invalid allowed RPC origin") ErrInvalidRPCInterface = errors.New("a2a: invalid RPC interface") ErrInvalidResult = errors.New("a2a: invalid send-message result") // ErrInvalidWaitAddress rejects metadata that does not name exactly one // external wait. ErrInvalidWaitAddress = errors.New("a2a: invalid wait address") )
var ErrContinuationBinding = errors.New("a2a: continuation does not match the original remote task")
Functions ¶
func NewHTTPHandler ¶
func NewHTTPHandler(config ServerConfig) (http.Handler, error)
NewHTTPHandler leaves listener, TLS, timeouts, and middleware ownership with the Host. The assembled request handler owns which protocol capabilities this server actually provides; the card projects that one fact. Construction rejects unencodable cards and any capability the fixed handler cannot fulfill.
Example ¶
handler, err := a2a.NewHTTPHandler(a2a.ServerConfig{
Agent: echoAgent{},
Card: &sdka2a.AgentCard{
Name: "Echo",
SupportedInterfaces: []*sdka2a.AgentInterface{
sdka2a.NewAgentInterface("https://agent.example/custom/rpc", sdka2a.TransportProtocolJSONRPC),
},
},
// The Host owns the task store and its retention; this one is unbounded.
TaskStore: taskstore.NewInMemory(&taskstore.InMemoryStoreConfig{Authenticator: a2asrv.NewTaskStoreAuthenticator()}),
})
if err != nil {
panic(err)
}
response := httptest.NewRecorder()
handler.ServeHTTP(response, httptest.NewRequest(http.MethodGet, a2asrv.WellKnownAgentCardPath, nil))
var card sdka2a.AgentCard
if err := jsonv2.Unmarshal(response.Body.Bytes(), &card); err != nil {
panic(err)
}
fmt.Println(card.SupportedInterfaces[0].URL)
Output: https://agent.example/custom/rpc
func NewProtocolHTTPHandler ¶ added in v0.59.0
func NewProtocolHTTPHandler(config ProtocolServerConfig) (http.Handler, error)
NewProtocolHTTPHandler serves a fixed agent card and an already-bound A2A request handler on its declared JSON-RPC route.
Types ¶
type Agent ¶
type Agent interface {
// Run handles one inbound A2A message, containing only plain 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. Errors are internal diagnostics; the
// remote task receives a fixed failure message without their contents.
Run(ctx context.Context, input string) iter.Seq2[string, error]
}
Agent is the text-only boundary served by NewHTTPHandler.
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) AgentCard URL.
CardURL string
// ContinuationIdentity is the Host's stable, non-secret identity for the
// endpoint's principal and execution revision. Reuse it across reconstruction
// only while both remain the same. Empty confines continuations to this
// ToolSet's lifetime; HTTPClient credentials cannot establish durable identity.
ContinuationIdentity string
// HTTPClient serves 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
// CardMaxBytes bounds the decoded AgentCard response body before parsing.
// Zero selects 1 MiB. Excess data returns an *http.MaxBytesError, even if a
// valid card precedes it. This limit does not apply to RPC responses.
CardMaxBytes int64
// 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
// ConcurrencyPolicy belongs to the host because distinct remote task IDs do
// not establish resource independence. Nil supplies no overlap declaration.
ConcurrencyPolicy ToolConcurrencyPolicy
// Effect is the host's replay guarantee for this endpoint. A message ID or
// task ID alone does not prove durable deduplication; the default is unsafe.
Effect toolcontract.Effect
}
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 ProtocolServerConfig ¶ added in v0.61.0
type ProtocolServerConfig struct {
// Card has the requirements of [ServerConfig.Card] apart from its text-only
// modes; Handler owns the semantics of every capability it declares.
Card *sdka2a.AgentCard
Handler a2asrv.RequestHandler
// MaxRequestBytes has the meaning of [ServerConfig.MaxRequestBytes].
MaxRequestBytes int64
}
ProtocolServerConfig serves an already-bound A2A request handler.
type RemoteAgentError ¶
RemoteAgentError reports that a reached remote A2A task did not complete, as distinct from transport or protocol failures. A task that failed, was canceled, or was rejected carries it as the Cause of a definite core/tool.Failure; a task that requires unsupported continuation returns it as an ordinary error, because the remote task may still advance.
func (*RemoteAgentError) Error ¶
func (r *RemoteAgentError) Error() string
type ServerConfig ¶
type ServerConfig struct {
Agent Agent
// Card is the AgentCard served at the well-known path. Required and
// snapshotted during construction. SupportedInterfaces must contain exactly
// one JSON-RPC interface using the SDK's protocol version and an absolute
// HTTP(S) URL. Its path is the exact RPC route; hosts own public-origin
// routing and reverse-proxy configuration. Its Capabilities must not declare
// push notifications or an extended agent card, which this handler does not
// provide. Default and skill input/output modes may declare only text/plain,
// which the Agent boundary can represent. Streaming and input modes are
// enforced from the published snapshot.
Card *sdka2a.AgentCard
// TaskStore persists A2A task state and is required: the Host owns the store
// and its retention rather than inheriting a hidden default. The SDK's bundled
// in-memory store (taskstore.NewInMemory) keeps every task for the process
// lifetime with no eviction, so a long-running server must supply a store it
// can bound; a short-lived one may pass that in-memory store explicitly.
TaskStore taskstore.Store
// MaxRequestBytes bounds one RPC request body. The protocol decoder buffers a
// whole request before any authentication or admission can refuse it, so an
// unbounded body lets any caller choose this server's memory use. Zero selects
// 4 MiB, far above an ordinary text or JSON message; a Host exchanging larger
// payloads raises it.
MaxRequestBytes int64
}
type ToolConcurrencyPolicy ¶ added in v0.17.0
type ToolConcurrencyPolicy func(toolcontract.Invocation) (key string, concurrent bool)
ToolConcurrencyPolicy supplies a Host classifier for calls from one model response. Its result follows the scheduling contract of github.com/Tangerg/scope/agent/interaction.ConcurrentTool. The adapter does not serialize direct Tool.Call invocations. The invocation has passed schema validation. The callback must be deterministic, side-effect-free, and safe for concurrent use and replay. It must not capture a Tool, client, or execution backend; schedulers retain this declaration independently of the endpoint's execution lifetime.
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 ¶
OpenToolSet closes every client opened before a later endpoint fails, so a failed construction never transfers partial lifecycle ownership to callers.
func (*ToolSet) Close ¶
Close releases clients in reverse acquisition order and is nil-safe and idempotent.
func (*ToolSet) Tools ¶
func (t *ToolSet) Tools() []toolcontract.Tool
Tools returns a snapshot so callers cannot mutate the set's ordered view.
type WaitAddress ¶ added in v0.60.0
WaitAddress names one external wait: the Process that opened it and its key. It is structured so a child key is never mistaken for a globally unique wait.
func ParseWaitAddress ¶ added in v0.60.0
func ParseWaitAddress(value any) (WaitAddress, error)
ParseWaitAddress reads the WaitAddress an SDK metadata value carries.
func (WaitAddress) Metadata ¶ added in v0.60.0
func (w WaitAddress) Metadata() (map[string]any, error)
func (WaitAddress) Valid ¶ added in v0.60.0
func (w WaitAddress) Valid() bool