the thing is good until i touch it

This commit is contained in:
lda
2026-04-30 02:42:56 +07:00 Verified
parent 0afd61cf27
commit c52db4a830
14 changed files with 147 additions and 31 deletions
-2
View File
@@ -1,2 +0,0 @@
def main() -> None:
print("Hello from lda-workflow-as-struct!")
+8
View File
@@ -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()
async def refresh_connection_catalog(connection_id: str) -> dict[str, Any]:
try:
@@ -133,6 +137,10 @@ def create_broker_server(service: WfMcpService) -> FastMCP:
def events_resource() -> str:
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(
name="plan_with_catalog",
description="Provide the broker catalog as planning context.",
+8 -6
View File
@@ -4,10 +4,12 @@ from dataclasses import dataclass, field
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)
class DiscoveredTool:
name: str
display_name: str | None
title: str | None
description: str | None
input_schema: dict[str, Any]
output_schema: dict[str, Any]
@@ -19,7 +21,7 @@ class DiscoveredTool:
class DiscoveredResource:
uri: str
name: str
display_name: str | None
title: str | None
description: str | None
mime_type: str | None = None
metadata: dict[str, Any] = field(default_factory=dict)
@@ -28,7 +30,7 @@ class DiscoveredResource:
@dataclass(slots=True)
class DiscoveredPrompt:
name: str
display_name: str | None
title: str | None
description: str | None
arguments: list[dict[str, Any]] = field(default_factory=list)
metadata: dict[str, Any] = field(default_factory=dict)
@@ -39,7 +41,7 @@ class CatalogNodeEntry:
qualified_name: str
connection_id: str
local_name: str
display_name: str | None
title: str | None
description: str | None
outcomes: tuple[str, ...]
input_schema: dict[str, Any]
@@ -51,7 +53,7 @@ class CatalogResourceEntry:
qualified_name: str
connection_id: str
local_name: str
display_name: str | None
title: str | None
uri: str
description: str | None
mime_type: str | None = None
@@ -63,7 +65,7 @@ class CatalogPromptEntry:
qualified_name: str
connection_id: str
local_name: str
display_name: str | None
title: str | None
description: str | None
arguments: list[dict[str, Any]] = field(default_factory=list)
metadata: dict[str, Any] = field(default_factory=dict)
+6 -6
View File
@@ -35,7 +35,7 @@ def snapshot_from_specs(
else qualify_node_name(connection_id, entry.name),
connection_id=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.display_name,
),
@@ -51,7 +51,7 @@ def snapshot_from_specs(
qualified_name=qualify_node_name(connection_id, resource.name),
connection_id=connection_id,
local_name=resource.name,
display_name=resource.display_name,
title=resource.title,
uri=resource.uri,
description=resource.description,
mime_type=resource.mime_type,
@@ -64,7 +64,7 @@ def snapshot_from_specs(
qualified_name=qualify_node_name(connection_id, prompt.name),
connection_id=connection_id,
local_name=prompt.name,
display_name=prompt.display_name,
title=prompt.title,
description=prompt.description,
arguments=prompt.arguments,
metadata=prompt.metadata,
@@ -123,7 +123,7 @@ class CombinedCatalog:
"qualified_name": entry.qualified_name,
"connection_id": entry.connection_id,
"local_name": entry.local_name,
"display_name": entry.display_name,
"title": entry.title,
"description": entry.description,
"outcomes": list(entry.outcomes),
"input_schema": entry.input_schema,
@@ -136,7 +136,7 @@ class CombinedCatalog:
"qualified_name": entry.qualified_name,
"connection_id": entry.connection_id,
"local_name": entry.local_name,
"display_name": entry.display_name,
"title": entry.title,
"uri": entry.uri,
"description": entry.description,
"mime_type": entry.mime_type,
@@ -149,7 +149,7 @@ class CombinedCatalog:
"qualified_name": entry.qualified_name,
"connection_id": entry.connection_id,
"local_name": entry.local_name,
"display_name": entry.display_name,
"title": entry.title,
"description": entry.description,
"arguments": entry.arguments,
"metadata": entry.metadata,
+5
View File
@@ -32,6 +32,7 @@ def build_parser() -> argparse.ArgumentParser:
)
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.")
refresh = subparsers.add_parser(
@@ -111,6 +112,10 @@ def main(argv: list[str] | None = None) -> int:
)
return 0
if args.command == "status":
_json_dump(service.connection_statuses())
return 0
if args.command == "catalog":
_json_dump(service.get_catalog().as_payload())
return 0
-1
View File
@@ -18,4 +18,3 @@ def error_payload(exc: BaseException) -> dict[str, str]:
"error_type": type(root).__name__,
"error": str(root),
}
+11 -6
View File
@@ -9,11 +9,16 @@ from mcp.client.session import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client
from mcp.client.streamable_http import streamable_http_client
from mcp.types import CallToolResult as McpCallToolResult
from mcp.types import ClientNotification, ClientRequest
from mcp.types import ListPromptsResult, ListResourcesResult
from mcp.types import ListToolsResult, Tool as McpTool
from mcp.types import (
ClientNotification,
ClientRequest,
ListPromptsResult,
ListResourcesResult,
ListToolsResult,
)
from mcp.types import Prompt as McpPrompt
from mcp.types import Resource as McpResource
from mcp.types import Tool as McpTool
from pydantic import AnyUrl
from .adapters import (
@@ -48,7 +53,7 @@ def _tool_to_discovered(tool: McpTool) -> DiscoveredTool:
)
return DiscoveredTool(
name=tool.name,
display_name=display_name,
title=display_name,
description=tool.description,
input_schema=tool.inputSchema,
output_schema=output_schema,
@@ -62,7 +67,7 @@ def _resource_to_discovered(resource: McpResource) -> DiscoveredResource:
return DiscoveredResource(
uri=str(resource.uri),
name=local_name,
display_name=resource.title,
title=resource.title,
description=resource.description,
mime_type=resource.mimeType,
metadata=resource.model_dump(by_alias=True, mode="json"),
@@ -76,7 +81,7 @@ def _prompt_to_discovered(prompt: McpPrompt) -> DiscoveredPrompt:
]
return DiscoveredPrompt(
name=prompt.name,
display_name=prompt.title,
title=prompt.title,
description=prompt.description,
arguments=arguments,
metadata=prompt.model_dump(by_alias=True, mode="json"),
+27 -1
View File
@@ -115,6 +115,32 @@ class WfMcpService:
self.connections.get(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(
self,
*,
@@ -315,7 +341,7 @@ class WfMcpService:
connection_id,
specs=self.specs_by_connection.get(connection_id, {}),
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,
prompts=capabilities.prompts,