refactor: route mcp runtime operations through client facade
This commit is contained in:
@@ -5,7 +5,8 @@ from typing import Any
|
||||
|
||||
import pytest
|
||||
from mcp.client.session import ClientSession
|
||||
from mcp.types import CallToolResult, TextContent
|
||||
from mcp.types import CallToolResult as RawCallToolResult
|
||||
from mcp.types import TextContent
|
||||
|
||||
from wf_sources_mcp.auth import AuthRecord
|
||||
from wf_sources_mcp.connections import McpSourceConnection
|
||||
@@ -15,6 +16,7 @@ from wf_sources_mcp.runtime import (
|
||||
connection_runtime_fingerprint,
|
||||
)
|
||||
from wf_sources_mcp.runtime.factory import PersistentSessionFactory
|
||||
from wf_sources_mcp.sdk import ToolCallResult
|
||||
from wf_sources_mcp.transports import StdioSourceTransport
|
||||
|
||||
|
||||
@@ -28,14 +30,11 @@ def _connection() -> McpSourceConnection:
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_persistent_session_call_callback_normalizes_tool_result() -> None:
|
||||
async def call_tool(tool_name: str, payload: dict[str, Any]) -> CallToolResult:
|
||||
async def test_persistent_session_call_callback_returns_canonical_result() -> None:
|
||||
async def call_tool(tool_name: str, payload: dict[str, Any]) -> ToolCallResult:
|
||||
assert tool_name == "echo"
|
||||
assert payload == {"text": "hi"}
|
||||
return CallToolResult(
|
||||
content=[TextContent(type="text", text="ok")],
|
||||
structuredContent={"echoed": "hi"},
|
||||
)
|
||||
return ToolCallResult(outcome="ok", output={"echoed": "hi"})
|
||||
|
||||
session = PersistentMcpSession(
|
||||
connection=_connection(),
|
||||
@@ -60,32 +59,30 @@ async def test_persistent_session_raises_without_transport() -> None:
|
||||
class _FakeFactory(PersistentSessionFactory):
|
||||
def __init__(self) -> None:
|
||||
self.calls: list[tuple[str, dict[str, object]]] = []
|
||||
self.closed = False
|
||||
self.created_connections: list[McpSourceConnection] = []
|
||||
|
||||
async def _call_tool(
|
||||
self, tool_name: str, payload: dict[str, object]
|
||||
) -> CallToolResult:
|
||||
) -> RawCallToolResult:
|
||||
self.calls.append((tool_name, payload))
|
||||
return CallToolResult(
|
||||
return RawCallToolResult(
|
||||
content=[TextContent(type="text", text="ok")],
|
||||
structuredContent={"echoed": payload["text"]},
|
||||
)
|
||||
|
||||
async def _close(self) -> None:
|
||||
self.closed = True
|
||||
|
||||
async def _create_with_stack(
|
||||
self,
|
||||
stack: AsyncExitStack,
|
||||
connection: McpSourceConnection,
|
||||
auth: AuthRecord | None,
|
||||
) -> ClientSession:
|
||||
self.created_connections.append(connection)
|
||||
factory = self
|
||||
|
||||
class _FakeClient:
|
||||
async def call_tool(
|
||||
self, tool_name: str, payload: dict[str, object]
|
||||
) -> CallToolResult:
|
||||
) -> RawCallToolResult:
|
||||
return await factory._call_tool(tool_name, payload)
|
||||
|
||||
return _FakeClient() # type: ignore[return-value]
|
||||
@@ -94,7 +91,8 @@ class _FakeFactory(PersistentSessionFactory):
|
||||
@pytest.mark.asyncio
|
||||
async def test_persistent_session_factory_serializes_tool_calls() -> None:
|
||||
factory = _FakeFactory()
|
||||
session = await factory.create(_connection(), None)
|
||||
connection = _connection()
|
||||
session = await factory.create(connection, None)
|
||||
|
||||
first = await session.call_tool("echo", {"text": "one"})
|
||||
second = await session.call_tool("echo", {"text": "two"})
|
||||
@@ -102,6 +100,7 @@ async def test_persistent_session_factory_serializes_tool_calls() -> None:
|
||||
|
||||
assert first.output == {"echoed": "one"}
|
||||
assert second.output == {"echoed": "two"}
|
||||
assert factory.created_connections == [connection]
|
||||
assert factory.calls == [
|
||||
("echo", {"text": "one"}),
|
||||
("echo", {"text": "two"}),
|
||||
@@ -117,11 +116,8 @@ async def test_runtime_pool_reuses_unchanged_connection() -> None:
|
||||
) -> PersistentMcpSession:
|
||||
created.append(connection)
|
||||
|
||||
async def _call(tool_name: str, payload: dict[str, Any]) -> CallToolResult:
|
||||
return CallToolResult(
|
||||
content=[TextContent(type="text", text="ok")],
|
||||
structuredContent={"echoed": payload["text"]},
|
||||
)
|
||||
async def _call(tool_name: str, payload: dict[str, Any]) -> ToolCallResult:
|
||||
return ToolCallResult(outcome="ok", output={"echoed": payload["text"]})
|
||||
|
||||
return PersistentMcpSession(
|
||||
connection=connection,
|
||||
@@ -150,3 +146,17 @@ def test_runtime_fingerprint_changes_when_transport_changes() -> None:
|
||||
assert connection_runtime_fingerprint(original) != connection_runtime_fingerprint(
|
||||
changed
|
||||
)
|
||||
|
||||
|
||||
def test_persistent_session_public_runtime_is_tool_call_only() -> None:
|
||||
public_operations = {
|
||||
name
|
||||
for name in dir(PersistentMcpSession)
|
||||
if not name.startswith("_") and callable(getattr(PersistentMcpSession, name))
|
||||
}
|
||||
|
||||
assert "call_tool" in public_operations
|
||||
assert "read_resource" not in public_operations
|
||||
assert "get_prompt" not in public_operations
|
||||
assert "invoke_method" not in public_operations
|
||||
assert "send_notification" not in public_operations
|
||||
|
||||
Reference in New Issue
Block a user