Skip to content

local-dev-loop

The fastest possible feedback loop: edit a Python tool, hit save, have Claude Desktop pick up the change on the next message - all without touching the Platos platform.

What’s in the box

examples/local-dev-loop/
├── README.md
├── pyproject.toml
├── .env.example
├── .gitignore
├── tools.py # three toy @platools.tool() functions
├── platools-tests.yaml # smoke tests
└── claude_desktop_config.json # sample Claude Desktop server entry

This example intentionally keeps everything in a single tools.py file - no FastAPI host, no package layout, no platools.connect(). The whole point is to show how short the “decorate -> run -> call from an LLM” path really is.

The tools

tools.py defines three sample tools you can call from Claude Desktop or Cursor immediately:

  • echo(text) -> str - no-auth sanity check. Confirms the transport is wired up.
  • word_count(text) -> dict[str, int] - returns {"words": N, "chars": M}. Deterministic, fast, useful for doctor + test smoke coverage.
  • stop_watch(label) -> dict[str, float] - starts / stops a named timer and returns the elapsed seconds. Demonstrates stateful tools (state lives in a module-level dict).

Running it

Terminal window
cd examples/local-dev-loop
# One-time setup
cp .env.example .env # empty is fine - this example doesn't connect anywhere
uv sync
# Ship gate
uv run platools doctor tools
# Run the smoke tests (no platform needed)
uv run platools test --module tools
# Launch the local MCP server over stdio - this is what Claude Desktop runs
uv run platools serve --module tools --list # dry run: print tools and exit
uv run platools serve --module tools # real run: wait on stdin for JSON-RPC

Wiring Claude Desktop

Copy the sample config into your Claude Desktop config file:

{
"mcpServers": {
"platools-local": {
"command": "uv",
"args": ["run", "--project", "/absolute/path/to/examples/local-dev-loop", "platools", "serve", "--module", "tools"],
"cwd": "/absolute/path/to/examples/local-dev-loop"
}
}
}

Edit the absolute paths to match your machine, restart Claude Desktop, and the three tools appear in the tool picker. The --list dry run is the fastest way to sanity-check the path before restarting the app.

What to learn from it

  1. Local MCP is a first-class ship target. platools serve isn’t a toy - it runs the same dispatcher the WebSocket transport uses, with the same doctor gate, under HTTP or stdio.
  2. Stateful tools are fine. Module-level state survives between calls as long as the stdio server stays up. Claude Desktop keeps the subprocess alive for the whole conversation.
  3. The feedback loop is instant. Edit tools.py, save, and the next restart of the server picks up the change. No platform deploys, no webhook plumbing.
  4. Doctor-then-serve. The platools serve command runs doctor automatically before starting the transport, so you can’t accidentally expose a broken registry.

Source: examples/local-dev-loop/

Next steps

  • Local mode - full platools serve reference with HTTP mode and CI examples.
  • python-billing-agent - a more realistic example with FastAPI hosting.
  • CLI - doctor, test, and serve subcommand reference.