refactor: move mcp sdk protocols to wf_sources_mcp

This commit is contained in:
lda
2026-06-07 01:30:40 +07:00 Verified
parent 9bef0b0f63
commit 1385f935ca
21 changed files with 904 additions and 116 deletions
+1 -2
View File
@@ -9,11 +9,10 @@ from mcp.types import METHOD_NOT_FOUND
from wf_authoring import NodeSpec
from wf_sources_mcp.catalog import DiscoveredPrompt, DiscoveredResource, DiscoveredTool
from wf_sources_mcp.sdk import BackendAdapter, ToolExecutor
from ..auth import AuthRecord
from ..models import ConnectionConfig
from ..runtime import ToolExecutor
from ..sdk import BackendAdapter
from ..shared import root_exception
from ..workflow import wrap_discovered_tool
from .events import McpEvent
+2 -1
View File
@@ -2,8 +2,9 @@ from __future__ import annotations
from collections.abc import Mapping
from wf_sources_mcp.sdk import BackendAdapter
from ...models import ConnectionConfig
from ...sdk import BackendAdapter
def require_adapter(
+1 -2
View File
@@ -26,6 +26,7 @@ from wf_sources_mcp.catalog import (
CatalogPromptEntry,
CatalogResourceEntry,
)
from wf_sources_mcp.sdk import BackendAdapter, ToolExecutor
from wf_sources_mcp.source_registry import SourceRegistryStore
from wf_sources_mcp.storage import AuthStore, CatalogStore, Store
@@ -36,8 +37,6 @@ from ...models import (
CatalogSnapshot,
ConnectionConfig,
)
from ...runtime import ToolExecutor
from ...sdk import BackendAdapter
from ..admin_capabilities import admin_source
from ..catalog import CombinedCatalog
from .builtins import builtin_sources
+1 -1
View File
@@ -23,6 +23,7 @@ from wf_sources_mcp.catalog import (
CatalogPromptEntry,
CatalogResourceEntry,
)
from wf_sources_mcp.sdk import ToolExecutor
from wf_sources_mcp.storage import CatalogStore
from ...connections import ConnectionConfig, qualify_node_name
@@ -30,7 +31,6 @@ from ...events import McpEvent, make_event
from ...models import (
CatalogSnapshot,
)
from ...runtime import ToolExecutor
from ...workflow.wrappers import _model_from_schema
from ..catalog import CombinedCatalog, snapshot_from_specs
from .specs import get_qualified_spec, qualify_spec
@@ -24,11 +24,10 @@ from wf_mcp.broker.discovery import (
)
from wf_mcp.events import McpEvent, make_event
from wf_mcp.models import ConnectionConfig
from wf_mcp.runtime import ToolExecutor
from wf_mcp.sdk import BackendAdapter
from wf_mcp.shared.errors import error_payload
from wf_sources_mcp.auth import AuthRecord, connection_auth_diagnostic
from wf_sources_mcp.catalog.models import CatalogSnapshot
from wf_sources_mcp.sdk import BackendAdapter, ToolExecutor
from wf_sources_mcp.storage import AuthStore, CatalogStore
from .adapters import require_adapter
+2 -1
View File
@@ -6,9 +6,10 @@ from dataclasses import asdict, dataclass, field
from inspect import isawaitable
from typing import Any, cast
from wf_sources_mcp.sdk import ToolCallResult
from ..auth import AuthRecord
from ..models import ConnectionConfig
from ..sdk import ToolCallResult
from .session import PersistentMcpSession
SessionFactory = Callable[
+9 -21
View File
@@ -1,24 +1,12 @@
"""Compatibility shim for MCP runtime execution protocol.
Canonical implementation lives in `wf_sources_mcp.sdk`.
"""
from __future__ import annotations
from typing import Any, Protocol
from wf_sources_mcp.sdk import ToolExecutor
from ..auth import AuthRecord
from ..models import ConnectionConfig
from ..sdk import ToolCallResult
class ToolExecutor(Protocol):
"""Runtime boundary for executing MCP tools from workflow nodes.
Discovery can stay one-shot, but workflow execution needs this smaller
protocol so a future persistent runtime pool can replace the current
adapter without changing generated NodeSpecs.
"""
async def call_tool(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
tool_name: str,
payload: dict[str, Any],
) -> ToolCallResult: ...
__all__ = [
"ToolExecutor",
]
+2 -1
View File
@@ -7,9 +7,10 @@ from typing import Any
from mcp.client.session import ClientSession
from mcp.types import CallToolResult
from wf_sources_mcp.sdk import ToolCallResult
from ..auth import AuthRecord
from ..models import ConnectionConfig
from ..sdk import ToolCallResult
from ..sdk.converters import tool_result_to_call_result
RawToolCaller = Callable[[str, dict[str, Any]], Awaitable[CallToolResult]]
+2 -1
View File
@@ -1,4 +1,5 @@
from wf_sources_mcp.sdk import BackendAdapter, ToolCallResult
from .adapter import McpSdkAdapter
from .base import BackendAdapter, ToolCallResult
__all__ = ["BackendAdapter", "McpSdkAdapter", "ToolCallResult"]
+1 -1
View File
@@ -19,9 +19,9 @@ from pydantic import AnyUrl
from wf_sources_mcp.auth import AuthRecord, mcp_auth_env, mcp_auth_headers
from wf_sources_mcp.catalog import DiscoveredPrompt, DiscoveredResource, DiscoveredTool
from wf_sources_mcp.sdk import BackendAdapter, ToolCallResult
from ..models import ConnectionConfig
from .base import BackendAdapter, ToolCallResult
from .converters import (
prompt_to_discovered,
resource_to_discovered,
+10 -78
View File
@@ -1,81 +1,13 @@
"""Compatibility shim for MCP upstream SDK protocol/result types.
Canonical implementation lives in `wf_sources_mcp.sdk`.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any, Protocol
from wf_sources_mcp.sdk import BackendAdapter, ToolCallResult
from wf_sources_mcp.catalog import DiscoveredPrompt, DiscoveredResource, DiscoveredTool
from ..auth import AuthRecord
from ..models import ConnectionConfig
@dataclass(slots=True)
class ToolCallResult:
outcome: str
output: dict[str, Any] = field(default_factory=dict)
meta: dict[str, Any] = field(default_factory=dict)
class BackendAdapter(Protocol):
async def list_tools(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
) -> list[DiscoveredTool]: ...
async def list_resources(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
) -> list[DiscoveredResource]: ...
async def list_prompts(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
) -> list[DiscoveredPrompt]: ...
async def get_connection_metadata(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
) -> dict[str, Any]: ...
async def read_resource(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
uri: str,
) -> dict[str, Any]: ...
async def get_prompt(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
prompt_name: str,
arguments: dict[str, str] | None = None,
) -> dict[str, Any]: ...
async def invoke_method(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
method: str,
params: dict[str, Any] | None = None,
) -> dict[str, Any]: ...
async def send_notification(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
method: str,
params: dict[str, Any] | None = None,
) -> None: ...
async def call_tool(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
tool_name: str,
payload: dict[str, Any],
) -> ToolCallResult: ...
__all__ = [
"BackendAdapter",
"ToolCallResult",
]
+1 -2
View File
@@ -8,8 +8,7 @@ from mcp.types import Resource as McpResource
from mcp.types import Tool as McpTool
from wf_sources_mcp.catalog import DiscoveredPrompt, DiscoveredResource, DiscoveredTool
from .base import ToolCallResult
from wf_sources_mcp.sdk import ToolCallResult
def tool_to_discovered(tool: McpTool) -> DiscoveredTool:
+1 -1
View File
@@ -10,10 +10,10 @@ from wf_authoring import NodeReturn, NodeSpec
from wf_core import RuntimeContext
from wf_mcp.broker.events import McpEvent, make_event
from wf_sources_mcp.catalog import DiscoveredTool
from wf_sources_mcp.sdk import ToolExecutor
from ..auth import AuthRecord
from ..models import ConnectionConfig
from ..runtime import ToolExecutor
_JSON_TYPE_MAP: dict[str, object] = {
"string": str,
+9
View File
@@ -0,0 +1,9 @@
from __future__ import annotations
from .protocols import BackendAdapter, ToolCallResult, ToolExecutor
__all__ = [
"BackendAdapter",
"ToolCallResult",
"ToolExecutor",
]
+112
View File
@@ -0,0 +1,112 @@
"""Protocol/result contracts for MCP upstream source providers.
The temporary `wf_mcp.broker.models.ConnectionConfig` dependency remains until
broker runtime connection DTOs move to a neutral/source-provider package.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, Protocol
from wf_sources_mcp.auth import AuthRecord
from wf_sources_mcp.catalog import DiscoveredPrompt, DiscoveredResource, DiscoveredTool
if TYPE_CHECKING:
from wf_mcp.broker.models import ConnectionConfig
@dataclass(slots=True)
class ToolCallResult:
outcome: str
output: dict[str, Any] = field(default_factory=dict)
meta: dict[str, Any] = field(default_factory=dict)
class BackendAdapter(Protocol):
async def list_tools(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
) -> list[DiscoveredTool]: ...
async def list_resources(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
) -> list[DiscoveredResource]: ...
async def list_prompts(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
) -> list[DiscoveredPrompt]: ...
async def get_connection_metadata(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
) -> dict[str, Any]: ...
async def read_resource(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
uri: str,
) -> dict[str, Any]: ...
async def get_prompt(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
prompt_name: str,
arguments: dict[str, str] | None = None,
) -> dict[str, Any]: ...
async def invoke_method(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
method: str,
params: dict[str, Any] | None = None,
) -> dict[str, Any]: ...
async def send_notification(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
method: str,
params: dict[str, Any] | None = None,
) -> None: ...
async def call_tool(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
tool_name: str,
payload: dict[str, Any],
) -> ToolCallResult: ...
class ToolExecutor(Protocol):
"""Runtime boundary for executing MCP tools from workflow nodes.
Discovery can stay one-shot, but workflow execution needs this smaller
protocol so persistent runtime pools can replace one-shot adapters without
changing generated NodeSpecs.
"""
async def call_tool(
self,
connection: ConnectionConfig,
auth: AuthRecord | None,
tool_name: str,
payload: dict[str, Any],
) -> ToolCallResult: ...
__all__ = [
"BackendAdapter",
"ToolCallResult",
"ToolExecutor",
]