docs for client and for fastmcp patches

This commit is contained in:
lda
2026-05-17 00:12:52 +07:00 Verified
parent b5c896b77b
commit 16591f3560
5 changed files with 51 additions and 16 deletions
+18 -11
View File
@@ -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)
+5 -1
View File
@@ -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": [
+13 -1
View File
@@ -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
+2 -1
View File
@@ -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", {})