platools serve - Local MCP mode
platools serve spins up a local MCP server directly from your @platools.tool() registry, with no Platos platform in the loop. It’s the right choice for:
- IDE integrations - Claude Desktop, Cursor, and any other MCP client that launches servers as child processes.
- Quick demos - show your tools to stakeholders without deploying the platform.
- CI dry-runs - spin up an HTTP server, hit
tools/list, assert the shape, tear down. - Local development - iterate on a tool without waiting for a cloud round-trip.
Implementation lives in platools/cli/serve.py, platools/serve/http.py, platools/serve/stdio.py, and platools/serve/dispatcher.py.
Two transports
# Stdio (default) - JSON-RPC over stdin/stdout, ideal for subprocess launchplatools serve --module my_app.tools
# HTTP transport - JSON-RPC over HTTP/1.1 on 127.0.0.1:3001 by defaultplatools serve --module my_app.tools --http \ --port 3001 \ --auth-token "$PLATOOLS_SERVE_TOKEN"Rules of the road (from cli/serve.py):
- Tool discovery mirrors
platools doctor/platools test- scan an imported module forPlatoolsinstances, merge their registries. Zero tools -> refuse to start with exit code 1. - Doctor gate. The CLI runs
platools doctoragainst the composite registry before the transport starts. Any error-severity finding aborts the start with exit code 1. - HTTP requires a bearer token. Passed as
--auth-tokenorPLATOOLS_SERVE_TOKEN. Stdio is trusted by its parent process and carries no token. - HTTP binds 127.0.0.1 by default. Use
--host 0.0.0.0if you know what you’re doing (e.g. exposing a dev server to a container on the same host). The server useshmac.compare_digeston the bearer token so timing attacks can’t walk it out. - 1 MiB request body cap. HTTP mode rejects larger requests with 413 before reading them into memory.
- No chunked transfer encoding. HTTP mode speaks a narrow subset of HTTP/1.1 - clients must send
Content-Length. Chunked requests get a 411.
Using it with Claude Desktop
Add a server entry to your claude_desktop_config.json:
{ "mcpServers": { "my-app": { "command": "platools", "args": ["serve", "--module", "my_app.tools"], "cwd": "/path/to/my_app" } }}Restart Claude Desktop. The tools from my_app.tools show up in the tool picker immediately. Because the stdio transport is trusted by its parent, no auth token is needed - Claude Desktop owns the subprocess.
Using it with Cursor
Cursor’s MCP config takes the same shape - command + args + cwd. Wire it up exactly like the Claude Desktop example above.
HTTP mode for CI
#!/usr/bin/env bashset -euo pipefail
export PLATOOLS_SERVE_TOKEN="$(openssl rand -hex 32)"
platools serve --module my_app.tools --http --port 3001 &SERVER_PID=$!trap "kill $SERVER_PID" EXIT
# Give the server a beat to bind the portsleep 0.5
curl -fsS \ -H "Authorization: Bearer $PLATOOLS_SERVE_TOKEN" \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}' \ http://127.0.0.1:3001/mcp | jq '.result.tools | length'Use the bearer token for any production-ish deploy - even on localhost, an unauthenticated tool surface is an invitation to a misbehaving neighbor process.
Tool allowlisting
Only expose a subset of your registry:
platools serve --module my_app.tools \ --tool process_refund \ --tool list_invoicesRepeat --tool for each allowlisted name. Typos fail loudly at startup, not silently at tools/list time - the CLI validates every name against the live registry before starting the transport.
Exit codes
| Code | Meaning |
|---|---|
0 | Clean shutdown (stdio EOF, HTTP stop_event fired). |
1 | Startup refused - empty registry, doctor errors, missing auth token, bad host/port/path. |
2 | Argparse / config errors (bad --tool name, invalid --transport). |
Development vs. production
platools serve (local) | Platos platform (production) | |
|---|---|---|
| Transport | stdio or HTTP on localhost | Outbound WebSocket to wss:// |
| Auth | Optional bearer token (HTTP) / none (stdio) | JWT via PLATOS_SECRET |
| Monitoring | None | Tool call logs, latency, quality checks |
| Multi-agent | Single client at a time | Multiple agents, concurrent sessions |
| Use case | Development, IDE integration, CI | Production workloads |
Use platools serve to iterate locally, then switch to platools.connect() for production.
Next steps
- CLI reference - full
platools serveoptions alongsidedoctorandtest. - Getting Started - install the SDK and decorate your first tool.
- Examples - runnable integrations, including the
local-dev-loopexample.