langchain-ai/deepagents · error · ValueError

MCPServerInfo {self.name!r}: status={self.status!r} cannot c

Error message

MCPServerInfo {self.name!r}: status={self.status!r} cannot carry tools

What it means

MCPServerInfo is a dataclass whose __post_init__ enforces state invariants. A server whose status is not a live/connectable state (e.g. 'disabled' or 'unauthenticated') must not be constructed with a non-empty tools list, because tools from a non-functional server are meaningless or misleading.

Source

Thrown at libs/code/deepagents_code/mcp_tools.py:191

            if self.error is not None:
                msg = (
                    f"MCPServerInfo {self.name!r}: status='ok' cannot carry "
                    f"an error (got {self.error!r})"
                )
                raise ValueError(msg)
        else:
            if self.error is None:
                msg = (
                    f"MCPServerInfo {self.name!r}: status={self.status!r} "
                    "requires an error message"
                )
                raise ValueError(msg)
            if self.tools:
                msg = (
                    f"MCPServerInfo {self.name!r}: status={self.status!r} "
                    "cannot carry tools"
                )
                raise ValueError(msg)
        if self.pending_reconnect and self.status != "disabled":
            msg = (
                f"MCPServerInfo {self.name!r}: pending_reconnect requires "
                f"status='disabled' (got {self.status!r})"
            )
            raise ValueError(msg)

    def needs_attention(self) -> bool:
        """Return whether this server is blocked on user login."""
        return self.status == "unauthenticated"


_SUPPORTED_REMOTE_TYPES = {"sse", "http"}
"""Supported transport types for remote MCP servers (SSE and HTTP)."""

_TRANSPORT_ALIASES = {"streamable_http": "http", "streamable-http": "http"}
"""Aliases that normalize to canonical transport names.

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Clear the tools list when the server status is not live (set tools=[])
  2. Set status to a live state only after tools have actually been loaded
  3. Check status before constructing MCPServerInfo and drop stale tools

Example fix

// before
MCPServerInfo(name='fs', status='disabled', tools=[tool_a])
// after
MCPServerInfo(name='fs', status='disabled', tools=[])
Defensive patterns

Strategy: validation

Validate before calling

def can_carry_tools(info) -> bool:
    return info.status not in ('disabled', 'unauthenticated')
# before constructing: tools=tools if can_live else []

Type guard

def is_live_server(info: MCPServerInfo) -> bool:
    return info.status == 'connected'

Try / catch

try:
    info = MCPServerInfo(name=name, status=status, tools=tools)
except ValueError as e:
    log.warning('dropping tools for non-live server: %s', e)
    info = MCPServerInfo(name=name, status=status, tools=[])

Prevention

When it happens

Trigger: Constructing or rebuilding MCPServerInfo (directly or via from-config/load paths) with status set to 'disabled'/'unauthenticated'/similar while passing a non-empty tools list.

Common situations: Persisting a server snapshot to disk and restoring it with stale tools attached; UI code caching tools across a status change to disabled; merging config state with previously loaded tool lists.

Related errors


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