mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-28 02:10:38 +02:00
Merge pull request #2902 from jlowin/swap-methods
This commit is contained in:
commit
96782c0330
17 changed files with 194 additions and 196 deletions
14
AGENTS.md
14
AGENTS.md
|
|
@ -6,23 +6,21 @@ FastMCP is a comprehensive Python framework (Python ≥3.10) for building Model
|
|||
|
||||
## Required Development Workflow
|
||||
|
||||
**CRITICAL**: Always run these commands in sequence before committing:
|
||||
**CRITICAL**: Always run these commands in sequence before committing.
|
||||
|
||||
```bash
|
||||
uv sync # Install dependencies
|
||||
uv run prek run --all-files # Ruff + Prettier + ty
|
||||
uv run pytest -n auto # Run full test suite
|
||||
```
|
||||
|
||||
**All three must pass** - this is enforced by CI. Alternative: `just build && just typecheck && just test`
|
||||
In addition, you must pass static checks. This is generally done as a pre-commit hook with `prek` but you can run it manually with:
|
||||
|
||||
```bash
|
||||
uv run prek run --all-files # Ruff + Prettier + ty
|
||||
```
|
||||
|
||||
**Tests must pass and lint/typing must be clean before committing.**
|
||||
|
||||
**Before creating a PR**, evaluate whether documentation needs updating:
|
||||
- New features or APIs require corresponding docs
|
||||
- Changed behavior should be reflected in existing docs
|
||||
- Check `docs/` for affected pages
|
||||
|
||||
## Repository Structure
|
||||
|
||||
| Path | Purpose |
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ class AuthMiddleware(Middleware):
|
|||
)
|
||||
|
||||
# Get tool (component auth is checked in get_tool, raises if unauthorized)
|
||||
tool = await fastmcp.fastmcp._get_tool(tool_name)
|
||||
tool = await fastmcp.fastmcp.get_tool(tool_name)
|
||||
if tool is None:
|
||||
raise AuthorizationError(
|
||||
f"Authorization failed for tool '{tool_name}': tool not found"
|
||||
|
|
@ -202,9 +202,9 @@ class AuthMiddleware(Middleware):
|
|||
)
|
||||
|
||||
# Get resource/template (component auth is checked in get_*, raises if unauthorized)
|
||||
component = await fastmcp.fastmcp._get_resource(str(uri))
|
||||
component = await fastmcp.fastmcp.get_resource(str(uri))
|
||||
if component is None:
|
||||
component = await fastmcp.fastmcp._get_resource_template(str(uri))
|
||||
component = await fastmcp.fastmcp.get_resource_template(str(uri))
|
||||
if component is None:
|
||||
raise AuthorizationError(
|
||||
f"Authorization failed for resource '{uri}': resource not found"
|
||||
|
|
@ -295,7 +295,7 @@ class AuthMiddleware(Middleware):
|
|||
)
|
||||
|
||||
# Get prompt (component auth is checked in get_prompt, raises if unauthorized)
|
||||
prompt = await fastmcp.fastmcp._get_prompt(prompt_name)
|
||||
prompt = await fastmcp.fastmcp.get_prompt(prompt_name)
|
||||
if prompt is None:
|
||||
raise AuthorizationError(
|
||||
f"Authorization failed for prompt '{prompt_name}': prompt not found"
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ Example:
|
|||
def __init__(self, db_url: str):
|
||||
self.db = Database(db_url)
|
||||
|
||||
async def list_tools(self) -> list[Tool]:
|
||||
async def _list_tools(self) -> list[Tool]:
|
||||
rows = await self.db.fetch("SELECT * FROM tools")
|
||||
return [self._make_tool(row) for row in rows]
|
||||
|
||||
async def get_tool(self, name: str) -> Tool | None:
|
||||
async def _get_tool(self, name: str) -> Tool | None:
|
||||
row = await self.db.fetchone("SELECT * FROM tools WHERE name = ?", name)
|
||||
return self._make_tool(row) if row else None
|
||||
|
||||
|
|
|
|||
|
|
@ -123,15 +123,15 @@ class AggregateProvider(Provider):
|
|||
# Tools
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_tools(self) -> Sequence[Tool]:
|
||||
async def _list_tools(self) -> Sequence[Tool]:
|
||||
"""List all tools from all providers (with transforms applied)."""
|
||||
results = await gather(
|
||||
*[p._list_tools() for p in self._providers],
|
||||
*[p.list_tools() for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
return self._collect_list_results(results, "list_tools")
|
||||
|
||||
async def get_tool(
|
||||
async def _get_tool(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Tool | None:
|
||||
"""Get tool by name.
|
||||
|
|
@ -142,7 +142,7 @@ class AggregateProvider(Provider):
|
|||
If specified, returns highest version matching the spec from any provider.
|
||||
"""
|
||||
results = await gather(
|
||||
*[p._get_tool(name, version) for p in self._providers],
|
||||
*[p.get_tool(name, version) for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
return self._get_highest_version_result(results, f"get_tool({name!r})") # type: ignore[return-value]
|
||||
|
|
@ -151,15 +151,15 @@ class AggregateProvider(Provider):
|
|||
# Resources
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_resources(self) -> Sequence[Resource]:
|
||||
async def _list_resources(self) -> Sequence[Resource]:
|
||||
"""List all resources from all providers (with transforms applied)."""
|
||||
results = await gather(
|
||||
*[p._list_resources() for p in self._providers],
|
||||
*[p.list_resources() for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
return self._collect_list_results(results, "list_resources")
|
||||
|
||||
async def get_resource(
|
||||
async def _get_resource(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> Resource | None:
|
||||
"""Get resource by URI.
|
||||
|
|
@ -170,7 +170,7 @@ class AggregateProvider(Provider):
|
|||
If specified, returns highest version matching the spec from any provider.
|
||||
"""
|
||||
results = await gather(
|
||||
*[p._get_resource(uri, version) for p in self._providers],
|
||||
*[p.get_resource(uri, version) for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
return self._get_highest_version_result(results, f"get_resource({uri!r})") # type: ignore[return-value]
|
||||
|
|
@ -179,15 +179,15 @@ class AggregateProvider(Provider):
|
|||
# Resource Templates
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
"""List all resource templates from all providers (with transforms applied)."""
|
||||
results = await gather(
|
||||
*[p._list_resource_templates() for p in self._providers],
|
||||
*[p.list_resource_templates() for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
return self._collect_list_results(results, "list_resource_templates")
|
||||
|
||||
async def get_resource_template(
|
||||
async def _get_resource_template(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> ResourceTemplate | None:
|
||||
"""Get resource template by URI.
|
||||
|
|
@ -198,7 +198,7 @@ class AggregateProvider(Provider):
|
|||
If specified, returns highest version matching the spec from any provider.
|
||||
"""
|
||||
results = await gather(
|
||||
*[p._get_resource_template(uri, version) for p in self._providers],
|
||||
*[p.get_resource_template(uri, version) for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
return self._get_highest_version_result(
|
||||
|
|
@ -209,15 +209,15 @@ class AggregateProvider(Provider):
|
|||
# Prompts
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_prompts(self) -> Sequence[Prompt]:
|
||||
async def _list_prompts(self) -> Sequence[Prompt]:
|
||||
"""List all prompts from all providers (with transforms applied)."""
|
||||
results = await gather(
|
||||
*[p._list_prompts() for p in self._providers],
|
||||
*[p.list_prompts() for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
return self._collect_list_results(results, "list_prompts")
|
||||
|
||||
async def get_prompt(
|
||||
async def _get_prompt(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Prompt | None:
|
||||
"""Get prompt by name.
|
||||
|
|
@ -228,7 +228,7 @@ class AggregateProvider(Provider):
|
|||
If specified, returns highest version matching the spec from any provider.
|
||||
"""
|
||||
results = await gather(
|
||||
*[p._get_prompt(name, version) for p in self._providers],
|
||||
*[p.get_prompt(name, version) for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
return self._get_highest_version_result(results, f"get_prompt({name!r})") # type: ignore[return-value]
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ Example:
|
|||
super().__init__()
|
||||
self.db = Database(db_url)
|
||||
|
||||
async def list_tools(self) -> list[Tool]:
|
||||
async def _list_tools(self) -> list[Tool]:
|
||||
rows = await self.db.fetch("SELECT * FROM tools")
|
||||
return [self._make_tool(row) for row in rows]
|
||||
|
||||
async def get_tool(self, name: str) -> Tool | None:
|
||||
async def _get_tool(self, name: str) -> Tool | None:
|
||||
row = await self.db.fetchone("SELECT * FROM tools WHERE name = ?", name)
|
||||
return self._make_tool(row) if row else None
|
||||
|
||||
|
|
@ -100,7 +100,7 @@ class Provider:
|
|||
# Internal transform chain building
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def _list_tools(self) -> Sequence[Tool]:
|
||||
async def list_tools(self) -> Sequence[Tool]:
|
||||
"""List tools with all transforms applied.
|
||||
|
||||
Builds a middleware chain: base → transforms (in order).
|
||||
|
|
@ -111,7 +111,7 @@ class Provider:
|
|||
"""
|
||||
|
||||
async def base() -> Sequence[Tool]:
|
||||
return await self.list_tools()
|
||||
return await self._list_tools()
|
||||
|
||||
chain = base
|
||||
for transform in self.transforms:
|
||||
|
|
@ -119,7 +119,7 @@ class Provider:
|
|||
|
||||
return await chain()
|
||||
|
||||
async def _get_tool(
|
||||
async def get_tool(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Tool | None:
|
||||
"""Get tool by transformed name with all transforms applied.
|
||||
|
|
@ -133,7 +133,7 @@ class Provider:
|
|||
"""
|
||||
|
||||
async def base(n: str, version: VersionSpec | None = None) -> Tool | None:
|
||||
return await self.get_tool(n, version)
|
||||
return await self._get_tool(n, version)
|
||||
|
||||
chain = base
|
||||
for transform in self.transforms:
|
||||
|
|
@ -141,11 +141,11 @@ class Provider:
|
|||
|
||||
return await chain(name, version=version)
|
||||
|
||||
async def _list_resources(self) -> Sequence[Resource]:
|
||||
async def list_resources(self) -> Sequence[Resource]:
|
||||
"""List resources with all transforms applied."""
|
||||
|
||||
async def base() -> Sequence[Resource]:
|
||||
return await self.list_resources()
|
||||
return await self._list_resources()
|
||||
|
||||
chain = base
|
||||
for transform in self.transforms:
|
||||
|
|
@ -153,7 +153,7 @@ class Provider:
|
|||
|
||||
return await chain()
|
||||
|
||||
async def _get_resource(
|
||||
async def get_resource(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> Resource | None:
|
||||
"""Get resource by transformed URI with all transforms applied.
|
||||
|
|
@ -164,7 +164,7 @@ class Provider:
|
|||
"""
|
||||
|
||||
async def base(u: str, version: VersionSpec | None = None) -> Resource | None:
|
||||
return await self.get_resource(u, version)
|
||||
return await self._get_resource(u, version)
|
||||
|
||||
chain = base
|
||||
for transform in self.transforms:
|
||||
|
|
@ -172,11 +172,11 @@ class Provider:
|
|||
|
||||
return await chain(uri, version=version)
|
||||
|
||||
async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
async def list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
"""List resource templates with all transforms applied."""
|
||||
|
||||
async def base() -> Sequence[ResourceTemplate]:
|
||||
return await self.list_resource_templates()
|
||||
return await self._list_resource_templates()
|
||||
|
||||
chain = base
|
||||
for transform in self.transforms:
|
||||
|
|
@ -184,7 +184,7 @@ class Provider:
|
|||
|
||||
return await chain()
|
||||
|
||||
async def _get_resource_template(
|
||||
async def get_resource_template(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> ResourceTemplate | None:
|
||||
"""Get resource template by transformed URI with all transforms applied.
|
||||
|
|
@ -197,7 +197,7 @@ class Provider:
|
|||
async def base(
|
||||
u: str, version: VersionSpec | None = None
|
||||
) -> ResourceTemplate | None:
|
||||
return await self.get_resource_template(u, version)
|
||||
return await self._get_resource_template(u, version)
|
||||
|
||||
chain = base
|
||||
for transform in self.transforms:
|
||||
|
|
@ -205,11 +205,11 @@ class Provider:
|
|||
|
||||
return await chain(uri, version=version)
|
||||
|
||||
async def _list_prompts(self) -> Sequence[Prompt]:
|
||||
async def list_prompts(self) -> Sequence[Prompt]:
|
||||
"""List prompts with all transforms applied."""
|
||||
|
||||
async def base() -> Sequence[Prompt]:
|
||||
return await self.list_prompts()
|
||||
return await self._list_prompts()
|
||||
|
||||
chain = base
|
||||
for transform in self.transforms:
|
||||
|
|
@ -217,7 +217,7 @@ class Provider:
|
|||
|
||||
return await chain()
|
||||
|
||||
async def _get_prompt(
|
||||
async def get_prompt(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Prompt | None:
|
||||
"""Get prompt by transformed name with all transforms applied.
|
||||
|
|
@ -228,7 +228,7 @@ class Provider:
|
|||
"""
|
||||
|
||||
async def base(n: str, version: VersionSpec | None = None) -> Prompt | None:
|
||||
return await self.get_prompt(n, version)
|
||||
return await self._get_prompt(n, version)
|
||||
|
||||
chain = base
|
||||
for transform in self.transforms:
|
||||
|
|
@ -237,10 +237,10 @@ class Provider:
|
|||
return await chain(name, version=version)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Public list/get methods (override these to provide components)
|
||||
# Private list/get methods (override these to provide components)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_tools(self) -> Sequence[Tool]:
|
||||
async def _list_tools(self) -> Sequence[Tool]:
|
||||
"""Return all available tools.
|
||||
|
||||
Override to provide tools dynamically. Returns ALL versions of all tools.
|
||||
|
|
@ -248,12 +248,12 @@ class Provider:
|
|||
"""
|
||||
return []
|
||||
|
||||
async def get_tool(
|
||||
async def _get_tool(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Tool | None:
|
||||
"""Get a specific tool by name.
|
||||
|
||||
Default implementation filters list_tools() and picks the highest version
|
||||
Default implementation filters _list_tools() and picks the highest version
|
||||
that matches the spec.
|
||||
|
||||
Args:
|
||||
|
|
@ -264,7 +264,7 @@ class Provider:
|
|||
Returns:
|
||||
The Tool if found, or None to continue searching other providers.
|
||||
"""
|
||||
tools = await self.list_tools()
|
||||
tools = await self._list_tools()
|
||||
matching = [t for t in tools if t.name == name]
|
||||
if version:
|
||||
matching = [t for t in matching if version.matches(t.version)]
|
||||
|
|
@ -272,7 +272,7 @@ class Provider:
|
|||
return None
|
||||
return max(matching, key=version_sort_key) # type: ignore[type-var]
|
||||
|
||||
async def list_resources(self) -> Sequence[Resource]:
|
||||
async def _list_resources(self) -> Sequence[Resource]:
|
||||
"""Return all available resources.
|
||||
|
||||
Override to provide resources dynamically. Returns ALL versions of all resources.
|
||||
|
|
@ -280,12 +280,12 @@ class Provider:
|
|||
"""
|
||||
return []
|
||||
|
||||
async def get_resource(
|
||||
async def _get_resource(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> Resource | None:
|
||||
"""Get a specific resource by URI.
|
||||
|
||||
Default implementation filters list_resources() and returns highest
|
||||
Default implementation filters _list_resources() and returns highest
|
||||
version matching the spec.
|
||||
|
||||
Args:
|
||||
|
|
@ -295,7 +295,7 @@ class Provider:
|
|||
Returns:
|
||||
The Resource if found, or None to continue searching other providers.
|
||||
"""
|
||||
resources = await self.list_resources()
|
||||
resources = await self._list_resources()
|
||||
matching = [r for r in resources if str(r.uri) == uri]
|
||||
if version:
|
||||
matching = [r for r in matching if version.matches(r.version)]
|
||||
|
|
@ -303,7 +303,7 @@ class Provider:
|
|||
return None
|
||||
return max(matching, key=version_sort_key) # type: ignore[type-var]
|
||||
|
||||
async def list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
"""Return all available resource templates.
|
||||
|
||||
Override to provide resource templates dynamically. Returns ALL versions.
|
||||
|
|
@ -311,7 +311,7 @@ class Provider:
|
|||
"""
|
||||
return []
|
||||
|
||||
async def get_resource_template(
|
||||
async def _get_resource_template(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> ResourceTemplate | None:
|
||||
"""Get a resource template that matches the given URI.
|
||||
|
|
@ -326,7 +326,7 @@ class Provider:
|
|||
Returns:
|
||||
The ResourceTemplate if a matching one is found, or None to continue searching.
|
||||
"""
|
||||
templates = await self.list_resource_templates()
|
||||
templates = await self._list_resource_templates()
|
||||
matching = [t for t in templates if t.matches(uri) is not None]
|
||||
if version:
|
||||
matching = [t for t in matching if version.matches(t.version)]
|
||||
|
|
@ -334,7 +334,7 @@ class Provider:
|
|||
return None
|
||||
return max(matching, key=version_sort_key) # type: ignore[type-var]
|
||||
|
||||
async def list_prompts(self) -> Sequence[Prompt]:
|
||||
async def _list_prompts(self) -> Sequence[Prompt]:
|
||||
"""Return all available prompts.
|
||||
|
||||
Override to provide prompts dynamically. Returns ALL versions of all prompts.
|
||||
|
|
@ -342,12 +342,12 @@ class Provider:
|
|||
"""
|
||||
return []
|
||||
|
||||
async def get_prompt(
|
||||
async def _get_prompt(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Prompt | None:
|
||||
"""Get a specific prompt by name.
|
||||
|
||||
Default implementation filters list_prompts() and picks the highest version
|
||||
Default implementation filters _list_prompts() and picks the highest version
|
||||
matching the spec.
|
||||
|
||||
Args:
|
||||
|
|
@ -357,7 +357,7 @@ class Provider:
|
|||
Returns:
|
||||
The Prompt if found, or None to continue searching other providers.
|
||||
"""
|
||||
prompts = await self.list_prompts()
|
||||
prompts = await self._list_prompts()
|
||||
matching = [p for p in prompts if p.name == name]
|
||||
if version:
|
||||
matching = [p for p in matching if version.matches(p.version)]
|
||||
|
|
@ -380,10 +380,10 @@ class Provider:
|
|||
"""
|
||||
# Fetch all component types in parallel
|
||||
results = await gather(
|
||||
self.list_tools(),
|
||||
self.list_resources(),
|
||||
self.list_resource_templates(),
|
||||
self.list_prompts(),
|
||||
self._list_tools(),
|
||||
self._list_resources(),
|
||||
self._list_resource_templates(),
|
||||
self._list_prompts(),
|
||||
)
|
||||
tools = cast(Sequence[Tool], results[0])
|
||||
resources = cast(Sequence[Resource], results[1])
|
||||
|
|
|
|||
|
|
@ -482,7 +482,7 @@ class FastMCPProvider(Provider):
|
|||
# Tool methods
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_tools(self) -> Sequence[Tool]:
|
||||
async def _list_tools(self) -> Sequence[Tool]:
|
||||
"""List all tools from the mounted server as FastMCPProviderTools.
|
||||
|
||||
Runs the mounted server's middleware so filtering/transformation applies.
|
||||
|
|
@ -492,16 +492,16 @@ class FastMCPProvider(Provider):
|
|||
raw_tools = await self.server.get_tools(run_middleware=True)
|
||||
return [FastMCPProviderTool.wrap(self.server, t) for t in raw_tools]
|
||||
|
||||
async def get_tool(
|
||||
async def _get_tool(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Tool | None:
|
||||
"""Get a tool by name as a FastMCPProviderTool.
|
||||
|
||||
Passes the full VersionSpec to the nested server, which handles both
|
||||
exact version matching and range filtering. Uses _get_tool to ensure
|
||||
exact version matching and range filtering. Uses get_tool to ensure
|
||||
the nested server's transforms are applied.
|
||||
"""
|
||||
raw_tool = await self.server._get_tool(name, version)
|
||||
raw_tool = await self.server.get_tool(name, version)
|
||||
if raw_tool is None:
|
||||
return None
|
||||
return FastMCPProviderTool.wrap(self.server, raw_tool)
|
||||
|
|
@ -510,7 +510,7 @@ class FastMCPProvider(Provider):
|
|||
# Resource methods
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_resources(self) -> Sequence[Resource]:
|
||||
async def _list_resources(self) -> Sequence[Resource]:
|
||||
"""List all resources from the mounted server as FastMCPProviderResources.
|
||||
|
||||
Runs the mounted server's middleware so filtering/transformation applies.
|
||||
|
|
@ -520,16 +520,16 @@ class FastMCPProvider(Provider):
|
|||
raw_resources = await self.server.get_resources(run_middleware=True)
|
||||
return [FastMCPProviderResource.wrap(self.server, r) for r in raw_resources]
|
||||
|
||||
async def get_resource(
|
||||
async def _get_resource(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> Resource | None:
|
||||
"""Get a concrete resource by URI as a FastMCPProviderResource.
|
||||
|
||||
Passes the full VersionSpec to the nested server, which handles both
|
||||
exact version matching and range filtering. Uses _get_resource to ensure
|
||||
exact version matching and range filtering. Uses get_resource to ensure
|
||||
the nested server's transforms are applied.
|
||||
"""
|
||||
raw_resource = await self.server._get_resource(uri, version)
|
||||
raw_resource = await self.server.get_resource(uri, version)
|
||||
if raw_resource is None:
|
||||
return None
|
||||
return FastMCPProviderResource.wrap(self.server, raw_resource)
|
||||
|
|
@ -538,7 +538,7 @@ class FastMCPProvider(Provider):
|
|||
# Resource template methods
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
"""List all resource templates from the mounted server.
|
||||
|
||||
Runs the mounted server's middleware so filtering/transformation applies.
|
||||
|
|
@ -550,16 +550,16 @@ class FastMCPProvider(Provider):
|
|||
FastMCPProviderResourceTemplate.wrap(self.server, t) for t in raw_templates
|
||||
]
|
||||
|
||||
async def get_resource_template(
|
||||
async def _get_resource_template(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> ResourceTemplate | None:
|
||||
"""Get a resource template that matches the given URI.
|
||||
|
||||
Passes the full VersionSpec to the nested server, which handles both
|
||||
exact version matching and range filtering. Uses _get_resource_template
|
||||
exact version matching and range filtering. Uses get_resource_template
|
||||
to ensure the nested server's transforms are applied.
|
||||
"""
|
||||
raw_template = await self.server._get_resource_template(uri, version)
|
||||
raw_template = await self.server.get_resource_template(uri, version)
|
||||
if raw_template is None:
|
||||
return None
|
||||
return FastMCPProviderResourceTemplate.wrap(self.server, raw_template)
|
||||
|
|
@ -568,7 +568,7 @@ class FastMCPProvider(Provider):
|
|||
# Prompt methods
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_prompts(self) -> Sequence[Prompt]:
|
||||
async def _list_prompts(self) -> Sequence[Prompt]:
|
||||
"""List all prompts from the mounted server as FastMCPProviderPrompts.
|
||||
|
||||
Runs the mounted server's middleware so filtering/transformation applies.
|
||||
|
|
@ -578,16 +578,16 @@ class FastMCPProvider(Provider):
|
|||
raw_prompts = await self.server.get_prompts(run_middleware=True)
|
||||
return [FastMCPProviderPrompt.wrap(self.server, p) for p in raw_prompts]
|
||||
|
||||
async def get_prompt(
|
||||
async def _get_prompt(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Prompt | None:
|
||||
"""Get a prompt by name as a FastMCPProviderPrompt.
|
||||
|
||||
Passes the full VersionSpec to the nested server, which handles both
|
||||
exact version matching and range filtering. Uses _get_prompt to ensure
|
||||
exact version matching and range filtering. Uses get_prompt to ensure
|
||||
the nested server's transforms are applied.
|
||||
"""
|
||||
raw_prompt = await self.server._get_prompt(name, version)
|
||||
raw_prompt = await self.server.get_prompt(name, version)
|
||||
if raw_prompt is None:
|
||||
return None
|
||||
return FastMCPProviderPrompt.wrap(self.server, raw_prompt)
|
||||
|
|
|
|||
|
|
@ -174,53 +174,53 @@ class FileSystemProvider(LocalProvider):
|
|||
|
||||
# Override provider methods to support reload mode
|
||||
|
||||
async def list_tools(self) -> Sequence[Tool]:
|
||||
async def _list_tools(self) -> Sequence[Tool]:
|
||||
"""Return all tools, reloading if in reload mode."""
|
||||
await self._ensure_loaded()
|
||||
return await super().list_tools()
|
||||
return await super()._list_tools()
|
||||
|
||||
async def get_tool(
|
||||
async def _get_tool(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Tool | None:
|
||||
"""Get a tool by name, reloading if in reload mode."""
|
||||
await self._ensure_loaded()
|
||||
return await super().get_tool(name, version)
|
||||
return await super()._get_tool(name, version)
|
||||
|
||||
async def list_resources(self) -> Sequence[Resource]:
|
||||
async def _list_resources(self) -> Sequence[Resource]:
|
||||
"""Return all resources, reloading if in reload mode."""
|
||||
await self._ensure_loaded()
|
||||
return await super().list_resources()
|
||||
return await super()._list_resources()
|
||||
|
||||
async def get_resource(
|
||||
async def _get_resource(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> Resource | None:
|
||||
"""Get a resource by URI, reloading if in reload mode."""
|
||||
await self._ensure_loaded()
|
||||
return await super().get_resource(uri, version)
|
||||
return await super()._get_resource(uri, version)
|
||||
|
||||
async def list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
"""Return all resource templates, reloading if in reload mode."""
|
||||
await self._ensure_loaded()
|
||||
return await super().list_resource_templates()
|
||||
return await super()._list_resource_templates()
|
||||
|
||||
async def get_resource_template(
|
||||
async def _get_resource_template(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> ResourceTemplate | None:
|
||||
"""Get a resource template, reloading if in reload mode."""
|
||||
await self._ensure_loaded()
|
||||
return await super().get_resource_template(uri, version)
|
||||
return await super()._get_resource_template(uri, version)
|
||||
|
||||
async def list_prompts(self) -> Sequence[Prompt]:
|
||||
async def _list_prompts(self) -> Sequence[Prompt]:
|
||||
"""Return all prompts, reloading if in reload mode."""
|
||||
await self._ensure_loaded()
|
||||
return await super().list_prompts()
|
||||
return await super()._list_prompts()
|
||||
|
||||
async def get_prompt(
|
||||
async def _get_prompt(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Prompt | None:
|
||||
"""Get a prompt by name, reloading if in reload mode."""
|
||||
await self._ensure_loaded()
|
||||
return await super().get_prompt(name, version)
|
||||
return await super()._get_prompt(name, version)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"FileSystemProvider(root={self._root!r}, reload={self._reload})"
|
||||
|
|
|
|||
|
|
@ -492,7 +492,7 @@ class LocalProvider(Provider):
|
|||
# Provider interface implementation
|
||||
# =========================================================================
|
||||
|
||||
async def list_tools(self) -> Sequence[Tool]:
|
||||
async def _list_tools(self) -> Sequence[Tool]:
|
||||
"""Return all visible tools."""
|
||||
return [
|
||||
v
|
||||
|
|
@ -500,7 +500,7 @@ class LocalProvider(Provider):
|
|||
if isinstance(v, Tool) and self._is_component_enabled(v)
|
||||
]
|
||||
|
||||
async def get_tool(
|
||||
async def _get_tool(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Tool | None:
|
||||
"""Get a tool by name.
|
||||
|
|
@ -520,7 +520,7 @@ class LocalProvider(Provider):
|
|||
return None
|
||||
return max(matching, key=version_sort_key) # type: ignore[type-var]
|
||||
|
||||
async def list_resources(self) -> Sequence[Resource]:
|
||||
async def _list_resources(self) -> Sequence[Resource]:
|
||||
"""Return all visible resources."""
|
||||
return [
|
||||
v
|
||||
|
|
@ -528,7 +528,7 @@ class LocalProvider(Provider):
|
|||
if isinstance(v, Resource) and self._is_component_enabled(v)
|
||||
]
|
||||
|
||||
async def get_resource(
|
||||
async def _get_resource(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> Resource | None:
|
||||
"""Get a resource by URI.
|
||||
|
|
@ -550,7 +550,7 @@ class LocalProvider(Provider):
|
|||
return None
|
||||
return max(matching, key=version_sort_key) # type: ignore[type-var]
|
||||
|
||||
async def list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
"""Return all visible resource templates."""
|
||||
return [
|
||||
v
|
||||
|
|
@ -558,7 +558,7 @@ class LocalProvider(Provider):
|
|||
if isinstance(v, ResourceTemplate) and self._is_component_enabled(v)
|
||||
]
|
||||
|
||||
async def get_resource_template(
|
||||
async def _get_resource_template(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> ResourceTemplate | None:
|
||||
"""Get a resource template that matches the given URI.
|
||||
|
|
@ -583,7 +583,7 @@ class LocalProvider(Provider):
|
|||
return None
|
||||
return max(matching, key=version_sort_key) # type: ignore[type-var]
|
||||
|
||||
async def list_prompts(self) -> Sequence[Prompt]:
|
||||
async def _list_prompts(self) -> Sequence[Prompt]:
|
||||
"""Return all visible prompts."""
|
||||
return [
|
||||
v
|
||||
|
|
@ -591,7 +591,7 @@ class LocalProvider(Provider):
|
|||
if isinstance(v, Prompt) and self._is_component_enabled(v)
|
||||
]
|
||||
|
||||
async def get_prompt(
|
||||
async def _get_prompt(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Prompt | None:
|
||||
"""Get a prompt by name.
|
||||
|
|
|
|||
|
|
@ -349,11 +349,11 @@ class OpenAPIProvider(Provider):
|
|||
# Provider interface
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_tools(self) -> Sequence[Tool]:
|
||||
async def _list_tools(self) -> Sequence[Tool]:
|
||||
"""Return all tools created from the OpenAPI spec."""
|
||||
return list(self._tools.values())
|
||||
|
||||
async def get_tool(
|
||||
async def _get_tool(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Tool | None:
|
||||
"""Get a tool by name."""
|
||||
|
|
@ -364,11 +364,11 @@ class OpenAPIProvider(Provider):
|
|||
return None
|
||||
return tool
|
||||
|
||||
async def list_resources(self) -> Sequence[Resource]:
|
||||
async def _list_resources(self) -> Sequence[Resource]:
|
||||
"""Return all resources created from the OpenAPI spec."""
|
||||
return list(self._resources.values())
|
||||
|
||||
async def get_resource(
|
||||
async def _get_resource(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> Resource | None:
|
||||
"""Get a resource by URI."""
|
||||
|
|
@ -379,11 +379,11 @@ class OpenAPIProvider(Provider):
|
|||
return None
|
||||
return resource
|
||||
|
||||
async def list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
"""Return all resource templates created from the OpenAPI spec."""
|
||||
return list(self._templates.values())
|
||||
|
||||
async def get_resource_template(
|
||||
async def _get_resource_template(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> ResourceTemplate | None:
|
||||
"""Get a resource template that matches the given URI."""
|
||||
|
|
@ -396,7 +396,7 @@ class OpenAPIProvider(Provider):
|
|||
return None
|
||||
return max(matching, key=version_sort_key) # type: ignore[type-var]
|
||||
|
||||
async def list_prompts(self) -> Sequence[Prompt]:
|
||||
async def _list_prompts(self) -> Sequence[Prompt]:
|
||||
"""Return empty list - OpenAPI doesn't create prompts."""
|
||||
return []
|
||||
|
||||
|
|
|
|||
|
|
@ -517,7 +517,7 @@ class ProxyProvider(Provider):
|
|||
# Tool methods
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_tools(self) -> Sequence[Tool]:
|
||||
async def _list_tools(self) -> Sequence[Tool]:
|
||||
"""List all tools from the remote server."""
|
||||
try:
|
||||
client = await self._get_client()
|
||||
|
|
@ -535,7 +535,7 @@ class ProxyProvider(Provider):
|
|||
# Resource methods
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_resources(self) -> Sequence[Resource]:
|
||||
async def _list_resources(self) -> Sequence[Resource]:
|
||||
"""List all resources from the remote server."""
|
||||
try:
|
||||
client = await self._get_client()
|
||||
|
|
@ -554,7 +554,7 @@ class ProxyProvider(Provider):
|
|||
# Resource template methods
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
"""List all resource templates from the remote server."""
|
||||
try:
|
||||
client = await self._get_client()
|
||||
|
|
@ -573,7 +573,7 @@ class ProxyProvider(Provider):
|
|||
# Prompt methods
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_prompts(self) -> Sequence[Prompt]:
|
||||
async def _list_prompts(self) -> Sequence[Prompt]:
|
||||
"""List all prompts from the remote server."""
|
||||
try:
|
||||
client = await self._get_client()
|
||||
|
|
|
|||
|
|
@ -867,38 +867,38 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
# Provider interface overrides (aggregate from sub-providers)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def list_tools(self) -> Sequence[Tool]:
|
||||
async def _list_tools(self) -> Sequence[Tool]:
|
||||
"""Aggregate tools from all sub-providers.
|
||||
|
||||
This is the Provider interface implementation. The inherited _list_tools()
|
||||
This is the Provider interface implementation. The inherited list_tools()
|
||||
applies server-level transforms over this method.
|
||||
"""
|
||||
results = await gather(
|
||||
*[p._list_tools() for p in self._providers],
|
||||
*[p.list_tools() for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
return self._collect_list_results(results, "list_tools")
|
||||
|
||||
async def list_resources(self) -> Sequence[Resource]:
|
||||
async def _list_resources(self) -> Sequence[Resource]:
|
||||
"""Aggregate resources from all sub-providers."""
|
||||
results = await gather(
|
||||
*[p._list_resources() for p in self._providers],
|
||||
*[p.list_resources() for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
return self._collect_list_results(results, "list_resources")
|
||||
|
||||
async def list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
"""Aggregate resource templates from all sub-providers."""
|
||||
results = await gather(
|
||||
*[p._list_resource_templates() for p in self._providers],
|
||||
*[p.list_resource_templates() for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
return self._collect_list_results(results, "list_resource_templates")
|
||||
|
||||
async def list_prompts(self) -> Sequence[Prompt]:
|
||||
async def _list_prompts(self) -> Sequence[Prompt]:
|
||||
"""Aggregate prompts from all sub-providers."""
|
||||
results = await gather(
|
||||
*[p._list_prompts() for p in self._providers],
|
||||
*[p.list_prompts() for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
return self._collect_list_results(results, "list_prompts")
|
||||
|
|
@ -1106,7 +1106,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
)
|
||||
|
||||
# Query through full transform chain (provider transforms + server transforms + visibility)
|
||||
tools = await self._list_tools()
|
||||
tools = await self.list_tools()
|
||||
|
||||
# Get auth context (skip_auth=True for STDIO which has no auth concept)
|
||||
skip_auth, token = _get_auth_context()
|
||||
|
|
@ -1125,12 +1125,12 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
|
||||
return _dedupe_with_versions(authorized, lambda t: t.name)
|
||||
|
||||
async def get_tool(
|
||||
async def _get_tool(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Tool | None:
|
||||
"""Get a tool by name via aggregation from providers.
|
||||
|
||||
This is the raw lookup that Provider._get_tool() wraps with transforms.
|
||||
This is the raw lookup that Provider.get_tool() wraps with transforms.
|
||||
Aggregates from all sub-providers and applies component-level auth.
|
||||
|
||||
Args:
|
||||
|
|
@ -1143,7 +1143,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
|
||||
# Aggregate from all sub-providers (each applies their own transforms)
|
||||
results = await gather(
|
||||
*[p._get_tool(name, version) for p in self._providers],
|
||||
*[p.get_tool(name, version) for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
|
||||
|
|
@ -1177,7 +1177,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
|
||||
return tool
|
||||
|
||||
# _get_tool is inherited from Provider - wraps get_tool() with transforms
|
||||
# get_tool() is inherited from Provider - wraps _get_tool() with transforms
|
||||
|
||||
async def get_resources(self, *, run_middleware: bool = False) -> list[Resource]:
|
||||
"""Get all enabled resources from providers.
|
||||
|
|
@ -1204,7 +1204,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
)
|
||||
|
||||
# Query through full transform chain (provider transforms + server transforms + visibility)
|
||||
resources = await self._list_resources()
|
||||
resources = await self.list_resources()
|
||||
|
||||
# Get auth context (skip_auth=True for STDIO which has no auth concept)
|
||||
skip_auth, token = _get_auth_context()
|
||||
|
|
@ -1223,12 +1223,12 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
|
||||
return _dedupe_with_versions(authorized, lambda r: str(r.uri))
|
||||
|
||||
async def get_resource(
|
||||
async def _get_resource(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> Resource | None:
|
||||
"""Get a resource by URI via aggregation from providers.
|
||||
|
||||
This is the raw lookup that Provider._get_resource() wraps with transforms.
|
||||
This is the raw lookup that Provider.get_resource() wraps with transforms.
|
||||
Aggregates from all sub-providers and applies component-level auth.
|
||||
|
||||
Args:
|
||||
|
|
@ -1240,7 +1240,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
"""
|
||||
# Aggregate from all sub-providers (each applies their own transforms)
|
||||
results = await gather(
|
||||
*[p._get_resource(uri, version) for p in self._providers],
|
||||
*[p.get_resource(uri, version) for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
|
||||
|
|
@ -1274,7 +1274,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
|
||||
return resource
|
||||
|
||||
# _get_resource is inherited from Provider - wraps get_resource() with transforms
|
||||
# get_resource() is inherited from Provider - wraps _get_resource() with transforms
|
||||
|
||||
async def get_resource_templates(
|
||||
self, *, run_middleware: bool = False
|
||||
|
|
@ -1305,7 +1305,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
)
|
||||
|
||||
# Query through full transform chain (provider transforms + server transforms + visibility)
|
||||
templates = await self._list_resource_templates()
|
||||
templates = await self.list_resource_templates()
|
||||
|
||||
# Get auth context (skip_auth=True for STDIO which has no auth concept)
|
||||
skip_auth, token = _get_auth_context()
|
||||
|
|
@ -1324,12 +1324,12 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
|
||||
return _dedupe_with_versions(authorized, lambda t: t.uri_template)
|
||||
|
||||
async def get_resource_template(
|
||||
async def _get_resource_template(
|
||||
self, uri: str, version: VersionSpec | None = None
|
||||
) -> ResourceTemplate | None:
|
||||
"""Get a resource template by URI via aggregation from providers.
|
||||
|
||||
This is the raw lookup that Provider._get_resource_template() wraps with transforms.
|
||||
This is the raw lookup that Provider.get_resource_template() wraps with transforms.
|
||||
Aggregates from all sub-providers and applies component-level auth.
|
||||
|
||||
Args:
|
||||
|
|
@ -1341,7 +1341,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
"""
|
||||
# Aggregate from all sub-providers (each applies their own transforms)
|
||||
results = await gather(
|
||||
*[p._get_resource_template(uri, version) for p in self._providers],
|
||||
*[p.get_resource_template(uri, version) for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
|
||||
|
|
@ -1375,7 +1375,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
|
||||
return template
|
||||
|
||||
# _get_resource_template is inherited from Provider - wraps get_resource_template() with transforms
|
||||
# get_resource_template() is inherited from Provider - wraps _get_resource_template() with transforms
|
||||
|
||||
async def get_prompts(self, *, run_middleware: bool = False) -> list[Prompt]:
|
||||
"""Get all enabled prompts from providers.
|
||||
|
|
@ -1402,7 +1402,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
)
|
||||
|
||||
# Query through full transform chain (provider transforms + server transforms + visibility)
|
||||
prompts = await self._list_prompts()
|
||||
prompts = await self.list_prompts()
|
||||
|
||||
# Get auth context (skip_auth=True for STDIO which has no auth concept)
|
||||
skip_auth, token = _get_auth_context()
|
||||
|
|
@ -1421,12 +1421,12 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
|
||||
return _dedupe_with_versions(authorized, lambda p: p.name)
|
||||
|
||||
async def get_prompt(
|
||||
async def _get_prompt(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Prompt | None:
|
||||
"""Get a prompt by name via aggregation from providers.
|
||||
|
||||
This is the raw lookup that Provider._get_prompt() wraps with transforms.
|
||||
This is the raw lookup that Provider.get_prompt() wraps with transforms.
|
||||
Aggregates from all sub-providers and applies component-level auth.
|
||||
|
||||
Args:
|
||||
|
|
@ -1438,7 +1438,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
"""
|
||||
# Aggregate from all sub-providers (each applies their own transforms)
|
||||
results = await gather(
|
||||
*[p._get_prompt(name, version) for p in self._providers],
|
||||
*[p.get_prompt(name, version) for p in self._providers],
|
||||
return_exceptions=True,
|
||||
)
|
||||
|
||||
|
|
@ -1472,7 +1472,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
|
||||
return prompt
|
||||
|
||||
# _get_prompt is inherited from Provider - wraps get_prompt() with transforms
|
||||
# get_prompt() is inherited from Provider - wraps _get_prompt() with transforms
|
||||
|
||||
@overload
|
||||
async def call_tool(
|
||||
|
|
@ -1559,7 +1559,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
with server_span(
|
||||
f"tools/call {name}", "tools/call", self.name, "tool", name
|
||||
) as span:
|
||||
tool = await self._get_tool(name, version=version)
|
||||
tool = await self.get_tool(name, version=version)
|
||||
if tool is None:
|
||||
raise NotFoundError(f"Unknown tool: {name!r}")
|
||||
span.set_attributes(tool.get_span_attributes())
|
||||
|
|
@ -1666,7 +1666,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
resource_uri=uri,
|
||||
) as span:
|
||||
# Try concrete resources first (transforms + auth via _get_resource)
|
||||
resource = await self._get_resource(uri, version=version)
|
||||
resource = await self.get_resource(uri, version=version)
|
||||
if resource is not None:
|
||||
span.set_attributes(resource.get_span_attributes())
|
||||
if task_meta is not None and task_meta.fn_key is None:
|
||||
|
|
@ -1686,8 +1686,8 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
f"Error reading resource {uri!r}: {e}"
|
||||
) from e
|
||||
|
||||
# Try templates (transforms + auth via _get_resource_template)
|
||||
template = await self._get_resource_template(uri, version=version)
|
||||
# Try templates (transforms + auth via get_resource_template)
|
||||
template = await self.get_resource_template(uri, version=version)
|
||||
if template is None:
|
||||
if version is None:
|
||||
raise NotFoundError(f"Unknown resource: {uri!r}")
|
||||
|
|
@ -1791,7 +1791,7 @@ class FastMCP(Provider, Generic[LifespanResultT]):
|
|||
with server_span(
|
||||
f"prompts/get {name}", "prompts/get", self.name, "prompt", name
|
||||
) as span:
|
||||
prompt = await self._get_prompt(name, version=version)
|
||||
prompt = await self.get_prompt(name, version=version)
|
||||
if prompt is None:
|
||||
raise NotFoundError(f"Unknown prompt: {name!r}")
|
||||
span.set_attributes(prompt.get_span_attributes())
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class SimpleProvider(Provider):
|
|||
super().__init__()
|
||||
self._tools = tools or []
|
||||
|
||||
async def list_tools(self) -> list[Tool]:
|
||||
async def _list_tools(self) -> list[Tool]:
|
||||
return self._tools
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -578,7 +578,7 @@ class TestProviderToolTransformations:
|
|||
|
||||
# Get tool through layer with call_next
|
||||
async def get_tool(name: str, version=None):
|
||||
return await provider.get_tool(name, version)
|
||||
return await provider._get_tool(name, version)
|
||||
|
||||
tool = await layer.get_tool("transformed_tool", get_tool)
|
||||
assert tool is not None
|
||||
|
|
@ -604,7 +604,7 @@ class TestProviderToolTransformations:
|
|||
)
|
||||
|
||||
async def get_tool(name: str, version=None):
|
||||
return await provider.get_tool(name, version)
|
||||
return await provider._get_tool(name, version)
|
||||
|
||||
tool = await layer.get_tool("my_tool", get_tool)
|
||||
assert tool is not None
|
||||
|
|
@ -621,12 +621,12 @@ class TestProviderToolTransformations:
|
|||
def my_tool(x: int) -> int:
|
||||
return x
|
||||
|
||||
# Add layer to provider (layers are applied by server, not list_tools)
|
||||
# Add layer to provider (layers are applied by server, not _list_tools)
|
||||
layer = ToolTransform({"my_tool": ToolTransformConfig(name="renamed")})
|
||||
provider.add_transform(layer)
|
||||
|
||||
# Provider's list_tools returns raw tools (transforms applied when queried via chain)
|
||||
original_tools = await provider.list_tools()
|
||||
# Provider's _list_tools returns raw tools (transforms applied when queried via list_tools)
|
||||
original_tools = await provider._list_tools()
|
||||
assert original_tools[0].name == "my_tool"
|
||||
|
||||
# Transform modifies them when applied via call_next
|
||||
|
|
|
|||
|
|
@ -377,8 +377,8 @@ class TestPromptEnabled:
|
|||
prompts = await mcp.get_prompts()
|
||||
assert len(prompts) == 0
|
||||
|
||||
# _get_prompt() applies visibility transform, returns None for disabled
|
||||
prompt = await mcp._get_prompt("sample_prompt")
|
||||
# get_prompt() applies visibility transform, returns None for disabled
|
||||
prompt = await mcp.get_prompt("sample_prompt")
|
||||
assert prompt is None
|
||||
|
||||
async def test_get_prompt_and_disable(self):
|
||||
|
|
@ -388,15 +388,15 @@ class TestPromptEnabled:
|
|||
def sample_prompt() -> str:
|
||||
return "Hello, world!"
|
||||
|
||||
prompt = await mcp._get_prompt("sample_prompt")
|
||||
prompt = await mcp.get_prompt("sample_prompt")
|
||||
assert prompt is not None
|
||||
|
||||
mcp.disable(keys=["prompt:sample_prompt@"])
|
||||
prompts = await mcp.get_prompts()
|
||||
assert len(prompts) == 0
|
||||
|
||||
# _get_prompt() applies visibility transform, returns None for disabled
|
||||
prompt = await mcp._get_prompt("sample_prompt")
|
||||
# get_prompt() applies visibility transform, returns None for disabled
|
||||
prompt = await mcp.get_prompt("sample_prompt")
|
||||
assert prompt is None
|
||||
|
||||
async def test_cant_get_disabled_prompt(self):
|
||||
|
|
@ -408,8 +408,8 @@ class TestPromptEnabled:
|
|||
|
||||
mcp.disable(keys=["prompt:sample_prompt@"])
|
||||
|
||||
# _get_prompt() applies visibility transform, returns None for disabled
|
||||
prompt = await mcp._get_prompt("sample_prompt")
|
||||
# get_prompt() applies visibility transform, returns None for disabled
|
||||
prompt = await mcp.get_prompt("sample_prompt")
|
||||
assert prompt is None
|
||||
|
||||
|
||||
|
|
@ -459,15 +459,15 @@ class TestPromptTags:
|
|||
result = await prompt.render({})
|
||||
assert result.messages[0].content.text == "1"
|
||||
|
||||
prompt = await mcp._get_prompt("prompt_2")
|
||||
prompt = await mcp.get_prompt("prompt_2")
|
||||
assert prompt is None
|
||||
|
||||
async def test_read_prompt_excludes_tags(self):
|
||||
mcp = self.create_server(exclude_tags={"a"})
|
||||
# _get_prompt applies visibility transform (tag filtering)
|
||||
prompt = await mcp._get_prompt("prompt_1")
|
||||
# get_prompt applies visibility transform (tag filtering)
|
||||
prompt = await mcp.get_prompt("prompt_1")
|
||||
assert prompt is None
|
||||
|
||||
prompt = await mcp._get_prompt("prompt_2")
|
||||
prompt = await mcp.get_prompt("prompt_2")
|
||||
result = await prompt.render({})
|
||||
assert result.messages[0].content.text == "2"
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ class TestTransformReverseLookup:
|
|||
|
||||
# Create call_next that delegates to provider
|
||||
async def get_tool(name: str, version=None):
|
||||
return await provider.get_tool(name, version)
|
||||
return await provider._get_tool(name, version)
|
||||
|
||||
tool = await layer.get_tool("ns_my_tool", get_tool)
|
||||
|
||||
|
|
@ -178,7 +178,7 @@ class TestTransformReverseLookup:
|
|||
layer = ToolTransform({"original": ToolTransformConfig(name="renamed")})
|
||||
|
||||
async def get_tool(name: str, version=None):
|
||||
return await provider.get_tool(name, version)
|
||||
return await provider._get_tool(name, version)
|
||||
|
||||
tool = await layer.get_tool("renamed", get_tool)
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ class TestTransformReverseLookup:
|
|||
layer = Namespace("ns")
|
||||
|
||||
async def get_resource(uri: str, version=None):
|
||||
return await provider.get_resource(uri, version)
|
||||
return await provider._get_resource(uri, version)
|
||||
|
||||
resource = await layer.get_resource("resource://ns/data", get_resource)
|
||||
|
||||
|
|
@ -216,7 +216,7 @@ class TestTransformReverseLookup:
|
|||
layer = Namespace("ns")
|
||||
|
||||
async def get_tool(name: str, version=None):
|
||||
return await provider.get_tool(name, version)
|
||||
return await provider._get_tool(name, version)
|
||||
|
||||
# Wrong namespace prefix
|
||||
assert await layer.get_tool("wrong_my_tool", get_tool) is None
|
||||
|
|
|
|||
|
|
@ -48,11 +48,11 @@ class SimpleToolProvider(Provider):
|
|||
self.list_tools_call_count = 0
|
||||
self.get_tool_call_count = 0
|
||||
|
||||
async def list_tools(self) -> list[Tool]:
|
||||
async def _list_tools(self) -> list[Tool]:
|
||||
self.list_tools_call_count += 1
|
||||
return self._tools
|
||||
|
||||
async def get_tool(
|
||||
async def _get_tool(
|
||||
self, name: str, version: VersionSpec | None = None
|
||||
) -> Tool | None:
|
||||
self.get_tool_call_count += 1
|
||||
|
|
@ -73,7 +73,7 @@ class ListOnlyProvider(Provider):
|
|||
self._tools = tools
|
||||
self.list_tools_call_count = 0
|
||||
|
||||
async def list_tools(self) -> list[Tool]:
|
||||
async def _list_tools(self) -> list[Tool]:
|
||||
self.list_tools_call_count += 1
|
||||
return self._tools
|
||||
|
||||
|
|
@ -384,7 +384,7 @@ class TestProviderExecutionMethods:
|
|||
"""Test that default read_resource uses get_resource and reads it."""
|
||||
|
||||
class ResourceProvider(Provider):
|
||||
async def list_resources(self) -> Sequence[Resource]:
|
||||
async def _list_resources(self) -> Sequence[Resource]:
|
||||
return [
|
||||
FunctionResource(
|
||||
uri=AnyUrl("test://data"),
|
||||
|
|
@ -406,7 +406,7 @@ class TestProviderExecutionMethods:
|
|||
"""Test that read_resource_template handles template-based resources."""
|
||||
|
||||
class TemplateProvider(Provider):
|
||||
async def list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
async def _list_resource_templates(self) -> Sequence[ResourceTemplate]:
|
||||
return [
|
||||
FunctionResourceTemplate.from_function(
|
||||
fn=lambda name: f"content of {name}",
|
||||
|
|
@ -428,7 +428,7 @@ class TestProviderExecutionMethods:
|
|||
"""Test that default render_prompt uses get_prompt and renders it."""
|
||||
|
||||
class PromptProvider(Provider):
|
||||
async def list_prompts(self) -> Sequence[Prompt]:
|
||||
async def _list_prompts(self) -> Sequence[Prompt]:
|
||||
return [
|
||||
FunctionPrompt.from_function(
|
||||
fn=lambda name: f"Hello, {name}!",
|
||||
|
|
|
|||
|
|
@ -483,13 +483,13 @@ class TestVersionFilter:
|
|||
assert len(tools) == 1
|
||||
assert tools[0].version == "3.0"
|
||||
|
||||
# Can request specific versions in range (use _get_tool to apply transforms)
|
||||
tool_v2 = await mcp._get_tool("add", VersionSpec(eq="2.0"))
|
||||
# Can request specific versions in range (use get_tool to apply transforms)
|
||||
tool_v2 = await mcp.get_tool("add", VersionSpec(eq="2.0"))
|
||||
assert tool_v2 is not None
|
||||
assert tool_v2.version == "2.0"
|
||||
|
||||
# Cannot request version outside range - returns None
|
||||
assert await mcp._get_tool("add", VersionSpec(eq="1.0")) is None
|
||||
assert await mcp.get_tool("add", VersionSpec(eq="1.0")) is None
|
||||
|
||||
async def test_version_range(self):
|
||||
"""VersionFilter(version_gte='2.0', version_lt='3.0') shows only v2.x."""
|
||||
|
|
@ -520,14 +520,14 @@ class TestVersionFilter:
|
|||
assert len(tools) == 1
|
||||
assert tools[0].version == "2.5"
|
||||
|
||||
# Can request specific versions in range (use _get_tool to apply transforms)
|
||||
tool_v2 = await mcp._get_tool("calc", VersionSpec(eq="2.0"))
|
||||
# Can request specific versions in range (use get_tool to apply transforms)
|
||||
tool_v2 = await mcp.get_tool("calc", VersionSpec(eq="2.0"))
|
||||
assert tool_v2 is not None
|
||||
assert tool_v2.version == "2.0"
|
||||
|
||||
# Versions outside range are not accessible - return None
|
||||
assert await mcp._get_tool("calc", VersionSpec(eq="1.0")) is None
|
||||
assert await mcp._get_tool("calc", VersionSpec(eq="3.0")) is None
|
||||
assert await mcp.get_tool("calc", VersionSpec(eq="1.0")) is None
|
||||
assert await mcp.get_tool("calc", VersionSpec(eq="3.0")) is None
|
||||
|
||||
async def test_unversioned_always_passes(self):
|
||||
"""Unversioned components pass through any filter."""
|
||||
|
|
@ -589,8 +589,8 @@ class TestVersionFilter:
|
|||
|
||||
mcp.add_transform(VersionFilter(version_lt="3.0"))
|
||||
|
||||
# Tool exists but is filtered out - returns None (use _get_tool to apply transforms)
|
||||
assert await mcp._get_tool("only_v5") is None
|
||||
# Tool exists but is filtered out - returns None (use get_tool to apply transforms)
|
||||
assert await mcp.get_tool("only_v5") is None
|
||||
|
||||
async def test_must_specify_at_least_one(self):
|
||||
"""VersionFilter() with no args raises ValueError."""
|
||||
|
|
@ -831,8 +831,8 @@ class TestMountedVersionFiltering:
|
|||
tools = await parent.get_tools()
|
||||
assert len(tools) == 0
|
||||
|
||||
# _get_tool should also return None (respects filter, applies transforms)
|
||||
assert await parent._get_tool("child_high_version_tool") is None
|
||||
# get_tool should also return None (respects filter, applies transforms)
|
||||
assert await parent.get_tool("child_high_version_tool") is None
|
||||
|
||||
|
||||
class TestMountedRangeFiltering:
|
||||
|
|
@ -857,8 +857,8 @@ class TestMountedRangeFiltering:
|
|||
parent.add_transform(VersionFilter(version_lt="2.0"))
|
||||
|
||||
# Should return v1.0 (the highest version that matches <2.0)
|
||||
# Use _get_tool to apply transforms
|
||||
tool = await parent._get_tool("child_calc")
|
||||
# Use get_tool to apply transforms
|
||||
tool = await parent.get_tool("child_calc")
|
||||
assert tool is not None
|
||||
assert tool.version == "1.0"
|
||||
|
||||
|
|
@ -884,13 +884,13 @@ class TestMountedRangeFiltering:
|
|||
parent.mount(child, "child")
|
||||
parent.add_transform(VersionFilter(version_gte="1.0", version_lt="3.0"))
|
||||
|
||||
# Request specific version within range (use _get_tool to apply transforms)
|
||||
tool = await parent._get_tool("child_calc", VersionSpec(eq="1.0"))
|
||||
# Request specific version within range (use get_tool to apply transforms)
|
||||
tool = await parent.get_tool("child_calc", VersionSpec(eq="1.0"))
|
||||
assert tool is not None
|
||||
assert tool.version == "1.0"
|
||||
|
||||
# Request version outside range should return None
|
||||
result = await parent._get_tool("child_calc", VersionSpec(eq="3.0"))
|
||||
result = await parent.get_tool("child_calc", VersionSpec(eq="3.0"))
|
||||
assert result is None
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue