Developer Guide: Server SDK (agentvault-server-sdk)¶
The agentvault-server-sdk provides tools and abstractions to simplify the development of A2A-compliant agent servers in Python, particularly when using the FastAPI web framework. It helps you focus on your agent's core logic while the SDK handles much of the A2A protocol boilerplate.
Installation¶
Install the SDK from PyPI:
(Note: This automatically installs theagentvault client library as a dependency).
See the main Installation Guide for more details, including setting up a development environment to run from source.
Core Concepts¶
The SDK revolves around implementing an agent logic class (inheriting from BaseA2AAgent) and integrating it with a web framework (currently FastAPI).
1. BaseA2AAgent (agent.py)¶
This is the abstract base class your agent logic should inherit from.
- Purpose: Defines the standard interface the A2A protocol expects an agent server to fulfill.
- Required Methods: If you are not using the
@a2a_methoddecorator for all standard methods, you must implement theseasyncmethods in your subclass:handle_task_send(task_id: Optional[str], message: Message) -> str: Processes incoming messages (tasks/sendJSON-RPC method). Should handle task creation or updates and return the task ID.handle_task_get(task_id: str) -> Task: Retrieves the full state (Taskmodel) of a specific task (tasks/getJSON-RPC method).handle_task_cancel(task_id: str) -> bool: Attempts to cancel a task (tasks/cancelJSON-RPC method), returningTrueif the request is accepted.handle_subscribe_request(task_id: str) -> AsyncGenerator[A2AEvent, None]: Returns an async generator yieldingA2AEventobjects for SSE streaming (tasks/sendSubscribeJSON-RPC method). The SDK router consumes this generator.
- Alternative (
@a2a_method): For agents handling only specific or custom methods, or if you prefer a decorator-based approach, you can use the@a2a_methoddecorator on individual methods instead of implementing allhandle_...methods (see below).
2. Task State Management (state.py)¶
Handling asynchronous tasks requires managing their state (Submitted, Working, Completed, etc.) and potentially associated data (messages, artifacts). The SDK provides tools for this.
TaskContext: A basic dataclass holdingtask_id,current_state,created_at,updated_at. You can subclass this to store agent-specific task data.# Example of extending TaskContext from dataclasses import dataclass, field from typing import List from agentvault.models import Message, Artifact from agentvault_server_sdk.state import TaskContext @dataclass class MyAgentTaskContext(TaskContext): conversation_history: List[Message] = field(default_factory=list) generated_artifacts: List[Artifact] = field(default_factory=list) # Add other fields your agent needs to track per taskBaseTaskStore: An abstract base class defining the interface for storing, retrieving, updating, and deletingTaskContextobjects (e.g.,create_task,get_task,update_task_state,delete_task). It also defines the interface for managing SSE event listeners (add_listener,remove_listener) and notifying them (notify_status_update,notify_message_event,notify_artifact_event).InMemoryTaskStore: A simple, non-persistent dictionary-based implementation ofBaseTaskStore. Suitable only for development or single-instance agents where task state loss on restart is acceptable. Production agents typically require implementing a customBaseTaskStorebacked by a persistent database (SQL, NoSQL) or a distributed cache (Redis).- Notification Helpers: When using a
BaseTaskStoreimplementation (likeInMemoryTaskStoreor your own), your agent logic (e.g., background processing tasks) should call methods liketask_store.notify_status_update(...),task_store.notify_message_event(...),task_store.notify_artifact_event(...)whenever a relevant event occurs (e.g., state change, message generation, artifact creation). Thecreate_a2a_routerintegration uses these notifications to automatically format and send the correct SSE events to subscribed clients via thehandle_subscribe_requeststream.
3. FastAPI Integration (fastapi_integration.py)¶
The create_a2a_router function bridges your agent logic (either a BaseA2AAgent subclass or a class using @a2a_method) with the FastAPI web framework.
- Purpose: Creates a FastAPI
APIRouterthat automatically exposes the standard A2A JSON-RPC methods (tasks/send,tasks/get,tasks/cancel,tasks/sendSubscribe) and routes them to your agent implementation's correspondinghandle_...methods or decorated methods. It also handles JSON-RPC request parsing, basic validation, and SSE stream setup. - Authentication: Note that authentication (e.g., checking
X-Api-KeyorAuthorizationheaders) is typically handled before the request reaches the A2A router, usually via FastAPI Dependencies applied to the router or the main app. The SDK router itself does not perform authentication checks. -
Usage: The following steps outline how to integrate the router into your FastAPI application:
-
Instantiate Agent and Task Store:
from fastapi import FastAPI from agentvault_server_sdk import BaseA2AAgent from agentvault_server_sdk.state import InMemoryTaskStore # Or your custom store # Import your agent class from my_agent_logic import MyAgent task_store = InMemoryTaskStore() my_agent_instance = MyAgent(task_store_ref=task_store) # Pass store if needed -
Create the A2A Router: Pass the agent instance and the task store to the factory function.
-
Create FastAPI App and Include Router: Mount the router at your desired prefix (typically
/a2a). -
Add Exception Handlers (CRITICAL): You must add the SDK's exception handlers to your main FastAPI
appinstance. These handlers translate internal Python exceptions raised by your agent or the SDK (likeTaskNotFoundError,ValueError,AgentServerError) into correctly formatted JSON-RPC error responses that clients expect. Without these, clients will receive generic HTTP 500 errors instead of specific, actionable JSON-RPC errors.from fastapi import Request from fastapi.responses import JSONResponse from pydantic import ValidationError as PydanticValidationError # from pydantic_core import ValidationError as PydanticValidationError # If using Pydantic v2 from agentvault_server_sdk.exceptions import AgentServerError, TaskNotFoundError from agentvault_server_sdk.fastapi_integration import ( task_not_found_handler, validation_exception_handler, agent_server_error_handler, generic_exception_handler ) # Assuming 'app' is your FastAPI instance from step 3 app.add_exception_handler(TaskNotFoundError, task_not_found_handler) app.add_exception_handler(ValueError, validation_exception_handler) app.add_exception_handler(TypeError, validation_exception_handler) app.add_exception_handler(PydanticValidationError, validation_exception_handler) app.add_exception_handler(AgentServerError, agent_server_error_handler) app.add_exception_handler(Exception, generic_exception_handler) # Catch-all
-
4. A2A Method Decorator (@a2a_method)¶
An alternative or supplement to implementing the full BaseA2AAgent interface.
- Purpose: Expose individual
async defmethods within your agent class as specific JSON-RPC methods. Useful for simpler agents, custom methods beyond the standard A2A set, or overriding specific standard methods with custom logic. - Usage:
from agentvault_server_sdk import BaseA2AAgent, a2a_method from agentvault.models import Task # Example import class DecoratedAgent(BaseA2AAgent): # Still inherit for structure @a2a_method("custom/ping") async def ping_handler(self) -> str: # No parameters needed return "pong" @a2a_method("tasks/get") # Override standard method async def custom_get_task(self, task_id: str) -> Task: # Params validated from type hints # ... custom logic to fetch task ... task_data = await get_my_task_data(task_id) # Return value validated against type hint return Task(**task_data) # If using only decorators for standard methods, you don't *need* # to implement the corresponding handle_ methods. The router will # prioritize decorated methods and return "Method not found" for others. - Validation: The
create_a2a_routerautomatically validates incoming JSON-RPCparamsagainst the decorated function's type hints (using Pydantic internally). If validation fails (e.g., client sends wrong type fortask_id), aValueErrororPydanticValidationErrorwill likely be raised, which should be caught by thevalidation_exception_handlerregistered on the FastAPI app, returning a JSON-RPCInvalid Paramserror. The return value is also validated against the function's return type hint.
5. Packaging Tool (agentvault-sdk package) (packager/cli.py)¶
A CLI tool to help prepare your agent project for deployment, typically via Docker.
- Command:
agentvault-sdk package [OPTIONS] - Functionality: Generates a standard multi-stage
Dockerfile, a.dockerignorefile, and copiesrequirements.txtand optionallyagent-card.jsonto a specified output directory, ready fordocker build. - Key Options:
--output-dir DIRECTORY/-o DIRECTORY: (Required) Directory to write Dockerfile and other artifacts.--entrypoint TEXT: (Required) Python import path to the FastAPI app instance (e.g.,my_agent.main:app).--python TEXT: Python version for the base image tag (e.g., 3.10, 3.11). [default: 3.11]--suffix TEXT: Suffix for the python base image (e.g., slim-bookworm, alpine). [default: slim-bookworm]--port INTEGER: Port the application will listen on inside the container. [default: 8000]--requirements PATH/-r PATH: Path to the requirements.txt file. If not provided, it looks for./requirements.txtin the current directory and copies it if found. Issues a warning if the SDK dependency seems missing.--agent-card PATH/-c PATH: Path to the agent-card.json file. If provided, it will be copied into the output directory.--app-dir TEXT: Directory inside the container where the application code will reside. [default: /app]
- Example:
# Assuming FastAPI app is in src/my_agent/main.py as 'app' # and requirements.txt / agent-card.json are in the current directory agentvault-sdk package \ --output-dir ./build \ --entrypoint my_agent.main:app \ --requirements ./requirements.txt \ --agent-card ./agent-card.json \ --python 3.11 # Then build the image from the project root: # docker build -t my-agent-image:latest -f ./build/Dockerfile .
Building a Basic Agent (Conceptual Steps)¶
- Define Agent Logic: Create a class inheriting from
BaseA2AAgent(or use decorators). - Implement Handlers/Methods: Implement the required
async handle_...methods (or decorate specific methods) to handle A2A requests. - Manage State: Choose or implement a
BaseTaskStore(start withInMemoryTaskStorefor development). Pass it to your agent instance. Crucially, calltask_store.notify_...methods from your agent's background processing logic (e.g., the code handling the actual work initiated byhandle_task_send) to send SSE updates to subscribed clients. - Create FastAPI App: Set up a standard FastAPI application (
main.py). - Instantiate Agent & Store: Create instances of your agent class and task store.
- Create & Include Router: Use
create_a2a_router(agent=..., task_store=...)and include the returned router in your FastAPI app (e.g., at prefix/a2a). - Add Exception Handlers: Add the required SDK exception handlers (
task_not_found_handler, etc.) to your main FastAPI app instance usingapp.add_exception_handler(...). - Create Agent Card: Write an
agent-card.jsondescribing your agent, ensuring theurlpoints to your FastAPI A2A endpoint (e.g.,http://your-host/a2a). Include appropriateauthSchemes. - Run: Use
uvicorn main:app --host ... --port .... - (Optional) Package: Use
agentvault-sdk packageto generate Docker artifacts for deployment.
Refer to the Basic A2A Server Example for a complete, runnable implementation.