mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-22 21:44:18 +02:00
Convert code-mode to the CodeMode plugin (#4002)
This commit is contained in:
parent
3c0d248526
commit
19fa2fc33e
12 changed files with 1019 additions and 645 deletions
|
|
@ -10,9 +10,9 @@ import { VersionBadge } from '/snippets/version-badge.mdx'
|
|||
|
||||
<VersionBadge version="3.1.0" />
|
||||
|
||||
<Warning>
|
||||
CodeMode is experimental. The core interface is stable, but the specific discovery tools and their parameters may evolve as we learn more about what works best in practice.
|
||||
</Warning>
|
||||
<Note>
|
||||
The core interface is stable, but the specific discovery tools and their parameters may evolve as we learn more about what works best in practice.
|
||||
</Note>
|
||||
|
||||
Standard MCP tool usage has two scaling problems. First, every tool in the catalog is loaded into the LLM's context upfront — with hundreds of tools, that's tens of thousands of tokens spent before the LLM even reads the user's request. Second, every tool call is a round-trip: the LLM calls a tool, the result passes back through the context window, the LLM reasons about it, calls another tool, and so on. Intermediate results that only exist to feed the next step still burn tokens flowing through the model.
|
||||
|
||||
|
|
@ -26,13 +26,13 @@ The approach was introduced by Cloudflare in [Code Mode](https://blog.cloudflare
|
|||
CodeMode requires the `code-mode` extra for sandbox support. Install it with `pip install "fastmcp[code-mode]"`.
|
||||
</Tip>
|
||||
|
||||
You take a normal server with normally registered tools and add a `CodeMode` transform. The transform wraps your existing tools in the code mode machinery — your tool functions don't change at all:
|
||||
You take a normal server with normally registered tools and attach the `CodeMode` plugin. The plugin wraps your existing tools in the code mode machinery — your tool functions don't change at all:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.experimental.transforms.code_mode import CodeMode
|
||||
from fastmcp.server.plugins.code_mode import CodeMode
|
||||
|
||||
mcp = FastMCP("Server", transforms=[CodeMode()])
|
||||
mcp = FastMCP("Server", plugins=[CodeMode()])
|
||||
|
||||
@mcp.tool
|
||||
def add(x: int, y: int) -> int:
|
||||
|
|
@ -165,7 +165,7 @@ If your tools use [tags](/servers/tools#tags), Search also accepts a `tags` para
|
|||
`ListTools` isn't included in the defaults — for large catalogs, search-based discovery is more token-efficient. But for smaller catalogs (under ~20 tools), letting the LLM see everything upfront can be faster than multiple search round-trips:
|
||||
|
||||
```python
|
||||
from fastmcp.experimental.transforms.code_mode import CodeMode, ListTools, GetSchemas
|
||||
from fastmcp.server.plugins.code_mode import CodeMode, ListTools, GetSchemas
|
||||
|
||||
code_mode = CodeMode(
|
||||
discovery_tools=[ListTools(), GetSchemas()],
|
||||
|
|
@ -182,23 +182,23 @@ The default. The LLM searches for candidates, inspects schemas for the ones it w
|
|||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.experimental.transforms.code_mode import CodeMode
|
||||
from fastmcp.server.plugins.code_mode import CodeMode
|
||||
|
||||
mcp = FastMCP("Server", transforms=[CodeMode()])
|
||||
mcp = FastMCP("Server", plugins=[CodeMode()])
|
||||
```
|
||||
|
||||
If your tools use [tags](/servers/tools#tags), add `GetTags` so the LLM can browse by category before searching — giving it four stages of progressive disclosure:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.experimental.transforms.code_mode import CodeMode
|
||||
from fastmcp.experimental.transforms.code_mode import GetTags, Search, GetSchemas
|
||||
from fastmcp.server.plugins.code_mode import CodeMode
|
||||
from fastmcp.server.plugins.code_mode import GetTags, Search, GetSchemas
|
||||
|
||||
code_mode = CodeMode(
|
||||
discovery_tools=[GetTags(), Search(), GetSchemas()],
|
||||
)
|
||||
|
||||
mcp = FastMCP("Server", transforms=[code_mode])
|
||||
mcp = FastMCP("Server", plugins=[code_mode])
|
||||
```
|
||||
|
||||
### Two-Stage
|
||||
|
|
@ -207,14 +207,14 @@ Search returns parameter schemas inline, so the LLM can go straight from search
|
|||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.experimental.transforms.code_mode import CodeMode
|
||||
from fastmcp.experimental.transforms.code_mode import Search, GetSchemas
|
||||
from fastmcp.server.plugins.code_mode import CodeMode
|
||||
from fastmcp.server.plugins.code_mode import Search, GetSchemas
|
||||
|
||||
code_mode = CodeMode(
|
||||
discovery_tools=[Search(default_detail="detailed"), GetSchemas()],
|
||||
)
|
||||
|
||||
mcp = FastMCP("Server", transforms=[code_mode])
|
||||
mcp = FastMCP("Server", plugins=[code_mode])
|
||||
```
|
||||
|
||||
`GetSchemas` is still available as a fallback — the LLM can call it with `detail="full"` if it encounters a tool with complex nested parameters where the compact markdown isn't enough.
|
||||
|
|
@ -225,7 +225,7 @@ Skip discovery entirely and bake tool instructions into the execute tool's descr
|
|||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
from fastmcp.experimental.transforms.code_mode import CodeMode
|
||||
from fastmcp.server.plugins.code_mode import CodeMode
|
||||
|
||||
code_mode = CodeMode(
|
||||
discovery_tools=[],
|
||||
|
|
@ -237,7 +237,7 @@ code_mode = CodeMode(
|
|||
),
|
||||
)
|
||||
|
||||
mcp = FastMCP("Server", transforms=[code_mode])
|
||||
mcp = FastMCP("Server", plugins=[code_mode])
|
||||
```
|
||||
|
||||
## Custom Discovery Tools
|
||||
|
|
@ -247,8 +247,8 @@ Discovery tools are composable — you can mix the built-ins with your own. Each
|
|||
Here's a minimal example:
|
||||
|
||||
```python
|
||||
from fastmcp.experimental.transforms.code_mode import CodeMode
|
||||
from fastmcp.experimental.transforms.code_mode import GetToolCatalog, GetSchemas
|
||||
from fastmcp.server.plugins.code_mode import CodeMode
|
||||
from fastmcp.server.plugins.code_mode import GetToolCatalog, GetSchemas
|
||||
from fastmcp.server.context import Context
|
||||
from fastmcp.tools.tool import Tool
|
||||
|
||||
|
|
@ -268,7 +268,7 @@ The LLM sees the docstring of each discovery tool's inner function as its descri
|
|||
Discovery tools and the execute tool can also have custom names:
|
||||
|
||||
```python
|
||||
from fastmcp.experimental.transforms.code_mode import Search, GetSchemas
|
||||
from fastmcp.server.plugins.code_mode import Search, GetSchemas
|
||||
|
||||
code_mode = CodeMode(
|
||||
discovery_tools=[
|
||||
|
|
@ -278,7 +278,7 @@ code_mode = CodeMode(
|
|||
execute_tool_name="run_workflow",
|
||||
)
|
||||
|
||||
mcp = FastMCP("Server", transforms=[code_mode])
|
||||
mcp = FastMCP("Server", plugins=[code_mode])
|
||||
```
|
||||
|
||||
## Sandbox Configuration
|
||||
|
|
@ -288,14 +288,14 @@ mcp = FastMCP("Server", transforms=[code_mode])
|
|||
The default `MontySandboxProvider` can enforce execution limits — timeouts, memory caps, recursion depth, and more. Without limits, LLM-generated scripts can run indefinitely.
|
||||
|
||||
```python
|
||||
from fastmcp.experimental.transforms.code_mode import CodeMode
|
||||
from fastmcp.experimental.transforms.code_mode import MontySandboxProvider
|
||||
from fastmcp.server.plugins.code_mode import CodeMode
|
||||
from fastmcp.server.plugins.code_mode import MontySandboxProvider
|
||||
|
||||
sandbox = MontySandboxProvider(
|
||||
limits={"max_duration_secs": 10, "max_memory": 50_000_000},
|
||||
)
|
||||
|
||||
mcp = FastMCP("Server", transforms=[CodeMode(sandbox_provider=sandbox)])
|
||||
mcp = FastMCP("Server", plugins=[CodeMode(sandbox_provider=sandbox)])
|
||||
```
|
||||
|
||||
All keys are optional — omit any to leave that dimension uncapped:
|
||||
|
|
@ -316,8 +316,8 @@ You can replace the default sandbox with any object implementing the `SandboxPro
|
|||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from fastmcp.experimental.transforms.code_mode import CodeMode
|
||||
from fastmcp.experimental.transforms.code_mode import SandboxProvider
|
||||
from fastmcp.server.plugins.code_mode import CodeMode
|
||||
from fastmcp.server.plugins.code_mode import SandboxProvider
|
||||
|
||||
class RemoteSandboxProvider:
|
||||
async def run(
|
||||
|
|
@ -332,7 +332,7 @@ class RemoteSandboxProvider:
|
|||
|
||||
mcp = FastMCP(
|
||||
"Server",
|
||||
transforms=[CodeMode(sandbox_provider=RemoteSandboxProvider())],
|
||||
plugins=[CodeMode(sandbox_provider=RemoteSandboxProvider())],
|
||||
)
|
||||
```
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue