Skip to content

fix(mastra): propagate Gemini thought_signature (providerMetadata) through the AG-UI stack#1314

Draft
antanj wants to merge 3 commits intoag-ui-protocol:mainfrom
antanj:fix/mastra-gemini-thought-signature-provider-metadata
Draft

fix(mastra): propagate Gemini thought_signature (providerMetadata) through the AG-UI stack#1314
antanj wants to merge 3 commits intoag-ui-protocol:mainfrom
antanj:fix/mastra-gemini-thought-signature-provider-metadata

Conversation

@antanj
Copy link
Copy Markdown

@antanj antanj commented Mar 16, 2026

Problem

Gemini 2.5+ models attach a thought_signature to tool calls via providerMetadata.
During a client-side tool round-trip, this metadata was silently dropped at every layer
of the AG-UI stack, causing Gemini to reject the follow-up request with:

AI_APICallError: Function call is missing a thought_signature
Fixes #1245.

Changes

@ag-ui/core

  • Added providerMetadata?: Record<string, Record<string, any>> to ToolCallSchema (types.ts)
  • Added the same field to ToolCallStartEventSchema (events.ts)

@ag-ui/client

  • apply/default.ts: destructure providerMetadata from TOOL_CALL_START events and
    conditionally include it on the ToolCall object stored in messages

@ag-ui/mastra

  • mastra.ts: forward providerMetadata from both the local agent (fullStream) and
    remote agent (processDataStream) tool-call chunks into the ToolCallStartEvent
  • utils.ts: restore providerMetadata onto tool-call parts when converting
    AssistantMessage.toolCalls back to Mastra CoreMessage format, so Gemini
    receives the signature on the next request

Tests

…mini thought_signature

Gemini 2.5+ attaches a thought_signature to tool calls via providerMetadata.
When a client tool round-trip occurs, this metadata was silently dropped at
every layer, causing Gemini to reject the next request.
- core: add optional providerMetadata to ToolCallSchema and ToolCallStartEventSchema
- client/apply: pass providerMetadata from TOOL_CALL_START event into the ToolCall object
- mastra: forward providerMetadata from both local (fullStream) and remote
  (processDataStream) agent paths into the ToolCallStartEvent
- mastra/utils: restore providerMetadata onto tool-call parts when converting
  AG-UI messages back to Mastra CoreMessages
@antanj antanj requested a review from a team as a code owner March 16, 2026 15:56
@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 16, 2026

@antanj is attempting to deploy a commit to the CopilotKit Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown
Contributor

@jpr5 jpr5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

Found 1 issue:

  1. Schema type mismatch on providerMetadata between ToolCallStartEventSchema and ToolCallSchema. The event schema uses the flat z.record(z.string(), z.any()) (Record<string, any>) while the ToolCall schema uses the nested z.record(z.string(), z.record(z.string(), z.any())) (Record<string, Record<string, any>>). These describe the same field but with different shapes -- the event schema is strictly looser than the type it feeds into. The fix is to make the event schema match the ToolCall schema.

parentMessageId: z.string().optional(),
});

vs.

encryptedValue: z.string().optional(),
});

@antanj
Copy link
Copy Markdown
Author

antanj commented Mar 16, 2026

Code review

Found 1 issue:

  1. Schema type mismatch on providerMetadata between ToolCallStartEventSchema and ToolCallSchema. The event schema uses the flat z.record(z.string(), z.any()) (Record<string, any>) while the ToolCall schema uses the nested z.record(z.string(), z.record(z.string(), z.any())) (Record<string, Record<string, any>>). These describe the same field but with different shapes -- the event schema is strictly looser than the type it feeds into. The fix is to make the event schema match the ToolCall schema.

parentMessageId: z.string().optional(),
});

vs.

encryptedValue: z.string().optional(),
});

Thanks, resolved.

Copy link
Copy Markdown
Contributor

@jpr5 jpr5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review (re-review after schema fix)

Schema mismatch is resolved. Found 2 issues:

  1. Python SDK not updated -- ToolCall and ToolCallStartEvent in sdks/python/ag_ui/core/ are missing provider_metadata. The repo has a strong sync discipline between Python and TypeScript SDKs (the 5 most recent commits to Python SDK types are all "align with TypeScript SDK" fixes, and commit bd5f93f updated both simultaneously for the name field). Python agents/clients will silently drop providerMetadata, breaking the Gemini thought_signature use case for Python consumers.

function: FunctionCallSchema,
encryptedValue: z.string().optional(),
providerMetadata: z.record(z.string(), z.record(z.string(), z.any())).optional(),
});

class ToolCall(ConfiguredBaseModel):
"""
A tool call, modelled after OpenAI tool calls.
"""
id: str
type: Literal["function"] = "function" # pyright: ignore[reportIncompatibleVariableOverride]
function: FunctionCall
encrypted_value: Optional[str] = None

  1. Protobuf schema not updated -- events.proto and types.proto are missing provider_metadata. The same commit bd5f93f updated proto and TypeScript simultaneously. Agents using the proto/binary transport path will silently lose providerMetadata during serialization.

syntax = "proto3";
package ag_ui;
import "google/protobuf/struct.proto";
import "patch.proto";
import "types.proto";
enum EventType {
TEXT_MESSAGE_START = 0;

Extend both ToolCall and ToolCallStartEvent with an optional
provider_metadata map (string → Struct) in the proto definitions,
Python models, and TypeScript encode/decode logic.
@antanj
Copy link
Copy Markdown
Author

antanj commented Mar 16, 2026

Code review (re-review after schema fix)

Schema mismatch is resolved. Found 2 issues:

  1. Python SDK not updated -- ToolCall and ToolCallStartEvent in sdks/python/ag_ui/core/ are missing provider_metadata. The repo has a strong sync discipline between Python and TypeScript SDKs (the 5 most recent commits to Python SDK types are all "align with TypeScript SDK" fixes, and commit bd5f93f updated both simultaneously for the name field). Python agents/clients will silently drop providerMetadata, breaking the Gemini thought_signature use case for Python consumers.

function: FunctionCallSchema,
encryptedValue: z.string().optional(),
providerMetadata: z.record(z.string(), z.record(z.string(), z.any())).optional(),
});

class ToolCall(ConfiguredBaseModel):
"""
A tool call, modelled after OpenAI tool calls.
"""
id: str
type: Literal["function"] = "function" # pyright: ignore[reportIncompatibleVariableOverride]
function: FunctionCall
encrypted_value: Optional[str] = None

  1. Protobuf schema not updated -- events.proto and types.proto are missing provider_metadata. The same commit bd5f93f updated proto and TypeScript simultaneously. Agents using the proto/binary transport path will silently lose providerMetadata during serialization.

syntax = "proto3";
package ag_ui;
import "google/protobuf/struct.proto";
import "patch.proto";
import "types.proto";
enum EventType {
TEXT_MESSAGE_START = 0;

Resolved.

@antanj antanj requested a review from jpr5 March 18, 2026 08:22
@antanj antanj marked this pull request as draft March 20, 2026 07:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Gemini 2.5 or 3 tool calls fail with missing thought_signature in @ag-ui/mastra roundtrip (frontend/client tool flows)

2 participants