mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-26 23:44:17 +02:00
Clean up ty diagnostics in fastmcp_slim for SDK v2 migration
This commit is contained in:
parent
9e2fef8254
commit
962578ed52
11 changed files with 77 additions and 38 deletions
|
|
@ -1217,7 +1217,7 @@ async def _list_tools(mcp_url: str) -> list[dict[str, Any]]:
|
|||
return []
|
||||
|
||||
try:
|
||||
async with streamable_http_client(mcp_url) as (read, write, _): # noqa: SIM117
|
||||
async with streamable_http_client(mcp_url) as (read, write): # noqa: SIM117
|
||||
async with ClientSession(read, write) as session:
|
||||
await session.initialize()
|
||||
result = await session.list_tools()
|
||||
|
|
@ -1232,15 +1232,14 @@ async def _read_mcp_resource(mcp_url: str, uri: str) -> str | None:
|
|||
try:
|
||||
from mcp import ClientSession
|
||||
from mcp.client.streamable_http import streamable_http_client
|
||||
from pydantic import AnyUrl
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
try:
|
||||
async with streamable_http_client(mcp_url) as (read, write, _): # noqa: SIM117
|
||||
async with streamable_http_client(mcp_url) as (read, write): # noqa: SIM117
|
||||
async with ClientSession(read, write) as session:
|
||||
await session.initialize()
|
||||
result = await session.read_resource(AnyUrl(uri))
|
||||
result = await session.read_resource(uri)
|
||||
for content in result.contents:
|
||||
text = getattr(content, "text", None)
|
||||
if text:
|
||||
|
|
|
|||
|
|
@ -862,17 +862,25 @@ class Client(
|
|||
message: str | None = None,
|
||||
) -> None:
|
||||
"""Send a progress notification."""
|
||||
await self.session.send_progress_notification(
|
||||
# Deprecated upstream in SDK v2 but deliberately kept per compat directive;
|
||||
# removed with the multi-round-trip follow-up.
|
||||
await self.session.send_progress_notification( # ty: ignore[deprecated]
|
||||
progress_token, progress, total, message
|
||||
)
|
||||
|
||||
async def set_logging_level(self, level: mcp_types.LoggingLevel) -> None:
|
||||
"""Send a logging/setLevel request."""
|
||||
await self._await_with_session_monitoring(self.session.set_logging_level(level))
|
||||
# Deprecated upstream in SDK v2 but deliberately kept per compat directive;
|
||||
# removed with the multi-round-trip follow-up.
|
||||
await self._await_with_session_monitoring(
|
||||
self.session.set_logging_level(level) # ty: ignore[deprecated]
|
||||
)
|
||||
|
||||
async def send_roots_list_changed(self) -> None:
|
||||
"""Send a roots/list_changed notification."""
|
||||
await self.session.send_roots_list_changed()
|
||||
# Deprecated upstream in SDK v2 but deliberately kept per compat directive;
|
||||
# removed with the multi-round-trip follow-up.
|
||||
await self.session.send_roots_list_changed() # ty: ignore[deprecated]
|
||||
|
||||
# --- Completion ---
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from typing import Any, Generic, TypeAlias
|
|||
|
||||
import mcp_types
|
||||
from mcp import ClientSession
|
||||
from mcp.client.session import ElicitationFnT
|
||||
from mcp.client.session import ClientRequestContext, ElicitationFnT
|
||||
from mcp_types import ElicitRequestFormParams, ElicitRequestParams
|
||||
from mcp_types import ElicitResult as MCPElicitResult
|
||||
from pydantic_core import to_jsonable_python
|
||||
|
|
@ -39,7 +39,7 @@ def create_elicitation_callback(
|
|||
elicitation_handler: ElicitationHandler,
|
||||
) -> ElicitationFnT:
|
||||
async def _elicitation_handler(
|
||||
context: RequestContext[ClientSession, LifespanContextT],
|
||||
context: ClientRequestContext,
|
||||
params: ElicitRequestParams,
|
||||
) -> MCPElicitResult | mcp_types.ErrorData:
|
||||
try:
|
||||
|
|
@ -53,8 +53,14 @@ def create_elicitation_callback(
|
|||
# URL-based elicitation doesn't have a schema
|
||||
response_type = None
|
||||
|
||||
# The public ElicitationHandler alias is typed against the
|
||||
# subscriptable RequestContext shim; the runtime object is the SDK's
|
||||
# ClientRequestContext, passed through opaquely.
|
||||
result = await elicitation_handler(
|
||||
params.message, response_type, params, context
|
||||
params.message,
|
||||
response_type,
|
||||
params,
|
||||
context, # ty: ignore[invalid-argument-type]
|
||||
)
|
||||
# if the user returns data, we assume they've accepted the elicitation
|
||||
if not isinstance(result, ElicitResult):
|
||||
|
|
|
|||
|
|
@ -164,18 +164,19 @@ class ClientPromptsMixin:
|
|||
|
||||
# If meta provided, use send_request for SEP-1686 task support
|
||||
if propagated_meta:
|
||||
task_dict = propagated_meta.get("modelcontextprotocol.io/task")
|
||||
# SDK v2: GetPromptRequestParams has no `task` field, so prompt
|
||||
# gets cannot be submitted as background tasks over the wire and
|
||||
# always graceful-degrade to immediate execution (sdk-feedback #3).
|
||||
request = mcp_types.GetPromptRequest(
|
||||
params=mcp_types.GetPromptRequestParams(
|
||||
name=name,
|
||||
arguments=serialized_arguments,
|
||||
task=mcp_types.TaskMetadata(**task_dict) if task_dict else None,
|
||||
_meta=request_meta, # type: ignore[unknown-argument] # pydantic alias
|
||||
)
|
||||
)
|
||||
result = await self._await_with_session_monitoring(
|
||||
self.session.send_request(
|
||||
request=request, # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
|
||||
request=request, # type: ignore[arg-type]
|
||||
result_type=mcp_types.GetPromptResult,
|
||||
)
|
||||
)
|
||||
|
|
@ -301,11 +302,14 @@ class ClientPromptsMixin:
|
|||
"utf-8"
|
||||
)
|
||||
|
||||
# SDK v2: GetPromptRequestParams has no `task` field, so this request
|
||||
# cannot carry task metadata over the wire and the server graceful-
|
||||
# degrades to immediate execution (sdk-feedback #3). `ttl` is retained on
|
||||
# the public API but has no wire representation here.
|
||||
request = mcp_types.GetPromptRequest(
|
||||
params=mcp_types.GetPromptRequestParams(
|
||||
name=name,
|
||||
arguments=serialized_arguments,
|
||||
task=mcp_types.TaskMetadata(ttl=ttl),
|
||||
_meta=request_meta, # type: ignore[unknown-argument] # pydantic alias
|
||||
)
|
||||
)
|
||||
|
|
@ -313,7 +317,7 @@ class ClientPromptsMixin:
|
|||
# Server returns CreateTaskResult (task accepted) or GetPromptResult (graceful degradation)
|
||||
wrapped_result = await self._await_with_session_monitoring(
|
||||
self.session.send_request(
|
||||
request=request, # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
|
||||
request=request, # type: ignore[arg-type]
|
||||
result_type=PromptTaskResponseUnion,
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -232,18 +232,19 @@ class ClientResourcesMixin:
|
|||
|
||||
# If meta provided, use send_request for SEP-1686 task support
|
||||
if propagated_meta:
|
||||
task_dict = propagated_meta.get("modelcontextprotocol.io/task")
|
||||
# SDK v2: ReadResourceRequestParams.uri is a plain string.
|
||||
# SDK v2: ReadResourceRequestParams has no `task` field, so
|
||||
# resource reads cannot be submitted as background tasks over the
|
||||
# wire and always graceful-degrade to immediate execution
|
||||
# (sdk-feedback #3). The uri is a plain string on the wire.
|
||||
request = mcp_types.ReadResourceRequest(
|
||||
params=mcp_types.ReadResourceRequestParams(
|
||||
uri=uri_str,
|
||||
task=mcp_types.TaskMetadata(**task_dict) if task_dict else None,
|
||||
_meta=request_meta, # type: ignore[unknown-argument] # pydantic alias
|
||||
)
|
||||
)
|
||||
result = await self._await_with_session_monitoring(
|
||||
self.session.send_request(
|
||||
request=request, # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
|
||||
request=request, # type: ignore[arg-type]
|
||||
result_type=mcp_types.ReadResourceResult,
|
||||
)
|
||||
)
|
||||
|
|
@ -363,10 +364,13 @@ class ClientResourcesMixin:
|
|||
# are stored under the AnyUrl-normalized form, so normalize to match.
|
||||
uri_str = str(AnyUrl(uri)) if isinstance(uri, str) else str(uri)
|
||||
|
||||
# SDK v2: ReadResourceRequestParams has no `task` field, so this request
|
||||
# cannot carry task metadata over the wire and the server graceful-
|
||||
# degrades to immediate execution (sdk-feedback #3). `ttl` is retained on
|
||||
# the public API but has no wire representation here.
|
||||
request = mcp_types.ReadResourceRequest(
|
||||
params=mcp_types.ReadResourceRequestParams(
|
||||
uri=uri_str,
|
||||
task=mcp_types.TaskMetadata(ttl=ttl),
|
||||
_meta=request_meta, # type: ignore[unknown-argument] # pydantic alias
|
||||
)
|
||||
)
|
||||
|
|
@ -374,7 +378,7 @@ class ClientResourcesMixin:
|
|||
# Server returns CreateTaskResult (task accepted) or ReadResourceResult (graceful degradation)
|
||||
wrapped_result = await self._await_with_session_monitoring(
|
||||
self.session.send_request(
|
||||
request=request, # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
|
||||
request=request, # type: ignore[arg-type]
|
||||
result_type=ResourceTaskResponseUnion,
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ class ClientTaskManagementMixin:
|
|||
request = GetTaskRequest(params=GetTaskRequestParams(task_id=task_id))
|
||||
return await self._await_with_session_monitoring(
|
||||
self.session.send_request(
|
||||
request=request, # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
|
||||
request=request, # type: ignore[arg-type]
|
||||
result_type=GetTaskResult,
|
||||
)
|
||||
)
|
||||
|
|
@ -94,7 +94,7 @@ class ClientTaskManagementMixin:
|
|||
# Return raw result - Task classes handle type-specific parsing
|
||||
result = await self._await_with_session_monitoring(
|
||||
self.session.send_request(
|
||||
request=request, # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
|
||||
request=request, # type: ignore[arg-type]
|
||||
result_type=_RawTaskPayloadResult,
|
||||
)
|
||||
)
|
||||
|
|
@ -132,7 +132,7 @@ class ClientTaskManagementMixin:
|
|||
request = ListTasksRequest(params=params)
|
||||
server_response = await self._await_with_session_monitoring(
|
||||
self.session.send_request(
|
||||
request=request, # type: ignore[invalid-argument-type] # ty:ignore[invalid-argument-type]
|
||||
request=request, # type: ignore[invalid-argument-type]
|
||||
result_type=mcp_types.ListTasksResult,
|
||||
)
|
||||
)
|
||||
|
|
@ -172,7 +172,7 @@ class ClientTaskManagementMixin:
|
|||
request = CancelTaskRequest(params=CancelTaskRequestParams(task_id=task_id))
|
||||
return await self._await_with_session_monitoring(
|
||||
self.session.send_request(
|
||||
request=request, # type: ignore[invalid-argument-type] # ty:ignore[invalid-argument-type]
|
||||
request=request, # type: ignore[invalid-argument-type]
|
||||
result_type=mcp_types.CancelTaskResult,
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from typing import TypeAlias, cast
|
|||
import mcp_types
|
||||
import pydantic
|
||||
from mcp import ClientSession
|
||||
from mcp.client.session import ListRootsFnT
|
||||
from mcp.client.session import ClientRequestContext, ListRootsFnT
|
||||
|
||||
from fastmcp.client._sdk_context_shim import LifespanContextT, RequestContext
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ def _create_roots_callback_from_roots(
|
|||
roots = convert_roots_list(roots)
|
||||
|
||||
async def _roots_callback(
|
||||
context: RequestContext[ClientSession, LifespanContextT],
|
||||
context: ClientRequestContext,
|
||||
) -> mcp_types.ListRootsResult:
|
||||
return mcp_types.ListRootsResult(roots=roots)
|
||||
|
||||
|
|
@ -61,10 +61,13 @@ def _create_roots_callback_from_fn(
|
|||
| Callable[[RequestContext[ClientSession, LifespanContextT]], Awaitable[RootsList]],
|
||||
) -> ListRootsFnT:
|
||||
async def _roots_callback(
|
||||
context: RequestContext[ClientSession, LifespanContextT],
|
||||
context: ClientRequestContext,
|
||||
) -> mcp_types.ListRootsResult | mcp_types.ErrorData:
|
||||
try:
|
||||
roots = fn(context)
|
||||
# The public RootsHandler alias is typed against the subscriptable
|
||||
# RequestContext shim; the runtime object is the SDK's
|
||||
# ClientRequestContext, passed through opaquely.
|
||||
roots = fn(context) # ty: ignore[invalid-argument-type]
|
||||
if inspect.isawaitable(roots):
|
||||
roots = await roots
|
||||
return mcp_types.ListRootsResult(
|
||||
|
|
|
|||
|
|
@ -947,7 +947,7 @@ class OAuthProxy(OAuthProvider, ConsentMixin):
|
|||
self._resource_url,
|
||||
)
|
||||
raise AuthorizeError(
|
||||
error="invalid_target", # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
|
||||
error="invalid_target", # type: ignore[arg-type]
|
||||
error_description="Resource does not match this server",
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -803,7 +803,9 @@ class Context:
|
|||
|
||||
async def list_roots(self) -> list[Root]:
|
||||
"""List the roots available to the server, as indicated by the client."""
|
||||
result = await self.session.list_roots()
|
||||
# Deprecated upstream in SDK v2 but deliberately kept per compat directive;
|
||||
# removed with the multi-round-trip follow-up.
|
||||
result = await self.session.list_roots() # ty: ignore[deprecated]
|
||||
return result.roots
|
||||
|
||||
async def send_notification(
|
||||
|
|
@ -1501,7 +1503,9 @@ async def _log_to_server_and_client(
|
|||
extra=data.extra,
|
||||
)
|
||||
|
||||
await session.send_log_message(
|
||||
# Deprecated upstream in SDK v2 but deliberately kept per compat directive;
|
||||
# removed with the multi-round-trip follow-up.
|
||||
await session.send_log_message( # ty: ignore[deprecated]
|
||||
level=level,
|
||||
data=data,
|
||||
logger=logger_name,
|
||||
|
|
|
|||
|
|
@ -230,7 +230,10 @@ async def call_sampling_handler(
|
|||
tools=sdk_tools,
|
||||
tool_choice=tool_choice,
|
||||
),
|
||||
context.request_context,
|
||||
# SamplingHandler is typed against the SDK's RequestContext placeholder,
|
||||
# but FastMCP hands handlers its own FastMCPRequestContext wrapper at
|
||||
# runtime; the two aren't structurally related in the type system.
|
||||
context.request_context, # ty: ignore[invalid-argument-type]
|
||||
)
|
||||
|
||||
if inspect.isawaitable(result):
|
||||
|
|
@ -555,7 +558,9 @@ async def sample_step_impl(
|
|||
tool_choice=effective_tool_choice,
|
||||
)
|
||||
else:
|
||||
response = await context.session.create_message(
|
||||
# Deprecated upstream in SDK v2 but deliberately kept per compat
|
||||
# directive; removed with the multi-round-trip follow-up.
|
||||
response = await context.session.create_message( # ty: ignore[deprecated]
|
||||
messages=current_messages,
|
||||
system_prompt=system_prompt,
|
||||
temperature=temperature,
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ class FastMCP(
|
|||
self._started: asyncio.Event = asyncio.Event()
|
||||
|
||||
# Generate random ID if no name provided
|
||||
self._mcp_server: LowLevelServer[LifespanResultT, Any] = LowLevelServer[
|
||||
self._mcp_server: LowLevelServer[LifespanResultT] = LowLevelServer[
|
||||
LifespanResultT
|
||||
](
|
||||
fastmcp=self,
|
||||
|
|
@ -1260,7 +1260,9 @@ class FastMCP(
|
|||
message=mcp_types.CallToolRequestParams(
|
||||
name=name,
|
||||
arguments=arguments or {},
|
||||
_meta=_version_request_meta(version), # type: ignore[unknown-argument] # pydantic alias
|
||||
# `_meta` carries the app-level `fastmcp` version key, which the
|
||||
# reserved-key RequestParamsMeta TypedDict can't express statically.
|
||||
_meta=_version_request_meta(version), # type: ignore[unknown-argument] # ty: ignore[invalid-argument-type]
|
||||
),
|
||||
source="client",
|
||||
type="request",
|
||||
|
|
@ -1428,7 +1430,9 @@ class FastMCP(
|
|||
mw_context = MiddlewareContext(
|
||||
message=mcp_types.ReadResourceRequestParams(
|
||||
uri=str(uri),
|
||||
_meta=_version_request_meta(version), # type: ignore[unknown-argument] # pydantic alias
|
||||
# `_meta` carries the app-level `fastmcp` version key, which the
|
||||
# reserved-key RequestParamsMeta TypedDict can't express statically.
|
||||
_meta=_version_request_meta(version), # type: ignore[unknown-argument] # ty: ignore[invalid-argument-type]
|
||||
),
|
||||
source="client",
|
||||
type="request",
|
||||
|
|
@ -1605,7 +1609,9 @@ class FastMCP(
|
|||
message=mcp_types.GetPromptRequestParams(
|
||||
name=name,
|
||||
arguments=arguments,
|
||||
_meta=_version_request_meta(version), # type: ignore[unknown-argument] # pydantic alias
|
||||
# `_meta` carries the app-level `fastmcp` version key, which the
|
||||
# reserved-key RequestParamsMeta TypedDict can't express statically.
|
||||
_meta=_version_request_meta(version), # type: ignore[unknown-argument] # ty: ignore[invalid-argument-type]
|
||||
),
|
||||
source="client",
|
||||
type="request",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue