Repository navigation
fix: preserve inputSchema $defs/definitions when converting preloaded MCP tools - #1030
akushonkamen wants to merge 3 commits into
Conversation
🦋 Changeset detectedLatest commit: 1d64078 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit bcb5a52. Configure here.
Review ask on truefoundry#1030: check model-vendor compatibility before merging. The converted parameters flow convertTools() -> jsonSchema() -> the configured @ai-sdk provider package. Against the pinned provider sources: @ai-sdk/anthropic preserves both $defs and legacy definitions recursively; @ai-sdk/google passes unknown keywords (and $defs explicitly) through unstripped; @ai-sdk/openai and the OpenAI-compatible vendors ship the parameters JSON Schema verbatim. The tests pin what the converter itself controls: emitted parameters stay on the vendor-accepted top-level surface (type/properties/required plus the two container spellings and nothing else), every local $ref resolves inside the emitted document for both spellings, and container-less schemas are emitted byte-identically so vendor behavior for them is unchanged.
|
Vendor compatibility check done (ffb3373) — new Pipeline: Per-vendor findings:
What the tests pin (the part our converter itself controls): emitted
|
|
Reporter of #945 here. I re-ran my original reproduction end to end against this branch, including the wire-level step that the vendor check above reasoned about from source. Setup: the FastMCP-shaped
With the probe added, On Gemini: #1046 carries the same source and tests plus the changeset (its diff against Investigated with AI assistance. The results above come from real runs. |
|
@akushonkamen can you please add changeset file and rebase against main? Will merge this after that |
|
Thanks @chiragjn! Sure — I'll add a |
…d MCP tools convertMCPServersToTools rebuilt function-tool parameters from only inputSchema.properties/required, dropping top-level $defs/definitions while keeping the $refs that point into them. FastMCP-style servers (official MCP Python SDK) hoist every nested pydantic model to top-level $defs, so any preloaded tool with structured args reached the LLM with dangling #/defs-style refs — failing provider schema validation or hiding the argument shape from the model. Carry both containers over only when present, so schemas without them are emitted byte-identically to before. Fixes truefoundry#945
Review ask on truefoundry#1030: check model-vendor compatibility before merging. The converted parameters flow convertTools() -> jsonSchema() -> the configured @ai-sdk provider package. Against the pinned provider sources: @ai-sdk/anthropic preserves both $defs and legacy definitions recursively; @ai-sdk/google passes unknown keywords (and $defs explicitly) through unstripped; @ai-sdk/openai and the OpenAI-compatible vendors ship the parameters JSON Schema verbatim. The tests pin what the converter itself controls: emitted parameters stay on the vendor-accepted top-level surface (type/properties/required plus the two container spellings and nothing else), every local $ref resolves inside the emitted document for both spellings, and container-less schemas are emitted byte-identically so vendor behavior for them is unchanged.
ffb3373 to
1d64078
Compare
|
Done — both items from your request are in:
Verification (local): ran the trueforge-core test suite scoped to the MCP/runtime tests via |

Summary
convertMCPServersToToolsrebuilt the function-toolparametersfrom onlyinputSchema.properties/required(packages/trueforge-core/src/core/mcp/convertMCPServers.ts:86-87at078808f4), dropping the top-level$defs/ legacydefinitionscontainers while keeping the$refs that point into them. FastMCP-style servers (official MCP Python SDK) hoist every nested pydantic model to top-level$defs, so any preloaded tool with structured args reached the LLM with dangling#/$defs/...refs — failing provider schema validation or hiding the argument shape from the model.Fix: carry both containers over only when present (one conditional spread each,
convertMCPServers.ts:79-92), so schemas without them are emitted byte-identically to before.Fixes #945
Changes
packages/trueforge-core/src/core/mcp/convertMCPServers.ts: passinputSchema.$defsand draft-04inputSchema.definitionsthrough into the function-toolparameters, each only when present.packages/trueforge-core/tests/core/mcp/convertMCPServersSchema.test.ts: regression cases — FastMCP-style ($defs+#/$defs/Addressref), draft-04definitions, and no-container schema; each local$refis asserted resolvable, and the default (no-container) output is asserted unchanged.How was this tested?
078808f4+ this test file):timeout 180 pnpm jest --config jest.config.cjs tests/core/mcp/convertMCPServersSchema.test.ts→ 2 failed ($defs/definitionslost), 1 passed / 3 totaltimeout 300 pnpm jest --config jest.config.cjs tests/core/mcp→ 6 suites passed, 40 passed / 40 totaltrueforge-core:timeout 600 pnpm jest --config jest.config.cjs→ 51 suites passed, 507 passed + 1 skipped / 508 total, exit 0pnpm buildexit 0,pnpm testexit 0,pnpm typecheckexit 0,pnpm lint:ciexit 0 (2 pre-existing warnings in an unrelatedtrueforge-uifile),pnpm format:checkexit 0Checklist
pnpm build,pnpm test,pnpm typecheck,pnpm lint:ci, andpnpm format:checkpass locallypackages/trueforge-sdk,python/trueforge_sdk,.github/fern/openapi/openapi.json,docs/openapi.json) — fork PRs omit SDK regen; maintainers regenerate after merge.env.exampleupdated if configuration or behavior changedNote
Implementation was done with an AI coding agent under my direction (root cause analysis, patch, and tests reviewed by me); also disclosed in the issue claim comment.
Note
Low Risk
Narrow change to preloaded MCP tool schema shaping with conditional spreads and tests; no auth or persistence impact.
Overview
Fixes preloaded MCP tool conversion so JSON Schema
$refpointers keep working when servers hoist nested models into top-level$defs(FastMCP / Python SDK) or legacydefinitions(draft-04).convertMCPServersToToolspreviously rebuilt function-toolparametersfrom onlypropertiesandrequired, leaving dangling#/$defs/...refs and broken or incomplete tool schemas for the LLM. It now copies$defsanddefinitionsthrough only when present, so plain schemas without those keys stay unchanged.Adds regression tests for container preservation and resolvable local
$refs, plus checks that emittedparametersstay within the top-level keywords expected across supported model vendors.Reviewed by Cursor Bugbot for commit 1d64078. Bugbot is set up for automated code reviews on this repo. Configure here.