Documentation
¶
Overview ¶
Package wire provides multi-protocol wire format adapters for tool communication.
It enables encoding and decoding of tool requests and responses across different protocols including MCP (Anthropic), A2A (Google), and ACP (IBM).
Ecosystem Position ¶
wire sits at the protocol boundary, translating between internal representations and protocol-specific wire formats:
┌─────────────────────────────────────────────────────────────────┐ │ Protocol Translation Flow │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ External Client wire Internal │ │ ┌───────────┐ ┌─────────┐ ┌─────────┐ │ │ │ MCP/A2A/ │─────────│ Decode │─────────│ Request │ │ │ │ ACP JSON │ │ │ │ struct │ │ │ └───────────┘ │ ┌─────┐ │ └─────────┘ │ │ ▲ │ │Wire │ │ │ │ │ │ │ │ Impl│ │ ▼ │ │ │ │ └─────┘ │ ┌─────────┐ │ │ ┌───────────┐ │ │ │ Execute │ │ │ │ MCP/A2A/ │◀────────│ Encode │◀────────│ Tool │ │ │ │ ACP JSON │ │ │ │ │ │ │ └───────────┘ └─────────┘ └─────────┘ │ │ │ │ │ ┌────┴────┐ │ │ │Registry │ │ │ │ (lookup)│ │ │ └─────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘
Core Components ¶
- Wire: Interface for protocol-specific encoding/decoding
- MCPWire: Model Context Protocol (Anthropic) - JSON-RPC 2.0 based
- A2AWire: Agent-to-Agent Protocol (Google) - JSON-RPC with artifacts
- ACPWire: Agent Communication Protocol (IBM) - JSON-RPC with agents
- Registry: Thread-safe registry of wire format handlers
- DefaultRegistry: Pre-configured registry with all standard formats
Quick Start ¶
// Use default registry for standard protocols
reg := wire.DefaultRegistry()
w := reg.Get("mcp")
// Encode a request
req := &wire.Request{
ID: "1",
Method: "tools/call",
ToolID: "search",
Arguments: map[string]any{"query": "golang"},
}
data, err := w.EncodeRequest(ctx, req)
// Decode a response
resp, err := w.DecodeResponse(ctx, responseData)
Available Formats ¶
MCP (Model Context Protocol):
- Version: 2025-11-25
- Streaming: Yes
- Batch requests: No
- Progress notifications: Yes
- Cancellation: Yes
A2A (Agent-to-Agent Protocol):
- Version: 0.2.1
- Streaming: Yes
- Batch requests: No
- Progress notifications: Yes
- Cancellation: Yes
ACP (Agent Communication Protocol):
- Version: 1.0.0
- Streaming: No
- Batch requests: Yes
- Progress notifications: No
- Cancellation: Yes
Thread Safety ¶
All exported types are safe for concurrent use:
- MCPWire, A2AWire, ACPWire: Stateless, concurrent-safe
- Registry: sync.RWMutex protects all operations
- DefaultRegistry: Returns shared instance, safe to use concurrently
Error Handling ¶
Sentinel errors (use errors.Is for checking):
- ErrUnsupportedFormat: Unknown wire format requested
- ErrEncodeFailure: Encoding to wire format failed
- ErrDecodeFailure: Decoding from wire format failed
Encode/Decode methods wrap underlying errors with context:
resp, err := w.DecodeResponse(ctx, data)
if err != nil {
// err contains: "decode response: <underlying error>"
}
Integration with ApertureStack ¶
wire integrates with other ApertureStack packages:
- transport: Uses wire for protocol-specific message encoding
- stream: Streaming responses use wire for event encoding
- discover: Tool lists encoded via EncodeToolList/DecodeToolList
- content: Response content types map to wire.Content
Index ¶
- Constants
- Variables
- type A2AWire
- func (w *A2AWire) Capabilities() *Capabilities
- func (w *A2AWire) DecodeRequest(ctx context.Context, data []byte) (*Request, error)
- func (w *A2AWire) DecodeResponse(ctx context.Context, data []byte) (*Response, error)
- func (w *A2AWire) DecodeToolList(ctx context.Context, data []byte) ([]Tool, error)
- func (w *A2AWire) EncodeRequest(ctx context.Context, req *Request) ([]byte, error)
- func (w *A2AWire) EncodeResponse(ctx context.Context, resp *Response) ([]byte, error)
- func (w *A2AWire) EncodeToolList(ctx context.Context, tools []Tool) ([]byte, error)
- func (w *A2AWire) Name() string
- func (w *A2AWire) Version() string
- type ACPWire
- func (w *ACPWire) Capabilities() *Capabilities
- func (w *ACPWire) DecodeRequest(ctx context.Context, data []byte) (*Request, error)
- func (w *ACPWire) DecodeResponse(ctx context.Context, data []byte) (*Response, error)
- func (w *ACPWire) DecodeToolList(ctx context.Context, data []byte) ([]Tool, error)
- func (w *ACPWire) EncodeRequest(ctx context.Context, req *Request) ([]byte, error)
- func (w *ACPWire) EncodeResponse(ctx context.Context, resp *Response) ([]byte, error)
- func (w *ACPWire) EncodeToolList(ctx context.Context, tools []Tool) ([]byte, error)
- func (w *ACPWire) Name() string
- func (w *ACPWire) Version() string
- type Capabilities
- type Content
- type ContentType
- type Error
- type MCPWire
- func (w *MCPWire) Capabilities() *Capabilities
- func (w *MCPWire) DecodeRequest(ctx context.Context, data []byte) (*Request, error)
- func (w *MCPWire) DecodeResponse(ctx context.Context, data []byte) (*Response, error)
- func (w *MCPWire) DecodeToolList(ctx context.Context, data []byte) ([]Tool, error)
- func (w *MCPWire) EncodeRequest(ctx context.Context, req *Request) ([]byte, error)
- func (w *MCPWire) EncodeResponse(ctx context.Context, resp *Response) ([]byte, error)
- func (w *MCPWire) EncodeToolList(ctx context.Context, tools []Tool) ([]byte, error)
- func (w *MCPWire) Name() string
- func (w *MCPWire) Version() string
- type Registry
- type Request
- type Response
- type Tool
- type Wire
Examples ¶
- A2AWire.Capabilities
- ACPWire.Capabilities
- DefaultRegistry
- Error.Error
- Error.Error (WithData)
- MCPWire.Capabilities
- MCPWire.DecodeRequest
- MCPWire.DecodeResponse
- MCPWire.DecodeResponse (Error)
- MCPWire.DecodeToolList
- MCPWire.EncodeRequest
- MCPWire.EncodeResponse
- MCPWire.EncodeToolList
- NewA2A
- NewACP
- NewMCP
- NewRegistry
- Registry.Get
- Registry.List
- Registry.Register
Constants ¶
const A2AVersion = "0.2.1"
A2AVersion is the A2A protocol version.
const ACPVersion = "1.0.0"
ACPVersion is the ACP protocol version.
const MCPVersion = "2025-11-25"
MCPVersion is the MCP specification version.
Variables ¶
var ( // ErrUnsupportedFormat is returned when an unknown wire format is requested. ErrUnsupportedFormat = errors.New("wire: unsupported format") // ErrEncodeFailure is returned when encoding fails. ErrEncodeFailure = errors.New("wire: encode failed") // ErrDecodeFailure is returned when decoding fails. ErrDecodeFailure = errors.New("wire: decode failed") )
Sentinel errors for wire operations. All errors use the "wire: " prefix for consistent error identification.
Functions ¶
This section is empty.
Types ¶
type A2AWire ¶
type A2AWire struct{}
A2AWire implements Wire for Google's Agent-to-Agent protocol.
func NewA2A ¶
func NewA2A() *A2AWire
NewA2A creates a new A2A wire format handler.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewA2A()
fmt.Println("Name:", w.Name())
fmt.Println("Version:", w.Version())
}
Output: Name: a2a Version: 0.2.1
func (*A2AWire) Capabilities ¶
func (w *A2AWire) Capabilities() *Capabilities
Capabilities returns A2A protocol capabilities.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewA2A()
caps := w.Capabilities()
fmt.Println("Streaming:", caps.Streaming)
fmt.Println("BatchRequests:", caps.BatchRequests)
fmt.Println("Progress:", caps.Progress)
fmt.Println("Cancellation:", caps.Cancellation)
}
Output: Streaming: true BatchRequests: false Progress: true Cancellation: true
func (*A2AWire) DecodeRequest ¶
DecodeRequest decodes A2A format to a Request.
func (*A2AWire) DecodeResponse ¶
DecodeResponse decodes A2A format to a Response.
func (*A2AWire) DecodeToolList ¶
DecodeToolList decodes A2A format to a tool list.
func (*A2AWire) EncodeRequest ¶
EncodeRequest encodes a Request to A2A format.
func (*A2AWire) EncodeResponse ¶
EncodeResponse encodes a Response to A2A format.
func (*A2AWire) EncodeToolList ¶
EncodeToolList encodes a tool list to A2A format.
type ACPWire ¶
type ACPWire struct{}
ACPWire implements Wire for IBM's Agent Communication Protocol.
func NewACP ¶
func NewACP() *ACPWire
NewACP creates a new ACP wire format handler.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewACP()
fmt.Println("Name:", w.Name())
fmt.Println("Version:", w.Version())
}
Output: Name: acp Version: 1.0.0
func (*ACPWire) Capabilities ¶
func (w *ACPWire) Capabilities() *Capabilities
Capabilities returns ACP protocol capabilities.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewACP()
caps := w.Capabilities()
fmt.Println("Streaming:", caps.Streaming)
fmt.Println("BatchRequests:", caps.BatchRequests)
fmt.Println("Progress:", caps.Progress)
fmt.Println("Cancellation:", caps.Cancellation)
}
Output: Streaming: false BatchRequests: true Progress: false Cancellation: true
func (*ACPWire) DecodeRequest ¶
DecodeRequest decodes ACP format to a Request.
func (*ACPWire) DecodeResponse ¶
DecodeResponse decodes ACP format to a Response.
func (*ACPWire) DecodeToolList ¶
DecodeToolList decodes ACP format to a tool list.
func (*ACPWire) EncodeRequest ¶
EncodeRequest encodes a Request to ACP format.
func (*ACPWire) EncodeResponse ¶
EncodeResponse encodes a Response to ACP format.
func (*ACPWire) EncodeToolList ¶
EncodeToolList encodes a tool list to ACP format.
type Capabilities ¶
type Capabilities struct {
// Streaming indicates support for streaming responses.
Streaming bool
// BatchRequests indicates support for batched requests.
BatchRequests bool
// Progress indicates support for progress notifications.
Progress bool
// Cancellation indicates support for request cancellation.
Cancellation bool
}
Capabilities describes protocol features.
type Content ¶
type Content struct {
// Type identifies the content type.
Type ContentType
// Text is the text content (for ContentTypeText).
Text string
// MIMEType is the MIME type (for images and resources).
MIMEType string
// Data is binary data (for images).
Data []byte
// URI is the resource URI (for ContentTypeResource).
URI string
}
Content represents a piece of response content.
type ContentType ¶
type ContentType string
ContentType identifies the type of content in a response.
const ( // ContentTypeText is plain text content. ContentTypeText ContentType = "text" // ContentTypeImage is image data. ContentTypeImage ContentType = "image" // ContentTypeResource is a resource reference. ContentTypeResource ContentType = "resource" )
type Error ¶
type Error struct {
// Code is the error code (JSON-RPC style).
Code int
// Message is a human-readable error message.
Message string
// Data contains additional error details.
Data any
}
Error represents a wire protocol error.
func (*Error) Error ¶
Error implements the error interface.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
err := &wire.Error{
Code: -32600,
Message: "Invalid Request",
}
fmt.Println(err.Error())
}
Output: Invalid Request (code: -32600)
Example (WithData) ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
err := &wire.Error{
Code: -32602,
Message: "Invalid params",
Data: "missing required field: query",
}
fmt.Println(err.Error())
}
Output: Invalid params (code: -32602, data: missing required field: query)
type MCPWire ¶
type MCPWire struct{}
MCPWire implements Wire for the Model Context Protocol.
func NewMCP ¶
func NewMCP() *MCPWire
NewMCP creates a new MCP wire format handler.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewMCP()
fmt.Println("Name:", w.Name())
fmt.Println("Version:", w.Version())
}
Output: Name: mcp Version: 2025-11-25
func (*MCPWire) Capabilities ¶
func (w *MCPWire) Capabilities() *Capabilities
Capabilities returns MCP protocol capabilities.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewMCP()
caps := w.Capabilities()
fmt.Println("Streaming:", caps.Streaming)
fmt.Println("BatchRequests:", caps.BatchRequests)
fmt.Println("Progress:", caps.Progress)
fmt.Println("Cancellation:", caps.Cancellation)
}
Output: Streaming: true BatchRequests: false Progress: true Cancellation: true
func (*MCPWire) DecodeRequest ¶
DecodeRequest decodes MCP JSON-RPC format to a Request.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewMCP()
ctx := context.Background()
// JSON-RPC 2.0 request
data := []byte(`{
"jsonrpc": "2.0",
"id": "req-1",
"method": "tools/call",
"params": {
"name": "search",
"arguments": {"query": "test"}
}
}`)
req, err := w.DecodeRequest(ctx, data)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("ID:", req.ID)
fmt.Println("Method:", req.Method)
fmt.Println("ToolID:", req.ToolID)
fmt.Println("Query:", req.Arguments["query"])
}
Output: ID: req-1 Method: tools/call ToolID: search Query: test
func (*MCPWire) DecodeResponse ¶
DecodeResponse decodes MCP JSON-RPC format to a Response.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewMCP()
ctx := context.Background()
// JSON-RPC 2.0 response
data := []byte(`{
"jsonrpc": "2.0",
"id": "req-1",
"result": {
"content": [
{"type": "text", "text": "Search results found"}
]
}
}`)
resp, err := w.DecodeResponse(ctx, data)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("ID:", resp.ID)
fmt.Println("IsError:", resp.IsError)
fmt.Println("Content count:", len(resp.Content))
fmt.Println("Text:", resp.Content[0].Text)
}
Output: ID: req-1 IsError: false Content count: 1 Text: Search results found
Example (Error) ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewMCP()
ctx := context.Background()
// JSON-RPC 2.0 error response
data := []byte(`{
"jsonrpc": "2.0",
"id": "req-1",
"error": {
"code": -32600,
"message": "Invalid Request"
}
}`)
resp, err := w.DecodeResponse(ctx, data)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("ID:", resp.ID)
fmt.Println("IsError:", resp.IsError)
fmt.Println("Error code:", resp.Error.Code)
fmt.Println("Error message:", resp.Error.Message)
}
Output: ID: req-1 IsError: true Error code: -32600 Error message: Invalid Request
func (*MCPWire) DecodeToolList ¶
DecodeToolList decodes MCP format to a tool list.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewMCP()
ctx := context.Background()
data := []byte(`{
"tools": [
{
"name": "search",
"description": "Search the web",
"inputSchema": {"type": "object"}
}
]
}`)
tools, err := w.DecodeToolList(ctx, data)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Tool count:", len(tools))
fmt.Println("Tool name:", tools[0].Name)
fmt.Println("Tool description:", tools[0].Description)
}
Output: Tool count: 1 Tool name: search Tool description: Search the web
func (*MCPWire) EncodeRequest ¶
EncodeRequest encodes a Request to MCP JSON-RPC format.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewMCP()
ctx := context.Background()
req := &wire.Request{
ID: "req-1",
Method: "tools/call",
ToolID: "search",
Arguments: map[string]any{
"query": "golang tutorials",
},
}
data, err := w.EncodeRequest(ctx, req)
if err != nil {
fmt.Println("Error:", err)
return
}
// Output is JSON-RPC 2.0 format
fmt.Println("Encoded successfully:", len(data) > 0)
}
Output: Encoded successfully: true
func (*MCPWire) EncodeResponse ¶
EncodeResponse encodes a Response to MCP JSON-RPC format.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewMCP()
ctx := context.Background()
resp := &wire.Response{
ID: "req-1",
Content: []wire.Content{
{Type: wire.ContentTypeText, Text: "Hello, world!"},
},
}
data, err := w.EncodeResponse(ctx, resp)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Encoded successfully:", len(data) > 0)
}
Output: Encoded successfully: true
func (*MCPWire) EncodeToolList ¶
EncodeToolList encodes a tool list to MCP format.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
w := wire.NewMCP()
ctx := context.Background()
tools := []wire.Tool{
{
Name: "search",
Description: "Search the web",
InputSchema: map[string]any{
"type": "object",
"properties": map[string]any{
"query": map[string]any{"type": "string"},
},
},
},
}
data, err := w.EncodeToolList(ctx, tools)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Encoded successfully:", len(data) > 0)
}
Output: Encoded successfully: true
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry manages wire format handlers.
Contract:
- Concurrency: All methods are safe for concurrent use via sync.RWMutex.
- Registration: Register replaces existing handlers with the same name.
- Lookup: Get returns nil for unknown format names (no error).
- Ownership: Wire implementations are shared; do not modify after registration.
func DefaultRegistry ¶
func DefaultRegistry() *Registry
DefaultRegistry returns the default wire format registry.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
reg := wire.DefaultRegistry()
// Default registry has mcp, a2a, and acp pre-registered
mcp := reg.Get("mcp")
a2a := reg.Get("a2a")
acp := reg.Get("acp")
fmt.Println("Has mcp:", mcp != nil)
fmt.Println("Has a2a:", a2a != nil)
fmt.Println("Has acp:", acp != nil)
}
Output: Has mcp: true Has a2a: true Has acp: true
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry creates a new empty registry.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
reg := wire.NewRegistry()
// Register a custom wire format
reg.Register("mcp", wire.NewMCP())
// List registered formats
names := reg.List()
fmt.Println("Registered:", len(names) > 0)
}
Output: Registered: true
func (*Registry) Get ¶
Get returns the wire handler for the given format name, or nil if not found.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
reg := wire.NewRegistry()
reg.Register("mcp", wire.NewMCP())
// Get existing format
w := reg.Get("mcp")
fmt.Println("Found mcp:", w != nil)
fmt.Println("Name:", w.Name())
// Get non-existent format
missing := reg.Get("unknown")
fmt.Println("Found unknown:", missing != nil)
}
Output: Found mcp: true Name: mcp Found unknown: false
func (*Registry) List ¶
List returns the names of all registered wire formats.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
reg := wire.NewRegistry()
reg.Register("mcp", wire.NewMCP())
reg.Register("a2a", wire.NewA2A())
names := reg.List()
fmt.Println("Count:", len(names))
}
Output: Count: 2
func (*Registry) Register ¶
Register adds a wire format handler to the registry. If a handler with the same name exists, it is replaced.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolprotocol/wire"
)
func main() {
reg := wire.NewRegistry()
// Register formats
reg.Register("mcp", wire.NewMCP())
reg.Register("a2a", wire.NewA2A())
reg.Register("acp", wire.NewACP())
fmt.Println("Registered count:", len(reg.List()))
}
Output: Registered count: 3
type Request ¶
type Request struct {
// ID is the request identifier.
ID string
// Method is the RPC method (e.g., "tools/call", "tools/list").
Method string
// ToolID is the tool to invoke.
ToolID string
// Arguments are the tool input parameters.
Arguments map[string]any
// Meta contains protocol-specific metadata.
Meta map[string]any
}
Request represents a tool invocation request.
type Response ¶
type Response struct {
// ID is the request identifier this responds to.
ID string
// Content is the response payload.
Content []Content
// IsError indicates if this is an error response.
IsError bool
// Error contains error details when IsError is true.
Error *Error
// Meta contains protocol-specific metadata.
Meta map[string]any
}
Response represents a tool invocation response.
type Tool ¶
type Tool struct {
// Name is the tool identifier.
Name string
// Description explains what the tool does.
Description string
// InputSchema is the JSON Schema for tool arguments.
InputSchema map[string]any
}
Tool describes a tool's interface.
type Wire ¶
type Wire interface {
// Name returns the protocol name (e.g., "mcp", "a2a", "acp").
Name() string
// Version returns the protocol version.
Version() string
// EncodeRequest encodes a request to wire format.
EncodeRequest(ctx context.Context, req *Request) ([]byte, error)
// DecodeRequest decodes a request from wire format.
DecodeRequest(ctx context.Context, data []byte) (*Request, error)
// EncodeResponse encodes a response to wire format.
EncodeResponse(ctx context.Context, resp *Response) ([]byte, error)
// DecodeResponse decodes a response from wire format.
DecodeResponse(ctx context.Context, data []byte) (*Response, error)
// EncodeToolList encodes a list of tools to wire format.
EncodeToolList(ctx context.Context, tools []Tool) ([]byte, error)
// DecodeToolList decodes a list of tools from wire format.
DecodeToolList(ctx context.Context, data []byte) ([]Tool, error)
// Capabilities returns the protocol capabilities.
Capabilities() *Capabilities
}
Wire encodes/decodes protocol-specific wire formats.
Contract:
- Concurrency: All implementations are safe for concurrent use. MCPWire, A2AWire, and ACPWire are stateless and thread-safe.
- Context: Encode/Decode methods accept context for future extensibility. Current implementations do not perform I/O and ignore context.
- Errors: Returns wrapped errors with context (e.g., "decode request: ..."). Use errors.Is/errors.As for error inspection.
- Ownership: Input data is not modified. Output []byte is owned by caller.
- Nil safety: Passing nil *Request or *Response to Encode methods will panic.