This example was extracted from AGPA β my fully autonomous general-purpose agent (closed-source, ~150k LOC).
A C# implementation of asynchronous tool execution buffering for Anthropic's Claude API. This library enables true parallel conversation and tool execution - users can continue chatting while long-running tools execute in the background.
Traditional AI tool implementations block the conversation while tools execute:
User: "Run this analysis"
AI: [calls analysis tool]
[User must wait 2 minutes...]
AI: "Here are the results"
AnthropicToolUseBuffer enables parallel execution:
User: "Run this analysis"
AI: [calls analysis tool - buffers it]
User: "What's the weather like?"
AI: "Sunny and 72Β°F" [continues conversation while tool runs]
[Tool completes in background]
AI: "The analysis is complete. Here are the results..."
- Asynchronous tool execution - Conversation continues while tools run
- Queue-based message pairing - Multiple concurrent tool calls supported
- ID-based matching - Each
tool_useautomatically paired with itstool_resultby ID - Thread-safe buffering - Concurrent tool execution without race conditions
- Timeout handling - Configurable timeout prevents stale buffers (default: 5 minutes)
- Smart ping exclusion - Cache-alive pings don't pollute message history
- Write once, use everywhere - Define tools once, convert to any provider format
- Type-safe tool definitions - Strongly-typed parameter definitions
- Nested object support - Complex parameter structures
- Provider agnostic - Same interface works across all AI providers
- Streaming responses - Real-time SSE parsing with delta handling
- Prompt caching - Automatic keep-alive timer before 5-minute expiry
- Message persistence - SQLite database with conversation history
- Permission system - Control which tools can call other tools (tool chaining)
- Message validation - Automatic alternation validation and placeholder injection
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FormAnthropicDemo β
β (WinForms UI) β
ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββ
β
ββββββββββββββ΄βββββββββββββ
β β
βββββββββββββΌβββββββββββ βββββββββββΌβββββββββββ
β AnthropicApiClass β β Tool Buffer β
β (API Client) β β (Message Pairing) β
βββββββββββββ¬βββββββββββ βββββββββββ¬βββββββββββ
β β
βββββββββββββΌβββββββββββ βββββββββββΌβββββββββββ
β Streaming Parser β β MessageDatabase β
β (SSE Handling) β β (SQLite) β
ββββββββββββββββββββββββ ββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββ
β Universal Tool Builder System β
β (Provider-agnostic tool definitions) β
ββββββββββββββββββββββββββββββββββββββββββββββββ
AnthropicToolUseBuffer/
βββ AIClassesAnthropic/ # Message and response classes
β βββ MessageClass.cs # Core message structures
β βββ ContentClass.cs # Message content types
β βββ DeltaClass.cs # Streaming delta handling
β βββ StreamBufferParser.cs # SSE stream parser
βββ ToolBuilder/ # Universal tool definition system
β βββ UniversalToolBuilder.cs # Provider-agnostic builder
β βββ ToolTransformerBuilderAnthropic.cs
β βββ LoadTools.cs # Tool registration
β βββ USAGE_EXAMPLE.cs # Usage examples
βββ ToolClasses/ # Tool implementations
β βββ Tool.cs # Base tool class
β βββ ToolClass.cs # Tool metadata
β βββ ToolBufferDemo.cs # Demo tool
βββ ApiAnthropic.cs # Main API client
βββ FormAnthropicDemo.cs # WinForms UI implementation
βββ MessageDatabase.cs # SQLite persistence
βββ AppSettings.cs # Configuration management
βββ NonBlockingTimerClass.cs # Cache-alive timer
- .NET 10.0 or higher
- Anthropic API key (Get one here)
- Clone the repository:
git clone https://github.com/johnbrodowski/AnthropicToolUseBuffer.git- Create
appsettings.jsonin the project root:
{
"anthropic": {
"apiKey": "YOUR_API_KEY_HERE",
"defaultModel": "claude-sonnet-4-5",
"cacheAliveIntervalMinutes": 4.75
},
"general": {
"useTools": true,
"toolPairTimeoutMinutes": 5
},
"database": {
"defaultDatabaseName": "ToolBufferDemoMessageDatabase.db"
}
}- Build and run:
dotnet build
dotnet runTraditional implementations send messages immediately:
// Traditional approach - blocks conversation
User message β API β Assistant with tool_use β Wait for tool β Send tool_result β API β ContinueAnthropicToolUseBuffer uses a queue-based buffer that supports multiple concurrent tool calls:
// Queue-based buffering - supports concurrent tools
User: "Run analysis A" β API β Assistant text saved
β tool_use A buffered by ID
β Tool A starts (30 sec)
User: "Run analysis B" β API β Assistant text saved
β tool_use B buffered by ID
β Tool B starts (20 sec)
User: "What's the status?" β API continues conversation
β
Tool B completes (20 sec) β tool_result B buffered
β Match found! Flush pair B
β API receives results for B
β
Tool A completes (30 sec) β tool_result A buffered
β Match found! Flush pair A
β API receives results for A// Queue-based buffering (thread-safe)
private readonly object _toolBufferLock = new object();
// Dictionaries indexed by tool_use ID for concurrent support
private readonly Dictionary<string, (MessageAnthropic message, DateTime timestamp)> _pendingToolUseMessages = new();
private readonly Dictionary<string, MessageAnthropic> _pendingToolResults = new();
// When tool_use received: buffer by ID
_pendingToolUseMessages[toolUseId] = (message, DateTime.Now);
// When tool_result received: find matching tool_use by ID
if (_pendingToolUseMessages.ContainsKey(toolUseId))
{
// Match found - flush this pair only
FlushPair(toolUseId);
}Define tools once using the Universal Tool Builder:
var weatherTool = new UniversalToolBuilder()
.AddToolName("get_weather")
.AddDescription("Retrieves current weather information.")
.AddNestedObject("weather_params", "Weather query parameters", isRequired: true)
.AddProperty("location", "string", "City name or coordinates", isRequired: true)
.AddProperty("units", "string", "Temperature units (celsius/fahrenheit)", isRequired: false)
.EndNestedObject()
.EndObject()
.Build();
// Convert to Anthropic format
var anthropicTool = weatherTool.ToAnthropic();See ToolBuilder/USAGE_EXAMPLE.cs for more comprehensive examples.
- Long-running analysis tools - Run data analysis while user continues conversation
- Concurrent API calls - Multiple API requests running simultaneously without blocking
- Parallel data processing - Process multiple datasets concurrently
- Multi-step workflows - Execute complex tool chains asynchronously
- Enterprise chatbots - Production-grade Claude integrations with concurrent tool support
- AI agent frameworks - Building blocks for autonomous agents with parallel task execution
All configuration is in appsettings.json:
| Setting | Description | Default |
|---|---|---|
anthropic.apiKey |
Your Anthropic API key | Required |
anthropic.defaultModel |
Claude model to use | claude-sonnet-4-5 |
anthropic.cacheAliveIntervalMinutes |
Keep-alive ping interval | 4.75 |
general.useTools |
Enable tool support | true |
general.toolPairTimeoutMinutes |
Tool buffer timeout | 5 |
database.defaultDatabaseName |
SQLite database name | ToolBufferDemoMessageDatabase.db |
Control which tools can call other tools:
_toolPermissions.RegisterTool(
toolName: "tool_buffer_demo",
canInitiateToolChain: true,
allowedTools: new[] { "tool_buffer_demo" }
);The included tool_buffer_demo demonstrates async tool execution:
User: "Try the tool_buffer_demo"
AI: "I'll call the tool_buffer_demo function for you."
[Tool starts executing - 10 second delay]
User: "Is it working?"
AI: "Yes! The tool is still running in the background..."
[Tool completes]
AI: "The tool has completed! The test was successful."βββββββββββββββ
β User Input β
ββββββββ¬βββββββ
β
βΌ
βββββββββββββββββββββββ
β FlushMatchedToolPairβ βββββ Check for pending pairs (all IDs)
ββββββββ¬βββββββββββββββ
β
βΌ
ββββββββββββββββββββ
β Send to API β
ββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββ
β Stream Response β
β (SSE Parsing) β
ββββββββ¬ββββββββββββββ
β
βββΊ text delta β Display immediately
βββΊ thinking delta β Show thinking process
βββΊ tool_use β Buffer in _pendingToolUseMessages[toolUseId]
βββΊ tool_result β Buffer in _pendingToolResults[toolUseId]
When IDs match:
βββΊ FlushMatchedToolPair β Find matching pairs by ID
β Flush matched pairs only
β Keep unmatched pairs in queue
- Never commit API keys - Use environment variables or secure configuration
- Validate tool inputs - Always sanitize user-provided tool parameters
- Permission system - Use tool permissions to control tool chaining
- Database security - Encrypt sensitive data in message database
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the Apache License 2.0 - see the LICENSE.txt file for details.
- Built for Anthropic's Claude API
- Inspired by the need for better async tool execution in AI applications
Author: John Brodowski Project Link: https://github.com/johnbrodowski/AnthropicToolUseBuffer Release Date: December 25, 2025
Note: This is a demonstration implementation extracted from a larger project. The tool buffering mechanism represents a novel approach to handling Claude's tool use capabilities and is being open-sourced to benefit the AI development community.