@ai-sdk-tool/parser
v4.1.13
Published
AI SDK middleware for tool call parsing
Readme
AI SDK middleware for parsing tool calls from models that do not natively support tools.
Install
pnpm add @ai-sdk-tool/parserAI SDK compatibility
Fact-checked from this repo CHANGELOG.md and npm release metadata (as of 2026-02-18).
| @ai-sdk-tool/parser major | AI SDK major | Maintenance status |
|---|---|---|
| v1.x | v4.x | Legacy (not actively maintained) |
| v2.x | v5.x | Legacy (not actively maintained) |
| v3.x | v6.x | Legacy (not actively maintained) |
| v4.x | v6.x | Active (current latest line) |
Note: there is no separate formal EOL announcement in releases/changelog for v1-v3; "legacy" here means non-current release lines.
Package map
| Import | Purpose |
|---|---|
| @ai-sdk-tool/parser | Main middleware factory, preconfigured middleware, protocol exports |
| @ai-sdk-tool/parser/community | Community middleware (Sijawara, UI-TARS) |
Quick start
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { morphXmlToolMiddleware } from "@ai-sdk-tool/parser";
import { stepCountIs, streamText, wrapLanguageModel } from "ai";
import { z } from "zod";
const model = createOpenAICompatible({
name: "openrouter",
apiKey: process.env.OPENROUTER_API_KEY,
baseURL: "https://openrouter.ai/api/v1",
})("arcee-ai/trinity-large-preview:free");
const result = streamText({
model: wrapLanguageModel({
model,
middleware: morphXmlToolMiddleware,
}),
stopWhen: stepCountIs(4),
prompt: "What is the weather in Seoul?",
tools: {
get_weather: {
description: "Get weather by city name",
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => ({ city, condition: "sunny", celsius: 23 }),
},
},
});
for await (const part of result.fullStream) {
// text-delta / tool-input-start / tool-input-delta / tool-input-end / tool-call / tool-result
}Choose middleware
Use the preconfigured middleware exports from src/preconfigured-middleware.ts:
| Middleware | Best for |
|---|---|
| hermesToolMiddleware | JSON-style tool payloads |
| morphXmlToolMiddleware | XML-style payloads with schema-aware coercion |
| yamlXmlToolMiddleware | XML tool tags + YAML bodies |
| qwen3CoderToolMiddleware | Qwen/UI-TARS style <tool_call> markup |
Build custom middleware
import { createToolMiddleware, qwen3CoderProtocol } from "@ai-sdk-tool/parser";
export const myToolMiddleware = createToolMiddleware({
protocol: qwen3CoderProtocol,
toolSystemPromptTemplate: (tools) =>
`Use these tools and emit <tool_call> blocks only: ${JSON.stringify(tools)}`,
});Streaming semantics
- Stream parsers emit
tool-input-start,tool-input-delta, andtool-input-endwhen a tool input can be incrementally reconstructed. tool-input-start.id,tool-input-end.id, and finaltool-call.toolCallIdare reconciled to the same ID.emitRawToolCallTextOnErrordefaults tofalse; malformed tool-call markup is suppressed fromtext-deltaunless explicitly enabled.
Configure parser error behavior through providerOptions.toolCallMiddleware:
const result = streamText({
// ...
providerOptions: {
toolCallMiddleware: {
onError: (message, metadata) => {
console.warn(message, metadata);
},
emitRawToolCallTextOnError: false,
},
},
});Local development
pnpm build
pnpm test
pnpm check:biome
pnpm check:types
pnpm checkExamples in this repo
- Parser middleware examples:
examples/parser-core/README.md - RXML examples:
examples/rxml-core/README.md
Run one example from repo root:
pnpm dlx tsx examples/parser-core/src/01-stream-tool-call.ts