{"record":{"id":"438bb75f4628a948","repo":"CherryHQ/cherry-studio","slug":"invalidparams","errorCode":"InvalidParams","errorMessage":"Unsupported product_info argument: ${unsupportedArgument}","messagePattern":"Unsupported product_info argument: (.+?)","errorType":"error_code","errorClass":"McpError","httpStatus":null,"severity":"warning","filePath":"src/main/ai/mcp/servers/assistant.ts","lineNumber":340,"sourceCode":"    if (typeof routes !== 'object' || routes === null || Array.isArray(routes)) {\n      throw new McpError(ErrorCode.InternalError, 'Product manifest routes are invalid')\n    }\n    const allRoutes = (routes as Record<string, unknown>).all\n    if (!Array.isArray(allRoutes)) {\n      throw new McpError(ErrorCode.InternalError, 'Product manifest routes are invalid')\n    }\n\n    return allRoutes.filter(\n      (route): route is string =>\n        typeof route === 'string' &&\n        (route === '/settings' || route.startsWith('/settings/') || route.startsWith('/app/'))\n    )\n  }\n\n  private async productInfo(args: Record<string, unknown>) {\n    const unsupportedArgument = Object.keys(args).find((key) => key !== 'source' && key !== 'section')\n    if (unsupportedArgument) {\n      throw new McpError(ErrorCode.InvalidParams, `Unsupported product_info argument: ${unsupportedArgument}`)\n    }\n\n    if (args.source !== 'manifest') {\n      throw new McpError(ErrorCode.InvalidParams, `Unknown product_info source: ${String(args.source)}`)\n    }\n\n    const manifest = this.readProductManifest()\n    const packageRecord = manifest.package as Record<string, unknown>\n    const manifestVersion = packageRecord.version as string\n    const section = args.section\n    if (section !== undefined && (typeof section !== 'string' || section.trim().length === 0)) {\n      throw new McpError(ErrorCode.InvalidParams, \"'section' must be a non-empty string\")\n    }\n\n    let result: Record<string, unknown>\n    if (section === undefined) {\n      result = {\n        runtimeVersion: app.getVersion(),","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/mcp/servers/assistant.ts#L322-L358","documentation":"Thrown by the `product_info` MCP tool handler when the CallToolRequest arguments contain any property other than `source` or `section`. The tool's inputSchema declares `additionalProperties: false` (assistant.ts:138), so this is a defense-in-depth runtime re-check: the handler scans `Object.keys(args)` for any disallowed key. It exists because not every MCP client validates against the JSON Schema before dispatching, so the server enforces the contract itself.","triggerScenarios":"Calling `product_info` with arguments like `{source:'manifest', detail:true}`, `{source:'manifest', version:1}`, or `{format:'json'}` — any object whose keys include something other than `source`/`section`.","commonSituations":"An LLM agent hallucinates an extra field (e.g. `detail`, `verbose`, `format`); a caller reuses an argument shape from a different tool; a client that does not run JSON-Schema validation before sending leaks through to the handler.","solutions":["Strip every property except `source` (required) and `section` (optional) from the arguments before calling.","Re-fetch the tool list via ListTools and read the `product_info` inputSchema to confirm the accepted properties.","If you genuinely need new data, extend the manifest and the tool schema in source rather than smuggling undocumented arguments."],"exampleFix":"// before\nclient.callTool('product_info', { source: 'manifest', detail: true })\n// after\nclient.callTool('product_info', { source: 'manifest' })","handlingStrategy":"validation","validationCode":"// Before calling product_info, keep only accepted keys.\nfunction sanitizeProductInfoArgs(args: Record<string, unknown>) {\n  const out: Record<string, unknown> = {}\n  if ('source' in args) out.source = args.source\n  if ('section' in args) out.section = args.section\n  return out\n}","typeGuard":"function isProductInfoArgs(\n  args: unknown\n): args is { source: string; section?: string } {\n  if (typeof args !== 'object' || args === null) return false\n  const keys = Object.keys(args as Record<string, unknown>)\n  return keys.every((k) => k === 'source' || k === 'section')\n}","tryCatchPattern":"// The assistant server wraps thrown errors into an isError result (assistant.ts:274-281).\nconst res = await client.callTool('product_info', args)\nif (res.isError) {\n  // res.content[0].text starts with \"Error: Unsupported product_info argument:\"\n  return sanitizeAndRetry(args)\n}","preventionTips":["Treat the tool's inputSchema (with additionalProperties:false) as authoritative and never send extra keys.","When dynamically building args, allow-list fields rather than passing through raw user/agent input."],"tags":["mcp","input-validation","product-info","invalid-params"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}