fix: mypy errors silently dropped during CI (#832)

Resolves
https://linear.app/arcadedev/issue/TOO-788/mypy-failures-are-silently-dropped-during-arcade-mcp-ci

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: primarily CI/Makefile behavior and type-annotation tweaks;
functional logic is unchanged aside from stricter failure propagation in
`make check`.
> 
> **Overview**
> **Stops CI from silently ignoring mypy failures.** The `make check`
target now runs `mypy` across `libs/arcade*/` and exits non-zero if any
package fails, reporting the failed libs.
> 
> Separately tightens typing to satisfy `mypy` (removing `type: ignore`
on OAuth helpers, adding `cast()`/`Any` annotations for JSON response
shapes and subprocess kwargs, and handling non-`str` `server_address`
hosts), and bumps patch versions for `arcade-mcp` and
`arcade-mcp-server`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e79575b13a2d03adf3548104a0064c643f1e21b1. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This commit is contained in:
Eric Gustin 2026-04-28 13:25:44 -07:00 committed by GitHub
parent be52f07930
commit cbe68462df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 27 additions and 20 deletions

View file

@ -10,11 +10,15 @@ install: ## Install the uv environment and all packages with dependencies
check: ## Run code quality tools.
@echo "🚀 Linting code: Running pre-commit"
@uv run pre-commit run -a
@echo "🚀 Static type checking: Running mypy on libs"
@for lib in libs/arcade*/ ; do \
echo "🔍 Type checking $$lib"; \
(cd $$lib && uv run mypy . --exclude tests || true); \
done
@echo "🚀 Static type checking: Running mypy on libs"
@failed_libs=""; for lib in libs/arcade*/ ; do \
echo "🔍 Type checking $$lib"; \
(cd $$lib && uv run mypy . --exclude tests) || failed_libs="$$failed_libs $$lib"; \
done; \
if [ -n "$$failed_libs" ]; then \
echo "❌ mypy failed in:$$failed_libs"; \
exit 1; \
fi
.PHONY: check-libs
check-libs: ## Run code quality tools for each lib package

View file

@ -75,7 +75,7 @@ def _get_default_oauth_timeout_seconds() -> int:
DEFAULT_OAUTH_TIMEOUT_SECONDS = _get_default_oauth_timeout_seconds()
def create_oauth_client(cli_config: CLIConfig) -> OAuth2Client: # type: ignore[no-any-unimported]
def create_oauth_client(cli_config: CLIConfig) -> OAuth2Client:
"""
Create an authlib OAuth2Client configured for the CLI.
@ -92,7 +92,7 @@ def create_oauth_client(cli_config: CLIConfig) -> OAuth2Client: # type: ignore[
)
def generate_authorization_url( # type: ignore[no-any-unimported]
def generate_authorization_url(
client: OAuth2Client,
cli_config: CLIConfig,
redirect_uri: str,
@ -123,7 +123,7 @@ def generate_authorization_url( # type: ignore[no-any-unimported]
return url, code_verifier
def exchange_code_for_tokens( # type: ignore[no-any-unimported]
def exchange_code_for_tokens(
client: OAuth2Client,
code: str,
redirect_uri: str,
@ -321,7 +321,7 @@ class _LoopbackHTTPServer(HTTPServer):
def server_bind(self) -> None:
socketserver.TCPServer.server_bind(self)
host, port = self.server_address[:2]
self.server_name = host
self.server_name = host if isinstance(host, str) else host.decode()
self.server_port = port

View file

@ -6,6 +6,7 @@ import json as _json
import logging
import time
from pathlib import Path
from typing import Any, cast
import httpx
from arcade_core.constants import PROD_COORDINATOR_HOST, PROD_ENGINE_HOST
@ -55,7 +56,7 @@ def _read_cache(debug: bool = False) -> dict[str, list[str]] | None:
return None
if debug:
console.print(f" [dim]Using cached tool catalog ({age:.0f}s old)[/dim]")
return data.get("toolkits", {})
return cast("dict[str, list[str]]", data.get("toolkits", {}))
except Exception:
return None
@ -318,7 +319,7 @@ def list_gateways(
return []
data = resp.json()
return data.get("items", [])
return cast("list[dict[Any, Any]]", data.get("items", []))
def find_matching_gateway(
@ -401,13 +402,13 @@ def create_gateway(
if resp.status_code not in (200, 201):
raise RuntimeError(f"Failed to create gateway ({resp.status_code}): {resp.text}")
data = resp.json()
data: dict[Any, Any] = resp.json()
# The API may return the gateway directly or wrapped in a list/items envelope
if "slug" in data:
return data
if data.get("items"):
return data["items"][0]
return cast("dict[Any, Any]", data["items"][0])
if "id" in data:
return data
@ -695,13 +696,13 @@ def _resolve_gateway_slug(
if gw.get("slug", "").lower() == input_lower:
if debug:
console.print(f" [dim]Matched by slug: {gw['slug']}[/dim]")
return gw["slug"]
return cast("str", gw["slug"])
for gw in gateways:
if gw.get("name", "").lower() == input_lower:
slug = gw["slug"]
if debug:
console.print(f" [dim]Matched by name '{gw['name']}' -> slug: {slug}[/dim]")
return slug
return cast("str", slug)
if debug:
available = [f"{g.get('name')} ({g.get('slug')})" for g in gateways]
console.print(f" [dim]No match for '{user_input}', available: {available}[/dim]")

View file

@ -4,7 +4,7 @@ import os
import subprocess
import sys
from pathlib import Path
from typing import Optional
from typing import Any, Optional
import click
import typer
@ -334,7 +334,7 @@ def mcp(
# window from appearing (e.g. when an MCP client spawns this
# command without an attached console). The child process still
# inherits stdin/stdout/stderr for stdio transport communication.
run_kwargs: dict[str, object] = {"check": False}
run_kwargs: dict[str, Any] = {"check": False}
creation_flags = get_windows_no_window_creationflags()
if creation_flags:
run_kwargs["creationflags"] = creation_flags

View file

@ -184,7 +184,9 @@ def build_input_schema_from_definition(definition: ToolDefinition) -> dict[str,
if getattr(definition, "input", None) and getattr(definition.input, "parameters", None):
for param in definition.input.parameters:
val_schema = getattr(param, "value_schema", None)
schema = _value_schema_to_json_schema(val_schema) if val_schema else {"type": "string"}
schema: dict[str, Any] = (
_value_schema_to_json_schema(val_schema) if val_schema else {"type": "string"}
)
if getattr(param, "description", None):
schema["description"] = param.description

View file

@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "arcade-mcp-server"
version = "1.21.0"
version = "1.21.1"
description = "Model Context Protocol (MCP) server framework for Arcade.dev"
readme = "README.md"
authors = [{ name = "Arcade.dev" }]

View file

@ -1,6 +1,6 @@
[project]
name = "arcade-mcp"
version = "1.14.0"
version = "1.14.1"
description = "Arcade.dev - Tool Calling platform for Agents"
readme = "README.md"
license = { file = "LICENSE" }