Repository navigation
Transport is resolved before environment variable expansion in mcp.json #10654
Description
Activity
This issue was auto-closed. All issues from new contributors are auto-closed by default.
Maintainers review auto-closed issues daily and reopen worthwhile ones. Issues that do not meet the quality bar in CONTRIBUTING.md will not be reopened or receive a reply.
If a maintainer replies
lgtmion one of your issues, your future issues will stay open. If a maintainer replieslgtm, your future issues and PRs will stay open. The command must be at the start of the reply (optionally after one or more@usernamementions) or at the end.See CONTRIBUTING.md.
- addeduntriagedThis issue has been auto closed and has not been triagedThis issue has been auto closed and has not been triaged
on Oct 8, 2026 - addedmcpIssues related to built in mcp extensionIssues related to built in mcp extension
on Oct 8, 2026 - removeduntriagedThis issue has been auto closed and has not been triagedThis issue has been auto closed and has not been triaged
on Oct 8, 2026 Figured out that http://${MY_VAR_WITHOUT_SCHEMA} makes the transport work but actually connection doesn't come up. Seems like env vars are not working in URL.
Confirmed, and there is a fourth place the raw url leaks into: the auth guard.
- validateMcpServerConfig rejects
${VAR}urls before expansion ever runs. - createDefaultTransport passes config.url to StreamableHttpTransport unexpanded (headers and env values next to it go through resolveConfigValueOrThrow).
- McpServerConnection.oauthUrl hands the raw url to OAuth discovery.
- validation is also what enforces "auth requires https or loopback", so simply skipping the url check for expandable values would make the credential guard bypassable through an env var. The expanded value needs the same check.
I have a fix that follows the headers pattern: urls may reference
${NAME}or!cmd(new isExpandableConfigValue helper), validation defers the url checks when the value is expandable, and createDefaultTransport expands with resolveConfigValueOrThrow and enforces the http(s) and auth-https constraints on the expanded value (shared httpServerUrlError helper, so both sites use the same rules). oauthUrl expands the same way but stays non-throwing since it runs in the constructor. A missing variable fails the connection with the resolver's error naming the variable.Regression test at test/suite/regressions/10654-mcp-url-env-expansion.test.ts covers: expandable urls pass validation, literal garbage still fails, transport gets the expanded url, non-http expansions are rejected, missing variable is named, and the auth guard holds on expanded urls. npm run check passes.
Happy to send the PR. lgtm?
Reacted by Valerio Di Gregorio- validateMcpServerConfig rejects
I'd really appreciate such a quick fix. I'm integrating PI and this is a blocker for me.
What happened?
If you use:
{
"mcpServers": {
"example": {"url": "${MY_VAR}"}
}
}
Doesn't resolve the transport correctly. If I use "http://${MY_VAR_WITHOUT_SCHEMA}" then it works.
The url should be resolved with the same logic used for headers and env values.
I believe mcp-servers.ts in validateMcpServerConfig() should use the url after environment variable expansion. Same issue in runtime.ts in createDefaultTransport(). Same issue in McpServerConnection.oauthUrl.
Steps to reproduce
use:
{
"mcpServers": {
"example": {"url": "${MY_VAR}"}
}
}
In mcp.json
Expected behavior
This mcp.json:
{
"mcpServers": {
"example": {"url": "${MY_VAR}"}
}
}
Should be able to connect as streamable-http.
Version
1.0.4