Skip to content

Commit 07750ef

Browse files
authored
Fix type errors for ty 0.0.1-alpha.31 upgrade (#2561)
* Fix type errors for ty 0.0.1-alpha.31 upgrade Add type ignores and fixes for ty's stricter checking: - Path(None) guards in cli.py - isinstance checks for ElicitRequestFormParams (URL elicitation support) - TODO(ty) comments for match/isinstance narrowing bugs - Method override type ignores for generic covariance - Starlette Middleware typing workarounds - Dynamic type construction ignores in json_schema_type.py * Fix remaining type errors for ty 0.0.1-alpha.31 - Add asserts for optional attribute access in tests - Add type ignores for dynamic httpx transport internals - Add TODO(ty) comments for `in` operator on str|bytes - Add TODO(ty) comments for Starlette Middleware typing - Use cast for prompt.fn async validation in server.py * Upgrade ty to 0.0.1-alpha.31 Fixes additional test file type errors discovered after upgrade.
1 parent 1ed976b commit 07750ef

35 files changed

+1115
-893
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ dev = [
7272
"pytest-timeout>=2.4.0",
7373
"pytest-xdist>=3.6.1",
7474
"ruff>=0.12.8",
75-
"ty==0.0.1a25",
75+
"ty==0.0.1a31",
7676
"prek>=0.2.12",
7777
]
7878

src/fastmcp/cli/cli.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def version(
103103
"MCP version": importlib.metadata.version("mcp"),
104104
"Python version": platform.python_version(),
105105
"Platform": platform.platform(),
106-
"FastMCP root path": Path(fastmcp.__file__).resolve().parents[1],
106+
"FastMCP root path": Path(fastmcp.__file__ or ".").resolve().parents[1],
107107
}
108108

109109
g = Table.grid(padding=(0, 1))
@@ -819,11 +819,13 @@ async def prepare(
819819
)
820820
sys.exit(1)
821821

822+
assert config_path is not None
822823
config_file = Path(config_path)
823824
if not config_file.exists():
824825
logger.error(f"Configuration file not found: {config_path}")
825826
sys.exit(1)
826827

828+
assert output_dir is not None
827829
output_path = Path(output_dir)
828830

829831
try:

src/fastmcp/client/elicitation.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from mcp import ClientSession
88
from mcp.client.session import ElicitationFnT
99
from mcp.shared.context import LifespanContextT, RequestContext
10-
from mcp.types import ElicitRequestParams
10+
from mcp.types import ElicitRequestFormParams, ElicitRequestParams
1111
from mcp.types import ElicitResult as MCPElicitResult
1212
from pydantic_core import to_jsonable_python
1313
from typing_extensions import TypeVar
@@ -26,7 +26,8 @@ class ElicitResult(MCPElicitResult, Generic[T]):
2626
ElicitationHandler: TypeAlias = Callable[
2727
[
2828
str, # message
29-
type[T], # a class for creating a structured response
29+
type[T]
30+
| None, # a class for creating a structured response (None for URL elicitation)
3031
ElicitRequestParams,
3132
RequestContext[ClientSession, LifespanContextT],
3233
],
@@ -42,10 +43,15 @@ async def _elicitation_handler(
4243
params: ElicitRequestParams,
4344
) -> MCPElicitResult | mcp.types.ErrorData:
4445
try:
45-
if params.requestedSchema == {"type": "object", "properties": {}}:
46-
response_type = None
46+
# requestedSchema only exists on ElicitRequestFormParams, not ElicitRequestURLParams
47+
if isinstance(params, ElicitRequestFormParams):
48+
if params.requestedSchema == {"type": "object", "properties": {}}:
49+
response_type = None
50+
else:
51+
response_type = json_schema_to_type(params.requestedSchema)
4752
else:
48-
response_type = json_schema_to_type(params.requestedSchema)
53+
# URL-based elicitation doesn't have a schema
54+
response_type = None
4955

5056
result = await elicitation_handler(
5157
params.message, response_type, params, context

src/fastmcp/client/messages.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,18 @@ async def dispatch(self, message: Message) -> None:
3535
# requests
3636
case RequestResponder():
3737
# handle all requests
38-
await self.on_request(message)
38+
# TODO(ty): remove when ty supports match statement narrowing
39+
await self.on_request(message) # type: ignore[arg-type]
3940

4041
# handle specific requests
41-
match message.request.root:
42+
# TODO(ty): remove type ignores when ty supports match statement narrowing
43+
match message.request.root: # type: ignore[union-attr]
4244
case mcp.types.PingRequest():
43-
await self.on_ping(message.request.root)
45+
await self.on_ping(message.request.root) # type: ignore[union-attr]
4446
case mcp.types.ListRootsRequest():
45-
await self.on_list_roots(message.request.root)
47+
await self.on_list_roots(message.request.root) # type: ignore[union-attr]
4648
case mcp.types.CreateMessageRequest():
47-
await self.on_create_message(message.request.root)
49+
await self.on_create_message(message.request.root) # type: ignore[union-attr]
4850

4951
# notifications
5052
case mcp.types.ServerNotification():

src/fastmcp/client/roots.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ def create_roots_callback(
3434
handler: RootsList | RootsHandler,
3535
) -> ListRootsFnT:
3636
if isinstance(handler, list):
37-
return _create_roots_callback_from_roots(handler)
37+
# TODO(ty): remove when ty supports isinstance union narrowing
38+
return _create_roots_callback_from_roots(handler) # type: ignore[arg-type]
3839
elif inspect.isfunction(handler):
3940
return _create_roots_callback_from_fn(handler)
4041
else:

src/fastmcp/client/tasks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def _handle_status_notification(self, status: GetTaskResult) -> None:
201201
result = callback(status)
202202
if inspect.isawaitable(result):
203203
# Fire and forget async callbacks
204-
asyncio.create_task(result) # noqa: RUF006
204+
asyncio.create_task(result) # type: ignore[arg-type] # noqa: RUF006
205205
except Exception as e:
206206
logger.warning(f"Task callback error: {e}", exc_info=True)
207207

src/fastmcp/client/transports.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,8 @@ async def connect(
375375
env=self.env,
376376
cwd=self.cwd,
377377
log_file=self.log_file,
378-
session_kwargs=session_kwargs,
378+
# TODO(ty): remove when ty supports Unpack[TypedDict] inference
379+
session_kwargs=session_kwargs, # type: ignore[arg-type]
379380
ready_event=self._ready_event,
380381
stop_event=self._stop_event,
381382
session_future=session_future,

src/fastmcp/experimental/sampling/handlers/openai.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def _select_model_from_preferences(
164164
) -> ChatModel:
165165
for model_option in self._iter_models_from_preferences(model_preferences):
166166
if model_option in get_args(ChatModel):
167-
chosen_model: ChatModel = model_option # pyright: ignore[reportAssignmentType]
167+
chosen_model: ChatModel = model_option # type: ignore[assignment]
168168
return chosen_model
169169

170170
return self.default_model

src/fastmcp/prompts/prompt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ def from_function(
206206
fn = fn.__call__
207207
# if the fn is a staticmethod, we need to work with the underlying function
208208
if isinstance(fn, staticmethod):
209-
fn = fn.__func__
209+
fn = fn.__func__ # type: ignore[assignment]
210210

211211
# Validate that task=True requires async functions (after unwrapping)
212212
if task and not inspect.iscoroutinefunction(fn):

src/fastmcp/server/auth/auth.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,13 @@ def get_middleware(self) -> list:
189189
Returns:
190190
List of Starlette Middleware instances to apply to the HTTP app
191191
"""
192+
# TODO(ty): remove type ignores when ty supports Starlette Middleware typing
192193
return [
193194
Middleware(
194-
AuthenticationMiddleware,
195+
AuthenticationMiddleware, # type: ignore[arg-type]
195196
backend=BearerAuthBackend(self),
196197
),
197-
Middleware(AuthContextMiddleware),
198+
Middleware(AuthContextMiddleware), # type: ignore[arg-type]
198199
]
199200

200201
def _get_resource_url(self, path: str | None = None) -> AnyHttpUrl | None:

0 commit comments

Comments
 (0)