more functions out of the Big Tool registers

This commit is contained in:
lda
2026-05-13 16:09:28 +07:00 Verified
parent bead0aadfd
commit c1a25c7eb3
10 changed files with 490 additions and 130 deletions
+3
View File
@@ -0,0 +1,3 @@
from .handlers import BrokerAdminHandlers, ProxyAdminRuntime, TransparentAdminHandlers
__all__ = ["BrokerAdminHandlers", "ProxyAdminRuntime", "TransparentAdminHandlers"]
@@ -0,0 +1,11 @@
from __future__ import annotations
from .broker import BrokerAdminHandlers
from .runtime import ProxyAdminRuntime
from .transparent import TransparentAdminHandlers
__all__ = [
"BrokerAdminHandlers",
"ProxyAdminRuntime",
"TransparentAdminHandlers",
]
+107
View File
@@ -0,0 +1,107 @@
from dataclasses import asdict
from typing import Any
from wf_mcp.broker.service import WfMcpService
from wf_mcp.shared.errors import error_payload
class BrokerAdminHandlers:
"""Shared implementation for service-backed broker admin operations."""
def __init__(self, service: WfMcpService) -> None:
self.service = service
def list_connections(self) -> list[dict[str, Any]]:
return [
asdict(connection)
for connection in sorted(
self.service.connections.list_all(),
key=lambda connection: connection.id,
)
]
def get_connection_statuses(self) -> list[dict[str, Any]]:
return self.service.connection_statuses()
async def refresh_connection_catalog(self, connection_id: str) -> dict[str, Any]:
try:
await self.service.refresh_connection_catalog(connection_id)
except Exception as exc:
return {
"connection_id": connection_id,
"refreshed": False,
**error_payload(exc),
}
snapshot = self.service.get_connection_snapshot(connection_id)
if snapshot is None:
return {"connection_id": connection_id, "refreshed": False}
return {
"connection_id": connection_id,
"refreshed": True,
"node_count": len(snapshot.nodes),
"resource_count": len(snapshot.resources),
"prompt_count": len(snapshot.prompts),
}
def get_catalog(self) -> dict[str, Any]:
return self.service.get_catalog().as_payload()
def get_planner_catalog(self) -> dict[str, Any]:
return self.service.get_planner_catalog().as_payload()
def list_spec_sources(self) -> list[dict[str, Any]]:
return self.service.list_spec_sources()
async def read_broker_resource(self, qualified_name: str) -> dict[str, Any]:
return await self.service.read_resource(qualified_name)
async def render_broker_prompt(
self,
qualified_name: str,
arguments: dict[str, str] | None = None,
) -> dict[str, Any]:
return await self.service.render_prompt(qualified_name, arguments=arguments)
async def invoke_broker_method(
self,
connection_id: str,
method: str,
params: dict[str, Any] | None = None,
) -> dict[str, Any]:
try:
return await self.service.invoke_method(connection_id, method, params=params)
except Exception as exc:
return {
"connection_id": connection_id,
"method": method,
"ok": False,
**error_payload(exc),
}
async def call_broker_tool(
self,
connection_id: str,
tool_name: str,
arguments: dict[str, Any] | None = None,
) -> dict[str, Any]:
try:
return {
"connection_id": connection_id,
"tool_name": tool_name,
"ok": True,
**await self.service.call_tool(
connection_id,
tool_name,
arguments=arguments,
),
}
except Exception as exc:
return {
"connection_id": connection_id,
"tool_name": tool_name,
"ok": False,
**error_payload(exc),
}
def get_broker_events(self) -> list[dict[str, Any]]:
return [asdict(event) for event in self.service.list_events()]
@@ -0,0 +1,36 @@
from typing import Any, Protocol
class ConfigManager(Protocol):
"""Config mutation methods used by transparent admin handlers."""
def get_payload(self) -> dict[str, Any]: ...
def add_connection(
self,
*,
connection_id: str,
server: str,
account: str,
metadata: dict[str, Any] | None = None,
enabled: bool = True,
) -> dict[str, Any]: ...
def update_connection(
self,
*,
connection_id: str,
server: str | None = None,
account: str | None = None,
metadata: dict[str, Any] | None = None,
enabled: bool | None = None,
) -> dict[str, Any]: ...
def set_connection_enabled(
self,
connection_id: str,
*,
enabled: bool,
) -> dict[str, Any]: ...
def remove_connection(self, connection_id: str) -> dict[str, Any]: ...
@@ -0,0 +1,28 @@
from typing import Any, Protocol
from wf_mcp.admin_surface.handlers.config import ConfigManager
from wf_mcp.models import BrokerConfig
class ProxyAdminRuntime(Protocol):
"""Runtime boundary needed by transparent proxy admin handlers."""
@property
def manager(self) -> ConfigManager | None: ...
def current_config(self) -> BrokerConfig: ...
def require_manager(self) -> ConfigManager: ...
def reload(self) -> dict[str, Any]: ...
async def list_proxy_tools_page(
self,
*,
connection_id: str | None = None,
query: str | None = None,
limit: int = 50,
cursor: str | None = None,
) -> dict[str, Any]: ...
async def get_proxy_tool(self, proxy_name: str) -> dict[str, Any]: ...
@@ -0,0 +1,111 @@
from dataclasses import asdict
from typing import Any
from .runtime import ProxyAdminRuntime
class TransparentAdminHandlers:
"""Shared implementation for transparent-proxy config/admin operations."""
def __init__(self, runtime: ProxyAdminRuntime) -> None:
self.runtime = runtime
def list_connections(self) -> list[dict[str, Any]]:
return [
asdict(connection)
for connection in sorted(
self.runtime.current_config().connections,
key=lambda connection: connection.id,
)
]
def get_connection_statuses(self) -> list[dict[str, Any]]:
return [
{
"connection_id": connection.id,
"server": connection.server,
"account": connection.account,
"enabled": connection.enabled,
"transport": connection.metadata.get("transport"),
}
for connection in sorted(
self.runtime.current_config().connections,
key=lambda connection: connection.id,
)
]
def get_config(self) -> dict[str, Any]:
if self.runtime.manager is not None:
return self.runtime.manager.get_payload()
config = self.runtime.current_config()
return {
"store_root": str(config.store_root),
"connections": [asdict(connection) for connection in config.connections],
}
def reload_config(self) -> dict[str, Any]:
return self.runtime.reload()
async def list_proxy_tools(
self,
connection_id: str | None = None,
query: str | None = None,
limit: int = 50,
cursor: str | None = None,
) -> dict[str, Any]:
return await self.runtime.list_proxy_tools_page(
connection_id=connection_id,
query=query,
limit=limit,
cursor=cursor,
)
async def get_proxy_tool(self, proxy_name: str) -> dict[str, Any]:
return await self.runtime.get_proxy_tool(proxy_name)
def add_connection(
self,
connection_id: str,
server: str,
account: str,
metadata: dict[str, Any] | None = None,
enabled: bool = True,
) -> dict[str, Any]:
return self.runtime.require_manager().add_connection(
connection_id=connection_id,
server=server,
account=account,
metadata=metadata,
enabled=enabled,
)
def update_connection(
self,
connection_id: str,
server: str | None = None,
account: str | None = None,
metadata: dict[str, Any] | None = None,
enabled: bool | None = None,
) -> dict[str, Any]:
return self.runtime.require_manager().update_connection(
connection_id=connection_id,
server=server,
account=account,
metadata=metadata,
enabled=enabled,
)
def enable_connection(self, connection_id: str) -> dict[str, Any]:
return self.runtime.require_manager().set_connection_enabled(
connection_id,
enabled=True,
)
def disable_connection(self, connection_id: str) -> dict[str, Any]:
return self.runtime.require_manager().set_connection_enabled(
connection_id,
enabled=False,
)
def remove_connection(self, connection_id: str) -> dict[str, Any]:
return self.runtime.require_manager().remove_connection(connection_id)
+17 -61
View File
@@ -1,77 +1,54 @@
from __future__ import annotations
from dataclasses import asdict
from typing import Any
from mcp.server.fastmcp import FastMCP
from ..shared.errors import error_payload
from ..admin_surface import BrokerAdminHandlers
from .service import WfMcpService
def register_broker_tools(server: FastMCP, service: WfMcpService) -> None:
"""Register broker tool handlers on a FastMCP server."""
handlers = BrokerAdminHandlers(service)
# These MCP tool names are compatibility exports. Their capability metadata
# belongs to the wf.admin source; future admin-enabled servers can project
# dotted wf.admin.* names from that source.
@server.tool()
async def list_connections() -> list[dict[str, Any]]:
return [
asdict(connection)
for connection in sorted(
service.connections.list_all(),
key=lambda connection: connection.id,
)
]
return handlers.list_connections()
@server.tool()
async def get_connection_statuses() -> list[dict[str, Any]]:
return service.connection_statuses()
return handlers.get_connection_statuses()
@server.tool()
async def refresh_connection_catalog(connection_id: str) -> dict[str, Any]:
try:
await service.refresh_connection_catalog(connection_id)
except Exception as exc:
return {
"connection_id": connection_id,
"refreshed": False,
**error_payload(exc),
}
snapshot = service.get_connection_snapshot(connection_id)
if snapshot is None:
return {"connection_id": connection_id, "refreshed": False}
return {
"connection_id": connection_id,
"refreshed": True,
"node_count": len(snapshot.nodes),
"resource_count": len(snapshot.resources),
"prompt_count": len(snapshot.prompts),
}
return await handlers.refresh_connection_catalog(connection_id)
@server.tool()
async def get_catalog() -> dict[str, Any]:
return service.get_catalog().as_payload()
return handlers.get_catalog()
@server.tool()
async def get_planner_catalog() -> dict[str, Any]:
return service.get_planner_catalog().as_payload()
return handlers.get_planner_catalog()
@server.tool()
async def list_spec_sources() -> list[dict[str, Any]]:
return service.list_spec_sources()
return handlers.list_spec_sources()
@server.tool()
async def read_broker_resource(qualified_name: str) -> dict[str, Any]:
return await service.read_resource(qualified_name)
return await handlers.read_broker_resource(qualified_name)
@server.tool()
async def render_broker_prompt(
qualified_name: str,
arguments: dict[str, str] | None = None,
) -> dict[str, Any]:
return await service.render_prompt(qualified_name, arguments=arguments)
return await handlers.render_broker_prompt(qualified_name, arguments=arguments)
@server.tool()
async def invoke_broker_method(
@@ -79,15 +56,7 @@ def register_broker_tools(server: FastMCP, service: WfMcpService) -> None:
method: str,
params: dict[str, Any] | None = None,
) -> dict[str, Any]:
try:
return await service.invoke_method(connection_id, method, params=params)
except Exception as exc:
return {
"connection_id": connection_id,
"method": method,
"ok": False,
**error_payload(exc),
}
return await handlers.invoke_broker_method(connection_id, method, params=params)
@server.tool()
async def call_broker_tool(
@@ -95,25 +64,12 @@ def register_broker_tools(server: FastMCP, service: WfMcpService) -> None:
tool_name: str,
arguments: dict[str, Any] | None = None,
) -> dict[str, Any]:
try:
return {
"connection_id": connection_id,
"tool_name": tool_name,
"ok": True,
**await service.call_tool(
connection_id,
tool_name,
arguments=arguments,
),
}
except Exception as exc:
return {
"connection_id": connection_id,
"tool_name": tool_name,
"ok": False,
**error_payload(exc),
}
return await handlers.call_broker_tool(
connection_id,
tool_name,
arguments=arguments,
)
@server.tool()
async def get_broker_events() -> list[dict[str, Any]]:
return [asdict(event) for event in service.list_events()]
return handlers.get_broker_events()
+14 -66
View File
@@ -1,33 +1,10 @@
from __future__ import annotations
from dataclasses import asdict
from typing import Any, Protocol
from typing import Any
from fastmcp import FastMCP
from ..control import BrokerConfigManager
from ..models import BrokerConfig
class ProxyAdminRuntime(Protocol):
manager: BrokerConfigManager | None
def current_config(self) -> BrokerConfig: ...
def require_manager(self) -> BrokerConfigManager: ...
def reload(self) -> dict[str, Any]: ...
async def list_proxy_tools_page(
self,
*,
connection_id: str | None = None,
query: str | None = None,
limit: int = 50,
cursor: str | None = None,
) -> dict[str, Any]: ...
async def get_proxy_tool(self, proxy_name: str) -> dict[str, Any]: ...
from ..admin_surface import ProxyAdminRuntime, TransparentAdminHandlers
def create_proxy_admin_server(
@@ -38,46 +15,23 @@ def create_proxy_admin_server(
"wf-mcp-admin",
instructions="Administrative tools for this wf-mcp proxy instance.",
)
handlers = TransparentAdminHandlers(runtime)
@admin.tool()
async def list_connections() -> list[dict[str, Any]]:
return [
asdict(connection)
for connection in sorted(
runtime.current_config().connections,
key=lambda connection: connection.id,
)
]
return handlers.list_connections()
@admin.tool()
async def get_connection_statuses() -> list[dict[str, Any]]:
return [
{
"connection_id": connection.id,
"server": connection.server,
"account": connection.account,
"enabled": connection.enabled,
"transport": connection.metadata.get("transport"),
}
for connection in sorted(
runtime.current_config().connections,
key=lambda connection: connection.id,
)
]
return handlers.get_connection_statuses()
@admin.tool()
async def get_config() -> dict[str, Any]:
if runtime.manager is not None:
return runtime.manager.get_payload()
config = runtime.current_config()
return {
"store_root": str(config.store_root),
"connections": [asdict(connection) for connection in config.connections],
}
return handlers.get_config()
@admin.tool()
async def reload_config() -> dict[str, Any]:
return runtime.reload()
return handlers.reload_config()
@admin.tool()
async def list_proxy_tools(
@@ -86,7 +40,7 @@ def create_proxy_admin_server(
limit: int = 50,
cursor: str | None = None,
) -> dict[str, Any]:
return await runtime.list_proxy_tools_page(
return await handlers.list_proxy_tools(
connection_id=connection_id,
query=query,
limit=limit,
@@ -95,7 +49,7 @@ def create_proxy_admin_server(
@admin.tool()
async def get_proxy_tool(proxy_name: str) -> dict[str, Any]:
return await runtime.get_proxy_tool(proxy_name)
return await handlers.get_proxy_tool(proxy_name)
@admin.tool()
async def add_connection(
@@ -105,7 +59,7 @@ def create_proxy_admin_server(
metadata: dict[str, Any] | None = None,
enabled: bool = True,
) -> dict[str, Any]:
return runtime.require_manager().add_connection(
return handlers.add_connection(
connection_id=connection_id,
server=server,
account=account,
@@ -121,7 +75,7 @@ def create_proxy_admin_server(
metadata: dict[str, Any] | None = None,
enabled: bool | None = None,
) -> dict[str, Any]:
return runtime.require_manager().update_connection(
return handlers.update_connection(
connection_id=connection_id,
server=server,
account=account,
@@ -131,20 +85,14 @@ def create_proxy_admin_server(
@admin.tool()
async def enable_connection(connection_id: str) -> dict[str, Any]:
return runtime.require_manager().set_connection_enabled(
connection_id,
enabled=True,
)
return handlers.enable_connection(connection_id)
@admin.tool()
async def disable_connection(connection_id: str) -> dict[str, Any]:
return runtime.require_manager().set_connection_enabled(
connection_id,
enabled=False,
)
return handlers.disable_connection(connection_id)
@admin.tool()
async def remove_connection(connection_id: str) -> dict[str, Any]:
return runtime.require_manager().remove_connection(connection_id)
return handlers.remove_connection(connection_id)
return admin