# How MTPLX Handles Tool Calling Differently for OpenAI vs Anthropic APIs

> Explore how MTPLX unifies OpenAI and Anthropic tool calling with dialect detection and bidirectional translation. Learn about its unique parsing architecture for seamless API integration.

- Repository: [Youssof Altoukhi/MTPLX](https://github.com/youssofal/MTPLX)
- Tags: deep-dive
- Published: 2026-09-13

---

**MTPLX implements a unified parsing architecture in [`mtplx/server/openai.py`](https://github.com/youssofal/MTPLX/blob/main/mtplx/server/openai.py) that bridges OpenAI's JSON-based function calling and Anthropic's XML-based tool markup through dialect detection, specialized streaming guards, and bidirectional translation utilities.**

The MTPLX server (youssofal/MTPLX) provides a compatibility layer that normalizes tool calling across different LLM providers. While OpenAI APIs expect JSON-encoded function objects with specific schema constraints, Anthropic models return XML-wrapped tool calls using `<tool_call>` tags, requiring MTPLX to maintain dual parsing strategies within a single codebase.

## Core Architectural Differences

### Payload Format and Dialect

The primary distinction lies in how each API serializes tool invocations.

- **OpenAI-style**: Uses JSON objects containing a `function` field with `name` and `arguments` keys. The arguments are escaped JSON strings that the server parses into dictionaries.

- **Anthropic-style**: Uses XML markup wrapped in `<tool_call>` tags with `<function=...>` headings. The function body contains raw argument payloads, often JSON or simple values, embedded directly within the XML structure.

In [`mtplx/server/openai.py`](https://github.com/youssofal/MTPLX/blob/main/mtplx/server/openai.py), the `_parse_generated_tool_calls` function handles both dialects by examining the first non-whitespace character of the incoming string: `{` triggers JSON parsing, while `<` triggers XML parsing.

### Parsing Entry Points and Translation Flow

Despite the format differences, MTPLX funnels both dialects through a single entry point.

The `_parse_generated_tool_calls` function in [`mtplx/server/openai.py`](https://github.com/youssofal/MTPLX/blob/main/mtplx/server/openai.py) serves as the unified parser. For Anthropic requests, the system first processes the raw input through `_anthropic_to_chat_request` and `_anthropic_payload_from_openai` helpers, which normalize the XML markup into a form that the generic parser can consume. This ensures that downstream components receive a canonical internal representation regardless of the source API.

## Streaming Semantics and Guard Logic

### Hidden-Tool Guard Implementation

Both dialects implement hidden-tool guards to prevent malformed tool calls from corrupting the response stream, but the detection mechanisms differ.

- **OpenAI**: Uses constants like `STREAM_HIDDEN_TOOL_GUARD_START` and `STREAM_HIDDEN_TOOL_GUARD_END` to track JSON parsing state. The guard monitors for incomplete JSON bodies (e.g., missing closing braces) and retries parsing when streams terminate.

- **Anthropic**: Re-arms the guard when opening `<tool_call>` tags are detected and disarms on matching `</tool_call>` closures. This protects against unclosed XML tags and suppresses "leaked" tool markup when models produce stray text before closing tags.

The parser sets `tool_argument_in_progress` flags during streaming to track whether it is currently inside a tool argument context.

### Error Handling Strategies

When a tool call is malformed, MTPLX returns a fallback reason (e.g., `"malformed tool_call: unterminated stream"`) rather than failing the entire request. For OpenAI-style calls, the `_repair_tool_argument_keys_for_schema` function attempts to repair missing argument keys based on the tool schema. For Anthropic, the guard ensures stray XML markup never reaches the user-facing response, allowing the request to succeed with the tool payload omitted.

## Parallel Tool Call Handling

MTPLX normalizes parallel tool execution flags between the two APIs through explicit mapping.

In `_anthropic_to_chat_request`, the Anthropic flag `disable_parallel_tool_use` is converted to the OpenAI-equivalent `parallel_tool_calls`. When disabled, MTPLX collapses multiple calls into a single payload (one JSON object for OpenAI, one XML block for Anthropic). When enabled, each call appears as a separate entry in the `tool_calls` list or as separate `<tool_call>` tags.

## Implementation Code Examples

The following examples demonstrate how MTPLX processes each dialect in practice.

### OpenAI-Style JSON Tool Call

```python

# From tests/test_tool_aware_stream_translator.py

PYTHONIC_CALL = "<|tool_call_start|>[get_time(city='Tokyo')]<|tool_call_end|>"
translator = _ToolAwareContentStreamTranslator(tools=EDIT_FILE_TOOL_SPECS)
translator.feed("content", PYTHONIC_CALL)
assert translator.tool_calls[0]["function"]["name"] == "get_time"

```

This example shows the "pythonic" envelope format that MTPLX supports alongside standard OpenAI function objects. The translator extracts the function name and arguments from the JSON structure.

### Anthropic-Style XML Tool Call

```python

# From tests/test_server_openai.py

anthropic_request = {
    "type": "message",
    "content": [
        {"type": "tool_use", "name": "lookup", "input": {"key": "value"}}
    ],
}
chat = openai._anthropic_to_chat_request(anthropic_request)
assert chat.messages[0]["role"] == "assistant"
assert chat.messages[0]["tool_calls"][0]["name"] == "lookup"

```

Here, `_anthropic_to_chat_request` converts Anthropic's `tool_use` content blocks into OpenAI-compatible message structures before the unified parser processes them.

### Hidden-Tool Guard Handling

```python

# From tests/test_tool_call_hidden_guard_and_key_repair.py

t = _QwenXMLToolCallStreamParser(tools=EDIT_FILE_TOOL_SPECS)
out = t.feed("content", "<tool_call>\n<function=write>\n")
assert t.tool_argument_in_progress is True
out.extend(t.feed("content", "\n</function>\n</tool_call>"))
assert t.has_tool_calls is True

```

This demonstrates the XML guard logic tracking whether the parser is currently inside a function argument block, preventing premature content emission.

## Summary

- MTPLX uses a unified `_parse_generated_tool_calls` parser in [`mtplx/server/openai.py`](https://github.com/youssofal/MTPLX/blob/main/mtplx/server/openai.py) that detects dialects by examining leading characters (`{` for JSON, `<` for XML).
- OpenAI tool calls use JSON `function` objects with string-encoded arguments, while Anthropic uses `<tool_call>` XML tags with embedded function payloads.
- The system implements distinct hidden-tool guards for each dialect to handle incomplete JSON braces or unclosed XML tags during streaming.
- Translation utilities like `_anthropic_to_chat_request` and `_anthropic_payload_from_openai` normalize Anthropic content into OpenAI-compatible structures before parsing.
- Parallel tool call flags are mapped between APIs, with `disable_parallel_tool_use` converting to `parallel_tool_calls` for downstream compatibility.
- Static tool specifications (`EDIT_FILE_TOOL_SPECS`, `LOOKUP_TOOL_SPECS`) provide schema validation that works uniformly across both dialects.

## Frequently Asked Questions

### How does MTPLX detect which tool calling dialect to use?

MTPLX examines the first non-whitespace character of the incoming tool call string within `_parse_generated_tool_calls`. If the content starts with `{`, the parser treats it as OpenAI-style JSON. If it starts with `<`, the parser processes it as Anthropic-style XML. The parser also looks for specific delimiters like `<|tool_call_start|>` used by the pythonic envelope format.

### What happens when a tool call is malformed during streaming?

MTPLX implements hidden-tool guards that track parsing state through flags like `tool_argument_in_progress`. If a stream terminates with incomplete JSON (missing closing braces) or unclosed XML tags, the system returns a fallback reason such as `"malformed tool_call: unterminated stream"` rather than failing the request. For OpenAI dialects, the system may attempt to repair missing argument keys using `_repair_tool_argument_keys_for_schema`.

### How does MTPLX handle parallel tool calls differently between OpenAI and Anthropic?

In `_anthropic_to_chat_request`, MTPLX maps Anthropic's `disable_parallel_tool_use` flag to OpenAI's `parallel_tool_calls` parameter. When parallel calls are disabled, multiple tool invocations collapse into a single payload object. When enabled, each call generates separate entries in the `tool_calls` array for OpenAI or distinct `<tool_call>` XML blocks for Anthropic, ensuring the scheduler receives a consistent representation regardless of source API.

### Can MTPLX convert Anthropic tool calls to OpenAI format for downstream processing?

Yes. The `_anthropic_content_to_chat_content` and `_anthropic_content_to_text` functions in [`mtplx/server/openai.py`](https://github.com/youssofal/MTPLX/blob/main/mtplx/server/openai.py) convert Anthropic content blocks (including images and `tool_use` blocks) into OpenAI-compatible structures before the unified parser processes them. This bidirectional translation allows MTPLX to serve as a compatibility bridge between the two API styles.