Summary
mcpb clean (via cleanMcpb) silently deletes unknown keys nested inside author, server, server.mcp_config, etc. from a user's manifest. The loose schemas exist to preserve unrecognised data while cleaning, but .passthrough() is applied only to the top-level object, so nested z.object() schemas strip unknown keys, and cleanMcpb writes the pruned object back to disk.
Mechanism
Zod's .passthrough() only affects the object it's called on. In src/schemas_loose/*.ts only the root object has .passthrough(); nested schemas (McpServerConfigSchema, McpbManifestAuthorSchema, McpbManifestServerSchema, McpbManifestRepositorySchema, …) are plain z.object() and strip unknowns. cleanMcpb (src/node/validate.ts:437-445) rewrites the manifest with result.data, so nested extras are lost.
Reproduction (executed)
A 0.4 manifest with top_level_unknown, author.twitter, server.custom_server_field, server.mcp_config.extra_exec_opt, run through the loose parse:
top_level_unknown kept: true
author.twitter kept: false
server.custom_server_field kept: false
server.mcp_config.extra_exec_opt kept: false
So mcpb clean silently discards nested custom fields the author put in their manifest.
Suggested fix
Add .passthrough() to the nested loose object schemas that should preserve data, across all schemas_loose/*.ts (McpServerConfigSchema, McpbManifestAuthorSchema, McpbManifestServerSchema, McpbManifestRepositorySchema, …). 0.3/0.4 already added .passthrough() to the localization/icon loose sub-schemas — the server/author/mcp_config ones were missed.
Environment: current main (70fe3b3).
Summary
mcpb clean(viacleanMcpb) silently deletes unknown keys nested insideauthor,server,server.mcp_config, etc. from a user's manifest. The loose schemas exist to preserve unrecognised data while cleaning, but.passthrough()is applied only to the top-level object, so nestedz.object()schemas strip unknown keys, andcleanMcpbwrites the pruned object back to disk.Mechanism
Zod's
.passthrough()only affects the object it's called on. Insrc/schemas_loose/*.tsonly the root object has.passthrough(); nested schemas (McpServerConfigSchema,McpbManifestAuthorSchema,McpbManifestServerSchema,McpbManifestRepositorySchema, …) are plainz.object()and strip unknowns.cleanMcpb(src/node/validate.ts:437-445) rewrites the manifest withresult.data, so nested extras are lost.Reproduction (executed)
A 0.4 manifest with
top_level_unknown,author.twitter,server.custom_server_field,server.mcp_config.extra_exec_opt, run through the loose parse:top_level_unknownkept: trueauthor.twitterkept: falseserver.custom_server_fieldkept: falseserver.mcp_config.extra_exec_optkept: falseSo
mcpb cleansilently discards nested custom fields the author put in their manifest.Suggested fix
Add
.passthrough()to the nested loose object schemas that should preserve data, across allschemas_loose/*.ts(McpServerConfigSchema,McpbManifestAuthorSchema,McpbManifestServerSchema,McpbManifestRepositorySchema, …). 0.3/0.4 already added.passthrough()to thelocalization/iconloose sub-schemas — the server/author/mcp_config ones were missed.Environment: current
main(70fe3b3).