langchain-ai/deepagents · error · MCPConfigError

{str(exc)}

Error message

{str(exc)}

What it means

`login` resolves the server's `${VAR}` env references and field types via `resolve_mcp_server_env` before starting the OAuth flow. If that resolution raises `RuntimeError` (unset env var / malformed `${...}`) or `TypeError` (wrong field type), login re-raises it as `MCPConfigError` (a `ValueError`) so existing config-error handling and `format_login_failure` keep the actionable, field-scoped message instead of collapsing it to a bare `RuntimeError`/`TypeError`.

Source

Thrown at libs/code/deepagents_code/mcp_auth.py:2200

    # OAuth login is discovery-based (RFC 9728), so it works for any remote
    # http/sse server — whether the config opted in with `auth: oauth` or the
    # server was auto-detected as needing auth via a 401 challenge. Only the
    # transport needs gating; stdio servers can't speak OAuth.
    transport = _resolve_server_type(server_config)
    if transport not in {"http", "sse"}:
        msg = (
            f"Server '{server_name}' uses {transport!r} transport; "
            "OAuth login is only valid for http/sse."
        )
        raise ValueError(msg)
    try:
        resolved_config = resolve_mcp_server_env(server_name, server_config)
    except (RuntimeError, TypeError) as exc:
        # Re-raise as MCPConfigError (a ValueError) so callers' existing
        # config-error handling catches it, and `format_login_failure`
        # preserves the actionable, field-scoped message instead of
        # collapsing it to a bare "RuntimeError"/"TypeError".
        raise MCPConfigError(str(exc)) from exc

    from deepagents_code.mcp_providers import resolve_provider

    storage = FileTokenStorage(server_name, server_url=resolved_config["url"])
    policy = resolve_provider(resolved_config["url"])
    result = await policy.run_login(
        server_name=server_name,
        server_url=resolved_config["url"],
        storage=storage,
        ui=ui,
    )

    success_message = f"Logged in to MCP server '{server_name}'."
    if is_env_truthy(DEBUG):
        success_message += f" Tokens saved to {storage.path}."

    if result.completed:
        await ui.show_success(success_message)

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Fix the underlying config error named in the message: export the missing env var, correct the `${...}` syntax to `${VAR}` or `${VAR:-default}`, or fix the field's type.
  2. Catch `MCPConfigError` (it is a `ValueError`) around `login` and surface the message via `format_login_failure`, which already renders the field-scoped detail.
  3. Add a `${VAR:-default}` fallback in the config if the variable is optional.

Example fix

// before
await login("myserver", {"url": "${MY_API_URL}"}, ui)  # MY_API_URL unset
// after
$ export MY_API_URL=https://api.example.com
# or in config:
{"url": "${MY_API_URL:-https://api.example.com}"}
Defensive patterns

Strategy: try-catch

Validate before calling

import os, re
REF = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^{}]*))?\}")
def validate_config(cfg: dict) -> list[str]:
    errs = []
    for f in ("command", "url"):
        if f in cfg and not isinstance(cfg[f], str):
            errs.append(f"{f} must be a string")
    if not isinstance(cfg.get("args", []), list):
        errs.append("args must be a list")
    for f in ("env", "headers"):
        for k, v in cfg.get(f, {}).items():
            if not isinstance(v, str):
                errs.append(f"{f}.{k} must be a string")
    return errs

Type guard

def is_str_or_str_list(v: object) -> TypeGuard[str | list[str]]:
    return isinstance(v, str) or (isinstance(v, list) and all(isinstance(x, str) for x in v))

Try / catch

try:
    await login(server_name, server_config, ui)
except (MCPConfigError, ValueError) as exc:  # MCPConfigError subclasses ValueError
    ui.show_error(format_login_failure(server_name, exc))
except RuntimeError as exc:  # device-flow/handshake failures
    ...

Prevention

When it happens

Trigger: Calling `login(server_name, server_config, ui)` for an http/sse server whose config contains a `${VAR}` reference to an unset env var, a malformed `${...}` reference, or a supported field (`command`, `url`, `args`, `env`, `headers`) with the wrong type.

Common situations: Running `/mcp login` (or `dcode mcp login`) against a server entry whose `url` or `headers` interpolates an env var that is not exported in the current shell, or whose config was hand-edited and a field type drifted (e.g. `args` as a string).

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/70ad37ab5d0f7b52. Report an issue: GitHub.