Summary
Agent.generate() response steps[].toolCalls[] uses a different structure than the AI SDK's generateText(), but this is not documented anywhere. Developers naturally expect the AI SDK format and get undefined when reading toolName.
The Difference
AI SDK generateText — flat structure:
{
"type": "tool-call",
"toolCallId": "call_xxx",
"toolName": "healthTool",
"input": {}
}
Mastra Agent.generate — wrapped in payload:
{
"type": "tool-call",
"runId": "...",
"from": "AGENT",
"payload": {
"toolCallId": "call_xxx",
"toolName": "healthTool",
"args": {}
}
}
Same pattern applies to toolResults[] — result is in payload.result not at the top level.
Impact
Any code that reads step.toolCalls[0].toolName gets undefined. You need step.toolCalls[0].payload.toolName instead. This caused us to incorrectly conclude tool calling was broken (see #15219), when in fact it was working the entire time.
What's Missing in Docs
The Agent overview states generate() returns text, toolCalls, toolResults, steps, usage but doesn't document:
- The structure of
steps[]
- The structure of
toolCalls[] (the payload wrapper)
- The structure of
toolResults[]
- How it differs from the AI SDK's direct
generateText() response
- Code examples reading tool call results
Suggestion
- Add a "Response Structure" section to the Agent docs showing the exact shape of
steps, toolCalls, toolResults
- Note the
{ type, runId, from, payload: { toolCallId, toolName, args } } wrapper format
- Provide a code example:
const response = await agent.generate([{ role: 'user', content: '...' }])
for (const step of response.steps) {
for (const tc of step.toolCalls) {
console.log(tc.payload.toolName) // ← not tc.toolName
console.log(tc.payload.args)
}
for (const tr of step.toolResults) {
console.log(tr.payload.toolName)
console.log(tr.payload.result)
}
}
Environment
@mastra/core: 1.24.1
ai: 6.0.154
@ai-sdk/openai-compatible: 2.0.41
- Ollama with Gemma4 26B
Summary
Agent.generate()responsesteps[].toolCalls[]uses a different structure than the AI SDK'sgenerateText(), but this is not documented anywhere. Developers naturally expect the AI SDK format and getundefinedwhen readingtoolName.The Difference
AI SDK
generateText— flat structure:{ "type": "tool-call", "toolCallId": "call_xxx", "toolName": "healthTool", "input": {} }Mastra
Agent.generate— wrapped in payload:{ "type": "tool-call", "runId": "...", "from": "AGENT", "payload": { "toolCallId": "call_xxx", "toolName": "healthTool", "args": {} } }Same pattern applies to
toolResults[]— result is inpayload.resultnot at the top level.Impact
Any code that reads
step.toolCalls[0].toolNamegetsundefined. You needstep.toolCalls[0].payload.toolNameinstead. This caused us to incorrectly conclude tool calling was broken (see #15219), when in fact it was working the entire time.What's Missing in Docs
The Agent overview states generate() returns
text, toolCalls, toolResults, steps, usagebut doesn't document:steps[]toolCalls[](the payload wrapper)toolResults[]generateText()responseSuggestion
steps,toolCalls,toolResults{ type, runId, from, payload: { toolCallId, toolName, args } }wrapper formatEnvironment
@mastra/core: 1.24.1ai: 6.0.154@ai-sdk/openai-compatible: 2.0.41