misc: display name + error handling
This commit is contained in:
@@ -8,6 +8,7 @@ from typing import Any, Literal
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
from .error_info import error_payload
|
||||
from .mcp_sdk_adapter import McpSdkAdapter
|
||||
from .models import BrokerConfig, ConnectionConfig
|
||||
from .service import WfMcpService
|
||||
@@ -63,8 +64,7 @@ def create_broker_server(service: WfMcpService) -> FastMCP:
|
||||
return {
|
||||
"connection_id": connection_id,
|
||||
"refreshed": False,
|
||||
"error_type": type(exc).__name__,
|
||||
"error": str(exc),
|
||||
**error_payload(exc),
|
||||
}
|
||||
snapshot = service.get_connection_snapshot(connection_id)
|
||||
if snapshot is None:
|
||||
|
||||
@@ -7,6 +7,7 @@ from typing import Any
|
||||
@dataclass(slots=True)
|
||||
class DiscoveredTool:
|
||||
name: str
|
||||
display_name: str | None
|
||||
description: str | None
|
||||
input_schema: dict[str, Any]
|
||||
output_schema: dict[str, Any]
|
||||
@@ -18,6 +19,7 @@ class DiscoveredTool:
|
||||
class DiscoveredResource:
|
||||
uri: str
|
||||
name: str
|
||||
display_name: str | None
|
||||
description: str | None
|
||||
mime_type: str | None = None
|
||||
metadata: dict[str, Any] = field(default_factory=dict)
|
||||
@@ -26,6 +28,7 @@ class DiscoveredResource:
|
||||
@dataclass(slots=True)
|
||||
class DiscoveredPrompt:
|
||||
name: str
|
||||
display_name: str | None
|
||||
description: str | None
|
||||
arguments: list[dict[str, Any]] = field(default_factory=list)
|
||||
metadata: dict[str, Any] = field(default_factory=dict)
|
||||
@@ -36,6 +39,7 @@ class CatalogNodeEntry:
|
||||
qualified_name: str
|
||||
connection_id: str
|
||||
local_name: str
|
||||
display_name: str | None
|
||||
description: str | None
|
||||
outcomes: tuple[str, ...]
|
||||
input_schema: dict[str, Any]
|
||||
@@ -47,6 +51,7 @@ class CatalogResourceEntry:
|
||||
qualified_name: str
|
||||
connection_id: str
|
||||
local_name: str
|
||||
display_name: str | None
|
||||
uri: str
|
||||
description: str | None
|
||||
mime_type: str | None = None
|
||||
@@ -58,6 +63,7 @@ class CatalogPromptEntry:
|
||||
qualified_name: str
|
||||
connection_id: str
|
||||
local_name: str
|
||||
display_name: str | None
|
||||
description: str | None
|
||||
arguments: list[dict[str, Any]] = field(default_factory=list)
|
||||
metadata: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
@@ -20,6 +20,7 @@ def snapshot_from_specs(
|
||||
connection_id: str,
|
||||
*,
|
||||
specs: dict[str, NodeSpec[Any, Any]],
|
||||
tool_display_names: dict[str, str | None] | None = None,
|
||||
resources: list[DiscoveredResource] | None = None,
|
||||
prompts: list[DiscoveredPrompt] | None = None,
|
||||
metadata: dict[str, Any] | None = None,
|
||||
@@ -34,6 +35,10 @@ 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(
|
||||
entry.name.removeprefix(f"{connection_id}."),
|
||||
entry.display_name,
|
||||
),
|
||||
description=entry.description,
|
||||
outcomes=entry.outcomes,
|
||||
input_schema=entry.input_schema,
|
||||
@@ -46,6 +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,
|
||||
uri=resource.uri,
|
||||
description=resource.description,
|
||||
mime_type=resource.mime_type,
|
||||
@@ -58,6 +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,
|
||||
description=prompt.description,
|
||||
arguments=prompt.arguments,
|
||||
metadata=prompt.metadata,
|
||||
@@ -116,6 +123,7 @@ class CombinedCatalog:
|
||||
"qualified_name": entry.qualified_name,
|
||||
"connection_id": entry.connection_id,
|
||||
"local_name": entry.local_name,
|
||||
"display_name": entry.display_name,
|
||||
"description": entry.description,
|
||||
"outcomes": list(entry.outcomes),
|
||||
"input_schema": entry.input_schema,
|
||||
@@ -128,6 +136,7 @@ class CombinedCatalog:
|
||||
"qualified_name": entry.qualified_name,
|
||||
"connection_id": entry.connection_id,
|
||||
"local_name": entry.local_name,
|
||||
"display_name": entry.display_name,
|
||||
"uri": entry.uri,
|
||||
"description": entry.description,
|
||||
"mime_type": entry.mime_type,
|
||||
@@ -140,6 +149,7 @@ class CombinedCatalog:
|
||||
"qualified_name": entry.qualified_name,
|
||||
"connection_id": entry.connection_id,
|
||||
"local_name": entry.local_name,
|
||||
"display_name": entry.display_name,
|
||||
"description": entry.description,
|
||||
"arguments": entry.arguments,
|
||||
"metadata": entry.metadata,
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def root_exception(exc: BaseException) -> BaseException:
|
||||
current: BaseException = exc
|
||||
while isinstance(current, ExceptionGroup) and current.exceptions:
|
||||
nested = current.exceptions[0]
|
||||
if isinstance(nested, BaseException):
|
||||
current = nested
|
||||
continue
|
||||
break
|
||||
return current
|
||||
|
||||
|
||||
def error_payload(exc: BaseException) -> dict[str, str]:
|
||||
root = root_exception(exc)
|
||||
return {
|
||||
"error_type": type(root).__name__,
|
||||
"error": str(root),
|
||||
}
|
||||
|
||||
@@ -41,8 +41,14 @@ def _tool_to_discovered(tool: McpTool) -> DiscoveredTool:
|
||||
"type": "object",
|
||||
"properties": {"content": {"type": "array"}},
|
||||
}
|
||||
display_name = (
|
||||
tool.annotations.title
|
||||
if tool.annotations is not None and tool.annotations.title
|
||||
else tool.title
|
||||
)
|
||||
return DiscoveredTool(
|
||||
name=tool.name,
|
||||
display_name=display_name,
|
||||
description=tool.description,
|
||||
input_schema=tool.inputSchema,
|
||||
output_schema=output_schema,
|
||||
@@ -56,6 +62,7 @@ def _resource_to_discovered(resource: McpResource) -> DiscoveredResource:
|
||||
return DiscoveredResource(
|
||||
uri=str(resource.uri),
|
||||
name=local_name,
|
||||
display_name=resource.title,
|
||||
description=resource.description,
|
||||
mime_type=resource.mimeType,
|
||||
metadata=resource.model_dump(by_alias=True, mode="json"),
|
||||
@@ -69,6 +76,7 @@ def _prompt_to_discovered(prompt: McpPrompt) -> DiscoveredPrompt:
|
||||
]
|
||||
return DiscoveredPrompt(
|
||||
name=prompt.name,
|
||||
display_name=prompt.title,
|
||||
description=prompt.description,
|
||||
arguments=arguments,
|
||||
metadata=prompt.model_dump(by_alias=True, mode="json"),
|
||||
|
||||
@@ -11,6 +11,7 @@ from .adapters import BackendAdapter
|
||||
from .catalog import CombinedCatalog, snapshot_from_specs
|
||||
from .connections import ConnectionRegistry, parse_connection_id, qualify_node_name
|
||||
from .discovery import discover_connection_capabilities, specs_from_discovered_tools
|
||||
from .error_info import error_payload
|
||||
from .events import McpEvent, make_event
|
||||
from .models import (
|
||||
AuthRecord,
|
||||
@@ -313,6 +314,9 @@ class WfMcpService:
|
||||
snapshot = snapshot_from_specs(
|
||||
connection_id,
|
||||
specs=self.specs_by_connection.get(connection_id, {}),
|
||||
tool_display_names={
|
||||
tool.name: tool.display_name for tool in capabilities.tools
|
||||
},
|
||||
resources=capabilities.resources,
|
||||
prompts=capabilities.prompts,
|
||||
metadata=capabilities.metadata,
|
||||
@@ -336,10 +340,7 @@ class WfMcpService:
|
||||
make_event(
|
||||
"catalog_refresh_failed",
|
||||
connection_id=connection_id,
|
||||
payload={
|
||||
"error_type": type(exc).__name__,
|
||||
"error": str(exc),
|
||||
},
|
||||
payload=error_payload(exc),
|
||||
)
|
||||
)
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user