thing
This commit is contained in:
@@ -246,3 +246,20 @@ names.
|
||||
|
||||
The implementation should avoid having broker mode and transparent proxy mode
|
||||
define separate copies of the same admin/control capabilities.
|
||||
|
||||
## Current Inventory Surfaces
|
||||
|
||||
Two source listings now exist on purpose:
|
||||
|
||||
- `list_spec_sources()`
|
||||
- compatibility/planner view
|
||||
- only returns enabled planner-visible sources with node specs
|
||||
- `list_sources()`
|
||||
- full capability-source inventory
|
||||
- returns every source plus visibility, permissions, counts, and the names
|
||||
owned in each capability bucket
|
||||
|
||||
The broader `list_sources()` view is the one humans and LLM authoring clients
|
||||
should use when deciding what exists. The narrower `list_spec_sources()` view is
|
||||
still useful when the question is only "what can the planner currently place in
|
||||
a graph?"
|
||||
|
||||
@@ -52,6 +52,9 @@ class BrokerAdminHandlers:
|
||||
def list_spec_sources(self) -> list[dict[str, Any]]:
|
||||
return self.service.list_spec_sources()
|
||||
|
||||
def list_sources(self) -> list[dict[str, Any]]:
|
||||
return self.service.list_sources()
|
||||
|
||||
async def read_broker_resource(self, qualified_name: str) -> dict[str, Any]:
|
||||
return await self.service.read_resource(qualified_name)
|
||||
|
||||
|
||||
@@ -63,3 +63,15 @@ class CapabilitySource:
|
||||
"prompt_count": len(self.capabilities.prompts),
|
||||
"resource_count": len(self.capabilities.resources),
|
||||
}
|
||||
|
||||
def as_inventory(self) -> dict[str, Any]:
|
||||
"""Return source metadata plus the capability names it owns."""
|
||||
return {
|
||||
**self.as_status(),
|
||||
"capabilities": {
|
||||
"tools": sorted(self.capabilities.tools),
|
||||
"node_specs": sorted(self.capabilities.node_specs),
|
||||
"prompts": sorted(self.capabilities.prompts),
|
||||
"resources": sorted(self.capabilities.resources),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -250,6 +250,16 @@ class WfMcpService:
|
||||
and source.visibility.planner
|
||||
]
|
||||
|
||||
def list_sources(self) -> list[dict[str, Any]]:
|
||||
"""Return every capability source with the names it currently owns."""
|
||||
return [
|
||||
source.as_inventory()
|
||||
for source in sorted(
|
||||
self.capability_sources.values(),
|
||||
key=lambda source: source.id,
|
||||
)
|
||||
]
|
||||
|
||||
def list_available_specs(self) -> list[CatalogNodeEntry]:
|
||||
"""Return planner-visible node catalog entries from every visible source."""
|
||||
return self.get_planner_catalog().entries()
|
||||
|
||||
@@ -39,6 +39,10 @@ def register_broker_tools(server: FastMCP, service: WfMcpService) -> None:
|
||||
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)
|
||||
|
||||
@@ -74,6 +74,7 @@ def test_create_broker_server_exposes_tools_resources_and_prompts() -> None:
|
||||
assert "get_connection_statuses" in tool_names
|
||||
assert "refresh_connection_catalog" in tool_names
|
||||
assert "get_planner_catalog" in tool_names
|
||||
assert "list_sources" in tool_names
|
||||
assert "list_spec_sources" in tool_names
|
||||
assert "invoke_broker_method" in tool_names
|
||||
assert "call_broker_tool" in tool_names
|
||||
@@ -101,6 +102,15 @@ def test_create_broker_server_exposes_tools_resources_and_prompts() -> None:
|
||||
assert "wf.mcp" in source_ids
|
||||
assert "wf.std" in source_ids
|
||||
|
||||
_content, all_sources_payload_raw = asyncio.run(
|
||||
server.call_tool("list_sources", {})
|
||||
)
|
||||
all_sources_payload = cast(dict[str, Any], cast(object, all_sources_payload_raw))
|
||||
all_sources = all_sources_payload["result"]
|
||||
all_source_ids = {source["id"] for source in all_sources}
|
||||
assert "wf.admin" in all_source_ids
|
||||
assert "demo.personal" in all_source_ids
|
||||
|
||||
|
||||
def test_broker_admin_tools_are_backed_by_wf_admin_source() -> None:
|
||||
service = WfMcpService(store=FileStore(local_temp_root() / "broker_admin_source"))
|
||||
|
||||
@@ -98,6 +98,24 @@ def test_service_installs_builtin_stdlib_specs_by_default() -> None:
|
||||
assert all(source["kind"] == "system" for source in sources)
|
||||
|
||||
|
||||
def test_service_lists_all_capability_sources_with_owned_capability_names() -> None:
|
||||
service = WfMcpService(store=FileStore(local_temp_root() / "source_inventory"))
|
||||
|
||||
sources = service.list_sources()
|
||||
sources_by_id = {source["id"]: source for source in sources}
|
||||
|
||||
std_source = sources_by_id["wf.std"]
|
||||
assert "wf.std.runtime_error" in std_source["capabilities"]["node_specs"]
|
||||
assert std_source["capabilities"]["tools"] == []
|
||||
|
||||
mcp_source = sources_by_id["wf.mcp"]
|
||||
assert mcp_source["capabilities"]["node_specs"] == ["wf.mcp.call_tool"]
|
||||
|
||||
admin_source = sources_by_id["wf.admin"]
|
||||
assert admin_source["visibility"]["planner"] is False
|
||||
assert "wf.admin.list_sources" in admin_source["capabilities"]["tools"]
|
||||
|
||||
|
||||
def test_wf_std_source_contains_authoring_ops() -> None:
|
||||
service = WfMcpService(store=FileStore(local_temp_root() / "stdlib_source_store"))
|
||||
specs = service.capability_sources["wf.std"].capabilities.node_specs
|
||||
|
||||
Reference in New Issue
Block a user