docs for client and for fastmcp patches
This commit is contained in:
@@ -1,28 +1,35 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
from .service import WfMcpService
|
||||
|
||||
_WORKFLOW_AUTHORING_GUIDE = """\
|
||||
Build workflows from current capabilities instead of assuming a stale catalog.
|
||||
|
||||
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
|
||||
available and which sources are planner-visible.
|
||||
Use `call_broker_tool` to test an upstream MCP tool manually before wrapping it
|
||||
into a workflow.
|
||||
|
||||
Prefer namespaced capabilities, inspect before you rely on them, and test the
|
||||
smallest reusable piece before saving a larger workflow artifact.
|
||||
"""
|
||||
|
||||
|
||||
def register_broker_prompts(server: FastMCP, service: WfMcpService) -> None:
|
||||
"""Register broker prompt handlers on a FastMCP server."""
|
||||
|
||||
@server.prompt(
|
||||
name="plan_with_catalog",
|
||||
description="Provide the broker catalog as planning context.",
|
||||
name="workflow_authoring_guide",
|
||||
description="Explain how to inspect capabilities and test tools before authoring.",
|
||||
)
|
||||
def plan_with_catalog() -> list[dict[str, str]]:
|
||||
payload = json.dumps(service.get_catalog().as_payload(), indent=2)
|
||||
def workflow_authoring_guide() -> list[dict[str, str]]:
|
||||
_ = service
|
||||
return [
|
||||
{
|
||||
"role": "user",
|
||||
"content": (
|
||||
"Plan a workflow using this broker catalog. "
|
||||
"Prefer existing namespaced capabilities.\n\n"
|
||||
f"{payload}"
|
||||
),
|
||||
"content": _WORKFLOW_AUTHORING_GUIDE,
|
||||
}
|
||||
]
|
||||
|
||||
@@ -14,7 +14,13 @@ from ..shared.names import connection_id_to_resource_path
|
||||
|
||||
|
||||
class ResourceLinkRewritingTool(Tool):
|
||||
"""Delegate tool execution while rewriting returned resource-link URIs."""
|
||||
"""Delegate execution while compensating for one FastMCP proxy gap.
|
||||
|
||||
FastMCP already rewrites resources listed through namespace transforms, but
|
||||
proxied tools can also return typed `ResourceLink` content blocks. Current
|
||||
FastMCP does not rewrite those result payloads for us, so this wrapper keeps
|
||||
the public tool schema and changes only the returned resource-link URIs.
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
|
||||
|
||||
@@ -50,7 +56,12 @@ class ResourceLinkRewritingTool(Tool):
|
||||
|
||||
|
||||
class ResourceLinkNamespace(Transform):
|
||||
"""Rewrite resource links returned by tools into one namespace."""
|
||||
"""Mirror namespace URI projection inside proxied tool results.
|
||||
|
||||
This is a local stand-in for the result-transform behavior a fully
|
||||
transparent FastMCP proxy would ideally provide itself. If upstream gains a
|
||||
general result rewrite hook, this class should become deletable.
|
||||
"""
|
||||
|
||||
def __init__(self, prefix: str) -> None:
|
||||
self._prefix = connection_id_to_resource_path(prefix)
|
||||
|
||||
@@ -26,7 +26,11 @@ def rewrite_call_tool_result_resource_links(
|
||||
result: mcp_types.CallToolResult,
|
||||
rewrite_uri: ResourceUriRewriter,
|
||||
) -> mcp_types.CallToolResult:
|
||||
"""Return a copy of a tool result with ResourceLink content URIs rewritten."""
|
||||
"""Return a copy of a tool result with ResourceLink content URIs rewritten.
|
||||
|
||||
This pure helper expresses the same missing proxy behavior as
|
||||
`ResourceLinkNamespace` without depending on FastMCP wrapper classes.
|
||||
"""
|
||||
return result.model_copy(
|
||||
update={
|
||||
"content": [
|
||||
|
||||
@@ -92,7 +92,19 @@ def namespace_resource_uri(connection_id: str, uri: str) -> str:
|
||||
|
||||
|
||||
class ProxyNamespace(Transform):
|
||||
"""Project MCP proxy names with dots for callables and slashes for URIs."""
|
||||
"""Project one upstream MCP server into wf-mcp's public namespace.
|
||||
|
||||
This intentionally replaces FastMCP's stock `Namespace` transform for
|
||||
proxied capabilities. FastMCP's transform is almost the right thing, but it
|
||||
uses underscore-prefixed callable names and does not match our desired
|
||||
downstream shape:
|
||||
|
||||
- tools/prompts use `connection.id.local_name`
|
||||
- resources/templates use URI paths like `scheme://connection/id/path`
|
||||
|
||||
Keep this class narrow. It should only remake the namespace projection
|
||||
behavior we need from FastMCP, not become a general proxy framework.
|
||||
"""
|
||||
|
||||
def __init__(self, connection_id: str) -> None:
|
||||
self._connection_id = connection_id
|
||||
|
||||
@@ -81,7 +81,8 @@ def test_create_broker_server_exposes_tools_resources_and_prompts() -> None:
|
||||
assert "catalog.all" in resource_names
|
||||
assert "events.all" in resource_names
|
||||
assert "status.all" in resource_names
|
||||
assert "plan_with_catalog" in prompt_names
|
||||
assert "workflow_authoring_guide" in prompt_names
|
||||
assert "plan_with_catalog" not in prompt_names
|
||||
|
||||
_content, planner_catalog_raw = asyncio.run(
|
||||
server.call_tool("get_planner_catalog", {})
|
||||
|
||||
Reference in New Issue
Block a user