Skip to content

Fix: --tools/--toolsets ignored in streamable-HTTP env-var mode (#376)#377

Open
sebin wants to merge 4 commits into
hashicorp:mainfrom
sebin:fix/streamable-http-env-mode-skips-flag-parsing
Open

Fix: --tools/--toolsets ignored in streamable-HTTP env-var mode (#376)#377
sebin wants to merge 4 commits into
hashicorp:mainfrom
sebin:fix/streamable-http-env-mode-skips-flag-parsing

Conversation

@sebin

@sebin sebin commented May 28, 2026

Copy link
Copy Markdown

Summary

Fixes #376.

When the server was launched via env vars (TRANSPORT_MODE / TRANSPORT_HOST / TRANSPORT_PORT / MCP_ENDPOINT) instead of the explicit streamable-http subcommand, main() called runHTTPServer directly without ever calling rootCmd.Execute(). cobra therefore never parsed os.Args[1:], so both --tools and --toolsets always fell back to their declared defaults ("" and "all"), regardless of what the operator passed on the command line. The HCP-Terraform toolset was silently enabled in full whenever TFE_TOKEN was present, even when the operator asked for a smaller set.

Change

  • Move the env-var-driven HTTP-mode detection inside runDefaultCommand (the default Run of rootCmd), so cobra's Execute() always runs first and the --tools / --toolsets persistent flags are populated before getToolsetsFromCmd reads them.
  • Collapse main() to a plain rootCmd.Execute().
  • The explicit `streamable-http` subcommand path is unchanged.
  • No exported API changes; no behavior change for stdio mode.

Diff is +18 / -18.

Verification

```bash
TRANSPORT_MODE=streamable-http MCP_SESSION_MODE=stateless TFE_TOKEN=x \
./terraform-mcp-server --tools=list_workspaces,get_workspace_details &

curl -s -X POST http://localhost:8080/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
| grep -oE '"name":"[a-zA-Z_]+"' | sort -u | wc -l
```

  • Before: 43 (full catalog).
  • After: 2 (only the requested tools).

Existing test suite passes:

```
$ go test ./...
ok ... 571 passed in 10 packages
```

Test plan

Notes for reviewers

I did not add a new test because `runDefaultCommand` blocks on the server until SIGINT and the existing tests don't have a harness for that path. Happy to add one (e.g. extracting the toolset-selection block into a helper that takes a stub for the server-run function) if maintainers want it.

When the server was launched via env vars (TRANSPORT_MODE / TRANSPORT_HOST /
TRANSPORT_PORT / MCP_ENDPOINT) instead of the explicit `streamable-http`
subcommand, main() called runHTTPServer directly without ever invoking
rootCmd.Execute(). As a result cobra never parsed os.Args[1:], so both
`--tools` and `--toolsets` always fell back to their declared defaults
(`""` and `"all"`), regardless of what the operator passed on the command
line. The HCP-Terraform toolset was silently enabled in full whenever
TFE_TOKEN was present, even when the operator asked for a smaller set.

The fix moves the env-var-driven HTTP-mode detection inside
runDefaultCommand (the default Run handler of rootCmd), so cobra's
Execute() always runs first and the persistent flags are populated before
getToolsetsFromCmd reads them. main() collapses to a plain
rootCmd.Execute(). The explicit `streamable-http` subcommand path is
unchanged.

Verified locally on linux/arm64:

  TRANSPORT_MODE=streamable-http MCP_SESSION_MODE=stateless TFE_TOKEN=x \
    ./terraform-mcp-server --tools=list_workspaces,get_workspace_details &
  curl -s -X POST http://localhost:8080/mcp \
    -H 'Content-Type: application/json' \
    -H 'Accept: application/json, text/event-stream' \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
    | grep -oE '"name":"[a-zA-Z_]+"' | sort -u | wc -l
  # Before: 43.  After: 2.

All 571 existing tests pass.

Fixes hashicorp#376
@sebin sebin requested a review from a team as a code owner May 28, 2026 13:48
@sebin

sebin commented May 28, 2026

Copy link
Copy Markdown
Author

Heads-up on a side effect of the structural change that I should have called out in the PR body — the four-line logger setup at the top of the old main() was removed:

logFile, _ := rootCmd.PersistentFlags().GetString("log-file")
logLevel := getLogLevel(rootCmd)
logFormat := getLogFormat(rootCmd)
logger, err := initLogger(logFile, logLevel, logFormat)

These reads ran before rootCmd.Execute() and therefore had the same defect as the --tools / --toolsets reads — they always returned the declared defaults ("", "info", "text") regardless of what the operator passed as --log-file=… / --log-level=debug / --log-format=json. The resulting logger was only consumed by the env-var-driven HTTP branch; every other code path (stdioCmd, streamableHTTPCmd, runDefaultCommand) constructs its own logger inside its Run after cobra has parsed flags.

After the fix, the env-var-driven HTTP branch lives inside runDefaultCommand, which already calls initLogger(…) with correctly-parsed flag values. The top-of-main() block became both redundant and incorrect, so it was removed. CLI log flags now actually take effect in the env-var-driven path — a small bonus fix from the same structural change.

@jaylonmcshan19-x

Copy link
Copy Markdown
Contributor

Hey @sebin Thanks for finding the issue and providing the fix :)
One final ask: Can you include a regression test? Given the bug silently exposed the whole catalog when someone asked for a subset, I'd be nice to have something locking the behavior down.

sebin and others added 3 commits June 2, 2026 19:34
Lock the fix in: when rootCmd.Execute() is invoked with --tools or
--toolsets while TRANSPORT_MODE / TRANSPORT_PORT / TRANSPORT_HOST /
MCP_ENDPOINT select streamable-HTTP, the cobra Run callback must
receive a command whose flag values are populated, so getToolsetsFromCmd
returns the operator's selection rather than the full default catalog.

The tests stub rootCmd.Run with a capture-only callback so no server is
started. resetPersistentFlag clears state between tests since rootCmd is
a package-level global.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

--tools and --toolsets flags silently ignored in streamable-HTTP env-var mode

2 participants