n8n-io/n8n · error · Error

MCP server "${cfg.name}": provide either "url" or "command",

Error message

MCP server "${cfg.name}": provide either "url" or "command", not both

What it means

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.

Source

Thrown at packages/@n8n/agents/src/sdk/mcp-client.ts:71

	 * Per-server connection failures recorded during the last `listTools()`.
	 * Tools from these servers were skipped; the run continues with the
	 * remaining servers' tools. Read via `getConnectionFailures()`.
	 */
	private connectionFailures: McpConnectionFailedEvent[] = [];

	/**
	 * @param configs - Server configurations. Each must have either `url` or `command`.
	 *   Duplicate names within the list are rejected.
	 */
	constructor(configs: McpServerConfig[]) {
		for (const cfg of configs) {
			if (!cfg.url && !cfg.command) {
				throw new Error(
					`MCP server "${cfg.name}": exactly one of "url" or "command" must be provided`,
				);
			}
			if (cfg.url && cfg.command) {
				throw new Error(`MCP server "${cfg.name}": provide either "url" or "command", not both`);
			}
		}

		const seen = new Set<string>();
		for (const cfg of configs) {
			if (seen.has(cfg.name)) {
				throw new Error(`MCP server name "${cfg.name}" is already registered`);
			}
			seen.add(cfg.name);
		}

		this.configs = configs;
		this.connections = configs.map((cfg) => new McpConnection(cfg));
	}

	/**
	 * Returns the names of all configured MCP servers. Does NOT require a
	 * network connection — safe to call before `listTools()` or `connect()`.

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Delete one of url or command so only the intended transport remains.
  2. Branch your config builder on a transport discriminator before constructing the object.
  3. Assert with a unit test that each emitted config has url XOR command.

Example fix

// before
new McpClient([{ name: 's', url: 'http://x', command: 'npx' }]);
// after
new McpClient([{ name: 's', url: 'http://x', transport: 'streamableHttp' }]);
Defensive patterns

Strategy: validation

Validate before calling

function assertExactlyOneTransport(c: { url?: string; command?: string }) {
  if (c.url && c.command) {
    throw new Error('Provide either url or command, not both');
  }
}

Type guard

function hasSingleTransport(c: { url?: string; command?: string }): boolean {
  return Boolean(c.url) !== Boolean(c.command);
}

Prevention

When it happens

Trigger: Passing { name: 'foo', url: 'http://x', command: 'npx' } — the runtime cannot pick a transport.

Common situations: 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.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/6f05cda04de8c1c0. Report an issue: GitHub.