fix(plugin-mcp): create/update tools crash the MCP endpoint under TypeScript 6 ("use strict" prologue)#17109
Open
ctsstc wants to merge 2 commits into
Open
fix(plugin-mcp): create/update tools crash the MCP endpoint under TypeScript 6 ("use strict" prologue)#17109ctsstc wants to merge 2 commits into
ctsstc wants to merge 2 commits into
Conversation
…ork under TypeScript 6
…void ASI returning undefined)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Fixes #16339 (and the duplicate #16743). On the
3.xline, every@payloadcms/plugin-mcpconsumer using TypeScript 6 gets a500on the firstPOST /api/mcprequest whenever an API key hascreateorupdateenabled on a collection — no tools register at all:Root cause
convertCollectionSchemaToZodbuilds the Zod schema by transpiling a generated source string andevaling it:TypeScript 6 prepends a
"use strict";directive to the transpiled module output that TypeScript 5 did not. The wrapper then evaluates as:so the function returns the string
"use strict"instead of aZodObject.resource/update.ts(andresource/create.ts,global/update.ts) then call.partial()/.shapeon it and throw. That throw propagates out ofgetMcpHandler's per-collectiontry/catchasAPIError(500), aborting tool registration for the whole endpoint.Secondary: the existing
catchfallback returnedz.record(z.any()), aZodRecordthat also lacks.partial()/.shape, so genuine conversion failures broke the same callers.The fix (
convertCollectionSchemaToZod.ts)"use strict";directive and the whitespace that follows it before thereturn ${...}wrapper. The transpiled output is"use strict";\n<expr>; stripping only the directive leavesreturn \n<expr>, where ASI inserts a semicolon afterreturnand the function returnsundefined(→ an empty input schema, so create/update silently accept no fields). Consuming the trailing whitespace keeps the expression on thereturnline.z.object({}).passthrough()if the eval somehow didn't produce aZodObject, so callers that use.partial()/.shapecan't crash.catchfallback fromz.record(z.any())toz.object({}).passthrough()for the same reason.No call sites change — centralizing the guarantee in the converter keeps the
create/update/globaltools safe.Relation to
main/ v4main(v4) is not affected: the breaking plugin refactor in #16726 replaced the transpile-and-evalconversion (convertCollectionSchemaToZod→new Function('z', 'return ' + tsTranspiled)(z)) with JSON-Schema-based input generation via Payload core'sentityToStandaloneJSONSchema+ a newtoStandardSchemahelper, so a"use strict"prologue can no longer reach aneval. That fix is a breaking refactor (note the!in its title) and isn't cherry-pickable onto3.x— and there is no smaller targeted commit (the 3.x history on this file is only #16114, which added thez.recordfallback, #15705, and #15660). This PR is the minimal equivalent for the3.xline: keep the existing conversion, just stop the prologue from poisoning theevaland guarantee a populatedZodObjectresult.Testing
I wasn't able to run the full monorepo suite in my environment, so I scoped this to the source fix, but I verified the behavior end-to-end by applying it (via
patch-package) to a downstream app on3.84.1+typescript@6.0.3with a heavily block-based collection:POST /api/mcp500s on first request when a key grantsupdate.undefined→empty, so the update tool registers with no field properties and silently drops all field data.convertCollectionSchemaToZodreturns a populatedZodObject, the update tool's input schema includes the collection's fields, and an MCPupdatecall persists field changes (verified against the DB).Happy to add a regression test (a TS 6 CI matrix entry, or a unit test asserting
convertCollectionSchemaToZod(...) instanceof z.ZodObjectand that its.shapeis non-empty) in whatever form you prefer.