use std::sync::Arc;
use axum::{Json, Router, extract::State, response::IntoResponse, routing::get};
use a2a_rs::adapter::{
InMemoryTaskStorage, JsonRpcAdapter, SimpleAgentInfo, jsonrpc_router, rest_router,
};
use a2a_rs::services::server::AgentInfoProvider;
mod common;
use common::SimpleAgentHandler;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let address = "127.0.0.1:8137";
let handler = SimpleAgentHandler::with_storage(InMemoryTaskStorage::new());
let adapter_card =
SimpleAgentInfo::new("jsonrpc-agent".to_string(), format!("http://{address}"));
let adapter = Arc::new(JsonRpcAdapter::with_handler(handler, adapter_card));
let base = format!("http://{address}");
let card_info = Arc::new(
SimpleAgentInfo::new("Example JSON-RPC A2A Agent".to_string(), base.clone())
.with_description("Wire-compatible JSON-RPC 2.0 + HTTP+JSON A2A server".to_string())
.with_preferred_transport("JSONRPC".to_string())
.add_interface(base, "HTTP+JSON".to_string())
.add_skill(
"echo".to_string(),
"Echo".to_string(),
Some("Echoes input".to_string()),
),
);
let card_router = Router::new()
.route("/.well-known/agent-card.json", get(agent_card))
.with_state(card_info);
let app: Router = jsonrpc_router(adapter.clone())
.merge(rest_router(adapter))
.merge(card_router);
println!("🚀 JSON-RPC + REST A2A server on http://{address}");
let listener = tokio::net::TcpListener::bind(address).await?;
axum::serve(listener, app).await?;
Ok(())
}
async fn agent_card(State(info): State<Arc<SimpleAgentInfo>>) -> impl IntoResponse {
match info.get_agent_card().await {
Ok(card) => Json(card).into_response(),
Err(e) => (
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({ "error": e.to_string() })),
)
.into_response(),
}
}