Skip to content

BBC6BAE9/a2a-swift

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A2A Swift SDK

License Swift Platforms

A2A Logo

A Swift library for building and consuming agentic applications following the Agent2Agent (A2A) Protocol.


✨ Features

  • A2A Protocol Compliance: Build agentic applications that adhere to the Agent2Agent (A2A) v1.0 Protocol Specification.
  • Client & Server SDKs: High-level APIs for both serving agentic functionality (A2AServer) and consuming it (A2AClient).
  • Multi-Transport Support: Protocol bindings for REST and JSON-RPC over SSE, with automatic transport negotiation.
  • Extensible & Pluggable: Extension points for custom transports, authentication middleware, and task store backends.
  • v0.3 Compatibility: Transparent backward-compatibility layer for legacy A2A v0.3 servers.

Note: The SDK version is distinct from the A2A specification version. The supported protocol version is 1.0.


🚀 Getting Started

Requires Swift 5.9+, Xcode 15+, and one of: iOS 16 / macOS 13 / tvOS 16 / watchOS 9 / visionOS 1

Add the package to your Package.swift:

dependencies: [
    .package(url: "https://github.com/BBC6BAE9/a2a-swift", from: "1.0.0"),
],
targets: [
    // Client only
    .target(name: "YourTarget", dependencies: ["A2AClient"]),

    // Server only
    .target(name: "YourTarget", dependencies: ["A2AServer"]),

    // Both
    .target(name: "YourTarget", dependencies: ["A2AClient", "A2AServer"]),
]

💡 Examples

Server

  1. Implement your agent logic by conforming to AgentExecutor:

    import A2AServer
    import A2ACore
    
    struct EchoExecutor: AgentExecutor {
        func execute(context: ExecutorContext) -> AsyncThrowingStream<AgentEvent, Error> {
            AsyncThrowingStream { continuation in
                var done = TaskStatusUpdateEvent()
                done.taskID = context.taskID
                done.status = TaskStatus.with {
                    $0.state = .completed
                    $0.message = context.message
                }
                continuation.yield(.statusUpdate(done))
                continuation.finish()
            }
        }
    }
  2. Build the request handler and server:

    let agentCard = AgentCard.with {
        $0.name = "Echo Agent"
        $0.description_p = "Echoes your message back"
        $0.version = "1.0.0"
        $0.capabilities = AgentCapabilities.with { $0.streaming = true }
    }
    
    let handler = DefaultRequestHandler(executor: EchoExecutor())
    let server = A2AServer(handler: handler, agentCard: agentCard)
  3. Dispatch requests from your HTTP framework (Vapor, Hummingbird, etc.):

    let req = ServerRequest(method: method, path: path, body: body, headers: headers)
    let result = await server.handle(req)
    // Write result.statusCode / result.headers / result.body to your response

Client

  1. Resolve an AgentCard to discover how an agent is exposed:

    import A2AClient
    
    let resolver = AgentCardResolver(url: "https://agent.example.com")
    let card = try await resolver.resolve()
  2. Create a client — transport is negotiated automatically from the card:

    let client = A2AClient(url: "https://agent.example.com")
  3. Send requests:

    import A2ACore
    
    let msg = A2ACore.Message.with {
        $0.messageID = UUID().uuidString
        $0.role = .user
        $0.parts = [Part.with { $0.text = TextPart.with { $0.text = "Hello!" } }]
    }
    
    let response = try await client.messageSend(msg)
  4. Or stream responses over SSE:

    for try await event in client.messageStream(msg) {
        switch event.result {
        case .statusUpdate(let e): print("State:", e.status.state)
        case .artifactUpdate(let e): print("Artifact:", e.artifact.name)
        default: break
        }
    }

🔧 Middleware (Handlers)

Chain handlers to intercept every request/response cycle:

let client = A2AClient(
    url: "https://agent.example.com",
    handlers: [
        LoggingHandler(level: .debug),
        AuthHandler(credentialsService: myCredentialStore),
        ExtensionActivator(extensionURIs:
            "https://a2aprotocol.ai/extensions/thinking/v1"
        ),
    ]
)

Implement your own by extending PassthroughContextualHandler:

class RetryHandler: PassthroughContextualHandler {
    override func handleRequest(_ request: A2ARequest) async throws -> [String: Any] {
        // Modify or log before sending
        return try await next(request)
    }
}

🌐 More Examples

You can find more detailed examples in the a2a-samples repository.


🤝 Contributing

Contributions are welcome! Please open an issue to discuss your proposed approach before starting work on a new feature or significant change.


📄 License

This project is licensed under the Apache 2.0 License. See the LICENSE file for more details.

About

About Community Swift SDK for the Agent2Agent (A2A) Protocol

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors