mcpositioner/CLAUDE.md
Ryan Malloy ce93070a04 Add README and CLAUDE.md
README covers installation, hardware setup, tools, prompts, and
cross-server integration with mcnanovna for 3D pattern measurement.
CLAUDE.md provides project guidance for Claude Code.
2026-02-03 09:10:36 -07:00

4.2 KiB

CLAUDE.md

This file provides guidance to Claude Code when working with code in this repository.

What This Is

mcpositioner is a FastMCP server that gives LLMs direct control of an ESP32 dual-axis antenna positioner over WiFi HTTP. It exposes 5 MCP tools and 3 guided workflow prompts for moving stepper motors, homing axes via StallGuard, and orchestrating 3D antenna radiation pattern measurement in coordination with a VNA MCP server (mcnanovna).

Commands

# Run the server
uv run mcpositioner          # from source
uvx mcpositioner             # from installed package
python -m mcpositioner       # module invocation

# Lint and format
ruff check src/
ruff format src/

# Install dependencies
uv sync

# Build/verify ESP32 firmware (requires PlatformIO)
cd firmware && pio run

# Add to Claude Code for testing
claude mcp add mcpositioner-local --scope project -- uv run --directory /home/rpm/claude/ham/nanovna/mcpositioner mcpositioner

There are no tests yet. The project has no README.

Architecture

src/mcpositioner/
  __init__.py        Package docstring
  __main__.py        python -m mcpositioner
  server.py          FastMCP server -- tool registration, version banner, entry point
  positioner.py      Async HTTP client for the ESP32 (httpx)
  tools.py           5 MCP tool methods on a simple PositionerTools class
  prompts.py         3 guided workflow prompts (home, configure, measure_pattern_grid)

firmware/            PlatformIO ESP32 project for antenna positioner hardware
  platformio.ini     Build config: espressif32, AccelStepper, TMCStepper, ESPAsyncWebServer
  include/config.h   Pin assignments, motor constants, TMC2209 UART config
  src/main.cpp       Dual-axis stepper control, HTTP API, mDNS, StallGuard homing

hardware/            KiCad 9 schematic for positioner wiring
  positioner.kicad_pro     Project file (JSON)
  positioner.kicad_sch     Root schematic -- ESP32 + 2x TMC2209 + motors + power
  positioner.kicad_sym     Local symbol library (6 custom module symbols)
  sym-lib-table            Registers local symbol lib with project
  generate_kicad.py        Python generator for all KiCad files (dev tool)

How tools get registered

server.py holds a flat list _TOOL_METHODS of method name strings. create_server() instantiates one PositionerTools object, iterates the list, and wraps each bound method with FunctionTool.from_function(). To add a new tool: add a public method to tools.py then add its name to _TOOL_METHODS.

HTTP client (positioner.py)

The Positioner class wraps httpx async calls to the ESP32's HTTP API:

  • GET /status -- theta/phi position, moving, homed
  • POST /move -- absolute move to theta/phi
  • POST /move/relative -- relative offset move
  • POST /home -- StallGuard sensorless homing
  • POST /stop -- emergency stop
  • GET /config -- read motion parameters
  • POST /config -- update speed, accel, microstepping

Default host: positioner.local (mDNS). Override with MCPOSITIONER_HOST env var.

Cross-server orchestration

This server controls physical positioning only. For 3D antenna pattern measurement, the LLM orchestrates both mcpositioner and mcnanovna (VNA MCP server):

  1. positioner_move (mcpositioner) -- position antenna at theta/phi
  2. scan (mcnanovna) -- measure S21 transmission
  3. Repeat across theta/phi grid
  4. Assemble pattern dict from collected measurements

The measure_pattern_grid prompt provides step-by-step guidance for this workflow.

ESP32 firmware (firmware/)

PlatformIO project targeting ESP32. Controls 2x NEMA 17 steppers via TMC2209 drivers in UART mode. Features:

  • AccelStepper for smooth acceleration profiles
  • TMCStepper for UART configuration and StallGuard sensorless homing
  • ESPAsyncWebServer for the HTTP API
  • mDNS advertisement as positioner.local
  • Pin assignments and motor constants in include/config.h

Key conventions

  • Date-based versioning: 2026.02.02 in pyproject.toml
  • Python 3.11+, ruff at 120-char line length
  • httpx is a core dependency (not optional -- it IS the transport layer)
  • Single PositionerTools instance per server lifetime
  • No mixin pattern -- flat class with 5 methods (single-concern server)
  • Custom exception: PositionerError
  • Env var: MCPOSITIONER_HOST (default positioner.local)