Agent-to-Agent (A2A)

Your iOS or macOS app has an on-device agent for fast, private, offline-capable responses. For tasks that are too heavy for the device, the on-device agent delegates to a backend agent your team hosts. A2A is the bridge between your app and your backend. The remote agent looks exactly like a local tool to the caller.

When to Use A2A

  • Private and offline tasks stay on device. Only heavy work goes to the backend.
  • A task is too compute-intensive for the device (processing a 200-page document, complex multi-step research)
  • Your backend ML team built their agent in Python or TypeScript Strands SDK and you want to call it from Swift without either team changing how they write agents
  • A task requires a specialized model that only runs on your backend server

How It Works

App (Swift, on-device) → A2AClient (tool call) → HTTP POST A2AServer Backend Agent
The on-device agent sees A2AClient as a regular tool. The backend agent is unaware it is being called remotely.

Serving a Backend Agent

Your team's backend service wraps an agent in an A2AServer to expose it over HTTP. The server handles request parsing, agent invocation, and response serialization:

backend-service/main.swiftSwift
import StrandsAgents

/// Process a long document and return a structured summary.
@Tool
func processDocument(content: String) async throws -> String {
    // heavy document processing; appropriate for a backend server
    return "Summary: ..."
}

let documentAgent = Agent(
    model: try BedrockProvider(config: BedrockConfig(
        modelId: "us.anthropic.claude-sonnet-4-20250514-v1:0"
    )),
    tools: [processDocument],
    systemPrompt: "You are a document analysis agent. Process long documents and return concise structured summaries."
)

// Your backend service exposes this agent on port 8080
let server = A2AServer(agent: documentAgent, name: "Document Agent", port: 8080)
try await server.start()

Calling the Backend from Your App

Your iOS or macOS app creates an A2AClient pointing at the backend. Pass it to the on-device agent's tool list. It behaves exactly like a local tool from the caller's perspective:

YourApp/AssistantView.swiftSwift
import StrandsAgents

// Point at your team's backend document agent
let backendDocumentAgent = A2AClient(
    name: "analyze_document",
    description: "Process a long document on the backend and return a structured summary",
    endpoint: URL(string: "https://agents.yourteam.com/documents")!
)

// The on-device agent delegates heavy document work to the backend
// and handles lightweight questions itself
let onDeviceAgent = Agent(
    model: try MLXProvider(modelId: "mlx-community/Qwen3-4B-4bit"),
    tools: [backendDocumentAgent, searchLocalNotes, readCalendar],
    systemPrompt: """
    You are a personal assistant running on device.
    For lightweight questions, answer directly.
    For long documents or intensive analysis, use the analyze_document tool to delegate to the backend.
    """
)

let result = try await onDeviceAgent.run("Summarize the 200-page contract I just uploaded")
print(result.output)

Working with Your Backend Team

Your app is Swift. Your backend ML team built their agent in Python Strands SDK. A2A connects them without either team changing how they write agents. The Swift app calls the Python backend over HTTP, and the Python agent serves responses with no knowledge of the Swift caller:

Calling your team's Python backend agent from SwiftSwift
// Your team's Python Strands agent, served at agents.yourteam.com
// The Python team wrote and deployed it independently
let pythonBackendAgent = A2AClient(
    name: "ml_analysis",
    description: "Run complex ML analysis using the backend's specialized models and data",
    endpoint: URL(string: "https://agents.yourteam.com/ml")!,
    bearerToken: authToken  // your app's credentials
)

// On-device Swift agent delegates ML-heavy work to the Python backend
let onDeviceAgent = Agent(model: localProvider, tools: [pythonBackendAgent])
let result = try await onDeviceAgent.run("Analyze the trends in my health data over the past year")

Authentication

Pass a bearer token or other credentials when creating the client. The token is included in every request to the backend agent:

Swift
let client = A2AClient(
    name: "secure_backend",
    description: "Your team's backend agent behind auth",
    endpoint: URL(string: "https://agents.yourteam.com/assistant")!,
    bearerToken: "eyJhbGciOiJSUzI1NiJ9..."
)

A2A vs. Graph and Swarm

Graph / SwarmA2A
Agent locationSame device, same processYour backend server
CommunicationIn-memory function callsHTTP
LanguagesSwift onlyPython, TypeScript, Swift, any
LatencyMicrosecondsNetwork round-trip
Works offlineYes (local model)No (requires network)
Data privacyStays on deviceSent to your backend
💡

A2A works alongside Graph and Swarm; they are not mutually exclusive. A graph node can be an A2A client, and a swarm member can hand off to a backend agent over A2A.