Content
# A2A Server SDK for .NET
This SDK provides tools and libraries for implementing an A2A (Agent-to-Agent) protocol server in .NET applications.
## Features
- Fully compliant with A2A protocol specifications
- Supports synchronous and streaming responses
- Memory and file system storage options
- Seamless integration with ASP.NET Core
- Complete C# type support and documentation
- Built-in task state management
- Supports task cancellation and resumption
- Rich error handling and logging
## Quick Start
### Installation
Install the A2A Server SDK NuGet package:
```shell
dotnet add package A2A.Server.SDK
```
### Basic Usage
#### 1. Create a Task Handler
```csharp
using A2A.Server.SDK.Handlers;
using A2A.Server.SDK.Schema;
public class MyTaskHandler : ITaskHandler
{
public async IAsyncEnumerable<TaskYieldUpdate> HandleTaskAsync(TaskContext context)
{
// Generate status update
yield return new TaskYieldUpdate
{
StatusUpdate = new TaskStatus
{
State = TaskState.Working,
Message = new Message
{
Role = "agent",
Parts = new List<Part>
{
new TextPart { Text = "Processing your request..." }
}
}
}
};
// Perform work
await Task.Delay(1000);
// Generate task result
yield return new TaskYieldUpdate
{
ArtifactUpdate = new Artifact
{
Name = "result.txt",
Parts = new List<Part>
{
new TextPart { Text = "This is the result" }
}
}
};
// Mark as completed
yield return new TaskYieldUpdate
{
StatusUpdate = new TaskStatus
{
State = TaskState.Completed,
Message = new Message
{
Role = "agent",
Parts = new List<Part>
{
new TextPart { Text = "Completed!" }
}
}
}
};
}
}
```
#### 2. Integrate into an ASP.NET Core Application
```csharp
using A2A.Server.SDK.Extensions;
using A2A.Server.SDK.Handlers;
var builder = WebApplication.CreateBuilder(args);
// Add A2A services
builder.Services.AddA2AServer(options =>
{
options.BasePath = "/a2a";
options.Card = new
{
name = "My A2A Agent",
description = "An example A2A agent",
version = "1.0.0",
capabilities = new
{
streaming = true
},
skills = new[]
{
new
{
id = "example",
name = "Example Skill"
}
}
};
});
var app = builder.Build();
// Configure CORS (if needed)
app.UseCors("A2APolicy");
// Map A2A endpoints
app.MapA2AEndpoints(new MyTaskHandler());
app.Run();
```
#### 3. Manually Use the A2AServer Class
```csharp
using A2A.Server.SDK;
using A2A.Server.SDK.Handlers;
using A2A.Server.SDK.Storage;
// Create task handler
var taskHandler = new MyTaskHandler();
// Create server instance
var server = new A2AServer(taskHandler, new A2AServerOptions
{
TaskStore = new FileStore("./tasks"), // Or use InMemoryTaskStore
BasePath = "/"
});
// Manually handle requests in ASP.NET Core
app.MapPost("/", async (HttpContext context) =>
{
var result = await server.HandleTaskSendAsync(context);
await result.ExecuteResultAsync(new ActionContext { HttpContext = context });
});
```
## Advanced Usage
### Handling Task Cancellation
```csharp
public async IAsyncEnumerable<TaskYieldUpdate> HandleTaskAsync(TaskContext context)
{
// Send work status update
yield return new TaskYieldUpdate
{
StatusUpdate = new TaskStatus { State = TaskState.Working }
};
for (int i = 0; i < 10; i++)
{
// Check if the task has been canceled
if (context.IsCancelled())
{
yield return new TaskYieldUpdate
{
StatusUpdate = new TaskStatus { State = TaskState.Canceled }
};
yield break;
}
await Task.Delay(500);
// Perform work...
}
// Complete the task
yield return new TaskYieldUpdate
{
StatusUpdate = new TaskStatus { State = TaskState.Completed }
};
}
```
### Custom Storage Implementation
```csharp
public class MyCustomTaskStore : ITaskStore
{
// Implement interface methods
public Task<TaskAndHistory> GetTaskAsync(string taskId)
{
// Implement fetching task from a database or other storage
}
public Task SaveAsync(TaskAndHistory taskAndHistory)
{
// Implement saving task to a database or other storage
}
// Other interface method implementations...
}
// Use custom storage
var server = new A2AServer(handler, new A2AServerOptions
{
TaskStore = new MyCustomTaskStore()
});
```
## Storage Options
The SDK provides various storage implementations:
- `InMemoryTaskStore`: Stores tasks in memory (for development/testing purposes)
- `FileStore`: Stores tasks in the file system (for persistent storage)
You can also create custom storage by implementing the `ITaskStore` interface, such as a database-based storage.
## Handling Different Types of Message Parts
The A2A protocol supports various message part types. Here’s how to handle them:
```csharp
public async IAsyncEnumerable<TaskYieldUpdate> HandleTaskAsync(TaskContext context)
{
// Access user message parts
foreach (var part in context.UserMessage.Parts)
{
if (part is TextPart textPart)
{
// Handle text part
var text = textPart.Text;
// ...
}
else if (part is ImagePart imagePart)
{
// Handle image part
var imageUrl = imagePart.Source;
// ...
}
// Other part types...
}
// Return a product containing multiple part types
yield return new TaskYieldUpdate
{
ArtifactUpdate = new Artifact
{
Name = "result",
Parts = new List<Part>
{
new TextPart { Text = "Text result" },
new ImagePart { Source = "https://example.com/image.jpg" }
}
}
};
}
```
## Examples
Check the `samples` and `tests` directories in the project for more examples:
- `SimpleA2AServer`: Basic server example
- `MovieAgent`: A movie information agent that integrates with third-party APIs
## Troubleshooting
### Common Issues
1. **Unable to receive requests**: Ensure CORS and endpoint paths are configured correctly.
2. **Task status not updating correctly**: Check if the asynchronous stream is yielding status updates properly.
3. **Product not generated correctly**: Ensure the product part is formatted correctly.
### Enable Debug Logging
```csharp
var server = new A2AServer(handler, new A2AServerOptions
{
EnableDebugLogging = true
}, loggerFactory.CreateLogger<A2AServer>());
```
## License
MIT
## Contributing
Pull requests are welcome. Please ensure to run tests and follow the project's coding style before submitting.