fix(provider-openai): forward raw MCP tool schema, stop rebuilding #90
No reviewers
Labels
No labels
bug
commercial
documentation
duplicate
enhancement
feature
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
jasoncouture/llama-shears!90
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "agent-refactor"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
ToolDescriptornow carries the source's raw JSON schema as aJsonElementalongside the parsedToolParameterlist.ModelContextProtocolClient.MapToolclones the MCPJsonElement(so it survives theMcpClientJsonDocumentdisposal) and stores it on the descriptor.OpenAiLanguageModelserializes thatJsonElementverbatim into the function definition'sparametersslot, falling back to a built schema only when none was advertised.Parametersstay around for caller-side introspection (required-ness, top-level type names).Why
Rebuilding the schema from
ToolParameterdrops fields likeitemson array properties. OpenAI itself accepts that, but strict downstream validators reject it — observed today with Google AI Studio via OpenRouter:Forwarding the raw MCP schema preserves whatever the tool advertised.
Test plan
dotnet buildclean (pre-existing[Obsolete]warnings only)dotnet test— 519/519 passINVALID_ARGUMENT400🤖 Generated with Claude Code
Pull request overview
This PR preserves tool parameter JSON Schemas end-to-end (especially for MCP-provided tools) by carrying and forwarding the raw schema instead of reconstructing it from the parsed parameter list, avoiding loss of required JSON Schema fields (e.g.,
itemsfor arrays) that strict validators reject.Changes:
ToolDescriptorto include the raw parameters JSON Schema (JsonElement Schema) alongside the parsedToolParameterlist.Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
Schema: JsonElementtoToolDescriptorto retain the raw JSON Schema.ToolDescriptor.ToolDescriptor.Schemainto OpenAI toolparameters, with fallback schema generation.Schemaproperty and updated semantics forParameters.💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
@ -12,2 +12,3 @@- `Parameters` — Parameter schema in declaration order.- `Parameters` — Parsed parameter list in declaration order; lossy projection of `Schema` kept for caller-side introspection (required-ness, top-level type names).- `Schema` — Raw JSON Schema for the tool's parameters as the source advertised it. Providers should forward this verbatim instead of rebuilding from `Parameters`, since rebuilding drops fields strict validators (Gemini, structured-output) require. JsonValueKind.`Undefined` means the source advertised no schema.Markdown formatting looks off here:
JsonValueKind.Undefined`` renders with onlyUndefinedin code ticks. Consider changing to a single code span likeJsonValueKind.Undefinedfor consistency/readability.@ -407,0 +418,4 @@return new JsonObject{["type"] = "function",["function"] = new JsonObjectToOpenAiToolonly falls back whenSchema.ValueKind == Undefined. If a caller provides a non-object schema (e.g.,Null),JsonNode.Parse(descriptor.Schema.GetRawText())can returnnullor a non-object node, and the request will serialize"parameters": null/ invalid shape. Consider treating any non-ObjectSchemavalue kind (or anullparse result) as “no schema” and using the fallback builder.New behavior forwards an advertised raw JSON schema into the OpenAI tool definition (with a fallback when absent), but the OpenAI provider tests don’t currently assert the emitted
tools[].function.parametersshape. Adding a unit test covering both the raw-schema path (including arrayitems) and the fallback path would help prevent regressions.This adds a new parameter to the primary constructor of a public record in a packable
src/publicAPI. Even with a default value, this changes the constructor signature and will break binary compatibility for consumers compiled against the previousToolDescriptor(string,string,ImmutableArray<ToolParameter>)(MissingMethodException at runtime). Consider adding an explicit overload with the old signature that delegates to the new one (or otherwise ensure a major-version bump / documented breaking change).