Skip to content

CLI: a URL wins over a positional command and over --transport stdio, both silently #2611

Closed

Description

@v0ropaev

Which version line?

v2 — current (@modelcontextprotocol/inspector@latest)

Which client?

CLI

Inspector version

2.9.0

Node version

v24.8.0

Operating system

macOS 27.0

Transport

Streamable HTTP

MCP server under inspection

Not a real server in any of these. Every case below is decided before a connection is attempted, by buildConfigFromOptions in core/mcp/node/config.ts, so https://example.com/mcp is enough to show which transport was chosen. No OAuth.

Steps to reproduce

  1. A positional stdio command together with --server-url:
npx @modelcontextprotocol/[email protected] --cli node ./nonexistent-server.js \
  --server-url https://example.com/mcp --method tools/list
  1. --transport stdio next to an HTTP URL:
npx @modelcontextprotocol/[email protected] --cli --transport stdio \
  --server-url https://example.com/mcp --method tools/list
  1. The same, with a URL whose path does not end in /mcp or /sse:
npx @modelcontextprotocol/[email protected] --cli --transport stdio \
  --server-url https://example.com/foo --method tools/list
  1. The first remote example in clients/cli/README.md, copied as it stands:
npx @modelcontextprotocol/[email protected] --cli https://my-mcp-server.example.com

Expected behavior

  1. The two targets contradict each other, so I expected the same kind of refusal the opposite mix already gets. A positional URL with extra arguments raises "Arguments cannot be passed to a URL-based MCP server", and a local command with --transport http raises "Only stdio transport can be used with local commands".
  2. Either an error saying stdio cannot be used with a URL, or stdio honoured. Not silence.
  3. If the transport cannot be used with this target, say that. The URL path is irrelevant once --transport has been given.
  4. A README example should run.

Actual behavior

  1. node ./nonexistent-server.js is dropped without a word and the Inspector connects to https://example.com/mcp over streamable HTTP. The giveaway is the error: it is a 405 from example.com, not a complaint about a missing file.
  2. --transport stdio has no effect. The connection goes out over streamable HTTP, picked from the /mcp suffix.
  3. The error is "Transport type not specified and could not be determined from URL: https://example.com/foo", which is wrong on its own terms, since the transport was specified.
  4. "Transport type not specified and could not be determined from URL: https://my-mcp-server.example.com", exit 1. The heading above it says "default is SSE", which has not been true since the path sniffing landed. The two examples after it pass --transport http and are fine.

Logs, errors, or screenshots

$ npx @modelcontextprotocol/[email protected] --cli node ./nonexistent-server.js --server-url https://example.com/mcp --method tools/list
{"error":{"code":"error","message":"Error POSTing to endpoint: <!doctype html><html lang=\"en\"><head><title>Example Domain</title>...","status":405}}

$ npx @modelcontextprotocol/[email protected] --cli --transport stdio --server-url https://example.com/mcp --method tools/list
{"error":{"code":"error","message":"Error POSTing to endpoint: <!doctype html><html lang=\"en\"><head><title>Example Domain</title>...","status":405}}

$ npx @modelcontextprotocol/[email protected] --cli --transport stdio --server-url https://example.com/foo --method tools/list
{"error":{"code":"error","message":"Transport type not specified and could not be determined from URL: https://example.com/foo."}}

$ npx @modelcontextprotocol/[email protected] --cli https://my-mcp-server.example.com --method tools/list
{"error":{"code":"error","message":"Transport type not specified and could not be determined from URL: https://my-mcp-server.example.com."}}

All four share one cause in buildConfigFromOptions. url is resolved as urlFromTarget ?? options.serverUrl ?? null, and once it is truthy the function builds an HTTP or SSE config and returns, so the positional command is never looked at again. The guard that catches the opposite mix is if (rest.length > 0 && urlFromTarget), which only fires when the URL itself came from the positional argument, and the Only stdio transport can be used with local commands check sits after the early return, on the command path. Inside the URL branch, options.transport is only consulted for sse, http and streamable-http; stdio falls through to the path sniffing and then produces the "not specified" message.

Already prototyped a fix?

No diff, and no prompt either, since I have not built it. What I would want out of it, if it helps:

Decide the target before the transport. A positional command together with --server-url is a contradiction and should be refused with a message naming both, the way the mirror case already is. --transport stdio with a URL is also a contradiction and should be refused rather than ignored. The path sniffing should only run when --transport was absent, so a given-but-impossible transport never reports itself as missing. And the README's first remote example needs either --transport sse or --transport http added, with the "default is SSE" heading dropped, since there is no default any more.

Before you submit

I searched the existing issues for transport stdio, server-url, Transport type not specified, buildConfigFromOptions and positional command url, and found nothing covering this. The closest open ones, #2562 and #2574, are about OAuth URL overrides and stored-auth write-back.

Activity

  1. added
    v2Issues and PRs for v2
    bugSomething isn't working
    on Oct 12, 2026
  2. cliffhall commented on Oct 12, 2026

    @cliffhall
    Member

    Triage: Priority Medium (total 6)

    • Severity 3 — a positional command or --transport stdio is silently overridden by a URL, and the first remote example in clients/cli/README.md fails as written
    • Urgency 2 — wanted eventually; workaround is passing consistent flags
    • Bonuses: +1 bug label

    Board: #28, Status Incoming (awaiting maintainer review — no milestone yet).

  3. added this to the v2.11.0 milestone on Oct 12, 2026
  4. self-assigned this
    on Oct 12, 2026
  5. cliffhall commented on Oct 12, 2026

    @cliffhall
    Member

    Fixed by #2701 (merged into v2/main).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingv2Issues and PRs for v2

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions