goodbye modes 2 working on it

This commit is contained in:
lda
2026-05-16 19:02:14 +07:00 Verified
parent 5369fa1bae
commit dbf7c0b2a9
12 changed files with 324 additions and 200 deletions
@@ -1,10 +1,10 @@
# Retire Legacy MCP Modes Plan # Retire Legacy MCP Modes Plan
> **Status:** current execution plan for the next cleanup pass. > **Status:** completed retirement pass; kept as the decision record.
## Goal ## Goal
Make **unified mode** the only public MCP server mode exposed by `wf-mcp`. Make one public MCP server surface exposed by `wf-mcp`.
`broker` mode and `proxy` mode were useful while the unified surface was being `broker` mode and `proxy` mode were useful while the unified surface was being
built, but keeping all three as public launch modes now creates the wrong mental built, but keeping all three as public launch modes now creates the wrong mental
@@ -12,7 +12,7 @@ model:
- broker mode suggests local workflow/admin tools are a separate product surface - broker mode suggests local workflow/admin tools are a separate product surface
- proxy mode suggests upstream projection is a separate product surface - proxy mode suggests upstream projection is a separate product surface
- unified mode is already the real target: one server that exposes local - the combined server is already the real target: one server that exposes local
capabilities and proxied upstream capabilities together capabilities and proxied upstream capabilities together
The public product should have one server mode. The implementation may still The public product should have one server mode. The implementation may still
@@ -62,7 +62,7 @@ Where historical explanation is useful, call them legacy migration surfaces.
Delete or rewrite tests whose only purpose is to prove the old public mode split. Delete or rewrite tests whose only purpose is to prove the old public mode split.
Keep behavior tests for the underlying capabilities when those behaviors still Keep behavior tests for the underlying capabilities when those behaviors still
exist through unified mode. exist through the public server.
## What Stays ## What Stays
@@ -82,7 +82,7 @@ source of confusion.
### Shared services ### Shared services
Keep the service/config/store/runtime objects that unified mode already uses. Keep the service/config/store/runtime objects that the public server already uses.
This pass is about removing duplicate **entrypoints**, not rewriting the This pass is about removing duplicate **entrypoints**, not rewriting the
underlying architecture. underlying architecture.
@@ -104,8 +104,8 @@ The cleanup should reduce surfaces, not churn the capability vocabulary.
2. Remove or privatize old broker/proxy server launch functions that only exist 2. Remove or privatize old broker/proxy server launch functions that only exist
for the retired public modes. for the retired public modes.
3. Collapse docs/help text that still describe three user-facing modes. 3. Collapse docs/help text that still describe three user-facing modes.
4. Keep implementation reuse through the existing unified server path. 4. Keep implementation reuse through the existing server path.
5. Update tests so they assert unified behavior directly instead of branching on 5. Update tests so they assert server behavior directly instead of branching on
legacy mode names. legacy mode names.
## Non-Goals For This Pass ## Non-Goals For This Pass
@@ -123,13 +123,13 @@ Those topics already have separate docs and should stay separate.
- `wf-mcp serve` has one public MCP server behavior. - `wf-mcp serve` has one public MCP server behavior.
- No public docs imply that broker/proxy are still supported product modes. - No public docs imply that broker/proxy are still supported product modes.
- Unified mode continues to expose: - The server continues to expose:
- proxied upstream capabilities - proxied upstream capabilities
- stable local workflow tools - stable local workflow tools
- optional admin tools - optional admin tools
- The test suite passes with the old public mode split removed. - The test suite passes with the old public mode split removed.
- Remaining roadmap docs still point to the real unresolved work instead of - Remaining roadmap docs still point to the real unresolved work instead of
making the reader rediscover why unified mode exists. making the reader rediscover why the combined server exists.
## Follow-On Work After This Pass ## Follow-On Work After This Pass
+10 -7
View File
@@ -194,17 +194,19 @@ The code now has the first capability-source layer in place.
- `wf.mcp` owns workflow MCP runtime node specs, currently - `wf.mcp` owns workflow MCP runtime node specs, currently
`wf.mcp.call_tool`. `wf.mcp.call_tool`.
- `wf.admin` owns privileged admin capability metadata and is not planner-visible - `wf.admin` owns privileged admin capability metadata and is not planner-visible
or MCP-client-visible by default. by default.
- Transparent proxy admin tools now use dotted `wf.admin.*` names through - Transparent proxy admin tools now use dotted `wf.admin.*` names through
`LdaNamespace`. `LdaNamespace`.
- `wf.admin` and `wf.mcp` are reserved connection ids. - `wf.admin` and `wf.mcp` are reserved connection ids.
- Planner catalog, `list_available_specs()`, and workflow spec resolution respect - Planner catalog, `list_available_specs()`, and workflow spec resolution respect
source `enabled` and `visibility.planner`. source `enabled` and `visibility.planner`.
Broker MCP tools still expose compatibility names such as `list_connections` and Legacy broker MCP tools still expose compatibility names such as
`get_planner_catalog`. Their metadata belongs to `wf.admin`, but dotted `list_connections` and `get_planner_catalog` on the compatibility-only broker
`wf.admin.*` broker tool projection is intentionally deferred until admin MCP server constructor. They now reuse the same service-admin registrar as the
exposure is explicit. public server, with only the visible names changed. The public server projects
service-backed admin tools such as `wf.admin.list_sources` alongside
proxy-backed admin tools under the same `wf.admin.*` namespace.
## Current Code Mapping ## Current Code Mapping
@@ -214,8 +216,9 @@ Current code has several useful pieces but the boundaries are blurred.
| --- | --- | --- | | --- | --- | --- |
| `wf_authoring.ops` | reusable workflow node specs | `wf.std.node_specs` | | `wf_authoring.ops` | reusable workflow node specs | `wf.std.node_specs` |
| `wf_mcp.broker.service.builtins` | local workflow specs | `wf.std`, `wf.mcp` | | `wf_mcp.broker.service.builtins` | local workflow specs | `wf.std`, `wf.mcp` |
| `wf_mcp.broker.tools` | compatibility broker MCP admin/control tools | `wf.admin.tools` | | `wf_mcp.broker.tools` | compatibility wrapper over shared service-admin registration | `wf.admin.tools` |
| `wf_mcp.transparent_proxy.admin` | dotted proxy MCP admin/control tools | `wf.admin.tools` | | `wf_mcp.admin_surface.tools` | shared service-backed admin tool registration | `wf.admin.tools` |
| `wf_mcp.transparent_proxy.admin` | proxy-backed public admin tools | `wf.admin.tools` |
| discovered MCP tools | upstream tools and workflow wrappers | connection source | | discovered MCP tools | upstream tools and workflow wrappers | connection source |
| broker resources/prompts | catalog/status/planning context | likely `wf.admin` or docs sources | | broker resources/prompts | catalog/status/planning context | likely `wf.admin` or docs sources |
+7 -1
View File
@@ -1,3 +1,9 @@
from .handlers import BrokerAdminHandlers, ProxyAdminRuntime, TransparentAdminHandlers from .handlers import BrokerAdminHandlers, ProxyAdminRuntime, TransparentAdminHandlers
from .tools import register_service_admin_tools
__all__ = ["BrokerAdminHandlers", "ProxyAdminRuntime", "TransparentAdminHandlers"] __all__ = [
"BrokerAdminHandlers",
"ProxyAdminRuntime",
"TransparentAdminHandlers",
"register_service_admin_tools",
]
+156
View File
@@ -0,0 +1,156 @@
from __future__ import annotations
from typing import Any
from wf_mcp.broker.service import WfMcpService
from .handlers.broker import BrokerAdminHandlers
def register_service_admin_tools(
server: Any,
service: WfMcpService,
*,
namespace: str | None = "wf.admin",
legacy_names: bool = False,
include_connection_tools: bool = True,
) -> None:
"""Register service-backed admin/control tools on an MCP server.
The public server uses dotted `wf.admin.*` names. The retired broker server
constructor still asks for bare compatibility names, so the namespace stays
configurable while the implementation remains single-sourced.
"""
handlers = BrokerAdminHandlers(service)
legacy_name_map = {
"read_resource": "read_broker_resource",
"render_prompt": "render_broker_prompt",
"invoke_method": "invoke_broker_method",
"call_tool": "call_broker_tool",
"get_events": "get_broker_events",
}
def name(local_name: str) -> str:
visible_name = legacy_name_map.get(local_name, local_name) if legacy_names else local_name
return visible_name if namespace is None else f"{namespace}.{visible_name}"
if include_connection_tools:
@server.tool(
name=name("list_connections"),
title="List Connections",
description="List configured MCP connections known to this server.",
)
async def list_connections() -> list[dict[str, Any]]:
return handlers.list_connections()
@server.tool(
name=name("get_connection_statuses"),
title="Get Connection Statuses",
description="Show configured MCP connection status and catalog counts.",
)
async def get_connection_statuses() -> list[dict[str, Any]]:
return handlers.get_connection_statuses()
@server.tool(
name=name("refresh_connection_catalog"),
title="Refresh Connection Catalog",
description="Refresh one connection catalog snapshot from its upstream MCP server.",
)
async def refresh_connection_catalog(connection_id: str) -> dict[str, Any]:
return await handlers.refresh_connection_catalog(connection_id)
@server.tool(
name=name("get_catalog"),
title="Get Catalog",
description="Return the current upstream MCP capability catalog.",
)
async def get_catalog() -> dict[str, Any]:
return handlers.get_catalog()
@server.tool(
name=name("get_planner_catalog"),
title="Get Planner Catalog",
description="Return the planner catalog including local workflow sources.",
)
async def get_planner_catalog() -> dict[str, Any]:
return handlers.get_planner_catalog()
@server.tool(
name=name("list_spec_sources"),
title="List Spec Sources",
description="List planner-visible sources that currently provide node specs.",
)
async def list_spec_sources() -> list[dict[str, Any]]:
return handlers.list_spec_sources()
@server.tool(
name=name("list_sources"),
title="List Sources",
description="List configured capability sources and what each source owns.",
)
async def list_sources() -> list[dict[str, Any]]:
return handlers.list_sources()
@server.tool(
name=name("read_resource"),
title="Read Resource",
description="Read a broker-catalog resource by qualified name.",
)
async def read_resource(qualified_name: str) -> dict[str, Any]:
return await handlers.read_broker_resource(qualified_name)
@server.tool(
name=name("render_prompt"),
title="Render Prompt",
description="Render a broker-catalog prompt by qualified name.",
)
async def render_prompt(
qualified_name: str,
arguments: dict[str, str] | None = None,
) -> dict[str, Any]:
return await handlers.render_broker_prompt(
qualified_name,
arguments=arguments,
)
@server.tool(
name=name("invoke_method"),
title="Invoke Method",
description="Invoke a raw MCP method on one configured connection.",
)
async def invoke_method(
connection_id: str,
method: str,
params: dict[str, Any] | None = None,
) -> dict[str, Any]:
return await handlers.invoke_broker_method(
connection_id,
method,
params=params,
)
@server.tool(
name=name("call_tool"),
title="Call Tool",
description="Call one upstream MCP tool through the broker service layer.",
)
async def call_tool(
connection_id: str,
tool_name: str,
arguments: dict[str, Any] | None = None,
) -> dict[str, Any]:
return await handlers.call_broker_tool(
connection_id,
tool_name,
arguments=arguments,
)
@server.tool(
name=name("get_events"),
title="Get Events",
description="Return locally recorded broker/platform events.",
)
async def get_events() -> list[dict[str, Any]]:
return handlers.get_broker_events()
+8 -71
View File
@@ -1,79 +1,16 @@
from __future__ import annotations from __future__ import annotations
from typing import Any
from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp import FastMCP
from ..admin_surface import BrokerAdminHandlers from ..admin_surface import register_service_admin_tools
from .service import WfMcpService from .service import WfMcpService
def register_broker_tools(server: FastMCP, service: WfMcpService) -> None: def register_broker_tools(server: FastMCP, service: WfMcpService) -> None:
"""Register broker tool handlers on a FastMCP server.""" """Register legacy bare-name broker tools from the shared admin registrar."""
handlers = BrokerAdminHandlers(service) register_service_admin_tools(
server,
# These MCP tool names are compatibility exports. Their capability metadata service,
# belongs to the wf.admin source; future admin-enabled servers can project namespace=None,
# dotted wf.admin.* names from that source. legacy_names=True,
@server.tool() )
async def list_connections() -> list[dict[str, Any]]:
return handlers.list_connections()
@server.tool()
async def get_connection_statuses() -> list[dict[str, Any]]:
return handlers.get_connection_statuses()
@server.tool()
async def refresh_connection_catalog(connection_id: str) -> dict[str, Any]:
return await handlers.refresh_connection_catalog(connection_id)
@server.tool()
async def get_catalog() -> dict[str, Any]:
return handlers.get_catalog()
@server.tool()
async def get_planner_catalog() -> dict[str, Any]:
return handlers.get_planner_catalog()
@server.tool()
async def list_spec_sources() -> list[dict[str, Any]]:
return handlers.list_spec_sources()
@server.tool()
async def list_sources() -> list[dict[str, Any]]:
return handlers.list_sources()
@server.tool()
async def read_broker_resource(qualified_name: str) -> dict[str, Any]:
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 handlers.render_broker_prompt(qualified_name, arguments=arguments)
@server.tool()
async def invoke_broker_method(
connection_id: str,
method: str,
params: dict[str, Any] | None = None,
) -> dict[str, Any]:
return await handlers.invoke_broker_method(connection_id, method, params=params)
@server.tool()
async def call_broker_tool(
connection_id: str,
tool_name: str,
arguments: dict[str, Any] | None = None,
) -> dict[str, Any]:
return await handlers.call_broker_tool(
connection_id,
tool_name,
arguments=arguments,
)
@server.tool()
async def get_broker_events() -> list[dict[str, Any]]:
return handlers.get_broker_events()
+2 -2
View File
@@ -10,7 +10,7 @@ from .broker import (
build_service_from_config, build_service_from_config,
load_broker_config, load_broker_config,
) )
from .server import run_unified_proxy_server from .server import run_server
def build_parser() -> argparse.ArgumentParser: def build_parser() -> argparse.ArgumentParser:
@@ -115,7 +115,7 @@ def main(argv: list[str] | None = None) -> int:
if args.command == "serve": if args.command == "serve":
config = load_broker_config(args.config) config = load_broker_config(args.config)
run_unified_proxy_server( run_server(
config, config,
args.transport, args.transport,
config_path=args.config, config_path=args.config,
+4 -8
View File
@@ -1,11 +1,7 @@
from .unified import ( from .core import create_server, create_server_client, run_server
create_unified_proxy_client,
create_unified_proxy_server,
run_unified_proxy_server,
)
__all__ = [ __all__ = [
"create_unified_proxy_client", "create_server",
"create_unified_proxy_server", "create_server_client",
"run_unified_proxy_server", "run_server",
] ]
+89
View File
@@ -0,0 +1,89 @@
from __future__ import annotations
from pathlib import Path
from typing import Any
from fastmcp import FastMCP
from fastmcp.client import Client
from fastmcp.client.transports.memory import FastMCPTransport
from ..admin_surface import register_service_admin_tools
from ..broker.config import build_service_from_config
from ..broker.transport import normalize_transport
from ..models import BrokerConfig
from ..transparent_proxy.runtime import ProxyRuntime
from ..workflow_surface import register_workflow_tools
def create_server(
config: BrokerConfig,
*,
config_path: str | Path | None = None,
resources_as_tools: bool = False,
prompts_as_tools: bool = False,
search_tools: bool = False,
admin_tools: bool = True,
) -> FastMCP[Any]:
"""Create the public MCP server with proxy, admin, and workflow tools."""
service = build_service_from_config(config)
runtime = ProxyRuntime(
config,
config_path=config_path,
resources_as_tools=resources_as_tools,
prompts_as_tools=prompts_as_tools,
search_tools=search_tools,
admin_tools=admin_tools,
event_bus=service.event_bus,
)
if admin_tools:
register_service_admin_tools(
runtime.server,
service,
include_connection_tools=False,
)
register_workflow_tools(runtime.server, service)
return runtime.server
def run_server(
config: BrokerConfig,
transport: str = "stdio",
*,
config_path: str | Path | None = None,
resources_as_tools: bool = False,
prompts_as_tools: bool = False,
search_tools: bool = False,
admin_tools: bool = True,
) -> None:
server = create_server(
config,
config_path=config_path,
resources_as_tools=resources_as_tools,
prompts_as_tools=prompts_as_tools,
search_tools=search_tools,
admin_tools=admin_tools,
)
server.run(transport=normalize_transport(transport), show_banner=False)
def create_server_client(
config: BrokerConfig,
*,
config_path: str | Path | None = None,
resources_as_tools: bool = False,
prompts_as_tools: bool = False,
search_tools: bool = False,
admin_tools: bool = True,
) -> Client[FastMCPTransport]:
return Client(
FastMCPTransport(
create_server(
config,
config_path=config_path,
resources_as_tools=resources_as_tools,
prompts_as_tools=prompts_as_tools,
search_tools=search_tools,
admin_tools=admin_tools,
)
)
)
+2 -1
View File
@@ -1,3 +1,4 @@
from .handlers import WorkflowSurfaceHandlers from .handlers import WorkflowSurfaceHandlers
from .tools import register_workflow_tools
__all__ = ["WorkflowSurfaceHandlers"] __all__ = ["WorkflowSurfaceHandlers", "register_workflow_tools"]
@@ -1,92 +1,17 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path
from typing import Any from typing import Any
from fastmcp import FastMCP from fastmcp import FastMCP
from fastmcp.client import Client
from fastmcp.client.transports.memory import FastMCPTransport
from ..broker.config import build_service_from_config from wf_mcp.broker.service import WfMcpService
from ..broker.transport import normalize_transport
from ..models import BrokerConfig from .handlers import WorkflowSurfaceHandlers
from ..transparent_proxy.runtime import ProxyRuntime
from ..workflow_surface import WorkflowSurfaceHandlers
def create_unified_proxy_server( def register_workflow_tools(server: FastMCP[Any], service: WfMcpService) -> None:
config: BrokerConfig, """Register stable workflow tools on the public MCP server surface."""
*, handlers = WorkflowSurfaceHandlers(service)
config_path: str | Path | None = None,
resources_as_tools: bool = False,
prompts_as_tools: bool = False,
search_tools: bool = False,
admin_tools: bool = True,
) -> FastMCP[Any]:
"""Create one MCP server with upstream proxy, admin, and workflow tools."""
service = build_service_from_config(config)
runtime = ProxyRuntime(
config,
config_path=config_path,
resources_as_tools=resources_as_tools,
prompts_as_tools=prompts_as_tools,
search_tools=search_tools,
admin_tools=admin_tools,
event_bus=service.event_bus,
)
_register_workflow_tools(runtime.server, WorkflowSurfaceHandlers(service))
return runtime.server
def run_unified_proxy_server(
config: BrokerConfig,
transport: str = "stdio",
*,
config_path: str | Path | None = None,
resources_as_tools: bool = False,
prompts_as_tools: bool = False,
search_tools: bool = False,
admin_tools: bool = True,
) -> None:
server = create_unified_proxy_server(
config,
config_path=config_path,
resources_as_tools=resources_as_tools,
prompts_as_tools=prompts_as_tools,
search_tools=search_tools,
admin_tools=admin_tools,
)
server.run(transport=normalize_transport(transport), show_banner=False)
def create_unified_proxy_client(
config: BrokerConfig,
*,
config_path: str | Path | None = None,
resources_as_tools: bool = False,
prompts_as_tools: bool = False,
search_tools: bool = False,
admin_tools: bool = True,
) -> Client[FastMCPTransport]:
return Client(
FastMCPTransport(
create_unified_proxy_server(
config,
config_path=config_path,
resources_as_tools=resources_as_tools,
prompts_as_tools=prompts_as_tools,
search_tools=search_tools,
admin_tools=admin_tools,
)
)
)
def _register_workflow_tools(
server: FastMCP[Any],
handlers: WorkflowSurfaceHandlers,
) -> None:
"""Register stable workflow tools on the unified MCP surface."""
@server.tool( @server.tool(
name="wf.workflow.list_artifacts", name="wf.workflow.list_artifacts",
@@ -107,9 +32,7 @@ def _register_workflow_tools(
@server.tool( @server.tool(
name="wf.workflow.create_artifact_from_plan", name="wf.workflow.create_artifact_from_plan",
title="Create Workflow Artifact From Plan", title="Create Workflow Artifact From Plan",
description=( description="Validate a raw workflow plan and save it as a versioned artifact.",
"Validate a raw workflow plan and save it as a versioned artifact."
),
) )
async def create_artifact_from_plan( async def create_artifact_from_plan(
artifact_id: str, artifact_id: str,
@@ -137,10 +60,7 @@ def _register_workflow_tools(
title="Inspect Workflow Artifact", title="Inspect Workflow Artifact",
description="Return the full saved artifact for artifact_id and version.", description="Return the full saved artifact for artifact_id and version.",
) )
async def inspect_artifact( async def inspect_artifact(artifact_id: str, version: int) -> dict[str, Any]:
artifact_id: str,
version: int,
) -> dict[str, Any]:
return await handlers.inspect_artifact( return await handlers.inspect_artifact(
artifact_id=artifact_id, artifact_id=artifact_id,
version=version, version=version,
@@ -165,9 +85,7 @@ def _register_workflow_tools(
@server.tool( @server.tool(
name="wf.workflow.validate_deployment", name="wf.workflow.validate_deployment",
title="Validate Workflow Deployment", title="Validate Workflow Deployment",
description=( description="Check whether a deployment_id can run with currently enabled sources.",
"Check whether a deployment_id can run with currently enabled sources."
),
) )
async def validate_deployment(deployment_id: str) -> dict[str, Any]: async def validate_deployment(deployment_id: str) -> dict[str, Any]:
return await handlers.validate_deployment(deployment_id=deployment_id) return await handlers.validate_deployment(deployment_id=deployment_id)
@@ -5,7 +5,7 @@ import sys
from typing import Any from typing import Any
from wf_mcp.models import BrokerConfig, ConnectionConfig from wf_mcp.models import BrokerConfig, ConnectionConfig
from wf_mcp.server import create_unified_proxy_client from wf_mcp.server import create_server_client
from .test_support import fixture_server_path, local_temp_root from .test_support import fixture_server_path, local_temp_root
@@ -16,7 +16,7 @@ def _structured(result: Any) -> dict[str, Any]:
return content return content
def test_unified_server_exposes_upstream_admin_and_workflow_tools() -> None: def test_server_exposes_upstream_admin_and_workflow_tools() -> None:
config = BrokerConfig( config = BrokerConfig(
store_root=local_temp_root() / "unified_server_store", store_root=local_temp_root() / "unified_server_store",
connections=[ connections=[
@@ -34,12 +34,23 @@ def test_unified_server_exposes_upstream_admin_and_workflow_tools() -> None:
) )
async def run_proxy() -> None: async def run_proxy() -> None:
client = create_unified_proxy_client(config) client = create_server_client(config)
async with client: async with client:
tools = await client.list_tools() tools = await client.list_tools()
names = [tool.name for tool in tools] names = [tool.name for tool in tools]
assert "fixture.personal.echo_tool" in names assert "fixture.personal.echo_tool" in names
assert "wf.admin.list_connections" in names assert "wf.admin.list_connections" in names
assert "wf.admin.get_connection_statuses" in names
assert "wf.admin.refresh_connection_catalog" in names
assert "wf.admin.get_catalog" in names
assert "wf.admin.get_planner_catalog" in names
assert "wf.admin.list_spec_sources" in names
assert "wf.admin.list_sources" in names
assert "wf.admin.read_resource" in names
assert "wf.admin.render_prompt" in names
assert "wf.admin.invoke_method" in names
assert "wf.admin.call_tool" in names
assert "wf.admin.get_events" in names
assert "wf.workflow.list_artifacts" in names assert "wf.workflow.list_artifacts" in names
assert "wf.workflow.run_deployment" in names assert "wf.workflow.run_deployment" in names
@@ -48,14 +59,21 @@ def test_unified_server_exposes_upstream_admin_and_workflow_tools() -> None:
{"text": "hello"}, {"text": "hello"},
) )
artifacts_result = await client.call_tool("wf.workflow.list_artifacts") artifacts_result = await client.call_tool("wf.workflow.list_artifacts")
sources_result = await client.call_tool("wf.admin.list_sources")
assert _structured(echo_result)["echoed"] == "hello" assert _structured(echo_result)["echoed"] == "hello"
assert _structured(artifacts_result)["nodes"] == [] assert _structured(artifacts_result)["nodes"] == []
source_ids = {
source["id"] for source in _structured(sources_result)["result"]
}
assert "wf.admin" in source_ids
assert "wf.mcp" in source_ids
assert "wf.std" in source_ids
asyncio.run(run_proxy()) asyncio.run(run_proxy())
def test_unified_server_can_hide_admin_tools() -> None: def test_server_can_hide_admin_tools() -> None:
config = BrokerConfig( config = BrokerConfig(
store_root=local_temp_root() / "unified_no_admin_store", store_root=local_temp_root() / "unified_no_admin_store",
connections=[ connections=[
@@ -73,7 +91,7 @@ def test_unified_server_can_hide_admin_tools() -> None:
) )
async def run_proxy() -> None: async def run_proxy() -> None:
client = create_unified_proxy_client(config, admin_tools=False) client = create_server_client(config, admin_tools=False)
async with client: async with client:
tools = await client.list_tools() tools = await client.list_tools()
names = [tool.name for tool in tools] names = [tool.name for tool in tools]
@@ -84,14 +102,14 @@ def test_unified_server_can_hide_admin_tools() -> None:
asyncio.run(run_proxy()) asyncio.run(run_proxy())
def test_unified_workflow_tools_have_human_metadata() -> None: def test_workflow_tools_have_human_metadata() -> None:
config = BrokerConfig( config = BrokerConfig(
store_root=local_temp_root() / "unified_metadata_store", store_root=local_temp_root() / "unified_metadata_store",
connections=[], connections=[],
) )
async def run_proxy() -> None: async def run_proxy() -> None:
client = create_unified_proxy_client(config, admin_tools=False) client = create_server_client(config, admin_tools=False)
async with client: async with client:
tools = await client.list_tools() tools = await client.list_tools()
by_name = {tool.name: tool for tool in tools} by_name = {tool.name: tool for tool in tools}
@@ -106,14 +124,14 @@ def test_unified_workflow_tools_have_human_metadata() -> None:
asyncio.run(run_proxy()) asyncio.run(run_proxy())
def test_unified_admin_tools_have_human_metadata() -> None: def test_admin_tools_have_human_metadata() -> None:
config = BrokerConfig( config = BrokerConfig(
store_root=local_temp_root() / "unified_admin_metadata_store", store_root=local_temp_root() / "unified_admin_metadata_store",
connections=[], connections=[],
) )
async def run_proxy() -> None: async def run_proxy() -> None:
client = create_unified_proxy_client(config) client = create_server_client(config)
async with client: async with client:
tools = await client.list_tools() tools = await client.list_tools()
by_name = {tool.name: tool for tool in tools} by_name = {tool.name: tool for tool in tools}
+1 -1
View File
@@ -19,7 +19,7 @@
"id": "playwright.default", "id": "playwright.default",
"server": "playwright", "server": "playwright",
"account": "default", "account": "default",
"enabled": true, "enabled": false,
"metadata": { "metadata": {
"transport": "stdio", "transport": "stdio",
"command": "pnpx", "command": "pnpx",