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_method
decorator for all standard methods, you must implement theseasync
methods in your subclass:handle_task_send(task_id: Optional[str], message: Message) -> str
: Processes incoming messages (tasks/send
JSON-RPC method). Should handle task creation or updates and return the task ID.handle_task_get(task_id: str) -> Task
: Retrieves the full state (Task
model) of a specific task (tasks/get
JSON-RPC method).handle_task_cancel(task_id: str) -> bool
: Attempts to cancel a task (tasks/cancel
JSON-RPC method), returningTrue
if the request is accepted.handle_subscribe_request(task_id: str) -> AsyncGenerator[A2AEvent, None]
: Returns an async generator yieldingA2AEvent
objects for SSE streaming (tasks/sendSubscribe
JSON-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_method
decorator 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 task
BaseTaskStore
: An abstract base class defining the interface for storing, retrieving, updating, and deletingTaskContext
objects (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 customBaseTaskStore
backed by a persistent database (SQL, NoSQL) or a distributed cache (Redis).- Notification Helpers: When using a
BaseTaskStore
implementation (likeInMemoryTaskStore
or 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_router
integration uses these notifications to automatically format and send the correct SSE events to subscribed clients via thehandle_subscribe_request
stream.
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
APIRouter
that 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-Key
orAuthorization
headers) 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
app
instance. 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 def
methods 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_router
automatically validates incoming JSON-RPCparams
against the decorated function's type hints (using Pydantic internally). If validation fails (e.g., client sends wrong type fortask_id
), aValueError
orPydanticValidationError
will likely be raised, which should be caught by thevalidation_exception_handler
registered on the FastAPI app, returning a JSON-RPCInvalid Params
error. 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.dockerignore
file, and copiesrequirements.txt
and optionallyagent-card.json
to 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.txt
in 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 withInMemoryTaskStore
for 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.json
describing your agent, ensuring theurl
points 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 package
to generate Docker artifacts for deployment.
Refer to the Basic A2A Server Example for a complete, runnable implementation.