fastmcp/examples/code_mode/README.md
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

1.8 KiB

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

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.