subclass Transform js to get our shit looking nice holy moly

This commit is contained in:
lda
2026-05-15 23:37:45 +07:00 Verified
parent 6935329d44
commit 9a035e9557
8 changed files with 224 additions and 24 deletions
+3
View File
@@ -3,8 +3,11 @@ from .resource_links import (
rewrite_call_tool_result_resource_links,
rewrite_resource_link_content,
)
from .resource_link_transform import ResourceLinkNamespace, ResourceLinkRewritingTool
__all__ = [
"ResourceLinkNamespace",
"ResourceLinkRewritingTool",
"ResourceUriRewriter",
"rewrite_call_tool_result_resource_links",
"rewrite_resource_link_content",
@@ -0,0 +1,87 @@
from __future__ import annotations
import re
from collections.abc import Callable, Sequence
from typing import Any
from fastmcp.server.transforms import GetToolNext, Transform
from fastmcp.tools.base import Tool, ToolResult
from fastmcp.utilities.versions import VersionSpec
from pydantic import ConfigDict
from pydantic.json_schema import SkipJsonSchema
from .resource_links import rewrite_resource_link_content
_URI_PATTERN = re.compile(r"^([^:]+://)(.*?)$")
class ResourceLinkRewritingTool(Tool):
"""Delegate tool execution while rewriting returned resource-link URIs."""
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
parent_tool: SkipJsonSchema[Tool]
rewrite_uri: SkipJsonSchema[Callable[[str], str]]
async def run(self, arguments: dict[str, Any]) -> ToolResult:
"""Run the parent tool and project any resource links downstream."""
result = await self.parent_tool.run(arguments)
return ToolResult(
content=[
rewrite_resource_link_content(content, self.rewrite_uri)
for content in result.content
],
structured_content=result.structured_content,
meta=result.meta,
)
@classmethod
def wrap(
cls,
tool: Tool,
rewrite_uri: Callable[[str], str],
) -> ResourceLinkRewritingTool:
"""Copy one tool's public schema while replacing only execution."""
return cls.model_validate(
{
**tool.model_dump(),
"parent_tool": tool,
"rewrite_uri": rewrite_uri,
}
)
class ResourceLinkNamespace(Transform):
"""Rewrite resource links returned by tools into one namespace."""
def __init__(self, prefix: str) -> None:
self._prefix = prefix
def __repr__(self) -> str:
return f"ResourceLinkNamespace({self._prefix!r})"
async def list_tools(self, tools: Sequence[Tool]) -> Sequence[Tool]:
"""Wrap listed tools so downstream callers receive projected links."""
return [self._wrap_tool(tool) for tool in tools]
async def get_tool(
self,
name: str,
call_next: GetToolNext,
*,
version: VersionSpec | None = None,
) -> Tool | None:
"""Wrap fetched tools so direct calls also receive projected links."""
tool = await call_next(name, version=version)
return None if tool is None else self._wrap_tool(tool)
def _wrap_tool(self, tool: Tool) -> ResourceLinkRewritingTool:
return ResourceLinkRewritingTool.wrap(tool, self._transform_uri)
def _transform_uri(self, uri: str) -> str:
"""Match FastMCP Namespace URI projection for tool-returned links."""
match = _URI_PATTERN.match(uri)
if match is None:
return uri
protocol, path = match.groups()
return f"{protocol}{self._prefix}/{path}"