Python library for reading and writing KiCAD schematic files
Generates valid .kicad_sch files that open in KiCAD 7/8. Focus on exact format preservation and simple API design.
Read and write KiCAD schematic files programmatically. This library parses and generates .kicad_sch files with exact format preservation - output matches KiCAD's native formatting byte-for-byte.
- Exact format preservation - Output matches KiCAD's native formatting byte-for-byte
- Standalone library - No KiCAD installation required
- Simple API - Object-oriented interface for components, wires, labels, and symbols
- Tested compatibility - 70+ tests verify format preservation against KiCAD reference files
- KiCAD library access - Read actual KiCAD symbol libraries for component validation
- Connectivity analysis - Trace electrical connections through wires, labels, and hierarchy
- Hierarchical design - Complete support for multi-sheet schematic projects
- Component bounding boxes - Calculate precise component boundaries for layout algorithms
- Wire routing - Manhattan-style orthogonal routing with basic obstacle avoidance
- BOM property management - Audit, update, and transform component properties for manufacturing
- MCP server - 15 tools for programmatic schematic manipulation via Model Context Protocol
# Install from PyPI
pip install kicad-sch-api
# Or install from source
git clone https://github.com/circuit-synth/kicad-sch-api.git
cd kicad-sch-api
uv pip install -e .import kicad_sch_api as ksa
# Create a new schematic
sch = ksa.create_schematic("My Circuit")
# Add components with proper validation
resistor = sch.components.add(
lib_id="Device:R",
reference="R1",
value="10k",
position=(100.0, 100.0),
footprint="Resistor_SMD:R_0603_1608Metric"
)
# Add wires for connectivity
sch.wires.add(start=(100, 110), end=(150, 110))
# Pin-to-pin wiring
wire_uuid = sch.add_wire_between_pins("R1", "2", "C1", "1")
# Add labels for nets
sch.add_label("VCC", position=(125, 110))
# Save with exact format preservation
sch.save("my_circuit.kicad_sch")Understanding this is CRITICAL for working with this library.
KiCAD uses two different Y-axis conventions:
- Symbol Space (library definitions): Normal Y-axis (+Y is UP, like math)
- Schematic Space (placed components): Inverted Y-axis (+Y is DOWN, like graphics)
When placing a symbol on a schematic, Y coordinates are negated:
# Symbol library (normal Y, +Y up):
Pin 1: (0, +3.81) # 3.81mm UPWARD in symbol
Pin 2: (0, -3.81) # 3.81mm DOWNWARD in symbol
# Component placed at (100, 100) in schematic (inverted Y, +Y down):
# Y is NEGATED during transformation:
Pin 1: (100, 100 + (-3.81)) = (100, 96.52) # LOWER Y = visually HIGHER
Pin 2: (100, 100 + (+3.81)) = (100, 103.81) # HIGHER Y = visually LOWERIn schematic space (inverted Y-axis):
- Lower Y values = visually HIGHER on screen (top)
- Higher Y values = visually LOWER on screen (bottom)
- X-axis is normal (increases to the right)
ALL positions MUST be grid-aligned:
- Default grid: 1.27mm (50 mil)
- Component positions, wire endpoints, pin positions, labels must all align to grid
- Common values: 0.00, 1.27, 2.54, 3.81, 5.08, 6.35, 7.62, 8.89, 10.16...
# Good - on grid
sch.components.add('Device:R', 'R1', '10k', position=(100.33, 101.60))
# Bad - off grid (will cause connectivity issues)
sch.components.add('Device:R', 'R2', '10k', position=(100.5, 101.3))This coordinate system is critical for:
- Pin position calculations
- Wire routing and connectivity
- Component placement
- Hierarchical connections
- Electrical connectivity detection
# Add and manage components
resistor = sch.components.add("Device:R", "R1", "10k", (100, 100))
# Search and filter
resistors = sch.components.find(lib_id_pattern='Device:R*')
# Bulk updates
sch.components.bulk_update(
criteria={'lib_id': 'Device:R'},
updates={'properties': {'Tolerance': '1%'}}
)
# Remove components
sch.components.remove("R1")π See API Reference for complete component API
# Read text effects from component properties
r1 = sch.components.get("R1")
effects = r1.get_property_effects("Reference")
# Returns: {'font_face': 'Arial', 'font_size': (2.0, 2.0), 'bold': True, ...}
# Modify text effects (partial updates - preserves other effects)
r1.set_property_effects("Reference", {
"color": (0, 255, 0, 1.0), # Green
"bold": True,
"font_size": (2.0, 2.0)
})
# Create components with custom styling
r2 = sch.components.add("Device:R", "R2", "10k", position=(100, 100))
r2.set_property_effects("Value", {
"rotation": 90.0, # Sideways
"justify_h": "left", # Left justified
"color": (160, 32, 240, 1.0), # Purple
"italic": True
})
# Supported effects: position, rotation, font_face, font_size, font_thickness,
# bold, italic, color (RGBA), justify_h, justify_v, visibleπ See API Reference for text effects details
# Check if pins are electrically connected
if sch.are_pins_connected("R1", "2", "R2", "1"):
print("Connected!")
# Get net information
net = sch.get_net_for_pin("R1", "2")
print(f"Net: {net.name}, Pins: {len(net.pins)}")
# Get all connected pins
connected = sch.get_connected_pins("R1", "2")Connectivity analysis includes:
- Direct wire connections
- Connections through junctions
- Local and global labels
- Hierarchical labels (cross-sheet)
- Power symbols (VCC, GND)
- Sheet pins (parent/child)
π See API Reference for complete connectivity API
# Build hierarchy tree
tree = sch.hierarchy.build_hierarchy_tree(sch, schematic_path)
# Find reused sheets
reused = sch.hierarchy.find_reused_sheets()
for filename, instances in reused.items():
print(f"{filename} used {len(instances)} times")
# Validate sheet connections
connections = sch.hierarchy.validate_sheet_pins()
errors = sch.hierarchy.get_validation_errors()
# Trace signals through hierarchy
paths = sch.hierarchy.trace_signal_path("VCC")
# Flatten design
flattened = sch.hierarchy.flatten_hierarchy(prefix_references=True)
# Visualize hierarchy
print(sch.hierarchy.visualize_hierarchy(include_stats=True))π See Hierarchy Features Guide for complete hierarchy documentation
# Direct pin-to-pin wiring
sch.add_wire_between_pins("R1", "2", "R2", "1")
# Manhattan routing with obstacle avoidance
wires = sch.auto_route_pins(
"R1", "2", "R2", "1",
routing_mode="manhattan",
avoid_components=True
)
# Get pin positions
pos = sch.get_component_pin_position("R1", "1")π See Recipes for routing patterns and examples
from kicad_sch_api.core.component_bounds import get_component_bounding_box
# Get bounding box
bbox = get_component_bounding_box(resistor, include_properties=False)
print(f"Size: {bbox.width:.2f}Γ{bbox.height:.2f}mm")
# Visualize with rectangles
sch.draw_bounding_box(bbox, stroke_color="blue")
sch.draw_component_bounding_boxes(include_properties=True)π See API Reference for bounding box details
import kicad_sch_api as ksa
# Customize property positioning
ksa.config.properties.reference_y = -2.0
ksa.config.properties.value_y = 2.0
# Tolerances
ksa.config.tolerance.position_tolerance = 0.05
# Grid settings
ksa.config.grid.component_spacing = 5.0π See API Reference for all configuration options
The library automatically discovers KiCAD symbol libraries from:
- Environment variables (
KICAD_SYMBOL_DIR,KICAD8_SYMBOL_DIR,KICAD7_SYMBOL_DIR) - Standard KiCAD installations (version-flexible detection)
- User document directories
Set environment variable (Unix/macOS):
# Single path
export KICAD_SYMBOL_DIR=/path/to/kicad/symbols
# Multiple paths (colon-separated)
export KICAD_SYMBOL_DIR=/path/to/symbols:/path/to/custom/symbolsSet environment variable (Windows):
# Single path
set KICAD_SYMBOL_DIR=C:\KiCad\symbols
# Multiple paths (semicolon-separated)
set KICAD_SYMBOL_DIR=C:\KiCad\symbols;D:\Custom\symbolsAdd library paths programmatically:
import kicad_sch_api as ksa
# Get cache
cache = ksa.library.get_symbol_cache()
# Add specific library file
cache.add_library_path("/path/to/Device.kicad_sym")
# Discover all libraries in directory
cache.discover_libraries(["/path/to/custom/symbols"])π See Library Configuration Guide for complete documentation
Learn by example with our polished reference circuits in the examples/ directory:
- voltage_divider.py - Simple 10k/10k voltage divider with grid-based parametric design
- rc_filter.py - RC low-pass filter demonstrating wire routing and junctions
- power_supply.py - AMS1117-3.3 voltage regulator (5V β 3.3V) with power symbols
- stm32_simple.py - STM32G030K8Tx with reset circuit, LED, and SWD debug interface
- COMBINED.py - Master script that generates all example schematics at once
Start with WALKTHROUGH.md - a complete tutorial from basics to advanced parametric circuits.
All examples use:
- Grid-based positioning with intuitive grid units (1.27mm)
- Parametric
p(x, y)helper for clean, readable coordinates - Individual
add_wire()calls with descriptive comments - Proper wire junctions at all T-connection points
- Comprehensive inline documentation
# Run any example
uv run python examples/voltage_divider.py
uv run python examples/rc_filter.py
uv run python examples/power_supply.py
uv run python examples/stm32_simple.py
# Or run all examples at once
uv run python examples/COMBINED.pyFor comprehensive documentation on all features:
- API Reference - Complete API documentation with examples
- Hierarchy Features - Multi-sheet design guide
- Recipes - Common patterns and examples
- Getting Started - Detailed tutorial
- Architecture - Library design and internals
Includes an MCP (Model Context Protocol) server with 15 tools for programmatic schematic manipulation:
# Start the MCP server
uv run kicad-sch-mcp
# Or install and run directly
pip install kicad-sch-api
kicad-sch-mcpComponent Management (5 tools):
add_component- Add components with auto-reference/positionlist_components- List all components with metadataupdate_component- Update properties (value, position, rotation, footprint)remove_component- Remove componentsfilter_components- Advanced filtering by lib_id, value, footprint
Connectivity (3 tools):
add_wire- Create wire connections between pointsadd_label- Add net labels for logical connectionsadd_junction- Add wire junctions for T-connections
Pin Discovery (3 tools):
get_component_pins- Complete pin information with positionsfind_pins_by_name- Semantic lookup with wildcards (, CLK, IN)find_pins_by_type- Filter by electrical type (passive, input, output, power_in)
Schematic Management (4 tools):
create_schematic- Create new KiCAD schematicsload_schematic- Load existing .kicad_sch filessave_schematic- Save schematics to diskget_schematic_info- Query schematic metadata
The MCP server provides tools for:
- Adding components, creating connections, and labeling nets
- Creating, loading, saving, and modifying schematic files
- Listing components, filtering by criteria, and discovering pin information
- Building circuits like voltage dividers, filters, LED drivers, and power supplies
Natural Language Request:
"Create a voltage divider with R1=10k and R2=20k, fully wired with VCC and GND labels"
The AI agent executes:
create_schematic(name="Voltage Divider")- Create new schematicadd_component(lib_id="Device:R", reference="R1", value="10k", position=(127.0, 76.2))- Add R1add_component(lib_id="Device:R", reference="R2", value="20k", position=(127.0, 95.25))- Add R2get_component_pins("R1")- Get R1 pin positionsget_component_pins("R2")- Get R2 pin positionsadd_wire(start=(127.0, 72.39), end=(127.0, 66.04))- VCC to R1add_wire(start=(127.0, 80.01), end=(127.0, 91.44))- R1 to R2add_wire(start=(127.0, 99.06), end=(127.0, 105.41))- R2 to GNDadd_label(text="VCC", position=(129.54, 66.04))- Add VCC labeladd_label(text="VOUT", position=(129.54, 85.725))- Add output labeladd_label(text="GND", position=(129.54, 105.41))- Add GND labeladd_junction(position=(127.0, 85.725))- Add junction at tap pointsave_schematic(file_path="voltage_divider.kicad_sch")- Save to disk
Result: β Fully functional KiCAD schematic verified to open perfectly in KiCAD!
Natural Language Request:
"Create an LED circuit with 220Ξ© current limiting resistor"
The AI agent will:
- Add LED and 220Ξ© resistor components
- Wire VCC β resistor β LED β GND
- Add appropriate net labels
- Save the complete circuit
Result: Ready-to-use LED driver circuit schematic!
Natural Language Request:
"Create an RC low-pass filter with R=10k, C=100nF"
The AI agent will:
- Add resistor (10k) and capacitor (100nF)
- Wire input β R β C β output
- Add GND connection to capacitor
- Label INPUT, OUTPUT, and GND nets
- Add junction at output tap
- Save filter schematic
Result: Complete filter circuit with proper connectivity!
Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"kicad-sch-api": {
"command": "uv",
"args": ["run", "kicad-sch-mcp"],
"env": {}
}
}
}MCP tools enable programmatic creation, modification, and analysis of KiCAD schematics.
π Complete Documentation:
- MCP Setup Guide - Installation, configuration, and troubleshooting
- MCP Examples - Comprehensive usage examples and patterns
- API Reference - Complete API documentation
- Building Block First: Designed to be the foundation for other tools
- Exact Format Preservation: Guaranteed byte-perfect KiCAD output
- Comprehensive Validation: Error handling and input validation
- MCP Integration: Includes MCP server for programmatic schematic manipulation
- Performance Optimized: Fast operations on large schematics
π See Architecture Guide for detailed design documentation
# Run all tests (29 tests covering all functionality)
uv run pytest tests/ -v
# Format preservation tests (critical - exact KiCAD output matching)
uv run pytest tests/reference_tests/ -v
# Code quality checks
uv run black kicad_sch_api/ tests/
uv run mypy kicad_sch_api/- Format Preservation: Byte-for-byte compatibility with KiCAD native files
- Component Management: Creation, modification, and removal
- Connectivity: Wire tracing, net analysis, hierarchical connections
- Hierarchy: Multi-sheet designs, sheet reuse, signal tracing
- Integration: Real KiCAD library compatibility
- High-level API: Object-oriented interface vs low-level S-expression manipulation
- Format Preservation: Byte-perfect output vs manual formatting
- Validation: Real KiCAD library integration and component validation
- Format Preservation: Exact KiCAD compatibility vs approximate output
- Object-Oriented Design: Modern collection classes vs legacy patterns
- MCP Integration: Included MCP server vs no programmatic interface
π See Why Use This Library for detailed comparison
- Global Labels: Explicit global label connections not yet fully implemented (power symbols like VCC/GND work correctly)
- Partial Implementation: ERC validators have incomplete features
- Net tracing, pin type checking, and power net detection are in development
- Core functionality works, advanced validation features coming soon
- Large schematics (>1000 components) may experience slower connectivity analysis
- Symbol cache helps, but first analysis can take time
- Optimization ongoing
Report issues: https://github.com/circuit-synth/kicad-sch-api/issues
Full documentation is available:
- Examples Walkthrough - Start here! Complete tutorial from basics to advanced
- Example Circuits - Polished reference circuits (voltage divider, RC filter, power supply, STM32)
- Getting Started Guide - Complete beginner's tutorial
- API Reference - Complete API documentation
- Hierarchy Features - Multi-sheet design guide
- Recipes & Patterns - Practical examples
- Why Use This Library - Value proposition
- Architecture - Internal design details
We welcome contributions! Key areas:
- KiCAD library integration and component validation
- Performance optimizations for large schematics
- MCP server tools and AI agent capabilities
- Test coverage and format preservation validation
See CONTRIBUTING.md for guidelines.
MIT License - see LICENSE for details.
- circuit-synth - High-level circuit design automation
- Claude Code - AI development environment with MCP support
- KiCAD - Open source electronics design automation
- Model Context Protocol - Standard for AI agent tool integration
Made with β€οΈ for the open hardware community