feat: add snap_to_grid schematic tool

Adds a new MCP tool that snaps wire endpoints, junction positions, and
net label coordinates to the nearest grid point (default 2.54 mm). Off-grid
coordinates cause wires that appear visually connected to fail ERC checks
because KiCAD uses exact IU integer matching internally; this tool eliminates
that class of error before running ERC.

- python/commands/schematic_snap.py: core snap logic with in-place sexp mutation
- python/kicad_interface.py: route + handler
- python/schemas/tool_schemas.py: JSON schema (gridSize, elements params)
- src/tools/schematic.ts: TypeScript MCP tool registration
- tests/test_snap_to_grid.py: 20 unit + integration tests (all passing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Eugene Mikhantyev
2026-04-12 15:03:35 +01:00
parent 58bb08a252
commit 9387683368
5 changed files with 539 additions and 0 deletions

View File

@@ -1814,6 +1814,52 @@ SCHEMATIC_TOOLS = [
"required": ["schematicPath"],
},
},
{
"name": "snap_to_grid",
"title": "Snap Schematic Elements to Grid",
"description": (
"Snap schematic element coordinates to the nearest grid point. "
"KiCAD eeschema uses exact integer matching (10 000 IU/mm) for connectivity, "
"so even a sub-pixel coordinate offset will make wires appear connected visually "
"but fail ERC checks. Running this tool before ERC eliminates that class of error. "
"Modifies the .kicad_sch file in place. "
"Does not require the KiCAD UI to be running."
),
"inputSchema": {
"type": "object",
"properties": {
"schematicPath": {
"type": "string",
"description": "Path to the .kicad_sch schematic file",
},
"gridSize": {
"type": "number",
"description": (
"Grid spacing in mm. "
"Standard KiCAD schematic grid is 2.54 mm (0.1 inch). "
"Use 1.27 mm for high-density layouts. "
"Defaults to 2.54."
),
"default": 2.54,
},
"elements": {
"type": "array",
"description": (
"Element types to snap. "
'Valid values: "wires", "junctions", "labels", "components". '
'Defaults to ["wires", "junctions", "labels"] when omitted. '
'"components" is opt-in because moving a component without re-routing '
"its wires will create new mismatches."
),
"items": {
"type": "string",
"enum": ["wires", "junctions", "labels", "components"],
},
},
},
"required": ["schematicPath"],
},
},
]
# =============================================================================