fastmcp/examples/code_mode/server.py
Adam Azzam b9153404f4
Add experimental CodeMode transform (#3297)
* code mode

* update uv.lock for monty optional dep

🤖 Generated with Claude Code

* retry CI

* Address PR review comments on CodeMode transform

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix ty unresolved-attribute error on search_helper

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* more idiomacy

* harden

* fix docs

* harden

* fix red CI

* Refactor CodeMode to use CatalogTransform base class

Removes the duplicate ContextVar bypass pattern in favor of the shared
CatalogTransform machinery. Also fixes a pre-existing bug where
`from __future__ import annotations` caused NameError for Annotated
in nested function scopes at runtime.

* Remove redundant _get_visible_tools wrapper in CodeMode

* Rewrite CodeMode docs with proper motivation and structure

* Fix type narrowing in collision test

* Stop unwrapping tool results in CodeMode's call_tool

call_tool() inside execute blocks now returns structured content as-is,
preserving the {"result": value} wrapping. This means the output schema
shown in search results accurately describes what call_tool() returns,
so LLMs can trust the schema when writing code.

Also adds examples/code_mode/ with a server and narrated client demo.

* Simplify call_tool return type: dict | str

* Fix example client to unwrap structured results

* Let server resolve tool versions instead of pinning first match

* Rewrite CodeMode docs to match current behavior

* Rename optional extra from monty to code-mode

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jeremiah Lowin <153965+jlowin@users.noreply.github.com>
2026-02-27 12:14:03 -05:00

84 lines
1.9 KiB
Python

"""Example: CodeMode transform — search and execute tools via code.
CodeMode replaces the entire tool catalog with two meta-tools: `search`
(keyword-based tool discovery) and `execute` (run Python code that chains
tool calls in a sandbox). This dramatically reduces round-trips and
context window usage when an LLM needs to orchestrate many tools.
Requires pydantic-monty for the sandbox:
pip install "fastmcp[code-mode]"
Run with:
uv run python examples/code_mode/server.py
"""
from fastmcp import FastMCP
from fastmcp.experimental.transforms import CodeMode
mcp = FastMCP("CodeMode Demo")
@mcp.tool
def add(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
@mcp.tool
def multiply(x: float, y: float) -> float:
"""Multiply two numbers."""
return x * y
@mcp.tool
def fibonacci(n: int) -> list[int]:
"""Generate the first n Fibonacci numbers."""
if n <= 0:
return []
seq = [0, 1]
while len(seq) < n:
seq.append(seq[-1] + seq[-2])
return seq[:n]
@mcp.tool
def reverse_string(text: str) -> str:
"""Reverse a string."""
return text[::-1]
@mcp.tool
def word_count(text: str) -> int:
"""Count the number of words in a text."""
return len(text.split())
@mcp.tool
def to_uppercase(text: str) -> str:
"""Convert text to uppercase."""
return text.upper()
@mcp.tool
def list_files(directory: str) -> list[str]:
"""List files in a directory."""
import os
return os.listdir(directory)
@mcp.tool
def read_file(path: str) -> str:
"""Read the contents of a file."""
with open(path) as f:
return f.read()
# CodeMode collapses all 8 tools into just `search` + `execute`.
# The LLM discovers tools via keyword search, then writes Python
# scripts that chain multiple tool calls in a single round-trip.
mcp.add_transform(CodeMode())
if __name__ == "__main__":
mcp.run()