fastmcp/docs/servers/providers/local.mdx
Jeremiah Lowin 07d89c4038
Add transform system for modifying components in provider chains (#2836)
* Consolidate tool transformation logic into TransformingProvider

Tool transformations were previously scattered across LocalProvider,
ProxyProvider, and MCPConfig. This consolidates all transformation
logic into TransformingProvider via with_transforms(tool_transforms={...}).

- Add tool_transforms parameter to TransformingProvider
- Add tool_transforms to Provider.with_transforms()
- Remove transformation storage from LocalProvider and ProxyProvider
- Remove add_tool_transformation() and remove_tool_transformation() from FastMCP
- Add tool_transforms parameter to factory methods (from_openapi, from_fastapi, create_proxy)
- Update tests to use new patterns

* Fix: reject tool lookups by pre-transform name

* Add collision validation for tool_transforms and fix docstring examples

- Validate duplicate target names in tool_transforms raise ValueError
- Fix docstring examples to use arguments/ArgTransformConfig (not args/ArgTransform)
- Add test for collision validation

* Add server-level tool transform APIs and fix task registration

- Add AggregateProvider to present multiple providers as one
- Add _get_root_provider() to apply server-level transforms uniformly
- Fix _docket_lifespan to use root provider (ensures renamed tools
  register with correct keys for background execution)
- Add tool_transforms kwarg to __init__ (non-deprecated)
- Add add_tool_transform(), remove_tool_transform(), tool_transforms property
- Deprecate old API names (tool_transformations, add_tool_transformation, etc.)
- Update tests to use new API

* Add graceful degradation for provider errors in AggregateProvider

* Match original behavior: parallel queries with DEBUG logging

* Refactor transforms to middleware-style call_next pattern

Replaces the ad-hoc transformation system with a unified Transform
abstraction using the same call_next pattern as server middleware.

Key changes:
- New src/fastmcp/server/transforms/ module with Transform base class
- Namespace, ToolTransform, Visibility all implement the same interface
- Transforms compose via functools.partial chain building
- Visibility is now just the first transform in provider._transforms
- Server-level transforms apply after provider aggregation
- Task registration now applies full transform chain

Removes TransformingProvider, _BoundTransform, ComponentSource protocol.
User-facing API unchanged: mount(), add_transform(), enable/disable all
work as before.

* Add comprehensive transforms and visibility documentation

New docs/servers/providers/transforms.mdx covering:
- Mental model for middleware-style transform pattern
- Built-in transforms (Namespace, ToolTransform)
- Server vs provider-level transforms and ordering
- Tool modification (immediate vs deferred)
- Custom transform creation

New docs/servers/visibility.mdx covering:
- Enable/disable API for runtime visibility control
- Keys and tags for targeting components
- Allowlist mode with only=True
- Server vs provider visibility layering

Updates existing docs to reference new pages and simplifies
redundant content. Visibility is documented as a user feature,
not as an implementation detail.

* Restructure transforms docs and delete tool-transformation pattern

* Cleanup: simplify get_tasks and remove unused Provider.get_component

* Update loq

* Update loq limits and add loq note to AGENTS.md

* Deprecate add_tool_transformation and tool_transformations param

* Address PR review feedback: remove redundant imports, fix path reference

* Add missing imports to code examples in v3-features.mdx
2026-01-12 22:11:16 -05:00

160 lines
3.8 KiB
Text

---
title: Local Provider
sidebarTitle: Local
description: The default provider for decorator-registered components
icon: house
---
import { VersionBadge } from '/snippets/version-badge.mdx'
<VersionBadge version="3.0.0" />
`LocalProvider` stores components that you define directly on your server. When you use `@mcp.tool`, `@mcp.resource`, or `@mcp.prompt`, you're adding components to your server's `LocalProvider`.
## How It Works
Every FastMCP server has a `LocalProvider` as its first provider. Components registered via decorators or direct methods are stored here:
```python
from fastmcp import FastMCP
mcp = FastMCP("MyServer")
# These are stored in the server's `LocalProvider`
@mcp.tool
def greet(name: str) -> str:
"""Greet someone by name."""
return f"Hello, {name}!"
@mcp.resource("data://config")
def get_config() -> str:
"""Return configuration data."""
return '{"version": "1.0"}'
@mcp.prompt
def analyze(topic: str) -> str:
"""Create an analysis prompt."""
return f"Please analyze: {topic}"
```
The `LocalProvider` is always queried first when clients request components, ensuring that your directly-defined components take precedence over those from mounted or proxied servers.
## Component Registration
### Using Decorators
The most common way to register components:
```python
@mcp.tool
def my_tool(x: int) -> str:
return str(x)
@mcp.resource("data://info")
def my_resource() -> str:
return "info"
@mcp.prompt
def my_prompt(topic: str) -> str:
return f"Discuss: {topic}"
```
### Using Direct Methods
You can also add pre-built component objects:
```python
from fastmcp.tools import Tool
# Create a tool object
my_tool = Tool.from_function(some_function, name="custom_tool")
# Add it to the server
mcp.add_tool(my_tool)
mcp.add_resource(my_resource)
mcp.add_prompt(my_prompt)
```
### Removing Components
Remove components by name or URI:
```python
mcp.remove_tool("my_tool")
mcp.remove_resource("data://info")
mcp.remove_prompt("my_prompt")
```
## Duplicate Handling
When you try to add a component that already exists, the behavior depends on the `on_duplicate` setting:
| Mode | Behavior |
|------|----------|
| `"error"` (default) | Raise `ValueError` |
| `"warn"` | Log warning and replace |
| `"replace"` | Silently replace |
| `"ignore"` | Keep existing component |
Configure this when creating the server:
```python
mcp = FastMCP("MyServer", on_duplicate="warn")
```
## Visibility Control
<VersionBadge version="3.0.0" />
Components can be dynamically enabled or disabled at runtime. Disabled components don't appear in listings and can't be called.
```python
@mcp.tool(tags={"admin"})
def delete_all() -> str:
"""Delete everything."""
return "Deleted"
@mcp.tool
def get_status() -> str:
"""Get system status."""
return "OK"
# Hide admin tools
mcp.disable(tags={"admin"})
# Or only show specific tools
mcp.enable(keys=["tool:get_status"], only=True)
```
See [Visibility](/servers/visibility) for the full documentation on keys, tags, allowlist mode, and provider-level visibility.
## Standalone LocalProvider
You can create a LocalProvider independently and attach it to multiple servers:
```python
from fastmcp import FastMCP
from fastmcp.server.providers import LocalProvider
# Create a reusable provider
shared_tools = LocalProvider()
@shared_tools.tool
def greet(name: str) -> str:
return f"Hello, {name}!"
@shared_tools.resource("data://version")
def get_version() -> str:
return "1.0.0"
# Attach to multiple servers
server1 = FastMCP("Server1", providers=[shared_tools])
server2 = FastMCP("Server2", providers=[shared_tools])
```
This is useful for:
- Sharing components across servers
- Testing components in isolation
- Building reusable component libraries
Standalone providers also support visibility control with `enable()` and `disable()`. See [Visibility](/servers/visibility) for details.