xml-toolcall
v0.1.0
Published
Parser, OpenCode provider wrapper, and Claude Code Router transformer for normalizing XML tool calls.
Downloads
132
Maintainers
Readme
xml-toolcall
Parser, OpenCode provider wrapper, and Claude Code Router transformer for Qwen-compatible endpoints that sometimes return raw tool calls in message.content as:
<tool_call>
{"name":"some_tool","arguments":{"x":1}}
</tool_call>The transformer normalizes those blocks into OpenAI-compatible message.tool_calls, sets finish_reason to tool_calls, and removes the raw XML from display content. It also disables Qwen thinking mode by default and forces non-streaming responses by default because raw XML tool-call parsing is safest after the full message is available.
The Qwen3 XML path is implemented as a JavaScript port of vLLM's qwen3xml_tool_parser.py streaming parser. It uses the request tools schema to match vLLM's parameter type conversion for strings, integers, floats, booleans, arrays, and objects.
OpenCode config
Use this package directly as the provider npm package. It wraps @ai-sdk/openai-compatible, forces upstream chat completions to non-streaming, parses XML tool calls from the returned JSON, then returns OpenAI-compatible tool-call chunks back to OpenCode.
{
"$schema": "https://opencode.ai/config.json",
"model": "qwen-xml/YOUR_MODEL",
"provider": {
"qwen-xml": {
"npm": "xml-toolcall",
"name": "Qwen XML ToolCall",
"options": {
"baseURL": "http://YOUR-ENDPOINT/v1",
"apiKey": "YOUR_KEY_OR_EMPTY"
},
"models": {
"YOUR_MODEL": {
"name": "YOUR_MODEL",
"limit": {
"context": 65536,
"output": 8192
}
}
}
}
}
}It also supports the Qwen3-Coder XML form:
<tool_call>
<function=read_file>
<parameter=path>
src/index.ts
</parameter>
<parameter=limit>
200
</parameter>
</function>
</tool_call>And the attribute-style variant:
<tool_call>
<function name="read_file">
<parameter name="path">src/index.ts</parameter>
</function>
</tool_call>Install
npm install -g xml-toolcallClaude Code Router config
Add the transformer to ~/.claude-code-router/config.json.
{
"stream": false,
"transformers": [
{
"name": "xml-toolcall",
"path": "/path/to/node_modules/xml-toolcall",
"options": {
"disableThinking": true,
"forceNonStreaming": true
}
}
],
"Providers": [
{
"name": "internal-qwen",
"api_base_url": "http://YOUR-INTERNAL-ENDPOINT/v1/chat/completions",
"api_key": "YOUR_KEY",
"models": ["YOUR_QWEN_MODEL"],
"transformer": {
"use": [
[
"maxtoken",
{
"max_tokens": 65536
}
],
"enhancetool",
"xml-toolcall"
]
}
}
]
}Put xml-toolcall last in the provider use list so it runs first on the reversed response path.
After editing config:
ccr restartOptions
disableThinking: defaults totrue. Addsenable_thinking: falseandchat_template_kwargs.enable_thinking: falseto outgoing requests.forceNonStreaming: defaults totrue. Setsstream: falseon outgoing requests.
License
MIT
