{"record":{"id":"6f05cda04de8c1c0","repo":"n8n-io/n8n","slug":"mcp-server-cfg-name-provide-either-url-or","errorCode":null,"errorMessage":"MCP server \"${cfg.name}\": provide either \"url\" or \"command\", not both","messagePattern":"MCP server \"(.+?)\": provide either \"url\" or \"command\", not both","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/sdk/mcp-client.ts","lineNumber":71,"sourceCode":"\t * Per-server connection failures recorded during the last `listTools()`.\n\t * Tools from these servers were skipped; the run continues with the\n\t * remaining servers' tools. Read via `getConnectionFailures()`.\n\t */\n\tprivate connectionFailures: McpConnectionFailedEvent[] = [];\n\n\t/**\n\t * @param configs - Server configurations. Each must have either `url` or `command`.\n\t *   Duplicate names within the list are rejected.\n\t */\n\tconstructor(configs: McpServerConfig[]) {\n\t\tfor (const cfg of configs) {\n\t\t\tif (!cfg.url && !cfg.command) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`MCP server \"${cfg.name}\": exactly one of \"url\" or \"command\" must be provided`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (cfg.url && cfg.command) {\n\t\t\t\tthrow new Error(`MCP server \"${cfg.name}\": provide either \"url\" or \"command\", not both`);\n\t\t\t}\n\t\t}\n\n\t\tconst seen = new Set<string>();\n\t\tfor (const cfg of configs) {\n\t\t\tif (seen.has(cfg.name)) {\n\t\t\t\tthrow new Error(`MCP server name \"${cfg.name}\" is already registered`);\n\t\t\t}\n\t\t\tseen.add(cfg.name);\n\t\t}\n\n\t\tthis.configs = configs;\n\t\tthis.connections = configs.map((cfg) => new McpConnection(cfg));\n\t}\n\n\t/**\n\t * Returns the names of all configured MCP servers. Does NOT require a\n\t * network connection — safe to call before `listTools()` or `connect()`.","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/sdk/mcp-client.ts#L53-L89","documentation":"Thrown by the McpClient constructor when a McpServerConfig sets BOTH url and command. The MCP SDK cannot decide which transport to start; exactly one must be present.","triggerScenarios":"Passing { name: 'foo', url: 'http://x', command: 'npx' } — the runtime cannot pick a transport.","commonSituations":"Copy-pasting a config block and forgetting to delete the unused transport field; templating configs where both fields get filled conditionally; YAML/JSON merge that leaves both keys.","solutions":["Delete one of url or command so only the intended transport remains.","Branch your config builder on a transport discriminator before constructing the object.","Assert with a unit test that each emitted config has url XOR command."],"exampleFix":"// before\nnew McpClient([{ name: 's', url: 'http://x', command: 'npx' }]);\n// after\nnew McpClient([{ name: 's', url: 'http://x', transport: 'streamableHttp' }]);","handlingStrategy":"validation","validationCode":"function assertExactlyOneTransport(c: { url?: string; command?: string }) {\n  if (c.url && c.command) {\n    throw new Error('Provide either url or command, not both');\n  }\n}","typeGuard":"function hasSingleTransport(c: { url?: string; command?: string }): boolean {\n  return Boolean(c.url) !== Boolean(c.command);\n}","tryCatchPattern":null,"preventionTips":["Branch config construction on a 'transport: \"stdio\" | \"sse\" | \"streamableHttp\"' discriminator.","Reject ambiguous configs in CI lint rather than at runtime."],"tags":["mcp","config","validation","transport"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}