Multi-provider tool call parsing and normalization for LLM applications.
- Unified Interface - Parse tool calls from any LLM provider into a common format
- Auto-Detection - Automatically detect the provider from response format
- 20+ Providers - OpenAI, Anthropic, Google, xAI, Mistral, Cohere, DeepSeek, AWS Bedrock, and more
- OpenSource Support - Ollama, vLLM, GpuStack, Qwen, LMStudio, LocalAI, TGI
- Result Formatting - Format tool results back to provider-specific format
- Extensible - Register custom parsers for new providers
dotnet add package ToolCallParserusing ToolCallParser;
// Parse tool calls with automatic provider detection
var response = """
{
"choices": [{
"message": {
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"Tokyo\",\"unit\":\"celsius\"}"
}
}]
}
}]
}
""";
var toolCalls = ToolCallParserFactory.Parse(response);
foreach (var call in toolCalls)
{
Console.WriteLine($"Tool: {call.Name}");
Console.WriteLine($"ID: {call.Id}");
Console.WriteLine($"Args: {call.Arguments}");
}// OpenAI-compatible (Mistral, xAI, DeepSeek, Ollama, etc.)
var openAiParser = ToolCallParserFactory.GetParser(Provider.OpenAI);
// Anthropic Claude
var claudeParser = ToolCallParserFactory.GetParser(Provider.Anthropic);
// Google Gemini
var geminiParser = ToolCallParserFactory.GetParser(Provider.Google);
// AWS Bedrock
var bedrockParser = ToolCallParserFactory.GetParser(Provider.Bedrock);
// Cohere Command R
var cohereParser = ToolCallParserFactory.GetParser(Provider.Cohere);| Provider | Format | Documentation |
|---|---|---|
| OpenAI | tool_calls |
docs |
| Azure OpenAI | OpenAI-compatible | docs |
| Anthropic (Claude) | tool_use blocks |
docs |
| Google Gemini | functionCall |
docs |
| xAI (Grok) | OpenAI-compatible | docs |
| Mistral | OpenAI-compatible | docs |
| Cohere | Unique format | docs |
| DeepSeek | OpenAI-compatible | docs |
| AWS Bedrock | toolUse blocks |
docs |
| Provider | Format | Documentation |
|---|---|---|
| Ollama | OpenAI-compatible | docs |
| GpuStack | OpenAI-compatible | docs |
| vLLM | OpenAI-compatible | docs |
| Qwen | OpenAI-compatible | docs |
| LMStudio | OpenAI-compatible | - |
| LocalAI | OpenAI-compatible | - |
| TGI (HuggingFace) | OpenAI-compatible | - |
// Check format compatibility
var provider = Provider.Ollama;
if (provider.IsOpenAICompatible())
{
Console.WriteLine("Uses OpenAI tool_calls format");
}
if (provider.IsAnthropicCompatible())
{
Console.WriteLine("Uses Anthropic tool_use format");
}
// Get documentation URL
var docUrl = provider.GetDocumentationUrl();{
"choices": [{
"message": {
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"Tokyo\"}"
}
}]
}
}]
}{
"content": [
{"type": "text", "text": "I'll check the weather."},
{
"type": "tool_use",
"id": "toolu_01XYZ",
"name": "get_weather",
"input": {"location": "Paris"}
}
],
"stop_reason": "tool_use"
}{
"candidates": [{
"content": {
"parts": [{
"functionCall": {
"name": "get_weather",
"args": {"location": "London"}
}
}]
}
}]
}{
"output": {
"message": {
"content": [{
"toolUse": {
"toolUseId": "tooluse_abc",
"name": "get_weather",
"input": {"location": "NYC"}
}
}]
}
},
"stopReason": "tool_use"
}var toolCall = toolCalls[0];
// Get a specific argument
var location = toolCall.GetArgument<string>("location");
// Get all arguments as a typed object
var args = toolCall.GetArguments<WeatherArgs>();
// Check if argument exists
if (toolCall.HasArgument("unit"))
{
var unit = toolCall.GetArgument<string>("unit");
}
public record WeatherArgs(string Location, string Unit = "celsius");// Create tool results
var results = new[]
{
ToolCallResult.Success("call_abc123", "Temperature: 22°C", "get_weather"),
ToolCallResult.Failure("call_def456", "Location not found", "get_weather")
};
// Format for OpenAI
var openAiParser = ToolCallParserFactory.GetParser(Provider.OpenAI);
var openAiResults = openAiParser.FormatResults(results);
// Format for Anthropic
var anthropicParser = ToolCallParserFactory.GetParser(Provider.Anthropic);
var claudeResults = anthropicParser.FormatResults(results);
// Format for Google Gemini
var geminiParser = ToolCallParserFactory.GetParser(Provider.Google);
var geminiResults = geminiParser.FormatResults(results);// Auto-detect provider from response
var provider = ToolCallParserFactory.DetectProvider(jsonResponse);
switch (provider)
{
case Provider.OpenAI:
Console.WriteLine("OpenAI format detected");
break;
case Provider.Anthropic:
Console.WriteLine("Anthropic format detected");
break;
case Provider.Google:
Console.WriteLine("Gemini format detected");
break;
case Provider.Bedrock:
Console.WriteLine("AWS Bedrock format detected");
break;
case Provider.Auto:
Console.WriteLine("Could not auto-detect");
break;
}// Create a custom parser
public class CustomProviderParser : IToolCallParser
{
public Provider Provider => Provider.OpenAICompatible;
public IReadOnlyList<ToolCall> Parse(string response) { /* ... */ }
public IReadOnlyList<ToolCall> Parse(JsonElement element) { /* ... */ }
public bool HasToolCalls(string response) { /* ... */ }
public bool HasToolCalls(JsonElement element) { /* ... */ }
public string FormatResults(IEnumerable<ToolCallResult> results) { /* ... */ }
}
// Register it
ToolCallParserFactory.RegisterParser(Provider.OpenAICompatible, new CustomProviderParser());public sealed record ToolCall
{
public required string Id { get; init; }
public required string Name { get; init; }
public required string Arguments { get; init; } // JSON string
public JsonDocument GetArgumentsAsJson();
public T? GetArguments<T>(JsonSerializerOptions? options = null);
public T? GetArgument<T>(string name, JsonSerializerOptions? options = null);
public bool HasArgument(string name);
}public sealed record ToolCallResult
{
public required string ToolCallId { get; init; }
public string? ToolName { get; init; }
public required string Content { get; init; }
public bool IsSuccess { get; init; } = true;
public string? ErrorMessage { get; init; }
public static ToolCallResult Success(string toolCallId, string content, string? toolName = null);
public static ToolCallResult Failure(string toolCallId, string errorMessage, string? toolName = null);
}public static class ProviderExtensions
{
public static ProviderFormat GetFormat(this Provider provider);
public static bool IsOpenAICompatible(this Provider provider);
public static bool IsAnthropicCompatible(this Provider provider);
public static string? GetDocumentationUrl(this Provider provider);
}- .NET 10.0
- System.Text.Json (included in .NET)
- Tool Call Format Guide - Detailed format specifications for each provider
- ironhive-cli - CLI agent using ToolCallParser
- TokenMeter - Token counting and cost calculation
MIT License - see LICENSE for details.
Contributions welcome! Especially for adding support for new providers.
- Fork the repository
- Create your feature branch (
git checkout -b feature/new-provider) - Add parser in
src/ToolCallParser/Parsers/ - Add tests in
tests/ToolCallParser.Tests/ - Update Provider enum and factory
- Submit a Pull Request
See docs/tool-call-format-guide.md for detailed instructions.
Made with care by iyulab