refactor: move mcp tool wrappers to wf_sources_mcp

This commit is contained in:
lda
2026-06-08 10:13:50 +07:00 Verified
parent 1d46752bf8
commit 42a4412c95
9 changed files with 922 additions and 79 deletions
+5
View File
@@ -69,6 +69,7 @@ __all__ = [
"tool_call_started_event",
"ToolWrapperEvent",
"ToolWrapperEventSink",
"wrap_discovered_tool",
"workflow_mcp_source_to_connection_config",
]
@@ -113,6 +114,10 @@ def __getattr__(name: str) -> object:
from . import schema_models
return schema_models.model_from_schema
if name == "wrap_discovered_tool":
from . import tool_wrappers
return tool_wrappers.wrap_discovered_tool
if name in {
"ToolWrapperEvent",
"ToolWrapperEventSink",
+83
View File
@@ -0,0 +1,83 @@
from __future__ import annotations
from pydantic import BaseModel
from wf_authoring import NodeReturn, NodeSpec
from wf_core import RuntimeContext
from wf_sources_mcp.auth import AuthRecord
from wf_sources_mcp.catalog import DiscoveredTool
from wf_sources_mcp.connections import McpSourceConnection
from wf_sources_mcp.schema_models import model_from_schema
from wf_sources_mcp.sdk import ToolExecutor
from wf_sources_mcp.tool_events import (
ToolWrapperEventSink,
tool_call_completed_event,
tool_call_started_event,
)
def wrap_discovered_tool(
*,
connection: McpSourceConnection,
auth: AuthRecord | None,
executor: ToolExecutor,
tool: DiscoveredTool,
emit_event: ToolWrapperEventSink | None = None,
) -> NodeSpec[BaseModel, BaseModel]:
input_model = model_from_schema(
f"{connection.id}_{tool.name}_Input",
tool.input_schema,
)
output_model = model_from_schema(
f"{connection.id}_{tool.name}_Output",
tool.output_schema,
)
async def invoke_tool(
payload: BaseModel,
ctx: RuntimeContext,
) -> NodeReturn[BaseModel]:
if emit_event is not None:
emit_event(
tool_call_started_event(
connection_id=connection.id,
capability_id=f"{connection.id}.{tool.name}",
input_payload=payload.model_dump(exclude_unset=True),
)
)
result = await executor.call_tool(
connection=connection,
auth=auth,
tool_name=tool.name,
# Pydantic fills absent optional fields with None, but strict MCP
# servers such as Playwright distinguish omitted from explicit null.
payload=payload.model_dump(exclude_unset=True),
)
if emit_event is not None:
emit_event(
tool_call_completed_event(
connection_id=connection.id,
capability_id=f"{connection.id}.{tool.name}",
outcome=result.outcome,
meta=result.meta,
)
)
return NodeReturn(
outcome=result.outcome,
output=output_model.model_validate(result.output),
)
return NodeSpec(
name=tool.name,
input_model=input_model,
output_model=output_model,
outcomes=tool.outcomes,
fn=invoke_tool,
description=tool.description,
is_async=True,
input_schema_contract=tool.input_schema,
output_schema_contract=tool.output_schema,
)
__all__ = ["wrap_discovered_tool"]