the thing is good until i touch it
This commit is contained in:
@@ -1,2 +0,0 @@
|
|||||||
def main() -> None:
|
|
||||||
print("Hello from lda-workflow-as-struct!")
|
|
||||||
@@ -56,6 +56,10 @@ def create_broker_server(service: WfMcpService) -> FastMCP:
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@server.tool()
|
||||||
|
async def get_connection_statuses() -> list[dict[str, Any]]:
|
||||||
|
return service.connection_statuses()
|
||||||
|
|
||||||
@server.tool()
|
@server.tool()
|
||||||
async def refresh_connection_catalog(connection_id: str) -> dict[str, Any]:
|
async def refresh_connection_catalog(connection_id: str) -> dict[str, Any]:
|
||||||
try:
|
try:
|
||||||
@@ -133,6 +137,10 @@ def create_broker_server(service: WfMcpService) -> FastMCP:
|
|||||||
def events_resource() -> str:
|
def events_resource() -> str:
|
||||||
return json.dumps([asdict(event) for event in service.list_events()], indent=2)
|
return json.dumps([asdict(event) for event in service.list_events()], indent=2)
|
||||||
|
|
||||||
|
@server.resource("wf-mcp://status", name="status.all")
|
||||||
|
def status_resource() -> str:
|
||||||
|
return json.dumps(service.connection_statuses(), indent=2)
|
||||||
|
|
||||||
@server.prompt(
|
@server.prompt(
|
||||||
name="plan_with_catalog",
|
name="plan_with_catalog",
|
||||||
description="Provide the broker catalog as planning context.",
|
description="Provide the broker catalog as planning context.",
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ from dataclasses import dataclass, field
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
## are these our own redef of MCP VERY MUCH NICE VERY MUCH READY structs?
|
||||||
|
## These are unfortunately boundary. Hence, we need good typecheck on these, and since mcp lib is good stuff, carry those over. Could be pro
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
class DiscoveredTool:
|
class DiscoveredTool:
|
||||||
name: str
|
name: str
|
||||||
display_name: str | None
|
title: str | None
|
||||||
description: str | None
|
description: str | None
|
||||||
input_schema: dict[str, Any]
|
input_schema: dict[str, Any]
|
||||||
output_schema: dict[str, Any]
|
output_schema: dict[str, Any]
|
||||||
@@ -19,7 +21,7 @@ class DiscoveredTool:
|
|||||||
class DiscoveredResource:
|
class DiscoveredResource:
|
||||||
uri: str
|
uri: str
|
||||||
name: str
|
name: str
|
||||||
display_name: str | None
|
title: str | None
|
||||||
description: str | None
|
description: str | None
|
||||||
mime_type: str | None = None
|
mime_type: str | None = None
|
||||||
metadata: dict[str, Any] = field(default_factory=dict)
|
metadata: dict[str, Any] = field(default_factory=dict)
|
||||||
@@ -28,7 +30,7 @@ class DiscoveredResource:
|
|||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
class DiscoveredPrompt:
|
class DiscoveredPrompt:
|
||||||
name: str
|
name: str
|
||||||
display_name: str | None
|
title: str | None
|
||||||
description: str | None
|
description: str | None
|
||||||
arguments: list[dict[str, Any]] = field(default_factory=list)
|
arguments: list[dict[str, Any]] = field(default_factory=list)
|
||||||
metadata: dict[str, Any] = field(default_factory=dict)
|
metadata: dict[str, Any] = field(default_factory=dict)
|
||||||
@@ -39,7 +41,7 @@ class CatalogNodeEntry:
|
|||||||
qualified_name: str
|
qualified_name: str
|
||||||
connection_id: str
|
connection_id: str
|
||||||
local_name: str
|
local_name: str
|
||||||
display_name: str | None
|
title: str | None
|
||||||
description: str | None
|
description: str | None
|
||||||
outcomes: tuple[str, ...]
|
outcomes: tuple[str, ...]
|
||||||
input_schema: dict[str, Any]
|
input_schema: dict[str, Any]
|
||||||
@@ -51,7 +53,7 @@ class CatalogResourceEntry:
|
|||||||
qualified_name: str
|
qualified_name: str
|
||||||
connection_id: str
|
connection_id: str
|
||||||
local_name: str
|
local_name: str
|
||||||
display_name: str | None
|
title: str | None
|
||||||
uri: str
|
uri: str
|
||||||
description: str | None
|
description: str | None
|
||||||
mime_type: str | None = None
|
mime_type: str | None = None
|
||||||
@@ -63,7 +65,7 @@ class CatalogPromptEntry:
|
|||||||
qualified_name: str
|
qualified_name: str
|
||||||
connection_id: str
|
connection_id: str
|
||||||
local_name: str
|
local_name: str
|
||||||
display_name: str | None
|
title: str | None
|
||||||
description: str | None
|
description: str | None
|
||||||
arguments: list[dict[str, Any]] = field(default_factory=list)
|
arguments: list[dict[str, Any]] = field(default_factory=list)
|
||||||
metadata: dict[str, Any] = field(default_factory=dict)
|
metadata: dict[str, Any] = field(default_factory=dict)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ def snapshot_from_specs(
|
|||||||
else qualify_node_name(connection_id, entry.name),
|
else qualify_node_name(connection_id, entry.name),
|
||||||
connection_id=connection_id,
|
connection_id=connection_id,
|
||||||
local_name=entry.name.removeprefix(f"{connection_id}."),
|
local_name=entry.name.removeprefix(f"{connection_id}."),
|
||||||
display_name=(tool_display_names or {}).get(
|
title=(tool_display_names or {}).get(
|
||||||
entry.name.removeprefix(f"{connection_id}."),
|
entry.name.removeprefix(f"{connection_id}."),
|
||||||
entry.display_name,
|
entry.display_name,
|
||||||
),
|
),
|
||||||
@@ -51,7 +51,7 @@ def snapshot_from_specs(
|
|||||||
qualified_name=qualify_node_name(connection_id, resource.name),
|
qualified_name=qualify_node_name(connection_id, resource.name),
|
||||||
connection_id=connection_id,
|
connection_id=connection_id,
|
||||||
local_name=resource.name,
|
local_name=resource.name,
|
||||||
display_name=resource.display_name,
|
title=resource.title,
|
||||||
uri=resource.uri,
|
uri=resource.uri,
|
||||||
description=resource.description,
|
description=resource.description,
|
||||||
mime_type=resource.mime_type,
|
mime_type=resource.mime_type,
|
||||||
@@ -64,7 +64,7 @@ def snapshot_from_specs(
|
|||||||
qualified_name=qualify_node_name(connection_id, prompt.name),
|
qualified_name=qualify_node_name(connection_id, prompt.name),
|
||||||
connection_id=connection_id,
|
connection_id=connection_id,
|
||||||
local_name=prompt.name,
|
local_name=prompt.name,
|
||||||
display_name=prompt.display_name,
|
title=prompt.title,
|
||||||
description=prompt.description,
|
description=prompt.description,
|
||||||
arguments=prompt.arguments,
|
arguments=prompt.arguments,
|
||||||
metadata=prompt.metadata,
|
metadata=prompt.metadata,
|
||||||
@@ -123,7 +123,7 @@ class CombinedCatalog:
|
|||||||
"qualified_name": entry.qualified_name,
|
"qualified_name": entry.qualified_name,
|
||||||
"connection_id": entry.connection_id,
|
"connection_id": entry.connection_id,
|
||||||
"local_name": entry.local_name,
|
"local_name": entry.local_name,
|
||||||
"display_name": entry.display_name,
|
"title": entry.title,
|
||||||
"description": entry.description,
|
"description": entry.description,
|
||||||
"outcomes": list(entry.outcomes),
|
"outcomes": list(entry.outcomes),
|
||||||
"input_schema": entry.input_schema,
|
"input_schema": entry.input_schema,
|
||||||
@@ -136,7 +136,7 @@ class CombinedCatalog:
|
|||||||
"qualified_name": entry.qualified_name,
|
"qualified_name": entry.qualified_name,
|
||||||
"connection_id": entry.connection_id,
|
"connection_id": entry.connection_id,
|
||||||
"local_name": entry.local_name,
|
"local_name": entry.local_name,
|
||||||
"display_name": entry.display_name,
|
"title": entry.title,
|
||||||
"uri": entry.uri,
|
"uri": entry.uri,
|
||||||
"description": entry.description,
|
"description": entry.description,
|
||||||
"mime_type": entry.mime_type,
|
"mime_type": entry.mime_type,
|
||||||
@@ -149,7 +149,7 @@ class CombinedCatalog:
|
|||||||
"qualified_name": entry.qualified_name,
|
"qualified_name": entry.qualified_name,
|
||||||
"connection_id": entry.connection_id,
|
"connection_id": entry.connection_id,
|
||||||
"local_name": entry.local_name,
|
"local_name": entry.local_name,
|
||||||
"display_name": entry.display_name,
|
"title": entry.title,
|
||||||
"description": entry.description,
|
"description": entry.description,
|
||||||
"arguments": entry.arguments,
|
"arguments": entry.arguments,
|
||||||
"metadata": entry.metadata,
|
"metadata": entry.metadata,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
)
|
)
|
||||||
|
|
||||||
subparsers.add_parser("connections", help="List configured connections.")
|
subparsers.add_parser("connections", help="List configured connections.")
|
||||||
|
subparsers.add_parser("status", help="Show connection status and snapshot counts.")
|
||||||
subparsers.add_parser("catalog", help="Print the broker catalog as JSON.")
|
subparsers.add_parser("catalog", help="Print the broker catalog as JSON.")
|
||||||
|
|
||||||
refresh = subparsers.add_parser(
|
refresh = subparsers.add_parser(
|
||||||
@@ -111,6 +112,10 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
)
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
if args.command == "status":
|
||||||
|
_json_dump(service.connection_statuses())
|
||||||
|
return 0
|
||||||
|
|
||||||
if args.command == "catalog":
|
if args.command == "catalog":
|
||||||
_json_dump(service.get_catalog().as_payload())
|
_json_dump(service.get_catalog().as_payload())
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -18,4 +18,3 @@ def error_payload(exc: BaseException) -> dict[str, str]:
|
|||||||
"error_type": type(root).__name__,
|
"error_type": type(root).__name__,
|
||||||
"error": str(root),
|
"error": str(root),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,16 @@ from mcp.client.session import ClientSession
|
|||||||
from mcp.client.stdio import StdioServerParameters, stdio_client
|
from mcp.client.stdio import StdioServerParameters, stdio_client
|
||||||
from mcp.client.streamable_http import streamable_http_client
|
from mcp.client.streamable_http import streamable_http_client
|
||||||
from mcp.types import CallToolResult as McpCallToolResult
|
from mcp.types import CallToolResult as McpCallToolResult
|
||||||
from mcp.types import ClientNotification, ClientRequest
|
from mcp.types import (
|
||||||
from mcp.types import ListPromptsResult, ListResourcesResult
|
ClientNotification,
|
||||||
from mcp.types import ListToolsResult, Tool as McpTool
|
ClientRequest,
|
||||||
|
ListPromptsResult,
|
||||||
|
ListResourcesResult,
|
||||||
|
ListToolsResult,
|
||||||
|
)
|
||||||
from mcp.types import Prompt as McpPrompt
|
from mcp.types import Prompt as McpPrompt
|
||||||
from mcp.types import Resource as McpResource
|
from mcp.types import Resource as McpResource
|
||||||
|
from mcp.types import Tool as McpTool
|
||||||
from pydantic import AnyUrl
|
from pydantic import AnyUrl
|
||||||
|
|
||||||
from .adapters import (
|
from .adapters import (
|
||||||
@@ -48,7 +53,7 @@ def _tool_to_discovered(tool: McpTool) -> DiscoveredTool:
|
|||||||
)
|
)
|
||||||
return DiscoveredTool(
|
return DiscoveredTool(
|
||||||
name=tool.name,
|
name=tool.name,
|
||||||
display_name=display_name,
|
title=display_name,
|
||||||
description=tool.description,
|
description=tool.description,
|
||||||
input_schema=tool.inputSchema,
|
input_schema=tool.inputSchema,
|
||||||
output_schema=output_schema,
|
output_schema=output_schema,
|
||||||
@@ -62,7 +67,7 @@ def _resource_to_discovered(resource: McpResource) -> DiscoveredResource:
|
|||||||
return DiscoveredResource(
|
return DiscoveredResource(
|
||||||
uri=str(resource.uri),
|
uri=str(resource.uri),
|
||||||
name=local_name,
|
name=local_name,
|
||||||
display_name=resource.title,
|
title=resource.title,
|
||||||
description=resource.description,
|
description=resource.description,
|
||||||
mime_type=resource.mimeType,
|
mime_type=resource.mimeType,
|
||||||
metadata=resource.model_dump(by_alias=True, mode="json"),
|
metadata=resource.model_dump(by_alias=True, mode="json"),
|
||||||
@@ -76,7 +81,7 @@ def _prompt_to_discovered(prompt: McpPrompt) -> DiscoveredPrompt:
|
|||||||
]
|
]
|
||||||
return DiscoveredPrompt(
|
return DiscoveredPrompt(
|
||||||
name=prompt.name,
|
name=prompt.name,
|
||||||
display_name=prompt.title,
|
title=prompt.title,
|
||||||
description=prompt.description,
|
description=prompt.description,
|
||||||
arguments=arguments,
|
arguments=arguments,
|
||||||
metadata=prompt.model_dump(by_alias=True, mode="json"),
|
metadata=prompt.model_dump(by_alias=True, mode="json"),
|
||||||
|
|||||||
+27
-1
@@ -115,6 +115,32 @@ class WfMcpService:
|
|||||||
self.connections.get(connection_id)
|
self.connections.get(connection_id)
|
||||||
return self.store.load_catalog(connection_id)
|
return self.store.load_catalog(connection_id)
|
||||||
|
|
||||||
|
def connection_statuses(self) -> list[dict[str, Any]]:
|
||||||
|
statuses: list[dict[str, Any]] = []
|
||||||
|
for connection in self.connections.list_all():
|
||||||
|
snapshot = self.store.load_catalog(connection.id)
|
||||||
|
statuses.append(
|
||||||
|
{
|
||||||
|
"connection_id": connection.id,
|
||||||
|
"server": connection.server,
|
||||||
|
"account": connection.account,
|
||||||
|
"enabled": connection.enabled,
|
||||||
|
"has_snapshot": snapshot is not None,
|
||||||
|
"fetched_at_epoch_ms": None
|
||||||
|
if snapshot is None
|
||||||
|
else snapshot.fetched_at_epoch_ms,
|
||||||
|
"max_age_seconds": None
|
||||||
|
if snapshot is None
|
||||||
|
else snapshot.max_age_seconds,
|
||||||
|
"node_count": 0 if snapshot is None else len(snapshot.nodes),
|
||||||
|
"resource_count": 0
|
||||||
|
if snapshot is None
|
||||||
|
else len(snapshot.resources),
|
||||||
|
"prompt_count": 0 if snapshot is None else len(snapshot.prompts),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return statuses
|
||||||
|
|
||||||
def list_resources(
|
def list_resources(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
@@ -315,7 +341,7 @@ class WfMcpService:
|
|||||||
connection_id,
|
connection_id,
|
||||||
specs=self.specs_by_connection.get(connection_id, {}),
|
specs=self.specs_by_connection.get(connection_id, {}),
|
||||||
tool_display_names={
|
tool_display_names={
|
||||||
tool.name: tool.display_name for tool in capabilities.tools
|
tool.name: tool.title for tool in capabilities.tools
|
||||||
},
|
},
|
||||||
resources=capabilities.resources,
|
resources=capabilities.resources,
|
||||||
prompts=capabilities.prompts,
|
prompts=capabilities.prompts,
|
||||||
|
|||||||
Vendored
+3
-1
@@ -6,7 +6,7 @@ from mcp.server.fastmcp import FastMCP
|
|||||||
server = FastMCP("echo-fixture")
|
server = FastMCP("echo-fixture")
|
||||||
|
|
||||||
|
|
||||||
@server.tool()
|
@server.tool(title="Echo tool")
|
||||||
async def echo_tool(text: str) -> dict[str, str]:
|
async def echo_tool(text: str) -> dict[str, str]:
|
||||||
return {"echoed": text}
|
return {"echoed": text}
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ async def echo_tool(text: str) -> dict[str, str]:
|
|||||||
@server.resource(
|
@server.resource(
|
||||||
"fixture://docs/welcome",
|
"fixture://docs/welcome",
|
||||||
name="resource.welcome",
|
name="resource.welcome",
|
||||||
|
title="Resource Welcome",
|
||||||
description="Welcome text resource for fixture tests.",
|
description="Welcome text resource for fixture tests.",
|
||||||
mime_type="text/plain",
|
mime_type="text/plain",
|
||||||
)
|
)
|
||||||
@@ -22,6 +23,7 @@ def welcome_resource() -> str:
|
|||||||
|
|
||||||
|
|
||||||
@server.prompt(
|
@server.prompt(
|
||||||
|
title="Prompt Summarize",
|
||||||
name="prompt.summarize",
|
name="prompt.summarize",
|
||||||
description="Summarize an input text for fixture tests.",
|
description="Summarize an input text for fixture tests.",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -60,10 +60,12 @@ def test_create_broker_server_exposes_tools_resources_and_prompts() -> None:
|
|||||||
resource_names = {resource.name for resource in resources}
|
resource_names = {resource.name for resource in resources}
|
||||||
prompt_names = {prompt.name for prompt in prompts}
|
prompt_names = {prompt.name for prompt in prompts}
|
||||||
|
|
||||||
|
assert "get_connection_statuses" in tool_names
|
||||||
assert "refresh_connection_catalog" in tool_names
|
assert "refresh_connection_catalog" in tool_names
|
||||||
assert "invoke_broker_method" in tool_names
|
assert "invoke_broker_method" in tool_names
|
||||||
assert "catalog.all" in resource_names
|
assert "catalog.all" in resource_names
|
||||||
assert "events.all" in resource_names
|
assert "events.all" in resource_names
|
||||||
|
assert "status.all" in resource_names
|
||||||
assert "plan_with_catalog" in prompt_names
|
assert "plan_with_catalog" in prompt_names
|
||||||
|
|
||||||
|
|
||||||
@@ -92,7 +94,9 @@ def test_broker_refresh_tool_returns_structured_error() -> None:
|
|||||||
server = create_broker_server(service)
|
server = create_broker_server(service)
|
||||||
|
|
||||||
_content, structured = asyncio.run(
|
_content, structured = asyncio.run(
|
||||||
server.call_tool("refresh_connection_catalog", {"connection_id": "demo.personal"})
|
server.call_tool(
|
||||||
|
"refresh_connection_catalog", {"connection_id": "demo.personal"}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
assert structured == {
|
assert structured == {
|
||||||
"connection_id": "demo.personal",
|
"connection_id": "demo.personal",
|
||||||
|
|||||||
@@ -66,3 +66,30 @@ def test_cli_catalog_prints_empty_catalog_when_not_refreshed(
|
|||||||
assert payload["nodes"] == []
|
assert payload["nodes"] == []
|
||||||
assert payload["resources"] == []
|
assert payload["resources"] == []
|
||||||
assert payload["prompts"] == []
|
assert payload["prompts"] == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_status_prints_connection_statuses(capsys) -> None:
|
||||||
|
tmp_path = local_temp_root() / "cli_status_test"
|
||||||
|
tmp_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
config_path = tmp_path / "wf_mcp.config.json"
|
||||||
|
_write_config(config_path)
|
||||||
|
|
||||||
|
exit_code = main(["--config", str(config_path), "status"])
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
|
||||||
|
assert exit_code == 0
|
||||||
|
payload = json.loads(captured.out)
|
||||||
|
assert payload == [
|
||||||
|
{
|
||||||
|
"connection_id": "demo.personal",
|
||||||
|
"server": "demo",
|
||||||
|
"account": "personal",
|
||||||
|
"enabled": True,
|
||||||
|
"has_snapshot": False,
|
||||||
|
"fetched_at_epoch_ms": None,
|
||||||
|
"max_age_seconds": None,
|
||||||
|
"node_count": 0,
|
||||||
|
"resource_count": 0,
|
||||||
|
"prompt_count": 0,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ def test_mcp_sdk_adapter_lists_and_calls_stdio_tool() -> None:
|
|||||||
"connection_id": "fixture.personal",
|
"connection_id": "fixture.personal",
|
||||||
"local_name": "resource.welcome",
|
"local_name": "resource.welcome",
|
||||||
"uri": "fixture://docs/welcome",
|
"uri": "fixture://docs/welcome",
|
||||||
|
"title": "Resource Welcome",
|
||||||
"description": "Welcome text resource for fixture tests.",
|
"description": "Welcome text resource for fixture tests.",
|
||||||
"mime_type": "text/plain",
|
"mime_type": "text/plain",
|
||||||
"metadata": payload["resources"][0]["metadata"],
|
"metadata": payload["resources"][0]["metadata"],
|
||||||
@@ -53,6 +54,7 @@ def test_mcp_sdk_adapter_lists_and_calls_stdio_tool() -> None:
|
|||||||
"qualified_name": "fixture.personal.prompt.summarize",
|
"qualified_name": "fixture.personal.prompt.summarize",
|
||||||
"connection_id": "fixture.personal",
|
"connection_id": "fixture.personal",
|
||||||
"local_name": "prompt.summarize",
|
"local_name": "prompt.summarize",
|
||||||
|
"title": "Prompt Summarize",
|
||||||
"description": "Summarize an input text for fixture tests.",
|
"description": "Summarize an input text for fixture tests.",
|
||||||
"arguments": payload["prompts"][0]["arguments"],
|
"arguments": payload["prompts"][0]["arguments"],
|
||||||
"metadata": payload["prompts"][0]["metadata"],
|
"metadata": payload["prompts"][0]["metadata"],
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import shutil
|
||||||
|
|
||||||
from wf_core import END, RunStatus
|
from wf_core import END, RunStatus
|
||||||
from wf_mcp import (
|
from wf_mcp import (
|
||||||
@@ -116,7 +117,7 @@ def test_service_refreshes_catalog_from_adapter() -> None:
|
|||||||
"qualified_name": "demo.personal.echo_tool",
|
"qualified_name": "demo.personal.echo_tool",
|
||||||
"connection_id": "demo.personal",
|
"connection_id": "demo.personal",
|
||||||
"local_name": "echo_tool",
|
"local_name": "echo_tool",
|
||||||
"display_name": "Echo Tool",
|
"title": "Echo Tool",
|
||||||
"description": "Echo text back",
|
"description": "Echo text back",
|
||||||
"outcomes": ["ok"],
|
"outcomes": ["ok"],
|
||||||
"input_schema": {
|
"input_schema": {
|
||||||
@@ -140,7 +141,7 @@ def test_service_refreshes_catalog_from_adapter() -> None:
|
|||||||
"qualified_name": "demo.personal.resource.welcome",
|
"qualified_name": "demo.personal.resource.welcome",
|
||||||
"connection_id": "demo.personal",
|
"connection_id": "demo.personal",
|
||||||
"local_name": "resource.welcome",
|
"local_name": "resource.welcome",
|
||||||
"display_name": "Welcome Resource",
|
"title": "Welcome Resource",
|
||||||
"uri": "demo://docs/welcome",
|
"uri": "demo://docs/welcome",
|
||||||
"description": "Welcome resource",
|
"description": "Welcome resource",
|
||||||
"mime_type": "text/plain",
|
"mime_type": "text/plain",
|
||||||
@@ -152,7 +153,7 @@ def test_service_refreshes_catalog_from_adapter() -> None:
|
|||||||
"qualified_name": "demo.personal.prompt.summarize",
|
"qualified_name": "demo.personal.prompt.summarize",
|
||||||
"connection_id": "demo.personal",
|
"connection_id": "demo.personal",
|
||||||
"local_name": "prompt.summarize",
|
"local_name": "prompt.summarize",
|
||||||
"display_name": "Summarize Prompt",
|
"title": "Summarize Prompt",
|
||||||
"description": "Summarize text",
|
"description": "Summarize text",
|
||||||
"arguments": [
|
"arguments": [
|
||||||
{
|
{
|
||||||
@@ -262,8 +263,45 @@ def test_service_can_inspect_resources_and_prompts() -> None:
|
|||||||
assert prompt.arguments[0]["name"] == "text"
|
assert prompt.arguments[0]["name"] == "text"
|
||||||
|
|
||||||
|
|
||||||
|
def test_service_reports_connection_statuses() -> None:
|
||||||
|
store = local_temp_root() / "status_store"
|
||||||
|
# clear store before test
|
||||||
|
shutil.rmtree(store)
|
||||||
|
service = WfMcpService(store=FileStore(store))
|
||||||
|
service.register_connection(
|
||||||
|
ConnectionConfig(id="demo.personal", server="demo", account="personal")
|
||||||
|
)
|
||||||
|
service.register_adapter("demo", FakeAdapter())
|
||||||
|
|
||||||
|
before = service.connection_statuses()
|
||||||
|
assert before == [
|
||||||
|
{
|
||||||
|
"connection_id": "demo.personal",
|
||||||
|
"server": "demo",
|
||||||
|
"account": "personal",
|
||||||
|
"enabled": True,
|
||||||
|
"has_snapshot": False,
|
||||||
|
"fetched_at_epoch_ms": None,
|
||||||
|
"max_age_seconds": None,
|
||||||
|
"node_count": 0,
|
||||||
|
"resource_count": 0,
|
||||||
|
"prompt_count": 0,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
asyncio.run(service.refresh_connection_catalog("demo.personal"))
|
||||||
|
after = service.connection_statuses()
|
||||||
|
assert after[0]["has_snapshot"] is True
|
||||||
|
assert after[0]["node_count"] == 1
|
||||||
|
assert after[0]["resource_count"] == 1
|
||||||
|
assert after[0]["prompt_count"] == 1
|
||||||
|
|
||||||
|
|
||||||
def test_service_can_proxy_resource_reads_and_prompt_gets() -> None:
|
def test_service_can_proxy_resource_reads_and_prompt_gets() -> None:
|
||||||
service = WfMcpService(store=FileStore(local_temp_root() / "proxy_store"))
|
store = local_temp_root() / "proxy_store"
|
||||||
|
# new store
|
||||||
|
shutil.rmtree(store)
|
||||||
|
service = WfMcpService(store=FileStore(store))
|
||||||
service.register_connection(
|
service.register_connection(
|
||||||
ConnectionConfig(id="demo.personal", server="demo", account="personal")
|
ConnectionConfig(id="demo.personal", server="demo", account="personal")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ class FakeAdapter:
|
|||||||
return [
|
return [
|
||||||
DiscoveredTool(
|
DiscoveredTool(
|
||||||
name="echo_tool",
|
name="echo_tool",
|
||||||
display_name="Echo Tool",
|
title="Echo Tool",
|
||||||
description="Echo text back",
|
description="Echo text back",
|
||||||
input_schema={
|
input_schema={
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -129,7 +129,7 @@ class FakeAdapter:
|
|||||||
DiscoveredResource(
|
DiscoveredResource(
|
||||||
uri="demo://docs/welcome",
|
uri="demo://docs/welcome",
|
||||||
name="resource.welcome",
|
name="resource.welcome",
|
||||||
display_name="Welcome Resource",
|
title="Welcome Resource",
|
||||||
description="Welcome resource",
|
description="Welcome resource",
|
||||||
mime_type="text/plain",
|
mime_type="text/plain",
|
||||||
metadata={"kind": "static"},
|
metadata={"kind": "static"},
|
||||||
@@ -144,7 +144,7 @@ class FakeAdapter:
|
|||||||
return [
|
return [
|
||||||
DiscoveredPrompt(
|
DiscoveredPrompt(
|
||||||
name="prompt.summarize",
|
name="prompt.summarize",
|
||||||
display_name="Summarize Prompt",
|
title="Summarize Prompt",
|
||||||
description="Summarize text",
|
description="Summarize text",
|
||||||
arguments=[
|
arguments=[
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user