Skip to content

mcp-server.spec.ts: all 6 stdio registrations are rejected — Langflow requires command to be a single executable #1091

Description

@Victor-w-Madeira

Found while validating #1087 (canvas-controls migration). Not caused by that PR:
the failure reproduces identically on clean main.

Symptom

mcp/server/mcp-server.spec.ts:622"mcp server tools should be refreshed when
editing a server"
— fails waiting for the add-server modal to close:

TimeoutError: page.waitForSelector: Timeout 30000ms exceeded.
  - waiting for locator('[data-testid="add-mcp-server-button"]') to be hidden
  at mcp-server.spec.ts:675

--workers=1 --retries=0, Nightly 1.12.0.dev7. Same run stashed to clean
main: same failure, same line. The button goes aria-busy and comes back
enabled — the save is REJECTED, and the modal correctly stays open. The reason is
in the error context:

Value error, MCP stdio command must be a single executable name or path;
put options and arguments in the 'args' field

Root cause — an intentional upstream security change, not a regression

src/lfx/src/lfx/base/mcp/security.pyvalidate_mcp_stdio_config() (line 440)
rejects any command carrying whitespace outside a path prefix:

# The structured command field is exactly one executable. Options belong in args so every
# policy layer sees the same argv. Parent-directory spaces remain valid for executable paths.

The intent is stated there: an option glued onto command means the policy
layers (allowlist, arg blocklist, interpreter hardening) do not all see the same
argv. It arrived with the multi-tenant hardening trail — #13530
(harden multi-tenant surfaces) → #14044 (close tenant-isolation gaps) →
f4d6ac4 fix(security): forward-port release-1.10.3 hardening (#14073,
2026-07-15).

Verdict: test defect. The feature is alive and npx / uvx are still
allowlisted (ALLOWED_MCP_COMMANDS, line 47) — only the input shape moved. This
is not a Langflow regression and not a dead surface.

Scope — every stdio registration in the file

All 6 fills of stdio-command-input pass <exe> <arg>:

Line Value
60 npx @modelcontextprotocol/server-everything
239 npx @modelcontextprotocol/server-everything
327 uvx mcp-server-test
670 uvx mcp-server-fetch
777 uvx mcp-server-time
894 uvx mcp-server-fetch

Line 327's test also fills stdio-args_0..2 already, so that one only needs the
package moved out of command.

Not affected: the @stable specs under mcp/client/ already register with
command: "npx" and the package in args — they satisfy the new contract as
written. This is confined to mcp/server/mcp-server.spec.ts, whose 6 tests carry
no @stable, which is why a break of this size went unnoticed: the file runs
in no automated lane.

The fix, and what it uncovers next

A local scout (command: "uvx" + stdio-args_0: "mcp-server-fetch", reverted)
got past the validation — registration succeeded and the modal closed. It then
failed one step later, at fetch-0-option:

Error loading server: Connection closed
Error updating tool list: Error updating tool list: unhandled errors in a TaskGroup (1 sub-exception)

with the tool dropdown showing "No options found" — the uvx stdio subprocess
does not come up in that environment. So splitting command/args is necessary but
not sufficient, and whoever picks this up should expect a second layer. Related
budget note: this test allows 30 s for the tool list and 10 s for the option,
while the sibling MCP stdio test in mcp-client-regression.spec.ts:391-396 had
to be raised to 120 s for cold npx startup (#463).

Coverage gap worth closing in the same pass

No spec asserts the new contract. A command with an embedded argument is now
rejected for a security reason, and nothing in the suite would notice if that
validation were dropped. A small @regression case ("stdio command with an
embedded argument is refused; command + args is accepted") is cheap and is real
security coverage.

Done when

  • All 6 registrations use command = single executable with the package/options in args
  • The tool-list/option budgets are reconciled with the qa-infra: MCP get-sum test hard-fails when npx server-everything doesn't register tools in time (daily 2026-07-01) #463 precedent, or the reason for keeping them tight is written down
  • The uvx-does-not-start layer above is either resolved or filed separately with its own evidence
  • A case covering the new command/args contract exists
  • The file is green --retries=0 on the current nightly — which is the entry gate for ever validating and promoting these 6 tests

Environment caveat

Everything above was reproduced on a local Nightly 1.12.0.dev7. The 2026-07-29
daily ran 1.12.0.dev9, so re-confirm on a refreshed image before assuming the
line numbers and messages still match.

Metadata

Metadata

Labels

area:mcpModel Context Protocolfollow-upApproved exception: follow-up of merged work (ROADMAP Intake)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions