Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,20 @@ 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:

**Tests must pass and lint/typing must be clean before committing.**
```bash
uv run prek run --all-files # Ruff + Prettier + ty
```

**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
**Tests must pass and lint/typing must be clean before committing.**

## Repository Structure

Expand Down
8 changes: 4 additions & 4 deletions src/fastmcp/server/middleware/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ async def on_call_tool(
)

# 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"
Expand Down Expand Up @@ -202,9 +202,9 @@ async def on_read_resource(
)

# 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"
Expand Down Expand Up @@ -295,7 +295,7 @@ async def on_get_prompt(
)

# 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"
Expand Down
4 changes: 2 additions & 2 deletions src/fastmcp/server/providers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ class DatabaseProvider(Provider):
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

Expand Down
32 changes: 16 additions & 16 deletions src/fastmcp/server/providers/aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,15 @@ def __repr__(self) -> str:
# 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.
Expand All @@ -142,7 +142,7 @@ async def get_tool(
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]
Expand All @@ -151,15 +151,15 @@ async def get_tool(
# 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.
Expand All @@ -170,7 +170,7 @@ async def get_resource(
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]
Expand All @@ -179,15 +179,15 @@ async def get_resource(
# 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.
Expand All @@ -198,7 +198,7 @@ async def get_resource_template(
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(
Expand All @@ -209,15 +209,15 @@ async def get_resource_template(
# 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.
Expand All @@ -228,7 +228,7 @@ async def get_prompt(
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]
Expand Down
Loading