HKUDS/DeepTutor · error · HTTPException

mcp.configure_command_or_url

Error message

mcp.configure_command_or_url

What it means

Validation error from _validate_servers (used by PUT /mcp settings and server upsert): a named MCP server entry has neither a command (stdio transport) nor a URL (sse/streamableHttp transport), so resolved_type() returns None and the server cannot be started or connected to.

Source

Thrown at deeptutor/api/routers/mcp_settings.py:45

    get_mcp_manager,
    load_mcp_config,
    save_mcp_config,
    validate_mcp_url,
)
from deeptutor.services.mcp.manager import probe_server

router = APIRouter(dependencies=[Depends(require_admin)])


class MCPSettingsPayload(BaseModel):
    servers: dict[str, MCPServerConfig] = Field(default_factory=dict)


def _validate_servers(config: MCPConfig) -> None:
    for name, cfg in config.servers.items():
        transport = cfg.resolved_type()
        if transport is None:
            raise HTTPException(
                status_code=400,
                detail=t("mcp.configure_command_or_url", name=name),
            )
        if transport in {"sse", "streamableHttp"}:
            ok, error = validate_mcp_url(cfg.url)
            if not ok:
                raise HTTPException(
                    status_code=400, detail=t("mcp.server_error", name=name, error=error)
                )


@router.get("")
async def get_mcp_settings() -> dict[str, Any]:
    config = load_mcp_config()
    manager = get_mcp_manager()
    await manager.ensure_started()
    return {
        "servers": {name: cfg.model_dump(mode="json") for name, cfg in config.servers.items()},

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Add either a command (for stdio servers, e.g. "npx -y @some/mcp-server") or a url (for SSE/streamable HTTP servers) to the offending server entry
  2. Check the server name in the error detail to identify which entry is incomplete
  3. Remove the half-configured server entry entirely if it is no longer needed
  4. If using a UI, ensure the transport selector actually writes the corresponding field

Example fix

// before
{"servers": {"my-server": {"name": "my-server"}}}

// after
{"servers": {"my-server": {"name": "my-server", "command": "npx -y @modelcontextprotocol/server-filesystem", "args": ["/tmp"]}}}
Defensive patterns

Strategy: type-guard

Validate before calling

for (const [name, cfg] of Object.entries(servers)) {
  if (!cfg.command && !cfg.url) {
    throw new Error(`Server '${name}' needs a command (stdio) or a url (http/sse)`);
  }
}

Type guard

function hasTransport(cfg: MCPServerConfig): boolean {
  return Boolean(cfg?.command || cfg?.url);
}

Try / catch

try {
  await api.put('/mcp', { servers });
} catch (e) {
  if (e.status === 400) console.error('MCP config rejected:', e.detail);
  throw e;
}

Prevention

When it happens

Trigger: PUT /api mcp settings (update_mcp_settings) or the per-server upsert endpoint with a servers entry that has empty/missing both 'command' and 'url' fields, e.g. {"servers": {"my-server": {"name": "my-server"}}}.

Common situations: Partially filled server config in a settings UI where the user entered only a name; YAML/JSON config edited by hand with a typo'd or omitted command/url key; a migration or template that ships a placeholder server entry with no transport fields.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/d5a117f77a218b27. Report an issue: GitHub.