this is finally goodbye. use the new one.
This commit is contained in:
@@ -187,8 +187,8 @@ The code now has the first capability-source layer in place.
|
|||||||
- `CapabilitySource` owns source metadata, visibility, permissions, and
|
- `CapabilitySource` owns source metadata, visibility, permissions, and
|
||||||
capability buckets.
|
capability buckets.
|
||||||
- `WfMcpService.capability_sources` is the canonical in-memory registry.
|
- `WfMcpService.capability_sources` is the canonical in-memory registry.
|
||||||
- `spec_sources` and `specs_by_connection` are compatibility views derived from
|
- Planner node lookup reads `CapabilitySource.capabilities.node_specs`
|
||||||
`capability_sources`.
|
directly; the old `SpecSource` compatibility layer has been removed.
|
||||||
- `wf.std` owns current `wf_authoring.ops` workflow node specs under
|
- `wf.std` owns current `wf_authoring.ops` workflow node specs under
|
||||||
`wf.std.*`.
|
`wf.std.*`.
|
||||||
- `wf.mcp` owns workflow MCP runtime node specs, currently
|
- `wf.mcp` owns workflow MCP runtime node specs, currently
|
||||||
@@ -222,8 +222,7 @@ Current code has several useful pieces but the boundaries are blurred.
|
|||||||
| 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 |
|
||||||
|
|
||||||
`SpecSource` is now a compatibility wrapper. New source behavior should be added
|
New source behavior should be added to `CapabilitySource`.
|
||||||
to `CapabilitySource` unless there is a specific compatibility reason not to.
|
|
||||||
|
|
||||||
## Naming Rules
|
## Naming Rules
|
||||||
|
|
||||||
@@ -250,19 +249,15 @@ names.
|
|||||||
The implementation should avoid having separate backend layers define copies of
|
The implementation should avoid having separate backend layers define copies of
|
||||||
the same admin/control capabilities.
|
the same admin/control capabilities.
|
||||||
|
|
||||||
## Current Inventory Surfaces
|
## Current Inventory Surface
|
||||||
|
|
||||||
Two source listings now exist on purpose:
|
`list_sources()` is the source inventory:
|
||||||
|
|
||||||
- `list_spec_sources()`
|
- returns every source
|
||||||
- compatibility/planner view
|
- includes visibility, permissions, counts, and owned capability names
|
||||||
- only returns enabled planner-visible sources with node specs
|
- lets callers answer planner questions by inspecting
|
||||||
- `list_sources()`
|
`visibility.planner` plus `capabilities.node_specs`
|
||||||
- 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
|
Humans and LLM authoring clients should use that one inventory when deciding
|
||||||
should use when deciding what exists. The narrower `list_spec_sources()` view is
|
what exists. Planner projection remains a different **use** of source metadata,
|
||||||
still useful when the question is only "what can the planner currently place in
|
not a second source model.
|
||||||
a graph?"
|
|
||||||
|
|||||||
@@ -49,9 +49,6 @@ class BrokerAdminHandlers:
|
|||||||
def get_planner_catalog(self) -> dict[str, Any]:
|
def get_planner_catalog(self) -> dict[str, Any]:
|
||||||
return self.service.get_planner_catalog().as_payload()
|
return self.service.get_planner_catalog().as_payload()
|
||||||
|
|
||||||
def list_spec_sources(self) -> list[dict[str, Any]]:
|
|
||||||
return self.service.list_spec_sources()
|
|
||||||
|
|
||||||
def list_sources(self) -> list[dict[str, Any]]:
|
def list_sources(self) -> list[dict[str, Any]]:
|
||||||
return self.service.list_sources()
|
return self.service.list_sources()
|
||||||
|
|
||||||
|
|||||||
@@ -77,14 +77,6 @@ def register_service_admin_tools(
|
|||||||
async def get_planner_catalog() -> dict[str, Any]:
|
async def get_planner_catalog() -> dict[str, Any]:
|
||||||
return handlers.get_planner_catalog()
|
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(
|
@server.tool(
|
||||||
name=name("list_sources"),
|
name=name("list_sources"),
|
||||||
title="List Sources",
|
title="List Sources",
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ _WORKFLOW_AUTHORING_GUIDE = """\
|
|||||||
Build workflows from current capabilities instead of assuming a stale catalog.
|
Build workflows from current capabilities instead of assuming a stale catalog.
|
||||||
|
|
||||||
Use `get_planner_catalog` when you need the current workflow-capability view.
|
Use `get_planner_catalog` when you need the current workflow-capability view.
|
||||||
Use `list_sources` and `list_spec_sources` when you need to understand what is
|
Use `list_sources` when you need to understand source ownership, visibility,
|
||||||
available and which sources are planner-visible.
|
and capability kinds.
|
||||||
Use `call_broker_tool` to test an upstream MCP tool manually before wrapping it
|
Use `call_broker_tool` to test an upstream MCP tool manually before wrapping it
|
||||||
into a workflow.
|
into a workflow.
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,12 @@ from wf_authoring import (
|
|||||||
truthy,
|
truthy,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .sources import SpecSource
|
from .capability_sources import (
|
||||||
|
CapabilityBuckets,
|
||||||
|
CapabilitySource,
|
||||||
|
SourcePermissions,
|
||||||
|
SourceVisibility,
|
||||||
|
)
|
||||||
from .specs import qualify_spec
|
from .specs import qualify_spec
|
||||||
|
|
||||||
BUILTIN_CONNECTION_ID = "wf.std"
|
BUILTIN_CONNECTION_ID = "wf.std"
|
||||||
@@ -121,22 +126,27 @@ def mcp_specs(service: ToolCaller) -> dict[str, NodeSpec[Any, Any]]:
|
|||||||
return {spec.name: spec for spec in qualified_specs}
|
return {spec.name: spec for spec in qualified_specs}
|
||||||
|
|
||||||
|
|
||||||
def builtin_sources(service: ToolCaller) -> dict[str, SpecSource]:
|
def builtin_sources(service: ToolCaller) -> dict[str, CapabilitySource]:
|
||||||
"""Return all broker-local spec sources."""
|
"""Return all broker-local capability sources."""
|
||||||
return {
|
return {
|
||||||
BUILTIN_CONNECTION_ID: SpecSource(
|
BUILTIN_CONNECTION_ID: CapabilitySource(
|
||||||
id=BUILTIN_CONNECTION_ID,
|
id=BUILTIN_CONNECTION_ID,
|
||||||
kind="system",
|
kind="system",
|
||||||
specs=builtin_specs(),
|
capabilities=CapabilityBuckets(node_specs=builtin_specs()),
|
||||||
mcp_client_visible=True,
|
visibility=SourceVisibility(
|
||||||
safe_for_workflow=True,
|
planner=True,
|
||||||
|
mcp_client=True,
|
||||||
|
admin_dashboard=True,
|
||||||
|
),
|
||||||
|
permissions=SourcePermissions(safe_for_workflow=True),
|
||||||
description="Workflow standard-library nodes.",
|
description="Workflow standard-library nodes.",
|
||||||
),
|
),
|
||||||
MCP_SOURCE_ID: SpecSource(
|
MCP_SOURCE_ID: CapabilitySource(
|
||||||
id=MCP_SOURCE_ID,
|
id=MCP_SOURCE_ID,
|
||||||
kind="system",
|
kind="system",
|
||||||
specs=mcp_specs(service),
|
capabilities=CapabilityBuckets(node_specs=mcp_specs(service)),
|
||||||
calls_upstream=True,
|
visibility=SourceVisibility(planner=True, admin_dashboard=True),
|
||||||
|
permissions=SourcePermissions(calls_upstream=True),
|
||||||
description="Broker MCP utility nodes.",
|
description="Broker MCP utility nodes.",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ from .capability_sources import (
|
|||||||
SourcePermissions,
|
SourcePermissions,
|
||||||
SourceVisibility,
|
SourceVisibility,
|
||||||
)
|
)
|
||||||
from .sources import SpecSource
|
|
||||||
from .specs import get_qualified_spec, qualify_spec
|
from .specs import get_qualified_spec, qualify_spec
|
||||||
|
|
||||||
|
|
||||||
@@ -71,41 +70,9 @@ class WfMcpService:
|
|||||||
self.artifact_store = FileWorkflowArtifactStore(_store_root(self.store))
|
self.artifact_store = FileWorkflowArtifactStore(_store_root(self.store))
|
||||||
if self.include_builtin_specs:
|
if self.include_builtin_specs:
|
||||||
for source in builtin_sources(self).values():
|
for source in builtin_sources(self).values():
|
||||||
self.register_spec_source(source)
|
self.register_capability_source(source)
|
||||||
self.register_capability_source(admin_source())
|
self.register_capability_source(admin_source())
|
||||||
|
|
||||||
@property
|
|
||||||
def spec_sources(self) -> dict[str, SpecSource]:
|
|
||||||
"""Compatibility view of node-spec capability sources."""
|
|
||||||
return {
|
|
||||||
source.id: SpecSource(
|
|
||||||
id=source.id,
|
|
||||||
kind=source.kind,
|
|
||||||
specs=dict(source.capabilities.node_specs),
|
|
||||||
visible=source.enabled and source.visibility.planner,
|
|
||||||
mcp_client_visible=source.enabled and source.visibility.mcp_client,
|
|
||||||
admin_dashboard_visible=(
|
|
||||||
source.enabled and source.visibility.admin_dashboard
|
|
||||||
),
|
|
||||||
safe_for_workflow=source.permissions.safe_for_workflow,
|
|
||||||
calls_upstream=source.permissions.calls_upstream,
|
|
||||||
mutates_config=source.permissions.mutates_config,
|
|
||||||
mutates_auth=source.permissions.mutates_auth,
|
|
||||||
description=source.description,
|
|
||||||
)
|
|
||||||
for source in self.capability_sources.values()
|
|
||||||
if source.capabilities.node_specs
|
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def specs_by_connection(self) -> dict[str, dict[str, NodeSpec[Any, Any]]]:
|
|
||||||
"""Compatibility view of source specs keyed by source id."""
|
|
||||||
return {
|
|
||||||
source.id: dict(source.capabilities.node_specs)
|
|
||||||
for source in self.capability_sources.values()
|
|
||||||
if source.capabilities.node_specs
|
|
||||||
}
|
|
||||||
|
|
||||||
def register_connection(self, connection: ConnectionConfig) -> None:
|
def register_connection(self, connection: ConnectionConfig) -> None:
|
||||||
parse_connection_id(connection.id)
|
parse_connection_id(connection.id)
|
||||||
if connection.id in RESERVED_CONNECTION_IDS:
|
if connection.id in RESERVED_CONNECTION_IDS:
|
||||||
@@ -155,14 +122,18 @@ class WfMcpService:
|
|||||||
# Catalog refreshes replace discovered specs, not operator policy.
|
# Catalog refreshes replace discovered specs, not operator policy.
|
||||||
existing_source.capabilities.node_specs = qualified_specs
|
existing_source.capabilities.node_specs = qualified_specs
|
||||||
else:
|
else:
|
||||||
self.register_spec_source(
|
self.register_capability_source(
|
||||||
SpecSource(
|
CapabilitySource(
|
||||||
id=connection_id,
|
id=connection_id,
|
||||||
kind="connection",
|
kind="connection",
|
||||||
specs=qualified_specs,
|
capabilities=CapabilityBuckets(node_specs=qualified_specs),
|
||||||
enabled=self.connections.get(connection_id).enabled,
|
enabled=self.connections.get(connection_id).enabled,
|
||||||
mcp_client_visible=True,
|
visibility=SourceVisibility(
|
||||||
calls_upstream=True,
|
planner=True,
|
||||||
|
mcp_client=True,
|
||||||
|
admin_dashboard=True,
|
||||||
|
),
|
||||||
|
permissions=SourcePermissions(calls_upstream=True),
|
||||||
description=(
|
description=(
|
||||||
f"Specs discovered or registered for {connection_id}."
|
f"Specs discovered or registered for {connection_id}."
|
||||||
),
|
),
|
||||||
@@ -237,19 +208,6 @@ class WfMcpService:
|
|||||||
snapshots[source.id].prompts = list(stored_snapshot.prompts)
|
snapshots[source.id].prompts = list(stored_snapshot.prompts)
|
||||||
return CombinedCatalog(snapshots=snapshots)
|
return CombinedCatalog(snapshots=snapshots)
|
||||||
|
|
||||||
def list_spec_sources(self) -> list[dict[str, Any]]:
|
|
||||||
"""Return planner spec sources without expanding every node schema."""
|
|
||||||
return [
|
|
||||||
source.as_status()
|
|
||||||
for source in sorted(
|
|
||||||
self.capability_sources.values(),
|
|
||||||
key=lambda source: source.id,
|
|
||||||
)
|
|
||||||
if source.capabilities.node_specs
|
|
||||||
and source.enabled
|
|
||||||
and source.visibility.planner
|
|
||||||
]
|
|
||||||
|
|
||||||
def list_sources(self) -> list[dict[str, Any]]:
|
def list_sources(self) -> list[dict[str, Any]]:
|
||||||
"""Return every capability source with the names it currently owns."""
|
"""Return every capability source with the names it currently owns."""
|
||||||
return [
|
return [
|
||||||
@@ -525,7 +483,7 @@ class WfMcpService:
|
|||||||
)
|
)
|
||||||
snapshot = snapshot_from_specs(
|
snapshot = snapshot_from_specs(
|
||||||
connection_id,
|
connection_id,
|
||||||
specs=self.specs_by_connection.get(connection_id, {}),
|
specs=self.capability_sources[connection_id].capabilities.node_specs,
|
||||||
tool_display_names={
|
tool_display_names={
|
||||||
tool.name: tool.title for tool in capabilities.tools
|
tool.name: tool.title for tool in capabilities.tools
|
||||||
},
|
},
|
||||||
@@ -619,10 +577,6 @@ class WfMcpService:
|
|||||||
"""Register a capability source as canonical service state."""
|
"""Register a capability source as canonical service state."""
|
||||||
self.capability_sources[source.id] = source
|
self.capability_sources[source.id] = source
|
||||||
|
|
||||||
def register_spec_source(self, source: SpecSource) -> None:
|
|
||||||
"""Register a legacy spec source through the capability model."""
|
|
||||||
self.register_capability_source(source.as_capability_source())
|
|
||||||
|
|
||||||
def _hydrate_connection_source_from_snapshot(
|
def _hydrate_connection_source_from_snapshot(
|
||||||
self,
|
self,
|
||||||
connection: ConnectionConfig,
|
connection: ConnectionConfig,
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from wf_authoring import NodeSpec
|
|
||||||
|
|
||||||
from .capability_sources import (
|
|
||||||
CapabilityBuckets,
|
|
||||||
CapabilitySource,
|
|
||||||
SourceKind,
|
|
||||||
SourcePermissions,
|
|
||||||
SourceVisibility,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
|
||||||
class SpecSource:
|
|
||||||
"""Compatibility wrapper for planner node specs.
|
|
||||||
|
|
||||||
Visibility and permissions stay explicit so callers do not infer source
|
|
||||||
semantics from the legacy ``kind`` field during the capability-source move.
|
|
||||||
"""
|
|
||||||
|
|
||||||
id: str
|
|
||||||
kind: SourceKind
|
|
||||||
specs: dict[str, NodeSpec[Any, Any]] = field(default_factory=dict)
|
|
||||||
enabled: bool = True
|
|
||||||
visible: bool = True
|
|
||||||
mcp_client_visible: bool = False
|
|
||||||
admin_dashboard_visible: bool = True
|
|
||||||
safe_for_workflow: bool = False
|
|
||||||
calls_upstream: bool = False
|
|
||||||
mutates_config: bool = False
|
|
||||||
mutates_auth: bool = False
|
|
||||||
description: str | None = None
|
|
||||||
|
|
||||||
def as_capability_source(self) -> CapabilitySource:
|
|
||||||
return CapabilitySource(
|
|
||||||
id=self.id,
|
|
||||||
kind=self.kind,
|
|
||||||
capabilities=CapabilityBuckets(node_specs=dict(self.specs)),
|
|
||||||
enabled=self.enabled,
|
|
||||||
visibility=SourceVisibility(
|
|
||||||
planner=self.visible,
|
|
||||||
mcp_client=self.mcp_client_visible,
|
|
||||||
admin_dashboard=self.admin_dashboard_visible,
|
|
||||||
),
|
|
||||||
permissions=SourcePermissions(
|
|
||||||
safe_for_workflow=self.safe_for_workflow,
|
|
||||||
calls_upstream=self.calls_upstream,
|
|
||||||
mutates_config=self.mutates_config,
|
|
||||||
mutates_auth=self.mutates_auth,
|
|
||||||
),
|
|
||||||
description=self.description,
|
|
||||||
)
|
|
||||||
|
|
||||||
def as_status(self) -> dict[str, Any]:
|
|
||||||
return self.as_capability_source().as_status()
|
|
||||||
@@ -75,7 +75,6 @@ def test_create_broker_server_exposes_tools_resources_and_prompts() -> None:
|
|||||||
assert "refresh_connection_catalog" in tool_names
|
assert "refresh_connection_catalog" in tool_names
|
||||||
assert "get_planner_catalog" in tool_names
|
assert "get_planner_catalog" in tool_names
|
||||||
assert "list_sources" in tool_names
|
assert "list_sources" in tool_names
|
||||||
assert "list_spec_sources" in tool_names
|
|
||||||
assert "invoke_broker_method" in tool_names
|
assert "invoke_broker_method" in tool_names
|
||||||
assert "call_broker_tool" in tool_names
|
assert "call_broker_tool" in tool_names
|
||||||
assert "catalog.all" in resource_names
|
assert "catalog.all" in resource_names
|
||||||
@@ -93,16 +92,6 @@ def test_create_broker_server_exposes_tools_resources_and_prompts() -> None:
|
|||||||
assert "wf.mcp.call_tool" in planner_names
|
assert "wf.mcp.call_tool" in planner_names
|
||||||
assert "wf.std.runtime_error" in planner_names
|
assert "wf.std.runtime_error" in planner_names
|
||||||
|
|
||||||
_content, source_payload_raw = asyncio.run(
|
|
||||||
server.call_tool("list_spec_sources", {})
|
|
||||||
)
|
|
||||||
source_payload = cast(dict[str, Any], cast(object, source_payload_raw))
|
|
||||||
sources = source_payload["result"]
|
|
||||||
source_ids = [source["id"] for source in sources]
|
|
||||||
assert "demo.personal" in source_ids
|
|
||||||
assert "wf.mcp" in source_ids
|
|
||||||
assert "wf.std" in source_ids
|
|
||||||
|
|
||||||
_content, all_sources_payload_raw = asyncio.run(
|
_content, all_sources_payload_raw = asyncio.run(
|
||||||
server.call_tool("list_sources", {})
|
server.call_tool("list_sources", {})
|
||||||
)
|
)
|
||||||
@@ -120,7 +109,6 @@ def test_broker_admin_tools_are_backed_by_wf_admin_source() -> None:
|
|||||||
tools = asyncio.run(server.list_tools())
|
tools = asyncio.run(server.list_tools())
|
||||||
tool_names = {tool.name for tool in tools}
|
tool_names = {tool.name for tool in tools}
|
||||||
|
|
||||||
assert "list_spec_sources" in tool_names
|
|
||||||
assert "get_planner_catalog" in tool_names
|
assert "get_planner_catalog" in tool_names
|
||||||
assert (
|
assert (
|
||||||
"wf.admin.list_sources"
|
"wf.admin.list_sources"
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ def test_server_exposes_upstream_admin_and_workflow_tools() -> None:
|
|||||||
assert "wf.admin.refresh_connection_catalog" in names
|
assert "wf.admin.refresh_connection_catalog" in names
|
||||||
assert "wf.admin.get_catalog" in names
|
assert "wf.admin.get_catalog" in names
|
||||||
assert "wf.admin.get_planner_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.list_sources" in names
|
||||||
assert "wf.admin.read_resource" in names
|
assert "wf.admin.read_resource" in names
|
||||||
assert "wf.admin.render_prompt" in names
|
assert "wf.admin.render_prompt" in names
|
||||||
|
|||||||
@@ -88,14 +88,12 @@ def test_service_rejects_reserved_connection_ids() -> None:
|
|||||||
def test_service_installs_builtin_stdlib_specs_by_default() -> None:
|
def test_service_installs_builtin_stdlib_specs_by_default() -> None:
|
||||||
service = WfMcpService(store=FileStore(local_temp_root() / "builtin_store"))
|
service = WfMcpService(store=FileStore(local_temp_root() / "builtin_store"))
|
||||||
|
|
||||||
assert "wf.std" in service.spec_sources
|
assert "wf.std.runtime_error" in service.capability_sources[
|
||||||
assert "wf.std.runtime_error" in service.spec_sources["wf.std"].specs
|
"wf.std"
|
||||||
assert "wf.mcp" in service.spec_sources
|
].capabilities.node_specs
|
||||||
assert "wf.mcp.call_tool" in service.spec_sources["wf.mcp"].specs
|
assert "wf.mcp.call_tool" in service.capability_sources[
|
||||||
|
"wf.mcp"
|
||||||
sources = service.list_spec_sources()
|
].capabilities.node_specs
|
||||||
assert {source["id"] for source in sources} == {"wf.mcp", "wf.std"}
|
|
||||||
assert all(source["kind"] == "system" for source in sources)
|
|
||||||
|
|
||||||
|
|
||||||
def test_service_lists_all_capability_sources_with_owned_capability_names() -> None:
|
def test_service_lists_all_capability_sources_with_owned_capability_names() -> None:
|
||||||
@@ -176,47 +174,17 @@ def test_wf_admin_source_exists_but_is_not_planner_visible() -> None:
|
|||||||
assert "wf.admin" not in service.get_planner_catalog().snapshots
|
assert "wf.admin" not in service.get_planner_catalog().snapshots
|
||||||
|
|
||||||
|
|
||||||
def test_service_spec_views_are_derived_from_capability_sources() -> None:
|
|
||||||
service = WfMcpService(store=FileStore(local_temp_root() / "source_view_store"))
|
|
||||||
|
|
||||||
assert "wf.std" in service.capability_sources
|
|
||||||
assert "wf.std" in service.spec_sources
|
|
||||||
assert "wf.std" in service.specs_by_connection
|
|
||||||
assert (
|
|
||||||
service.spec_sources["wf.std"].specs
|
|
||||||
is not service.capability_sources["wf.std"].capabilities.node_specs
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
service.specs_by_connection["wf.std"]
|
|
||||||
is not service.capability_sources["wf.std"].capabilities.node_specs
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
service.specs_by_connection["wf.std"]["wf.std.runtime_error"]
|
|
||||||
is service.capability_sources["wf.std"].capabilities.node_specs[
|
|
||||||
"wf.std.runtime_error"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
service.spec_sources["wf.std"].specs.clear()
|
|
||||||
service.specs_by_connection["wf.std"].clear()
|
|
||||||
assert (
|
|
||||||
"wf.std.runtime_error"
|
|
||||||
in service.capability_sources["wf.std"].capabilities.node_specs
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_service_can_disable_builtin_stdlib_specs() -> None:
|
def test_service_can_disable_builtin_stdlib_specs() -> None:
|
||||||
service = WfMcpService(
|
service = WfMcpService(
|
||||||
store=FileStore(local_temp_root() / "no_builtin_store"),
|
store=FileStore(local_temp_root() / "no_builtin_store"),
|
||||||
include_builtin_specs=False,
|
include_builtin_specs=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert "wf.std" not in service.spec_sources
|
assert "wf.std" not in service.capability_sources
|
||||||
assert "wf.mcp" not in service.spec_sources
|
assert "wf.mcp" not in service.capability_sources
|
||||||
assert service.list_spec_sources() == []
|
|
||||||
|
|
||||||
|
|
||||||
def test_service_list_spec_sources_excludes_hidden_sources() -> None:
|
def test_service_planner_catalog_excludes_hidden_sources() -> None:
|
||||||
service = WfMcpService(store=FileStore(local_temp_root() / "hidden_list_store"))
|
service = WfMcpService(store=FileStore(local_temp_root() / "hidden_list_store"))
|
||||||
hidden_echo_tool = NodeSpec(
|
hidden_echo_tool = NodeSpec(
|
||||||
name="hidden.source.echo_tool",
|
name="hidden.source.echo_tool",
|
||||||
@@ -241,9 +209,10 @@ def test_service_list_spec_sources_excludes_hidden_sources() -> None:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
source_ids = {source["id"] for source in service.list_spec_sources()}
|
planner_names = {
|
||||||
|
entry.qualified_name for entry in service.get_planner_catalog().entries()
|
||||||
assert "hidden.source" not in source_ids
|
}
|
||||||
|
assert "hidden.source.echo_tool" not in planner_names
|
||||||
|
|
||||||
|
|
||||||
def test_service_catalog_split_keeps_system_specs_out_of_backend_catalog() -> None:
|
def test_service_catalog_split_keeps_system_specs_out_of_backend_catalog() -> None:
|
||||||
@@ -693,7 +662,9 @@ def test_service_wrapped_tool_adapter_model_validates_simple_schema_types() -> N
|
|||||||
service.register_adapter("demo", FakeAdapter())
|
service.register_adapter("demo", FakeAdapter())
|
||||||
|
|
||||||
asyncio.run(service.refresh_connection_catalog("demo.personal"))
|
asyncio.run(service.refresh_connection_catalog("demo.personal"))
|
||||||
spec = service.spec_sources["demo.personal"].specs["demo.personal.echo_tool"]
|
spec = service.capability_sources["demo.personal"].capabilities.node_specs[
|
||||||
|
"demo.personal.echo_tool"
|
||||||
|
]
|
||||||
|
|
||||||
parsed = spec.input_model.model_validate({"text": "hello"})
|
parsed = spec.input_model.model_validate({"text": "hello"})
|
||||||
assert parsed.model_dump() == {"text": "hello"}
|
assert parsed.model_dump() == {"text": "hello"}
|
||||||
|
|||||||
Reference in New Issue
Block a user