chore: tighten mcp source typing

This commit is contained in:
lda
2026-06-09 07:03:36 +07:00 Verified
parent 19d65be77c
commit 5ebe1451c7
19 changed files with 103 additions and 43 deletions
+6 -1
View File
@@ -59,4 +59,9 @@ def require_adapter(
return adapter
__all__ = ["AdapterLookupRef", "LegacyAdapterRef", "SourceAdapterRef", "require_adapter"]
__all__ = [
"AdapterLookupRef",
"LegacyAdapterRef",
"SourceAdapterRef",
"require_adapter",
]
+25 -5
View File
@@ -8,9 +8,11 @@ from mcp.types import (
CallToolResult,
ClientNotification,
ClientRequest,
GetPromptResult,
ListPromptsResult,
ListResourcesResult,
ListToolsResult,
ReadResourceResult,
)
from pydantic import AnyUrl
@@ -21,33 +23,41 @@ if TYPE_CHECKING:
DiscoveredPrompt,
DiscoveredResource,
DiscoveredTool,
)
)
from wf_sources_mcp.sdk.protocols import ToolCallResult
class McpClientSession(Protocol):
"""Subset of MCP SDK ClientSession operations used by source clients."""
"""Subset of MCP SDK ClientSession operations used by source clients.
This protocol targets the low-level MCP SDK ``ClientSession`` shape, not
``fastmcp.client.Client``. FastMCP exposes higher-level convenience methods
with different return types; if we use it here later, wrap it in an adapter
instead of pretending it satisfies this session protocol.
"""
# ClientSession stuff. we dont even use their Pagination system...
async def list_tools(self) -> ListToolsResult: ...
async def list_resources(self) -> ListResourcesResult: ...
async def list_prompts(self) -> ListPromptsResult: ...
async def read_resource(self, uri: AnyUrl) -> Any: ...
async def read_resource(self, uri: AnyUrl) -> ReadResourceResult: ...
async def get_prompt(
self,
name: str,
arguments: dict[str, str] | None = None,
/,
) -> Any: ...
) -> GetPromptResult: ...
# BaseSession stuff. not even complete signature, thats crazy
async def send_request(
self,
request: ClientRequest,
result_type: type[ClientResult],
) -> Any: ...
) -> ClientResult: ...
async def send_notification(self, notification: ClientNotification) -> None: ...
@@ -59,6 +69,16 @@ class McpClientSession(Protocol):
) -> CallToolResult: ...
if TYPE_CHECKING:
from mcp.client.session import ClientSession as SdkClientSession
def _typecheck_sdk_client_session(
session: SdkClientSession,
) -> McpClientSession:
"""Static-only guard: MCP SDK ClientSession must satisfy our subset."""
return session
@dataclass(slots=True)
class McpSourceClient:
"""Operation facade over an initialized MCP SDK ClientSession.
+2 -6
View File
@@ -131,9 +131,7 @@ def _transport_from_connection_metadata(
args=tuple(str(arg) for arg in cast("tuple[object, ...]", args_raw)),
env={
str(key): str(value)
for key, value in cast(
"dict[str, object]", env_raw
).items()
for key, value in cast("dict[str, object]", env_raw).items()
},
cwd=(
str(connection.metadata["cwd"])
@@ -148,9 +146,7 @@ def _transport_from_connection_metadata(
url=url if isinstance(url, str) else str(url), # type: ignore[arg-type]
headers={
str(key): str(value)
for key, value in cast(
"dict[str, object]", headers_raw
).items()
for key, value in cast("dict[str, object]", headers_raw).items()
},
)
raise ValueError(
+3 -1
View File
@@ -36,7 +36,9 @@ async def discover_connection_capabilities(
resources = await _list_optional_capabilities(
lambda: adapter.list_resources(connection, auth)
)
prompts = await _list_optional_capabilities(lambda: adapter.list_prompts(connection, auth))
prompts = await _list_optional_capabilities(
lambda: adapter.list_prompts(connection, auth)
)
metadata = await adapter.get_connection_metadata(connection, auth)
return DiscoveredConnectionCapabilities(
tools=tools,