chore: import upstream snapshot with attribution
Upgrade checks / Notify on failure (push) Has been cancelled
Upgrade checks / Close issue on success (push) Has been cancelled
Schema Crash Test / Real-world schema crash test (232K schemas) (push) Has been cancelled
Run static analysis / static_analysis (push) Has been cancelled
Tests / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Tests / Tests with lowest-direct dependencies (push) Has been cancelled
Tests / MCP conformance tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
Tests / Package install smoke (push) Has been cancelled
Upgrade checks / Static analysis (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Upgrade checks / Integration tests (push) Has been cancelled
Update MCPServerConfig Schema / update-config-schema (push) Has been cancelled
Update SDK Documentation / update-sdk-docs (push) Has been cancelled
Upgrade checks / Notify on failure (push) Has been cancelled
Upgrade checks / Close issue on success (push) Has been cancelled
Schema Crash Test / Real-world schema crash test (232K schemas) (push) Has been cancelled
Run static analysis / static_analysis (push) Has been cancelled
Tests / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Tests / Tests with lowest-direct dependencies (push) Has been cancelled
Tests / MCP conformance tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
Tests / Package install smoke (push) Has been cancelled
Upgrade checks / Static analysis (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Upgrade checks / Integration tests (push) Has been cancelled
Update MCPServerConfig Schema / update-config-schema (push) Has been cancelled
Update SDK Documentation / update-sdk-docs (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
# Code Mode
|
||||
|
||||
CodeMode collapses an entire tool catalog into two meta-tools: `search` (keyword-based discovery) and `execute` (run Python scripts that chain tool calls in a sandbox). Instead of burning context tokens on every intermediate result, the LLM writes a script that runs server-side and returns only the final answer.
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
uv run python server.py # in one terminal
|
||||
uv run python client.py # in another
|
||||
```
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
══════════════════ CodeMode Transform ══════════════════
|
||||
|
||||
┌────────────── list_tools() ──────────────┐
|
||||
│ Tool Description │
|
||||
│ search Search for available tools ... │
|
||||
│ execute Chain `await call_tool(...)` ... │
|
||||
└── 8 backend tools collapsed into 2 ──────┘
|
||||
|
||||
┌──── search(query="math arithmetic") ─────┐
|
||||
│ # Tool Description │
|
||||
│ 1 add Add two numbers together. │
|
||||
│ 2 multiply Multiply two numbers. │
|
||||
│ 3 fibonacci Generate the first n ... │
|
||||
└── 3 results ─────────────────────────────┘
|
||||
|
||||
┌────────────── execute ───────────────────┐
|
||||
│ a = await call_tool("add", {"a": 3 ... │
|
||||
│ b = await call_tool("multiply", ... │
|
||||
│ return b │
|
||||
└── result: 14.0 ──────────────────────────┘
|
||||
```
|
||||
|
||||
The key insight: with standard MCP, each `call_tool` is a round-trip through the LLM. With CodeMode, the LLM writes one script and all the tool calls happen server-side. Intermediate data never touches the context window.
|
||||
@@ -0,0 +1,142 @@
|
||||
"""Example: Client using CodeMode to discover and chain tools.
|
||||
|
||||
CodeMode exposes just two tools: `search` (keyword query) and `execute`
|
||||
(run Python code with `call_tool` available). This client demonstrates
|
||||
both: searching for tools, then chaining multiple calls in a single
|
||||
execute block — one round-trip instead of many.
|
||||
|
||||
Run with:
|
||||
uv run python examples/code_mode/client.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.syntax import Syntax
|
||||
from rich.table import Table
|
||||
|
||||
from fastmcp.client import Client
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def _get_result(result) -> Any:
|
||||
"""Extract the value from a CallToolResult (structured or text)."""
|
||||
if result.structured_content is not None:
|
||||
data = result.structured_content
|
||||
if isinstance(data, dict) and set(data) == {"result"}:
|
||||
return data["result"]
|
||||
return data
|
||||
return result.content[0].text
|
||||
|
||||
|
||||
def _format_params(tool: dict) -> str:
|
||||
"""Format inputSchema properties as a compact signature."""
|
||||
schema = tool.get("inputSchema", {})
|
||||
props = schema.get("properties", {})
|
||||
if not props:
|
||||
return "()"
|
||||
parts = []
|
||||
for name, info in props.items():
|
||||
typ = info.get("type", "")
|
||||
parts.append(f"{name}: {typ}" if typ else name)
|
||||
return f"({', '.join(parts)})"
|
||||
|
||||
|
||||
def _tool_table(
|
||||
tools: list[dict], *, ranked: bool = False, show_params: bool = False
|
||||
) -> Table:
|
||||
table = Table(show_header=True, show_edge=False, pad_edge=False, expand=True)
|
||||
if ranked:
|
||||
table.add_column("#", style="dim", width=3, justify="right")
|
||||
table.add_column("Tool", style="cyan", no_wrap=True)
|
||||
if show_params:
|
||||
table.add_column("Parameters", style="dim", no_wrap=True)
|
||||
table.add_column("Description", style="dim")
|
||||
for i, tool in enumerate(tools, 1):
|
||||
row = [tool["name"]]
|
||||
if show_params:
|
||||
row.append(_format_params(tool))
|
||||
row.append(tool.get("description", ""))
|
||||
if ranked:
|
||||
row.insert(0, str(i))
|
||||
table.add_row(*row)
|
||||
return table
|
||||
|
||||
|
||||
async def main():
|
||||
async with Client("examples/code_mode/server.py") as client:
|
||||
console.print()
|
||||
console.rule("[bold]CodeMode[/bold]")
|
||||
console.print()
|
||||
|
||||
# Step 1: list_tools only returns two synthetic meta-tools
|
||||
console.print(
|
||||
"The server has 8 tools. CodeMode replaces them with "
|
||||
"two synthetic tools — [bold]search[/bold] and [bold]execute[/bold]:"
|
||||
)
|
||||
console.print()
|
||||
tools = await client.list_tools()
|
||||
visible = [{"name": t.name, "description": t.description} for t in tools]
|
||||
console.print(
|
||||
Panel(
|
||||
_tool_table(visible),
|
||||
title="[bold]list_tools()[/bold]",
|
||||
title_align="left",
|
||||
border_style="blue",
|
||||
)
|
||||
)
|
||||
console.print()
|
||||
|
||||
# Step 2: search discovers available tools
|
||||
console.print("The LLM calls [bold]search[/bold] to discover available tools:")
|
||||
console.print()
|
||||
result = await client.call_tool("search", {"query": "add multiply numbers"})
|
||||
found = _get_result(result)
|
||||
if isinstance(found, str):
|
||||
found = json.loads(found)
|
||||
console.print(
|
||||
Panel(
|
||||
_tool_table(found, ranked=True, show_params=True),
|
||||
title='[bold]search[/bold] [dim]query="add multiply numbers"[/dim]',
|
||||
title_align="left",
|
||||
border_style="green",
|
||||
)
|
||||
)
|
||||
console.print()
|
||||
|
||||
# Step 3: execute chains tool calls in one round-trip
|
||||
console.print(
|
||||
"Now the LLM writes a Python script that chains "
|
||||
"the tools it found. All of it runs server-side in a "
|
||||
"sandbox — [bold]one round-trip[/bold], intermediate "
|
||||
"data never hits the context window:"
|
||||
)
|
||||
console.print()
|
||||
code = """\
|
||||
a = await call_tool("add", {"a": 3, "b": 4})
|
||||
b = await call_tool("multiply", {"x": a["result"], "y": 2})
|
||||
fib = await call_tool("fibonacci", {"n": b["result"]})
|
||||
return {"sum": a["result"], "product": b["result"], "fibonacci": fib["result"]}
|
||||
"""
|
||||
result = await client.call_tool("execute", {"code": code})
|
||||
console.print(
|
||||
Panel(
|
||||
Syntax(code.strip(), "python", theme="monokai"),
|
||||
title="[bold]execute[/bold]",
|
||||
title_align="left",
|
||||
border_style="yellow",
|
||||
)
|
||||
)
|
||||
console.print()
|
||||
|
||||
# Final result
|
||||
console.print(f" Result: [bold green]{_get_result(result)}[/bold green]")
|
||||
console.print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,84 @@
|
||||
"""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.code_mode 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()
|
||||
Reference in New Issue
Block a user