Skip to content

Latest commit

 

History

43 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
apcore-a2a logo

apcore-a2a (Rust)

Crates.io License

What is apcore-a2a?

apcore-a2a is the A2A (Agent-to-Agent) protocol adapter for the apcore ecosystem.

It solves a common problem: you've built AI capabilities with apcore modules, but you need them to talk to other AI agents over a standard protocol. apcore-a2a bridges that gap — it reads your existing module metadata (schemas, descriptions, examples) and automatically exposes them as a standards-compliant A2A server. No hand-written Agent Cards, no JSON-RPC boilerplate, no manual task lifecycle management.

In short: apcore modules + apcore-a2a = a fully functional A2A agent, ready to be discovered and invoked by any A2A-compatible client.

Built on axum and tokio.

Also available in: Python | TypeScript

Features

  • One-call server — launch a compliant A2A server with serve(source, config)
  • Automatic Agent Card/.well-known/agent-card.json (A2A 1.0; /.well-known/agent.json kept as a 0.3 alias) generated from module metadata
  • Skill mapping — apcore modules become A2A Skills with names, descriptions, tags, and examples; metadata.display.a2a overrides surface-facing fields (§5.13)
  • Full task lifecycle — submitted, working, completed, failed, canceled, input-required
  • JWT authentication — tokens bridged to apcore's Identity context
  • Built-in clientA2AClient for calling remote A2A agents
  • SSE streamingmessage/stream server-sent events; A2AClient::stream_message on the client
  • Push notificationstasks/pushNotificationConfig/{set,get,delete} with webhook delivery
  • CLI supportapcore-a2a --extensions-dir ./extensions for zero-code startup
  • Pluggable storageTaskStore / PushConfigStore traits for custom backends, owner-scoped per call
  • Observability/health endpoint
  • Config Bus — registers apcore-a2a namespace with APCORE_A2A env prefix (apcore 0.22)
  • Error Formatter Registry — registers A2A error formatter with apcore ecosystem (§8.8)

Note: Metrics (/metrics) are Python/TypeScript-only — the Rust crate does not serve a /metrics endpoint. SSE streaming (message/stream) and push notifications (tasks/pushNotificationConfig/*) are fully supported.

Requirements

  • Rust edition 2021
  • apcore 0.22
  • apcore-toolkit 0.8

Getting Started

Add to your project

[dependencies]
apcore-a2a = "0.4"

Expose your modules as an A2A Agent

use apcore_a2a::{APCoreA2A, APCoreA2AConfig, BackendSource};
use std::path::PathBuf;

#[tokio::main]
async fn main() {
    let source = BackendSource::ExtensionsDir(PathBuf::from("./extensions"));
    let config = APCoreA2AConfig::default();

    apcore_a2a::serve(source, config).await.unwrap();
}

Call a remote A2A Agent

use apcore_a2a::A2AClient;
use serde_json::json;

#[tokio::main]
async fn main() {
    let client = A2AClient::new("http://remote-agent:8000");

    // `message` is an A2A message object; `metadata.skillId` selects the module.
    let task = client
        .send_message(
            json!({
                "messageId": "m1",
                "role": "ROLE_USER",
                "parts": [{ "data": { "name": "Tercel" } }]
            }),
            Some(json!({ "skillId": "demo.greet" })),
            None, // optional contextId
        )
        .await
        .unwrap();

    println!("Result: {}", task);
}

Add authentication

use apcore_a2a::{JWTAuthenticator, ClaimMapping};

let auth = JWTAuthenticator::new("your-secret-key")
    .with_claim_mapping(ClaimMapping {
        id_claim: "sub".into(),
        roles_claim: "roles".into(),
        ..Default::default()
    });

Architecture

A2A Concept apcore Mapping
Agent Card Derived from Registry configuration
Skill id module_id
Skill name metadata.display.a2a.alias or humanized module_id
Skill desc metadata.display.a2a.description or module.description
Skill tags metadata.display.tags or module.tags
Task Managed execution via ApCoreAgentExecutor::call / stream_channel
Security Bridged to apcore's Identity context

Module Structure

src/
  adapters/    AgentCardBuilder, SkillMapper, SchemaConverter, ErrorMapper, PartConverter
  auth/        JWTAuthenticator, AuthMiddleware, Authenticator trait
  server/      A2AServerFactory, ApCoreAgentExecutor
  client/      A2AClient, AgentCardFetcher
  storage/     TaskStore + PushConfigStore traits, in-memory implementations, CallContext
  explorer/    Explorer UI HTML + card handler
  apcore_a2a.rs  APCoreA2A builder, serve(), async_serve()
  cli.rs       CLI entrypoint

Examples

A self-contained example lives in examples/run/main.rs. It registers a tiny in-code demo.greet module and serves it as an A2A agent — no extensions directory or deployed modules required:

# Serve the demo module on port 8000
cargo run --example run

# Bind to a different port if 8000 is taken (e.g. by a Docker container)
A2A_URL=http://localhost:8001 cargo run --example run

Port 8000 already in use? If curl http://localhost:8000/... returns a non-apcore response such as {"detail":"Not Found"} with a server: uvicorn header, another process (often a Docker container) owns the port. Find it with lsof -nP -iTCP:8000 -sTCP:LISTEN, then either stop it or set A2A_URL to a free port as shown above.

Once it's running, probe the agent from another terminal:

curl http://localhost:8000/.well-known/agent-card.json   # Agent Card (A2A 1.0; lists demo.greet)
# (0.3 alias) curl http://localhost:8000/.well-known/agent.json
curl http://localhost:8000/health                   # Health check
open  http://localhost:8000/explorer                # Explorer UI (browser)

# Invoke the skill — inputs go in a `data` part; metadata.skillId picks the module:
curl -X POST http://localhost:8000/ -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":"1","method":"message/send",
       "params":{"message":{"messageId":"m1","role":"ROLE_USER",
                            "parts":[{"data":{"name":"Tercel"}}]},
                 "metadata":{"skillId":"demo.greet"}}}'
# => artifacts[0].parts[0].data == {"greeting":"Hello, Tercel!"}

To serve your own modules instead, build an apcore::registry::Registry, register your modules, and pass BackendSource::Registry(Arc::new(registry)) to serve — or point a BackendSource::ExtensionsDir at a directory of deployed modules. See the serve entry point for all backend sources.

To verify the example compiles as part of a check, build all examples:

cargo build --examples

Contributing

git clone https://github.com/aiperceivable/apcore-a2a-rust.git
cd apcore-a2a-rust
cargo test            # run the test suite
cargo build --examples  # ensure examples still compile

Documentation

License

Apache 2.0 — see LICENSE.

About

Automatic A2A Protocol Adapter for apcore Module Registry.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages