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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions