What happened?
Running qwen --prompt "..." against an MCP server configured in ~/.qwen/settings.json no longer gives the model access to that server's tools. The CLI starts, the server connects, but the model answers the prompt as if no MCP tools were available — it never calls them.
The bundled integration test makes this easy to reproduce. With an addition-server MCP exposing one add(a, b) tool, the test asks the model add 5 and 10, use tool if you can. On main today the model answers 15 directly without invoking mcp__addition-server__add. The test has been failing all three retries on the scheduled Release workflow since 2026-05-14:
- 2026-05-13 scheduled release at
14512080e — green
- 2026-05-14 scheduled release at
d343e2c15 — red, only the integration-test jobs failing
- 2026-05-15 scheduled release at
cc800d013 — red, same failure
Both the No-Sandbox and Docker integration-test jobs fail identically.
The failure is not a flake — it reproduces deterministically locally on my machine in 167s (all retries exhausted) with the same Expected to find an add tool call assertion error.
What did you expect to happen?
Headless --prompt runs should see the same MCP tool surface as interactive sessions. If I configure an MCP server in settings, the model should be able to call its tools on the first prompt, exactly as it could before the progressive-MCP availability change.
Steps to reproduce
- Check out
main.
npm install && npm run build && npm run bundle
cd integration-tests && QWEN_SANDBOX=false npx vitest run cli/simple-mcp-server.test.ts
- Observe: the test fails with
Expected to find an add tool call: expected false to be truthy on all three retries.
- Re-run with the documented rollback flag:
QWEN_CODE_LEGACY_MCP_BLOCKING=1 QWEN_SANDBOX=false npx vitest run cli/simple-mcp-server.test.ts
- Observe: the test passes on the first try in ~27 seconds.
Where this regressed
#3994 — feat(perf): progressive MCP availability — MCP no longer blocks first input (commit d343e2c15) — the only commit in the 2026-05-13 → 2026-05-14 bisect window that touches MCP discovery or tool registration.
The PR's intent is sound: the --prompt path in packages/cli/src/gemini.tsx does explicitly await config.waitForMcpReady() before sending the first request, exactly as the PR description requires. And startMcpDiscoveryInBackground does call await this.geminiClient?.setTools() after discovery completes, with a comment noting this is the only path that updates chat.tools for non-interactive runs.
But the wiring isn't actually flowing through to the model — even after waitForMcpReady() resolves, the next sendMessage is dispatched without the MCP tool in scope. So either setTools() is racing the chat's first turn, or the tool list it pushes isn't being picked up by GeminiChat, or something is stripping MCP tools from the request after setTools() runs.
The legacy synchronous path (gated by QWEN_CODE_LEGACY_MCP_BLOCKING=1) has none of this and the model picks up the tool reliably — which is the evidence that this is a wiring issue in the new path, not a model-behavior shift.
Why this matters
Anyone running qwen non-interactively with MCP servers (CI pipelines, scripts, batch jobs, ACP integrations using the single-prompt path) is silently running with built-in tools only. There is no error message; the model just produces an answer without invoking the configured server. For workflows that exist specifically to call out to an MCP server, the result is silently wrong rather than failing loud.
The QWEN_CODE_LEGACY_MCP_BLOCKING=1 flag is a viable short-term escape hatch, but it's an internal rollback meant for diagnosis, not a long-term answer — and it costs the startup-latency win the PR set out to deliver.
Suggested next step
Investigate why the post-discovery setTools() in Config.startMcpDiscoveryInBackground (packages/core/src/config/config.ts:1406-1431) is not making the MCP tools visible to the first non-interactive model request, even though await config.waitForMcpReady() resolves before that request is dispatched. The interactive path uses a different mechanism (the mcp-client-update subscriber in AppContainer) and is presumably unaffected.
Workaround
Set QWEN_CODE_LEGACY_MCP_BLOCKING=1 in the environment for non-interactive runs. The release workflow should set this on the integration-test jobs to unblock CI while the root cause is fixed.
What happened?
Running
qwen --prompt "..."against an MCP server configured in~/.qwen/settings.jsonno longer gives the model access to that server's tools. The CLI starts, the server connects, but the model answers the prompt as if no MCP tools were available — it never calls them.The bundled integration test makes this easy to reproduce. With an
addition-serverMCP exposing oneadd(a, b)tool, the test asks the modeladd 5 and 10, use tool if you can.Onmaintoday the model answers15directly without invokingmcp__addition-server__add. The test has been failing all three retries on the scheduledReleaseworkflow since 2026-05-14:14512080e— greend343e2c15— red, only the integration-test jobs failingcc800d013— red, same failureBoth the No-Sandbox and Docker integration-test jobs fail identically.
The failure is not a flake — it reproduces deterministically locally on my machine in 167s (all retries exhausted) with the same
Expected to find an add tool callassertion error.What did you expect to happen?
Headless
--promptruns should see the same MCP tool surface as interactive sessions. If I configure an MCP server in settings, the model should be able to call its tools on the first prompt, exactly as it could before the progressive-MCP availability change.Steps to reproduce
main.npm install && npm run build && npm run bundlecd integration-tests && QWEN_SANDBOX=false npx vitest run cli/simple-mcp-server.test.tsExpected to find an add tool call: expected false to be truthyon all three retries.QWEN_CODE_LEGACY_MCP_BLOCKING=1 QWEN_SANDBOX=false npx vitest run cli/simple-mcp-server.test.tsWhere this regressed
#3994 — feat(perf): progressive MCP availability — MCP no longer blocks first input (commit
d343e2c15) — the only commit in the 2026-05-13 → 2026-05-14 bisect window that touches MCP discovery or tool registration.The PR's intent is sound: the
--promptpath inpackages/cli/src/gemini.tsxdoes explicitlyawait config.waitForMcpReady()before sending the first request, exactly as the PR description requires. AndstartMcpDiscoveryInBackgrounddoes callawait this.geminiClient?.setTools()after discovery completes, with a comment noting this is the only path that updateschat.toolsfor non-interactive runs.But the wiring isn't actually flowing through to the model — even after
waitForMcpReady()resolves, the nextsendMessageis dispatched without the MCP tool in scope. So eithersetTools()is racing the chat's first turn, or the tool list it pushes isn't being picked up byGeminiChat, or something is stripping MCP tools from the request aftersetTools()runs.The legacy synchronous path (gated by
QWEN_CODE_LEGACY_MCP_BLOCKING=1) has none of this and the model picks up the tool reliably — which is the evidence that this is a wiring issue in the new path, not a model-behavior shift.Why this matters
Anyone running
qwennon-interactively with MCP servers (CI pipelines, scripts, batch jobs, ACP integrations using the single-prompt path) is silently running with built-in tools only. There is no error message; the model just produces an answer without invoking the configured server. For workflows that exist specifically to call out to an MCP server, the result is silently wrong rather than failing loud.The
QWEN_CODE_LEGACY_MCP_BLOCKING=1flag is a viable short-term escape hatch, but it's an internal rollback meant for diagnosis, not a long-term answer — and it costs the startup-latency win the PR set out to deliver.Suggested next step
Investigate why the post-discovery
setTools()inConfig.startMcpDiscoveryInBackground(packages/core/src/config/config.ts:1406-1431) is not making the MCP tools visible to the first non-interactive model request, even thoughawait config.waitForMcpReady()resolves before that request is dispatched. The interactive path uses a different mechanism (themcp-client-updatesubscriber inAppContainer) and is presumably unaffected.Workaround
Set
QWEN_CODE_LEGACY_MCP_BLOCKING=1in the environment for non-interactive runs. The release workflow should set this on the integration-test jobs to unblock CI while the root cause is fixed.