{"record":{"id":"a0954f8dbe2f3829","repo":"different-ai/openwork","slug":"mcp-catalog-duplicate-tool-a0954f","errorCode":"MCP_CATALOG_DUPLICATE_TOOL","errorMessage":"MCP_CATALOG_DUPLICATE_TOOL","messagePattern":"MCP_CATALOG_DUPLICATE_TOOL","errorType":"error_code","errorClass":"ExternalMcpDiagnosticError","httpStatus":null,"severity":"error","filePath":"ee/apps/den-api/src/capability-sources/external-mcp-client.ts","lineNumber":701,"sourceCode":"      operation: (options) => input.listPage(cursor, options),\n    })\n    if (tools.length + result.tools.length > itemLimit) {\n      throw catalogDiagnosticError({\n        tracker: input.diagnostic,\n        code: \"MCP_CATALOG_ITEM_LIMIT\",\n        operatorAction: `Reduce the provider catalog below ${itemLimit} tools or use a scoped MCP server.`,\n      })\n    }\n    for (const tool of result.tools) {\n      catalogBytes += measureCatalogTool({\n        diagnostic: input.diagnostic,\n        tool,\n        remainingBytes: EXTERNAL_MCP_CATALOG_LIMIT_BYTES - catalogBytes,\n      })\n      if (seenToolNames.has(tool.name)) {\n        throw catalogDiagnosticError({\n          tracker: input.diagnostic,\n          code: \"MCP_CATALOG_DUPLICATE_TOOL\",\n          operatorAction: \"Ensure every tools/list page uses a unique, stable tool name.\",\n        })\n      }\n      seenToolNames.add(tool.name)\n      tools.push(tool)\n    }\n    if (!result.nextCursor) {\n      input.diagnostic.passed(\"MCP_TOOL_DISCOVERY\", \"catalog_ready\")\n      return tools\n    }\n    if (serializedStringBytes(result.nextCursor) > EXTERNAL_MCP_CURSOR_LIMIT_BYTES) {\n      throw catalogDiagnosticError({\n        tracker: input.diagnostic,\n        code: \"MCP_CATALOG_CURSOR_SIZE_LIMIT\",\n        operatorAction: `Reduce each serialized tools/list cursor below ${EXTERNAL_MCP_CURSOR_LIMIT_BYTES} UTF-8 bytes.`,\n      })\n    }\n    const cursorMeasurement = measureSerializedJson(","sourceCodeStart":683,"sourceCodeEnd":719,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-api/src/capability-sources/external-mcp-client.ts#L683-L719","documentation":"Thrown when two tools on the same external MCP server advertise the same `name` across tools/list pages. The gateway requires unique, stable tool names to address capabilities unambiguously (search_capabilities/execute_capability key on the name).","triggerScenarios":"During page collection, seenToolNames already contains tool.name when a new page (or a repeated page) yields a tool with an identical name.","commonSituations":"Provider bugs where pagination repeats already-returned tools; servers namespacing tools per-user but emitting the same bare name; template-driven servers instantiating the same tool name multiple times.","solutions":["Fix the provider server so each tool has a unique, stable name","Ensure tools/list pagination does not re-emit tools from earlier pages","Namespace tools by prefix (e.g. \"db.query\" vs \"docs.query\") if multiple modules produce tools","Use a scoped server exposing a deduplicated subset"],"exampleFix":"// before: two tools both named \"search\"\n// after: tools named \"web_search\" and \"docs_search\"","handlingStrategy":"validation","validationCode":"function assertUniqueToolNames(tools: { name: string }[]): void {\n  const seen = new Set<string>()\n  for (const t of tools) {\n    if (seen.has(t.name)) throw new Error(`duplicate tool name: ${t.name}`)\n    seen.add(t.name)\n  }\n}","typeGuard":"function allToolNamesUnique(tools: { name: string }[]): boolean {\n  return new Set(tools.map((t) => t.name)).size === tools.length\n}","tryCatchPattern":"try {\n  await connectExternalMcp(...)\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"MCP_CATALOG_DUPLICATE_TOOL\")) {\n    console.error(\"Provider returned duplicate tool names across pages; dedupe at the server\")\n  } else throw err\n}","preventionTips":["Guarantee stable, unique tool names in your server implementation","Verify pagination does not re-emit earlier tools","Namespace tools when generating them from multiple modules","Add a dedupe assertion test to your MCP server"],"tags":["mcp","catalog-validation","duplicate"],"backgroundTag":"duplicate-tool-name","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}