Releases: EmilLindfors/a2a-rs
a2a-web-client v0.4.0
Added
- (a2a-agents) MCP server over Streamable HTTP transport
Documentation
- Doc-comment audit, add ROADMAP, retire stale planning docs
Feat
- (a2a-rs) Client Transport port + JSON-RPC 2.0 client + card negotiation
Refactor
- (a2a-rs) Split streaming & push out of storage adapters (Phase 4 final)
a2a-rs v0.4.0
Added
- (a2a-rs) Add runnable jsonrpc_client example
- (a2a-agents) MCP server over Streamable HTTP transport
- (0.4) Typed error details, task versioning, call interceptors, streaming wiring + doc audit
Changed
- (a2a-rs) Remove unused synchronous port traits
Documentation
- Doc-comment audit, add ROADMAP, retire stale planning docs
Fixed
- Fix json rpc
Feat
- (a2a-rs) Client Transport port + JSON-RPC 2.0 client + card negotiation
Refactor
- (a2a-rs) Split streaming & push out of storage adapters (Phase 4 final)
Added
impl AsyncStreamingHandler for Arc<dyn AsyncStreamingHandler>— a
forwarding blanket impl so a type-erased, shared streaming backend can be
passed wherever animpl AsyncStreamingHandleris expected (e.g.
TaskService::with_streaming_handler). This lets one streaming instance be
injected into both a message handler and a transport adapter without naming
its concrete type, so handler broadcasts and SSE subscribers share a registry.
Breaking Changes — Port capability decomposition
The server-side AsyncTaskManager port trait carried 17 methods spanning four
distinct capabilities. It has been removed and split into focused capability
traits. All consumers are in-workspace; there is no deprecation shim.
Task ports
- Removed
AsyncTaskManager. - Added
AsyncTaskLifecycle— per-task CRUD:create,get,update_status,
cancel,exists. - Added
AsyncTaskQuery— cross-task listing:list. - Added
AsyncTaskLifecycleExt(blanket-implemented) — validation
conveniences:get_validated,cancel_validated. - Express requirements at the use site (e.g.
T: AsyncTaskLifecycle + AsyncTaskQuery);
there is no umbrella trait.
Method renames (the noun-prefix is redundant once the trait carries it):
Old (AsyncTaskManager) |
New |
|---|---|
create_task(id, ctx) |
AsyncTaskLifecycle::create |
get_task(id, hist) |
AsyncTaskLifecycle::get |
update_task_status(...) |
AsyncTaskLifecycle::update_status |
cancel_task(id) |
AsyncTaskLifecycle::cancel |
task_exists(id) |
AsyncTaskLifecycle::exists |
list_tasks_v3(params) |
AsyncTaskQuery::list |
- Removed the dead
get_task_metadataand legacylist_tasks(context, limit)
methods (never called).
Push-notification ports
- The four v1.0.0 push-config methods moved off
AsyncTaskManagerand were
reconciled intoAsyncNotificationManager, now expressed in terms of the
richer multi-config model:set_config,get_config,list_configs,
delete_config. - Added
AsyncNotificationManagerExt(blanket-implemented):validate_config,
set_validated. - Removed the drifting single-config methods (
set_task_notification,
get_task_notification,remove_task_notification,has_task_notification,
send_test_notification) and the unusednotify_task_status_update/
notify_task_artifact_updatestubs from the async trait. The synchronous
NotificationManagertrait is unchanged.
Strongly-typed identifiers
- Added
TaskId,ContextId,PushConfigIdnewtypes (domain::ids,
re-exported from the crate root). Each validates non-emptiness on construction
(FromStr/TryFrom), making argument-order mix-ups a compile error. They
appear in the new port signatures; conversion from wire strings happens once at
the RPC boundary.#[serde(transparent)]deserialization bypasses validation
by design (validated at the boundary).
Dispatch — ports held as Arc<dyn …> at the composition edge
The composition-edge structs no longer carry viral generic parameters; they hold
their ports as Arc<dyn …> trait objects. Dispatch goes through the vtable —
one indirect call per RPC, negligible on the I/O-bound port boundary — and the
generic noise disappears from every type that holds a processor or handler.
DefaultRequestProcessorlost its five generic parameters
(<M, T, N, A, S>). It is now a plain non-generic struct with
Arc<dyn AsyncMessageHandler>,Arc<dyn AsyncTaskLifecycle>,
Arc<dyn AsyncTaskQuery>,Arc<dyn AsyncNotificationManager>,
Arc<dyn AgentInfoProvider>, andArc<dyn AsyncStreamingHandler>fields.
Constructors (new,with_handler,with_streaming_handler) now take
impl Traitarguments, so call sites are unchanged.DefaultMessageHandlerlost its<T>parameter; it holds
Arc<dyn AsyncTaskLifecycle>and its constructor takes
impl AsyncTaskLifecycle + 'static.ReimbursementHandler(ina2a-agents) lost its<T>parameter; it holds
Arc<dyn AsyncTaskLifecycle>+Arc<dyn AsyncStreamingHandler>. TheClone
bound it forced on storage is gone (cloning anArc<dyn …>is a refcount bump).
Migration
- Construction is source-compatible: the de-generic'd constructors accept the
same arguments viaimpl Trait, so existingDefaultRequestProcessor::new(…)
/ReimbursementHandler::new(…)call sites compile unchanged. - Code that named the processor's generic parameters
(DefaultRequestProcessor<M, T, N, A, S>) must drop the type arguments — the
type is now non-generic. - The HTTP client API (
HttpClient::get_task,cancel_task, etc.) is
unaffected — those names belong to the client surface, not the server port.
Added — cross-port TaskStatusBroadcast mixin
The capability-mixin pattern from .claude/rules/hexagonal_architecture.md §9,
applied at the port boundary (application::task_status_broadcast, behind the
server feature):
- Added accessor ingredients
HasTaskLifecycleandHasStreaming— each
hands out a&dynport, never a concrete adapter. - Added
TaskStatusBroadcast, a blanket-implemented mixin giving any host
that exposes both ingredients anupdate_and_broadcast("commit the status
through the lifecycle port, then announce it through the streaming port")
method for free. A host exposing only one ingredient does not get the method —
acompile_faildoc test pins that guarantee. TaskServiceimplements both accessors (see below), so it gains
update_and_broadcastwithout coupling its lifecycle and streaming ports.
This is additive (no behavior change to existing call paths). Consuming it in
the request flow — and shedding the storage adapter's internal self-broadcast —
is deferred (REFACTORING_PLAN.md §4.0.2).
Added — application/transport split (REFACTORING_PLAN.md §4.2)
DefaultRequestProcessor previously did two jobs: orchestrating the ports and
serving as the ConnectRPC transport adapter. Those layers are now separated.
- Added
application::TaskService(behind theserverfeature) — the inner
application service. It owns the six ports asArc<dyn …>and holds all
use-case orchestration (send_message,send_streaming_message,get,
list,cancel,subscribe, push-config CRUD,extended_agent_card),
speaking only domain types andA2AError. It hosts theHasTaskLifecycle/
HasStreamingaccessors, so it ownsupdate_and_broadcast. DefaultRequestProcessoris now a thin ConnectRPC transport adapter that
decodesbuffawire views, delegates to aTaskService, and re-encodes the
results. Its public constructors (new,with_handler,
with_streaming_handler) are unchanged, so all call sites compile as before.
map_*helpers andNoopStreamingHandlerremain transport-side.
Changed — storage no longer self-broadcasts (REFACTORING_PLAN.md §4.0.2)
Persistence and streaming are now decoupled in the adapters; "commit then
announce" is owned by the orchestration layer via the TaskStatusBroadcast
mixin.
InMemoryTaskStorage/SqlxTaskStorageupdate_statusandcancelare
now persistence-only — they no longer callbroadcast_status_updateas a side
effect. (Both structs still implementAsyncStreamingHandler; that is where
streaming subscribers live. Shedding that role entirely is a later struct
split, not done here.)- Added
TaskStatusBroadcast::cancel_and_broadcast, the cancellation
counterpart toupdate_and_broadcast.TaskService::cancelnow routes through
it, so cancellations still reach subscribers. DefaultMessageHandlernow hosts the broadcast mixin: it holds a streaming
port in addition to the lifecycle port and routes every transition in
process_messagethroughupdate_and_broadcast. Breaking: its
constructor takes a streaming port (and a responder — see below); use
DefaultMessageHandler::echo(lifecycle, streaming)for the previous behavior.ReimbursementHandler(ina2a-agents) implementsHasTaskLifecycle/
HasStreamingand broadcasts at all five transition sites, including the
background AI worker — its updates and push notifications no longer depend on a
storage side effect.- Behavioral note: an agent that drives
update_status/canceldirectly on
storage no longer streams as a side effect. To announce transitions, host the
TaskStatusBroadcastmixin (hold both ports) or useDefaultMessageHandler.
Breaking — storage/streaming/push struct-split (REFACTORING_PLAN.md §4.3, final)
The storage adapters shed their two non-persistence jobs. InMemoryTaskStorage
and SqlxTaskStorage previously implemented persistence and streaming
fan-out and fired push notifications inside their broadcast helpers. Each of
those is now its own adapter behind its own port, wired at the composition edge.
- Removed the
AsyncStreamingHandlerimpl (and the internalsubscribers
map) fromInMemoryTaskStorageandSqlxTaskStorage. They now implement only
AsyncTaskLifecycle+AsyncTaskQuery+AsyncNotificationManager
(persistence and push-config CRUD). - Added `adapte...
a2a-mcp v0.4.0
Added
- (0.4) Finish mcp-client framework integration + a2a-mcp edition 2024
- (a2a-agents) MCP server over Streamable HTTP transport
Documentation
- Doc-comment audit, add ROADMAP, retire stale planning docs
Feat
- (a2a-rs) Client Transport port + JSON-RPC 2.0 client + card negotiation
Refactor
- (a2a-rs) Split streaming & push out of storage adapters (Phase 4 final)
a2a-ap2 v0.3.1
Other
- fmt,clippy
- migrate to Connect-Rust, refactor project structure, update protobuf specs, and clean up temporary scripts
- docs
a2a-agents v0.4.0
Added
- (0.4) Finish mcp-client framework integration + a2a-mcp edition 2024
- (a2a-agents) MCP server over Streamable HTTP transport
- (0.4) Typed error details, task versioning, call interceptors, streaming wiring + doc audit
Changed
- (a2a-agents) Drop the stale ws_port config field
Documentation
- Doc-comment audit, add ROADMAP, retire stale planning docs
Feat
- (a2a-rs) Client Transport port + JSON-RPC 2.0 client + card negotiation
Refactor
- (a2a-rs) Split streaming & push out of storage adapters (Phase 4 final)
Added
- MCP server over Streamable HTTP —
run_mcp_servercan now serve a
TOML-configured agent over MCP's Streamable HTTP transport (rmcp's
StreamableHttpServiceon anaxumrouter) in addition to stdio. Configure it
via a new[features.mcp_server.http]section (McpHttpConfig:enabled,
host,port,path); whenhttp.enabledit takes precedence over stdio.
DNS-rebinding protection defaults to loopback-only and is tunable via
allowed_hosts/allowed_origins(emptyallowed_hostsdisablesHost
validation for proxy-fronted public binds). Enables the
transport-streamable-http-serverrmcp feature. Newmcp_http_agentexample
(examples/mcp_http_agent.{rs,toml}) plus an end-to-endinitialize-handshake
andHost-allow-list integration test (tests/mcp_http_test.rs). AgentBuilder::with_streaming/AgentRuntime::with_streaming— attach a
shared streaming backend sotasks/subscribeSSE streams observe the
broadcasts a handler emits (e.g. via theTaskStatusBroadcastmixin). Pass the
sameInMemoryStreamingHandleryour handler broadcasts to (clones share
their subscriber registry); the runtime injects it into the transport via
ConnectRpcAdapter::with_streaming_handlerand logs "📡 Streaming backend
wired into transport" when active.complex_agentexample (examples/complex_agent.rs+
examples/complex_agent.toml, behind--features mcp-server) — a kitchen-sink
"Research Assistant" that wires declarative TOML config, optional LLM
tool-calling (with a keyless, deterministic rule-based fallback), MCP tool
consumption viaMcpToA2ABridge(against an in-process tool server over
tokio::io::duplex), live SSE streaming of progress artifacts, and native A2A
task lifecycle through the broadcast mixin.
Fixed
- Streaming through the builder reached a no-op.
AgentRuntime::start_http
built its transport withConnectRpcAdapter::new(...), which defaults to a
NoopStreamingHandler— so broadcasts from a builder-constructed handler never
reachedtasks/subscribeSSE clients. They now do when the streaming backend
is supplied viawith_streaming(see Added).
a2a-agents-common v0.3.1
Added
- (a2a-agents) MCP server over Streamable HTTP transport
a2a-web-client-v0.3.0
Other
- fmt,clippy
- Fix clippy warnings and failing tests
- migrate to Connect-Rust, refactor project structure, update protobuf specs, and clean up temporary scripts
- docs
a2a-rs-v0.3.0
Fixed
- allow clippy::result_large_err in request processor
Other
- fmt,clippy
- Fix clippy warnings and failing tests
- migrate to Connect-Rust, refactor project structure, update protobuf specs, and clean up temporary scripts
- docs
Changed
- Demoted the
⚠️ No WebSocket subscribers found for tasklog in
InMemoryTaskStorage::broadcast_status_updatefrom WARN to DEBUG. The
no-subscriber case is the steady state formessage/send(non-streaming)
flows and was previously flooding logs on every status broadcast.
a2a-rs/src/adapter/storage/task_storage.rs:189.
Added - v1.0.0 Compliance
New API Methods
tasks/list- List tasks with comprehensive filtering and pagination- Filter by context_id, status, last_updated_after, and metadata
- Offset-based pagination with page tokens
- Configurable history length and artifact inclusion per request
- Returns ListTasksResult with tasks, total_size, page_size, and next_page_token
tasks/pushNotificationConfig/list- List all push notification configs for a tasktasks/pushNotificationConfig/delete- Delete a specific push notification configagent/getAuthenticatedExtendedCard- Get extended agent card for authenticated clients
Core Type Enhancements
- Added
TransportProtocolenum with JSONRPC, GRPC, and HTTP+JSON variants - Added
AgentInterfacetype for additional transport protocol interfaces - Added
AgentExtensiontype for protocol extension framework- URI-based extension identification
- Optional description and required flags
- Arbitrary parameters via JSON object
- Added
extensionsfield toMessageandArtifacttypes - Added
extensionsfield toAgentCapabilities
Agent Card Updates
- Added
protocol_versionfield (defaults to "0.3.0") - Added
preferred_transportfield (defaults to "JSONRPC") - Added
additional_interfacesfield for multi-transport support - Added
icon_urlfield for agent branding - Changed
signaturetosignatures(now supports multiple signatures)
Push Notification Enhancements
- Added
idfield toPushNotificationConfigfor unique identification - Added
GetTaskPushNotificationConfigParamsfor retrieving specific configs - Added
ListTaskPushNotificationConfigParamsfor listing all configs - Added
DeleteTaskPushNotificationConfigParamsfor config deletion - Updated storage implementations with full CRUD operations for push notification configs
Task Management
- Added
ListTasksParamswith comprehensive filtering options:context_id: Filter by contextstatus: Filter by task statepage_size: Control pagination (1-100, default 50)page_token: Offset-based paginationhistory_length: Control message history depthinclude_artifacts: Toggle artifact inclusionlast_updated_after: Filter by timestamp (ms since epoch)metadata: Filter by metadata fields
- Added
ListTasksResultwith pagination metadata - Added
list_tasks_v3toAsyncTaskManagertrait - Added push notification config management methods to
AsyncTaskManager:get_push_notification_configlist_push_notification_configsdelete_push_notification_config
Protocol Migration & ConnectRPC
- Completely migrated from legacy JSON-RPC and WebSocket transport to ConnectRPC (gRPC-compatible) over HTTP.
- Replaced manual JSON-RPC routing with
connectrpc-buildandprost-generated models based on the official A2Av1.0.0protocol buffers. - Deleted legacy WebSocket transport infrastructure across the workspace.
- Updated
a2a-clientto utilizeconnectrpcstubs for client-server communication. - Built and verified a resilient in-process dispatch architecture for MCP standard I/O.
Storage Layer
- Implemented all v1.0.0 methods in
InMemoryTaskStorage:- Full filtering support for task listing
- Timestamp-based filtering
- Metadata filtering
- Push notification config CRUD operations
- All new trait methods have default implementations returning
UnsupportedOperationerror - Proper sorting of tasks by timestamp (most recent first)
- Efficient pagination with configurable page sizes
Error Handling
- Added
AuthenticatedExtendedCardNotConfigurederror variant - Moved
DATABASE_ERRORcode from -32007 to -32100 to avoid conflict with spec
Changed - Breaking Changes
Agent Card
- BREAKING:
signaturefield renamed tosignaturesand changed toOption<Vec<AgentCardSignature>>- Migration: Wrap single signature in a Vec:
signature: Some(sig)→signatures: Some(vec![sig])
- Migration: Wrap single signature in a Vec:
- BREAKING: Added required
protocol_versionfield (has default: "0.3.0") - BREAKING: Added required
preferred_transportfield (has default: "JSONRPC") - New optional fields:
additional_interfaces,icon_url
Message and Artifact
- BREAKING: Added
extensionsfield (optional, defaults to None)- Migration: Add
extensions: Noneto all struct initializations
- Migration: Add
AgentCapabilities
- BREAKING: Added
extensionsfield (optional, defaults to None)- Migration: Add
extensions: Noneto all struct initializations
- Migration: Add
PushNotificationConfig
- BREAKING: Added
idfield (optional, used for multi-config support)- Migration: Add
id: Noneto existing configs
- Migration: Add
MessageSendConfiguration
- BREAKING:
accepted_output_modesis now optional (was required)- Migration: Wrap existing values in Some():
vec![...]→Some(vec![...])
- Migration: Wrap existing values in Some():
Error Codes
- BREAKING:
DATABASE_ERRORmoved from -32007 to -32100 - New error code: -32007 now used for
AUTHENTICATED_EXTENDED_CARD_NOT_CONFIGURED
Migration Guide
Updating Agent Card Initializations
// Before (v0.2.x)
let card = AgentCard {
signature: Some(my_signature),
// ... other fields
};
// After (v1.0.0)
let card = AgentCard {
protocol_version: "0.3.0".to_string(), // NEW - required
preferred_transport: "JSONRPC".to_string(), // NEW - required
additional_interfaces: None, // NEW - optional
icon_url: None, // NEW - optional
signatures: Some(vec![my_signature]), // CHANGED - now plural, wrapped in Vec
// ... other fields
};Updating Message and Artifact Initializations
// Before (v0.2.x)
let message = Message {
message_id: "msg-1".to_string(),
// ... other fields
};
// After (v1.0.0)
let message = Message {
message_id: "msg-1".to_string(),
extensions: None, // NEW - add this field
// ... other fields
};
// Same for Artifact
let artifact = Artifact {
artifact_id: "art-1".to_string(),
extensions: None, // NEW - add this field
// ... other fields
};Updating Capabilities
// Before (v0.2.x)
let caps = AgentCapabilities {
streaming: true,
push_notifications: false,
state_transition_history: true,
};
// After (v1.0.0)
let caps = AgentCapabilities {
streaming: true,
push_notifications: false,
state_transition_history: true,
extensions: None, // NEW - add this field
};Updating Push Notification Configs
// Before (v0.2.x)
let config = PushNotificationConfig {
url: "https://example.com/webhook".to_string(),
token: Some("token".to_string()),
authentication: None,
};
// After (v1.0.0)
let config = PushNotificationConfig {
id: None, // NEW - for multi-config support
url: "https://example.com/webhook".to_string(),
token: Some("token".to_string()),
authentication: None,
};Using New Task Listing API
use a2a_rs::domain::{ListTasksParams, TaskState};
// List tasks with filtering and pagination
let params = ListTasksParams {
context_id: Some("ctx-123".to_string()),
status: Some(TaskState::Working),
page_size: Some(25),
page_token: None, // Start at beginning
history_length: Some(10),
include_artifacts: Some(true),
last_updated_after: None,
metadata: None,
};
let result = task_manager.list_tasks_v3(¶ms).await?;
println!("Found {} tasks, showing {}", result.total_size, result.tasks.len());
// Get next page if available
if !result.next_page_token.is_empty() {
let next_params = ListTasksParams {
page_token: Some(result.next_page_token),
..params
};
let next_result = task_manager.list_tasks_v3(&next_params).await?;
}Managing Push Notification Configs
use a2a_rs::domain::{
GetTaskPushNotificationConfigParams,
ListTaskPushNotificationConfigParams,
DeleteTaskPushNotificationConfigParams,
};
// List all configs for a task
let list_params = ListTaskPushNotificationConfigParams {
id: "task-123".to_string(),
metadata: None,
};
let configs = task_manager.list_push_notification_configs(&list_params).await?;
// Get a specific config
let get_params = GetTaskPushNotificationConfigParams {
id: "task-123".to_string(),
push_notification_config_id: Some("config-1".to_string()),
metadata: None,
};
let config = task_manager.get_push_notification_config(&get_params).await?;
// Delete a config
let delete_params = DeleteTaskPushNotificationConfigParams {
id: "task-123".to_string(),
push_notification_config_id: "config-1".to_string(),
metadata: None,
};
task_manager.delete_push_notification_config(&delete_params).await?;Notes
- All new trait methods have default implementations that return
UnsupportedOperationerror - Existing code will continue to work after adding required fields to struct initializations
- The
InMemoryTaskStorageimplementation supports all new features - SQLx storage implementations need to be updated to support multi-config push notifications
a2a-mcp-v0.3.0
Changed - Breaking
-
McpToA2ABridgetool-call wire format replaced. The bridge no longer
detects tool calls by scanningMessage.partsfor aTextpart starting
withTOOL_CALL: <name>. It now reads a typed [McpToolCall] envelope
fromMessage.metadataunder the keya2a_rs_tool_call(exported as
MCP_TOOL_CALL_METADATA_KEY). Thepartsvector is no longer inspected
for routing and is free for any display/logging payload.Migration: replace ad-hoc message construction with
create_tool_call_message(name, args)orattach_tool_call(&mut msg, name, args).
Messages built with the previousTOOL_CALL:text prefix are now treated as
ordinary text and forwarded to the innerAsyncMessageHandlerunchanged.// Before let msg = Message::builder() .role(Role::User) .parts(vec![ Part::Text { text: "TOOL_CALL: add".into(), metadata: None }, Part::Data { data: data_map, metadata: None }, ]) .message_id(id) .build(); // After let msg = a2a_mcp::create_tool_call_message("add", json!({"a": 5, "b": 7}));
-
AgentToMcpBridge::newsignature simplified. The redundant
agent_url: Stringparameter has been removed; the bridge now derives its
MCP tool-name namespace fromagent_card.url. For the rare cases where the
namespace should differ from the advertised URL (e.g. tunnels, reverse
proxies), use the newAgentToMcpBridge::with_namespace(client, card, namespace)
constructor.// Before let bridge = AgentToMcpBridge::new(client, card, "https://...".to_string()); // After let bridge = AgentToMcpBridge::new(client, card); // Or, with explicit namespace: let bridge = AgentToMcpBridge::with_namespace(client, card, "internal-alias".into());
Added
-
MCP prompts ↔ A2A skills mapping for
McpToA2ABridge:- Automatically query downstream MCP prompts via
list_promptsand expose them as A2A skills. - Route prompt calls using a typed
MCP_PROMPT_CALL_METADATA_KEY("a2a_rs_prompt_call") envelope inMessage.metadatatoself.mcp_peer.get_prompt. - Convert
PromptMessages back to A2A messages.
- Automatically query downstream MCP prompts via
-
Dynamic Auth Bridging:
- Parse
self.agent_card.security_schemesinAgentToMcpBridge::get_infoand advertise"io.modelcontextprotocol/oauth-client-credentials"extension capability if OAuth2 client credentials scheme is present.
- Parse
-
Bidirectional Task Cancellation:
- Added
cancel_taskto theBridgeBackendtrait and implemented it for HTTP and WebSocket backends. - Implemented
cancel_taskonAgentToMcpBridgeto support upstream cancellation. - Implemented
RequestCancelGuardinMcpToA2ABridgeto cancel downstream MCP tool/prompt requests if the wrapping A2A task is canceled/dropped.
- Added
-
McpToA2ABridgeProgress Streaming & Token Matching:- Dynamic matching of auto-generated client progress tokens returned by
rmcpto correctly route downstream progress updates to A2A clients viastreaming_handler.
- Dynamic matching of auto-generated client progress tokens returned by
-
Task streaming, progress reporting, and client sampling support for
AgentToMcpBridge:- Support streaming task status, progress updates, and artifacts from the agent back to MCP clients.
- Implement MCP progress notification support using
progress_token(retrieved fromRequestContext::meta). - Implement client-driven LLM sampling support: if the streaming task or polling loop transitions to
InputRequired, the bridge suspends execution, converts the task history and current prompt into sampling messages, requests input from the client peer viapeer.create_message, and resumes the task with the response. - Added polling fallback loop for backends that do not support status streams (e.g. HTTP backends).
-
Public
BridgeBackendtrait & WebSocket Backend:- Re-exported
BridgeBackendas a public trait to allow custom backend implementations. - Implemented
WebSocketBackendfor communication with WebSocket-based agents (enabled via the"ws-client"feature). - Added
AgentToMcpBridge::with_websocketandwith_websocket_and_namespaceconstructors.
- Re-exported
-
In-process backend for
AgentToMcpBridge. Two new constructors —
AgentToMcpBridge::with_handler(handler, card)and
with_handler_and_namespace(handler, card, namespace)— call an
in-processAsyncMessageHandlerdirectly instead of dialing the agent
over HTTP. When the bridge and the wrapped agent live in the same
process, this skips the loopback HTTP server entirely.// HTTP-backed (unchanged) — for agents in another process/host: let bridge = AgentToMcpBridge::new(HttpClient::new(url), card); // In-process — for agents living in the same process: let bridge = AgentToMcpBridge::with_handler(my_handler, card);
Covered by
tests/agent_to_mcp_integration.rs::test_in_process_backend_dispatches_to_handler. -
McpToolCallpublic struct ({ name, arguments }) representing the
tool-call envelope carried inMessage.metadata. -
MCP_TOOL_CALL_METADATA_KEYconstant ("a2a_rs_tool_call") — the metadata
key the bridge looks at. -
attach_tool_call(&mut Message, name, arguments)helper for adding a
tool-call envelope to an existing message without losing itsparts. -
AgentToMcpBridge::with_namespace(...)constructor for explicit
tool-name namespacing. -
Examples:
a2a_as_mcp_server.rs(A2A agent exposed as MCP tools),
a2a_with_mcp_tools.rs(A2A handler augmented with MCP tools), and
bidirectional_demo.rs(both bridges in one process: upstream MCP
client →AgentToMcpBridge→ A2A HTTP server →McpToA2ABridge→
downstream calculator MCP server).
Documentation
- The metadata tool-call envelope is now documented in the crate-level
rustdoc (lib.rs) and inMcpToA2ABridge's struct docs, including a
worked example of the on-wire JSON shape. - Crate-level rustdoc examples in
lib.rswere promoted from
rust,ignoreto compile-checkedno_rundoctests, rewritten against
the real public API (the previous snippets referenced a non-existent
A2AClient). Doctests are now part ofcargo test --doc -p a2a-mcp.
a2a-ap2-v0.3.0
Other
- fmt,clippy
- migrate to Connect-Rust, refactor project structure, update protobuf specs, and clean up temporary scripts
- docs