Skip to content

open-terminal mcp always sends empty Authorization header, causing 401 on all tool calls — plus stdio transport crashes with unexpected host kwarg #115

Description

@Varshith-JV-1410

Bug: open-terminal mcp always sends empty Authorization header → 401 on all tool calls + stdio transport crashes with unexpected host kwarg

Environment

  • open-terminal version: latest (uvx open-terminal[mcp])
  • fastmcp version: 3.2.4
  • OS: Windows 11
  • Python: 3.14

Bug 1: MCP server always sends empty Authorization: Bearer header → 401 on every tool call

Steps to reproduce

$env:OPEN_TERMINAL_API_KEY = "12345678"
uvx "open-terminal[mcp]" mcp --transport streamable-http --host 0.0.0.0 --port 9000

Call any tool (e.g. run_command) → 401 Unauthorized

Also reproduces with --config pointing to a config.toml containing api_key = "12345678".

Error

HTTPStatusError: Client error '401 Unauthorized' for url 'http://fastapi/execute?wait=10'
ValueError: HTTP error 401: Unauthorized - {'detail': 'Invalid API key'}

Root cause

mcp_server.py captures API_KEY at module import time:

# open_terminal/mcp_server.py
from open_terminal.env import API_KEY   # ← captured at import, always ""

mcp = FastMCP.from_fastapi(
    app=app,
    name="Open Terminal",
    httpx_client_kwargs={
        "headers": {
            "Authorization": f"Bearer {API_KEY}",  # ← always empty string
        }
    },
)

In cli.py, mcp_server is imported after config.init() runs, but open_terminal/env.py line 31 already executed at first import — before config.init() and before os.environ["OPEN_TERMINAL_API_KEY"] is set. So API_KEY is always "" regardless of env vars or config file.

The run command works correctly because it explicitly sets os.environ["OPEN_TERMINAL_API_KEY"] before anything reads it. The mcp command has no equivalent step and also has no --api-key CLI option.

Proposed fix

Option A — add --api-key to the mcp command and set the env var before importing mcp_server (mirrors what run does):

# open_terminal/cli.py

@main.command()
@click.option("--transport", default="stdio", type=click.Choice(["stdio", "streamable-http"]))
@click.option("--host", default=None)
@click.option("--port", default=None, type=int)
@click.option("--config", "config_path", default=None, type=click.Path(...))
@click.option("--cwd", default=None, type=click.Path(...))
@click.option(
    "--api-key",
    default="",
    envvar="OPEN_TERMINAL_API_KEY",
    help="Bearer API key (or set OPEN_TERMINAL_API_KEY env var)",
)
def mcp(transport, host, port, config_path, cwd, api_key):
    from open_terminal import config

    cfg = config.init(config_path)

    host = host or cfg.get("host", "0.0.0.0")
    port = port if port is not None else cfg.get("port", 8000)

    # Resolve API key: CLI flag > env var > config file
    if not api_key:
        api_key = cfg.get("api_key", "")

    # Must set BEFORE importing mcp_server so env.py picks it up at import time
    if api_key:
        os.environ["OPEN_TERMINAL_API_KEY"] = api_key

    if cwd:
        os.chdir(cwd)

    from open_terminal.mcp_server import mcp as mcp_server
    ...

Option B — lazily resolve API_KEY in mcp_server.py instead of at import time:

# open_terminal/mcp_server.py
from fastmcp import FastMCP
from open_terminal.main import app
import os

def _auth_headers() -> dict:
    key = os.environ.get("OPEN_TERMINAL_API_KEY", "")
    return {"Authorization": f"Bearer {key}"} if key else {}

mcp = FastMCP.from_fastapi(
    app=app,
    name="Open Terminal",
    httpx_client_kwargs={"headers": _auth_headers()},
)

Bug 2: stdio transport crashes — TypeError: unexpected keyword argument 'host'

Steps to reproduce

Use the following VS Code mcp.json:

{
  "servers": {
    "open-terminal": {
      "type": "stdio",
      "command": "uvx",
      "args": ["open-terminal[mcp]", "mcp"],
      "env": { "OPEN_TERMINAL_API_KEY": "12345678" }
    }
  }
}

Error

TypeError: TransportMixin.run_stdio_async() got an unexpected keyword argument 'host'

Root cause

cli.py line 187 unconditionally passes host and port to mcp_server.run() regardless of transport:

mcp_server.run(transport=transport, host=host, port=port)  # always passes host/port

FastMCP's stdio transport does not accept host or port kwargs.

Proposed fix

# open_terminal/cli.py
kwargs: dict = {"transport": transport}
if transport == "streamable-http":
    kwargs["host"] = host
    kwargs["port"] = port

mcp_server.run(**kwargs)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions