refactor: enforce BasedPyright strict type checking (#5158)

This commit is contained in:
chengyongru
2026-07-29 21:37:11 +08:00
committed by GitHub
parent e703481755
commit 757ad9c764
166 changed files with 4728 additions and 2621 deletions
+53 -30
View File
@@ -14,7 +14,7 @@ from contextlib import suppress
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Literal, Mapping
from typing import Any, Literal, Mapping, cast
from nanobot.agent.tools.registry import ToolRegistry
from nanobot.apps.protocol import app_manifest, compact_dict
@@ -455,9 +455,10 @@ def normalize_mcp_preset_mentions(raw: Any) -> list[dict[str, Any]]:
known = _known_mcp_names()
out: list[dict[str, Any]] = []
seen: set[str] = set()
for item in raw[:8]:
if not isinstance(item, dict):
for item_value in cast(list[object], raw)[:8]:
if not isinstance(item_value, dict):
continue
item = cast(dict[str, Any], item_value)
name = _clip_ws_string(item.get("name"), 64)
if not name or _MCP_PRESET_NAME_RE.match(name) is None:
continue
@@ -959,7 +960,7 @@ async def mcp_presets_test_action(query: QueryParams) -> dict[str, Any]:
"missing_dependency" if cfg.command and not _command_available(cfg.command) else "configured"
)
if status == "missing_credentials":
last_action = {
last_action: dict[str, Any] = {
"ok": False,
"message": f"{display_name} is missing required credentials.",
"error": "missing credentials",
@@ -988,7 +989,11 @@ async def mcp_presets_test_action(query: QueryParams) -> dict[str, Any]:
timeout=_test_timeout(cfg),
)
tool_prefix = f"mcp_{name}_"
tool_names = sorted(name for name in registry.tool_names if name.startswith(tool_prefix))
tool_names = sorted(
tool_name
for tool_name in registry.tool_names
if tool_name.startswith(tool_prefix)
)
ok = name in stacks
if ok:
last_action = {
@@ -1033,7 +1038,8 @@ async def mcp_presets_test_action(query: QueryParams) -> dict[str, Any]:
finally:
await _close_mcp_stacks(stacks)
preview = {name: last_action.get("tool_names", [])} if last_action.get("tool_names") else None
tool_names = last_action.get("tool_names", [])
preview = {name: tool_names} if tool_names else None
return mcp_presets_payload(last_action=last_action, tool_preview=preview)
@@ -1050,8 +1056,10 @@ def _parse_string_list(raw: str | None) -> list[str]:
if raw is None or not raw.strip():
return []
parsed = _parse_json_value(raw, fallback=None)
if isinstance(parsed, list) and all(isinstance(item, str) for item in parsed):
return [item for item in parsed if item.strip()]
if isinstance(parsed, list):
items = cast(list[object], parsed)
if all(isinstance(item, str) for item in items):
return [item for item in cast(list[str], items) if item.strip()]
if isinstance(parsed, str):
return shlex.split(parsed)
raise McpPresetError("expected a JSON string array")
@@ -1062,7 +1070,7 @@ def _parse_string_map(raw: str | None) -> dict[str, str]:
if not isinstance(parsed, dict):
raise McpPresetError("expected a JSON object")
out: dict[str, str] = {}
for key, value in parsed.items():
for key, value in cast(dict[object, object], parsed).items():
if not isinstance(key, str) or not isinstance(value, str):
raise McpPresetError("JSON object values must be strings")
if key.strip():
@@ -1141,42 +1149,56 @@ def _mcp_server_config(name: str, raw: Any) -> tuple[str, MCPServerConfig]:
server_name = _validated_server_name(name)
if not isinstance(raw, Mapping):
raise McpPresetError(f"MCP server '{server_name}' must be an object")
command = str(raw.get("command") or "").strip()
url = str(raw.get("url") or "").strip()
transport_value = str(raw.get("type", raw.get("transport", "")) or "")
server = cast(Mapping[str, Any], raw)
command = str(server.get("command") or "").strip()
url = str(server.get("url") or "").strip()
transport_value = str(server.get("type", server.get("transport", "")) or "")
transport = _normalize_transport(transport_value, command=command, url=url)
if transport == "stdio" and not command:
raise McpPresetError(f"MCP server '{server_name}' stdio transport requires a command")
if transport in {"sse", "streamableHttp"} and not url:
raise McpPresetError(f"MCP server '{server_name}' remote transport requires a URL")
args = raw.get("args") or []
env = raw.get("env") or {}
headers = raw.get("headers") or {}
cwd = str(raw.get("cwd") or "").strip()
enabled_tools = raw.get("enabledTools", raw.get("enabled_tools", ["*"]))
tool_timeout = raw.get("toolTimeout", raw.get("tool_timeout", _DEFAULT_CUSTOM_TIMEOUT))
args_value: object = server.get("args") or []
env_value: object = server.get("env") or {}
headers_value: object = server.get("headers") or {}
cwd = str(server.get("cwd") or "").strip()
enabled_tools_value: object = server.get("enabledTools", server.get("enabled_tools", ["*"]))
tool_timeout: object = server.get("toolTimeout", server.get("tool_timeout", _DEFAULT_CUSTOM_TIMEOUT))
try:
timeout_int = max(5, min(int(tool_timeout), 600))
timeout_int = max(5, min(int(cast(Any, tool_timeout)), 600))
except (TypeError, ValueError):
timeout_int = _DEFAULT_CUSTOM_TIMEOUT
if not isinstance(args, list) or not all(isinstance(item, str) for item in args):
if not isinstance(args_value, list):
raise McpPresetError(f"MCP server '{server_name}' args must be a string array")
if not isinstance(env, dict) or not all(isinstance(k, str) and isinstance(v, str) for k, v in env.items()):
args = cast(list[object], args_value)
if not all(isinstance(item, str) for item in args):
raise McpPresetError(f"MCP server '{server_name}' args must be a string array")
if not isinstance(env_value, dict):
raise McpPresetError(f"MCP server '{server_name}' env must be a string object")
if not isinstance(headers, dict) or not all(isinstance(k, str) and isinstance(v, str) for k, v in headers.items()):
env = cast(dict[object, object], env_value)
if not all(isinstance(k, str) and isinstance(v, str) for k, v in env.items()):
raise McpPresetError(f"MCP server '{server_name}' env must be a string object")
if not isinstance(headers_value, dict):
raise McpPresetError(f"MCP server '{server_name}' headers must be a string object")
if not isinstance(enabled_tools, list) or not all(isinstance(item, str) for item in enabled_tools):
enabled_tools = ["*"]
headers = cast(dict[object, object], headers_value)
if not all(isinstance(k, str) and isinstance(v, str) for k, v in headers.items()):
raise McpPresetError(f"MCP server '{server_name}' headers must be a string object")
if not isinstance(enabled_tools_value, list):
enabled_tools_value = ["*"]
else:
enabled_tools = cast(list[object], enabled_tools_value)
if not all(isinstance(item, str) for item in enabled_tools):
enabled_tools_value = ["*"]
return server_name, MCPServerConfig(
type=transport,
command=command if transport == "stdio" else "",
args=args,
env=dict(env),
args=cast(list[str], args),
env=cast(dict[str, str], env),
cwd=cwd if transport == "stdio" else "",
url=url if transport in {"sse", "streamableHttp"} else "",
headers=dict(headers),
headers=cast(dict[str, str], headers),
tool_timeout=timeout_int,
enabled_tools=list(enabled_tools),
enabled_tools=cast(list[str], enabled_tools_value),
)
@@ -1184,11 +1206,12 @@ def _import_mcp_servers(raw_json: str | None) -> dict[str, MCPServerConfig]:
parsed = _parse_json_value(raw_json, fallback=None)
if not isinstance(parsed, Mapping):
raise McpPresetError("MCP config must be a JSON object")
servers = parsed.get("mcpServers", parsed)
parsed_mapping = cast(Mapping[str, Any], parsed)
servers: object = parsed_mapping.get("mcpServers", parsed_mapping)
if not isinstance(servers, Mapping):
raise McpPresetError("MCP config must contain mcpServers")
out: dict[str, MCPServerConfig] = {}
for name, raw_server in servers.items():
for name, raw_server in cast(Mapping[object, object], servers).items():
if not isinstance(name, str):
raise McpPresetError("MCP server names must be strings")
server_name, cfg = _mcp_server_config(name, raw_server)