kicad-sch-api 0.1.7__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kicad-sch-api might be problematic. Click here for more details.
- kicad_sch_api/cli.py +64 -222
- {kicad_sch_api-0.1.7.dist-info → kicad_sch_api-0.2.0.dist-info}/METADATA +32 -43
- {kicad_sch_api-0.1.7.dist-info → kicad_sch_api-0.2.0.dist-info}/RECORD +7 -9
- {kicad_sch_api-0.1.7.dist-info → kicad_sch_api-0.2.0.dist-info}/entry_points.txt +0 -1
- kicad_sch_api/mcp/__init__.py +0 -7
- kicad_sch_api/mcp/server.py +0 -1511
- {kicad_sch_api-0.1.7.dist-info → kicad_sch_api-0.2.0.dist-info}/WHEEL +0 -0
- {kicad_sch_api-0.1.7.dist-info → kicad_sch_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {kicad_sch_api-0.1.7.dist-info → kicad_sch_api-0.2.0.dist-info}/top_level.txt +0 -0
kicad_sch_api/mcp/server.py
DELETED
|
@@ -1,1511 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Basic KiCAD Schematic MCP Server
|
|
4
|
-
|
|
5
|
-
Simple FastMCP server for schematic manipulation with stateful operation.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import logging
|
|
9
|
-
import sys
|
|
10
|
-
import traceback
|
|
11
|
-
from typing import Optional, Dict, Any, List, Tuple
|
|
12
|
-
|
|
13
|
-
# Configure logging to stderr (required for MCP STDIO)
|
|
14
|
-
logging.basicConfig(
|
|
15
|
-
level=logging.INFO,
|
|
16
|
-
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
17
|
-
handlers=[logging.StreamHandler(sys.stderr)]
|
|
18
|
-
)
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
try:
|
|
22
|
-
from mcp.server import FastMCP
|
|
23
|
-
except ImportError:
|
|
24
|
-
logger.error("MCP not installed. Run: uv add 'mcp[cli]'")
|
|
25
|
-
sys.exit(1)
|
|
26
|
-
|
|
27
|
-
try:
|
|
28
|
-
import kicad_sch_api as ksa
|
|
29
|
-
from ..library.cache import get_symbol_cache
|
|
30
|
-
from ..discovery.search_index import get_search_index, ensure_index_built
|
|
31
|
-
except ImportError:
|
|
32
|
-
logger.error("kicad-sch-api not found. Make sure it's installed.")
|
|
33
|
-
sys.exit(1)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class SchematicState:
|
|
37
|
-
"""Maintains current schematic state for stateful operations."""
|
|
38
|
-
|
|
39
|
-
def __init__(self):
|
|
40
|
-
self.current_schematic: Optional[ksa.Schematic] = None
|
|
41
|
-
self.current_file_path: Optional[str] = None
|
|
42
|
-
|
|
43
|
-
def load_schematic(self, file_path: str) -> bool:
|
|
44
|
-
"""Load a schematic file."""
|
|
45
|
-
try:
|
|
46
|
-
self.current_schematic = ksa.load_schematic(file_path)
|
|
47
|
-
self.current_file_path = file_path
|
|
48
|
-
logger.info(f"Loaded schematic: {file_path}")
|
|
49
|
-
return True
|
|
50
|
-
except Exception as e:
|
|
51
|
-
logger.error(f"Failed to load schematic {file_path}: {e}")
|
|
52
|
-
return False
|
|
53
|
-
|
|
54
|
-
def create_schematic(self, name: str) -> bool:
|
|
55
|
-
"""Create a new schematic."""
|
|
56
|
-
try:
|
|
57
|
-
self.current_schematic = ksa.create_schematic(name)
|
|
58
|
-
self.current_file_path = None # Not saved yet
|
|
59
|
-
logger.info(f"Created new schematic: {name}")
|
|
60
|
-
return True
|
|
61
|
-
except Exception as e:
|
|
62
|
-
logger.error(f"Failed to create schematic {name}: {e}")
|
|
63
|
-
return False
|
|
64
|
-
|
|
65
|
-
def save_schematic(self, file_path: Optional[str] = None) -> bool:
|
|
66
|
-
"""Save the current schematic."""
|
|
67
|
-
if not self.current_schematic:
|
|
68
|
-
return False
|
|
69
|
-
|
|
70
|
-
try:
|
|
71
|
-
save_path = file_path or self.current_file_path
|
|
72
|
-
if not save_path:
|
|
73
|
-
return False
|
|
74
|
-
|
|
75
|
-
self.current_schematic.save(save_path)
|
|
76
|
-
self.current_file_path = save_path
|
|
77
|
-
logger.info(f"Saved schematic to: {save_path}")
|
|
78
|
-
return True
|
|
79
|
-
except Exception as e:
|
|
80
|
-
logger.error(f"Failed to save schematic: {e}")
|
|
81
|
-
return False
|
|
82
|
-
|
|
83
|
-
def is_loaded(self) -> bool:
|
|
84
|
-
"""Check if a schematic is currently loaded."""
|
|
85
|
-
return self.current_schematic is not None
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
# Global state instance
|
|
89
|
-
state = SchematicState()
|
|
90
|
-
|
|
91
|
-
# Initialize FastMCP server
|
|
92
|
-
mcp = FastMCP("KiCAD-Sch-API")
|
|
93
|
-
|
|
94
|
-
# Initialize discovery system on server startup
|
|
95
|
-
logger.info("Initializing component discovery system...")
|
|
96
|
-
try:
|
|
97
|
-
# Ensure search index is available (build if needed)
|
|
98
|
-
component_count = ensure_index_built()
|
|
99
|
-
logger.info(f"Component discovery ready: {component_count} components indexed")
|
|
100
|
-
except Exception as e:
|
|
101
|
-
logger.warning(f"Component discovery initialization failed: {e}")
|
|
102
|
-
logger.warning("Search features may not work until manually initialized")
|
|
103
|
-
|
|
104
|
-
@mcp.tool()
|
|
105
|
-
def create_schematic(name: str) -> Dict[str, Any]:
|
|
106
|
-
"""Create a new schematic file."""
|
|
107
|
-
try:
|
|
108
|
-
success = state.create_schematic(name)
|
|
109
|
-
return {
|
|
110
|
-
"success": success,
|
|
111
|
-
"message": f"Created schematic: {name}" if success else "Failed to create schematic",
|
|
112
|
-
"current_schematic": name if success else None
|
|
113
|
-
}
|
|
114
|
-
except Exception as e:
|
|
115
|
-
return {
|
|
116
|
-
"success": False,
|
|
117
|
-
"message": f"Error creating schematic: {str(e)}",
|
|
118
|
-
"errorDetails": traceback.format_exc()
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
@mcp.tool()
|
|
122
|
-
def load_schematic(file_path: str) -> Dict[str, Any]:
|
|
123
|
-
"""Load an existing schematic file."""
|
|
124
|
-
try:
|
|
125
|
-
success = state.load_schematic(file_path)
|
|
126
|
-
return {
|
|
127
|
-
"success": success,
|
|
128
|
-
"message": f"Loaded schematic: {file_path}" if success else f"Failed to load: {file_path}",
|
|
129
|
-
"current_schematic": file_path if success else None
|
|
130
|
-
}
|
|
131
|
-
except Exception as e:
|
|
132
|
-
return {
|
|
133
|
-
"success": False,
|
|
134
|
-
"message": f"Error loading schematic: {str(e)}",
|
|
135
|
-
"errorDetails": traceback.format_exc()
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
@mcp.tool()
|
|
139
|
-
def save_schematic(file_path: Optional[str] = None) -> Dict[str, Any]:
|
|
140
|
-
"""Save the current schematic to a file."""
|
|
141
|
-
try:
|
|
142
|
-
if not state.is_loaded():
|
|
143
|
-
return {
|
|
144
|
-
"success": False,
|
|
145
|
-
"message": "No schematic loaded"
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
success = state.save_schematic(file_path)
|
|
149
|
-
save_path = file_path or state.current_file_path
|
|
150
|
-
return {
|
|
151
|
-
"success": success,
|
|
152
|
-
"message": f"Saved to: {save_path}" if success else "Failed to save",
|
|
153
|
-
"file_path": save_path if success else None
|
|
154
|
-
}
|
|
155
|
-
except Exception as e:
|
|
156
|
-
return {
|
|
157
|
-
"success": False,
|
|
158
|
-
"message": f"Error saving schematic: {str(e)}",
|
|
159
|
-
"errorDetails": traceback.format_exc()
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
@mcp.tool()
|
|
163
|
-
def get_schematic_info() -> Dict[str, Any]:
|
|
164
|
-
"""Get information about the currently loaded schematic."""
|
|
165
|
-
try:
|
|
166
|
-
if not state.is_loaded():
|
|
167
|
-
return {
|
|
168
|
-
"success": False,
|
|
169
|
-
"message": "No schematic loaded"
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
sch = state.current_schematic
|
|
173
|
-
component_count = len(sch.components) if hasattr(sch, 'components') else 0
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
"success": True,
|
|
177
|
-
"file_path": state.current_file_path,
|
|
178
|
-
"component_count": component_count,
|
|
179
|
-
"message": f"Schematic loaded with {component_count} components"
|
|
180
|
-
}
|
|
181
|
-
except Exception as e:
|
|
182
|
-
return {
|
|
183
|
-
"success": False,
|
|
184
|
-
"message": f"Error getting schematic info: {str(e)}",
|
|
185
|
-
"errorDetails": traceback.format_exc()
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
@mcp.tool()
|
|
189
|
-
def add_component(lib_id: str, reference: str, value: str,
|
|
190
|
-
position: Tuple[float, float], **properties) -> Dict[str, Any]:
|
|
191
|
-
"""Add a component to the current schematic with enhanced error handling.
|
|
192
|
-
|
|
193
|
-
IMPORTANT DESIGN RULES:
|
|
194
|
-
1. REFERENCES: Always provide proper component references (R1, R2, C1, C2, etc.)
|
|
195
|
-
- Never use "?" or leave references undefined
|
|
196
|
-
- Use standard prefixes: R=resistor, C=capacitor, U=IC, D=diode, L=inductor
|
|
197
|
-
|
|
198
|
-
2. COMPONENT SPACING: Space components appropriately for readability
|
|
199
|
-
- Minimum 50 units between components
|
|
200
|
-
- Use grid-aligned positions (multiples of 25.4 or 12.7)
|
|
201
|
-
- Leave room for labels and connections
|
|
202
|
-
|
|
203
|
-
3. FOOTPRINTS: Specify appropriate footprints in properties
|
|
204
|
-
- Common SMD: R_0603_1608Metric, C_0603_1608Metric
|
|
205
|
-
- Through-hole: R_Axial_DIN0207, C_Disc_D3.0mm
|
|
206
|
-
|
|
207
|
-
4. VALUES: Use standard component values
|
|
208
|
-
- Resistors: 1k, 10k, 100k (E12 series)
|
|
209
|
-
- Capacitors: 100nF, 10uF, 100uF
|
|
210
|
-
"""
|
|
211
|
-
try:
|
|
212
|
-
if not state.is_loaded():
|
|
213
|
-
return {
|
|
214
|
-
"success": False,
|
|
215
|
-
"message": "No schematic loaded. Use load_schematic() or create_schematic() first."
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
# Pre-validate the component exists
|
|
219
|
-
cache = get_symbol_cache()
|
|
220
|
-
symbol = cache.get_symbol(lib_id)
|
|
221
|
-
|
|
222
|
-
if not symbol:
|
|
223
|
-
# Provide suggestions for invalid lib_id
|
|
224
|
-
search_term = lib_id.split(":")[-1] if ":" in lib_id else lib_id
|
|
225
|
-
suggestions = cache.search_symbols(search_term, limit=3)
|
|
226
|
-
|
|
227
|
-
return {
|
|
228
|
-
"success": False,
|
|
229
|
-
"message": f"Component '{lib_id}' not found",
|
|
230
|
-
"suggestions": [
|
|
231
|
-
{
|
|
232
|
-
"lib_id": s.lib_id,
|
|
233
|
-
"name": s.name,
|
|
234
|
-
"description": s.description,
|
|
235
|
-
"common_footprints": _suggest_common_footprints(s)[:2]
|
|
236
|
-
}
|
|
237
|
-
for s in suggestions
|
|
238
|
-
],
|
|
239
|
-
"help": "Use search_components() to find valid component lib_ids"
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
# Validate and fix reference if needed
|
|
243
|
-
if reference == "?" or not reference or reference.strip() == "":
|
|
244
|
-
# Auto-generate proper reference based on component type
|
|
245
|
-
prefix = _get_reference_prefix(lib_id)
|
|
246
|
-
reference = _generate_next_reference(state.current_schematic, prefix)
|
|
247
|
-
logger.info(f"Auto-generated reference: {reference} for {lib_id}")
|
|
248
|
-
|
|
249
|
-
# Add component using our API
|
|
250
|
-
component = state.current_schematic.components.add(
|
|
251
|
-
lib_id=lib_id,
|
|
252
|
-
reference=reference,
|
|
253
|
-
value=value,
|
|
254
|
-
position=position,
|
|
255
|
-
**properties
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
return {
|
|
259
|
-
"success": True,
|
|
260
|
-
"message": f"Added component {reference} ({lib_id}) at {position}",
|
|
261
|
-
"reference": reference,
|
|
262
|
-
"lib_id": lib_id,
|
|
263
|
-
"position": position,
|
|
264
|
-
"pins": len(symbol.pins),
|
|
265
|
-
"suggested_footprints": _suggest_common_footprints(symbol)[:3]
|
|
266
|
-
}
|
|
267
|
-
except Exception as e:
|
|
268
|
-
return {
|
|
269
|
-
"success": False,
|
|
270
|
-
"message": f"Error adding component: {str(e)}",
|
|
271
|
-
"errorDetails": traceback.format_exc()
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
@mcp.tool()
|
|
275
|
-
def list_components() -> Dict[str, Any]:
|
|
276
|
-
"""List all components in the current schematic."""
|
|
277
|
-
try:
|
|
278
|
-
if not state.is_loaded():
|
|
279
|
-
return {
|
|
280
|
-
"success": False,
|
|
281
|
-
"message": "No schematic loaded"
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
components = []
|
|
285
|
-
for comp in state.current_schematic.components:
|
|
286
|
-
components.append({
|
|
287
|
-
"reference": comp.reference,
|
|
288
|
-
"lib_id": getattr(comp, 'lib_id', 'Unknown'),
|
|
289
|
-
"value": getattr(comp, 'value', ''),
|
|
290
|
-
"position": getattr(comp, 'position', (0, 0))
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
return {
|
|
294
|
-
"success": True,
|
|
295
|
-
"components": components,
|
|
296
|
-
"count": len(components),
|
|
297
|
-
"message": f"Found {len(components)} components"
|
|
298
|
-
}
|
|
299
|
-
except Exception as e:
|
|
300
|
-
return {
|
|
301
|
-
"success": False,
|
|
302
|
-
"message": f"Error listing components: {str(e)}",
|
|
303
|
-
"errorDetails": traceback.format_exc()
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
@mcp.tool()
|
|
307
|
-
def add_wire(start_pos: Tuple[float, float], end_pos: Tuple[float, float]) -> Dict[str, Any]:
|
|
308
|
-
"""Add a wire connection between two points.
|
|
309
|
-
|
|
310
|
-
⚠️ WARNING: AVOID USING WIRES IN MOST CASES!
|
|
311
|
-
|
|
312
|
-
BETTER ALTERNATIVES:
|
|
313
|
-
1. **Use hierarchical labels instead** - add_hierarchical_label() for clean connections
|
|
314
|
-
2. **Use global labels** - add_label() with appropriate label type
|
|
315
|
-
3. **Use power symbols** - for VCC/GND connections
|
|
316
|
-
|
|
317
|
-
ONLY use wires for:
|
|
318
|
-
- Very short connections (< 25 units)
|
|
319
|
-
- Direct pin-to-pin connections on same component
|
|
320
|
-
- Internal component connections that can't use labels
|
|
321
|
-
|
|
322
|
-
For hierarchical designs, NEVER use wires - use hierarchical labels exclusively!
|
|
323
|
-
"""
|
|
324
|
-
try:
|
|
325
|
-
if not state.is_loaded():
|
|
326
|
-
return {
|
|
327
|
-
"success": False,
|
|
328
|
-
"message": "No schematic loaded"
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
# Add wire using our API
|
|
332
|
-
wire = state.current_schematic.wires.add(
|
|
333
|
-
start=start_pos,
|
|
334
|
-
end=end_pos
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
return {
|
|
338
|
-
"success": True,
|
|
339
|
-
"message": f"Added wire from {start_pos} to {end_pos}",
|
|
340
|
-
"start": start_pos,
|
|
341
|
-
"end": end_pos
|
|
342
|
-
}
|
|
343
|
-
except Exception as e:
|
|
344
|
-
return {
|
|
345
|
-
"success": False,
|
|
346
|
-
"message": f"Error adding wire: {str(e)}",
|
|
347
|
-
"errorDetails": traceback.format_exc()
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
@mcp.tool()
|
|
351
|
-
def search_components(query: str, library: Optional[str] = None,
|
|
352
|
-
category: Optional[str] = None, limit: int = 20) -> Dict[str, Any]:
|
|
353
|
-
"""Search for components across KiCAD symbol libraries using fast SQLite index."""
|
|
354
|
-
try:
|
|
355
|
-
# Ensure search index is built
|
|
356
|
-
ensure_index_built()
|
|
357
|
-
|
|
358
|
-
# Use the fast search index
|
|
359
|
-
search_index = get_search_index()
|
|
360
|
-
results = search_index.search(query, library, category, limit)
|
|
361
|
-
|
|
362
|
-
# Enhance results with footprint suggestions and usage context
|
|
363
|
-
formatted_results = []
|
|
364
|
-
for result in results:
|
|
365
|
-
# Get footprint suggestions based on component type
|
|
366
|
-
common_footprints = _suggest_common_footprints_by_prefix(result["reference_prefix"])
|
|
367
|
-
|
|
368
|
-
formatted_results.append({
|
|
369
|
-
"lib_id": result["lib_id"],
|
|
370
|
-
"name": result["name"],
|
|
371
|
-
"description": result["description"],
|
|
372
|
-
"library": result["library"],
|
|
373
|
-
"pins": result["pin_count"],
|
|
374
|
-
"reference_prefix": result["reference_prefix"],
|
|
375
|
-
"keywords": result["keywords"],
|
|
376
|
-
"category": result["category"],
|
|
377
|
-
"match_score": round(result["match_score"], 2),
|
|
378
|
-
"common_footprints": common_footprints,
|
|
379
|
-
"usage_context": _get_usage_context_by_prefix(result["reference_prefix"])
|
|
380
|
-
})
|
|
381
|
-
|
|
382
|
-
return {
|
|
383
|
-
"success": True,
|
|
384
|
-
"results": formatted_results,
|
|
385
|
-
"total_found": len(results),
|
|
386
|
-
"query": query,
|
|
387
|
-
"search_time_ms": 0, # SQLite searches are very fast
|
|
388
|
-
"message": f"Found {len(results)} components matching '{query}'"
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
except Exception as e:
|
|
392
|
-
return {
|
|
393
|
-
"success": False,
|
|
394
|
-
"message": f"Error searching components: {str(e)}",
|
|
395
|
-
"errorDetails": traceback.format_exc()
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
@mcp.tool()
|
|
399
|
-
def list_libraries() -> Dict[str, Any]:
|
|
400
|
-
"""List all available KiCAD symbol libraries."""
|
|
401
|
-
try:
|
|
402
|
-
cache = get_symbol_cache()
|
|
403
|
-
|
|
404
|
-
# Get library statistics
|
|
405
|
-
stats = cache.get_performance_stats()
|
|
406
|
-
|
|
407
|
-
# Get available libraries from the cache
|
|
408
|
-
libraries = []
|
|
409
|
-
for lib_name, lib_stats in cache._lib_stats.items():
|
|
410
|
-
libraries.append({
|
|
411
|
-
"name": lib_name,
|
|
412
|
-
"path": str(lib_stats.library_path),
|
|
413
|
-
"symbol_count": lib_stats.symbol_count,
|
|
414
|
-
"file_size_mb": round(lib_stats.file_size / (1024 * 1024), 2)
|
|
415
|
-
})
|
|
416
|
-
|
|
417
|
-
return {
|
|
418
|
-
"success": True,
|
|
419
|
-
"libraries": sorted(libraries, key=lambda x: x["name"]),
|
|
420
|
-
"total_libraries": len(libraries),
|
|
421
|
-
"cache_stats": {
|
|
422
|
-
"total_symbols_cached": stats["total_symbols_cached"],
|
|
423
|
-
"cache_hit_rate": stats["hit_rate_percent"]
|
|
424
|
-
},
|
|
425
|
-
"message": f"Found {len(libraries)} symbol libraries"
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
except Exception as e:
|
|
429
|
-
return {
|
|
430
|
-
"success": False,
|
|
431
|
-
"message": f"Error listing libraries: {str(e)}",
|
|
432
|
-
"errorDetails": traceback.format_exc()
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
@mcp.tool()
|
|
436
|
-
def validate_component(lib_id: str) -> Dict[str, Any]:
|
|
437
|
-
"""Validate that a component exists and get its details."""
|
|
438
|
-
try:
|
|
439
|
-
# First check search index for fast validation
|
|
440
|
-
search_index = get_search_index()
|
|
441
|
-
result = search_index.validate_component(lib_id)
|
|
442
|
-
|
|
443
|
-
if not result:
|
|
444
|
-
# Search for similar components
|
|
445
|
-
search_term = lib_id.split(":")[-1] if ":" in lib_id else lib_id
|
|
446
|
-
suggestions = search_index.search(search_term, limit=5)
|
|
447
|
-
|
|
448
|
-
return {
|
|
449
|
-
"success": False,
|
|
450
|
-
"exists": False,
|
|
451
|
-
"lib_id": lib_id,
|
|
452
|
-
"message": f"Component {lib_id} not found",
|
|
453
|
-
"suggestions": [
|
|
454
|
-
{
|
|
455
|
-
"lib_id": s["lib_id"],
|
|
456
|
-
"name": s["name"],
|
|
457
|
-
"description": s["description"]
|
|
458
|
-
}
|
|
459
|
-
for s in suggestions
|
|
460
|
-
]
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
return {
|
|
464
|
-
"success": True,
|
|
465
|
-
"exists": True,
|
|
466
|
-
"lib_id": lib_id,
|
|
467
|
-
"details": {
|
|
468
|
-
"name": result["name"],
|
|
469
|
-
"description": result["description"],
|
|
470
|
-
"library": result["library"],
|
|
471
|
-
"pins": result["pin_count"],
|
|
472
|
-
"reference_prefix": result["reference_prefix"],
|
|
473
|
-
"keywords": result["keywords"],
|
|
474
|
-
"category": result["category"]
|
|
475
|
-
},
|
|
476
|
-
"message": f"Component {lib_id} exists"
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
except Exception as e:
|
|
480
|
-
return {
|
|
481
|
-
"success": False,
|
|
482
|
-
"message": f"Error validating component: {str(e)}",
|
|
483
|
-
"errorDetails": traceback.format_exc()
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
@mcp.tool()
|
|
487
|
-
def browse_library(library_name: str, limit: int = 50) -> Dict[str, Any]:
|
|
488
|
-
"""Browse components in a specific library."""
|
|
489
|
-
try:
|
|
490
|
-
cache = get_symbol_cache()
|
|
491
|
-
|
|
492
|
-
# Get all symbols from the library
|
|
493
|
-
symbols = cache.get_library_symbols(library_name)
|
|
494
|
-
|
|
495
|
-
if not symbols:
|
|
496
|
-
return {
|
|
497
|
-
"success": False,
|
|
498
|
-
"message": f"Library '{library_name}' not found or empty"
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
# Format results
|
|
502
|
-
components = []
|
|
503
|
-
for symbol in symbols[:limit]:
|
|
504
|
-
components.append({
|
|
505
|
-
"lib_id": symbol.lib_id,
|
|
506
|
-
"name": symbol.name,
|
|
507
|
-
"description": symbol.description,
|
|
508
|
-
"pins": len(symbol.pins),
|
|
509
|
-
"reference_prefix": symbol.reference_prefix
|
|
510
|
-
})
|
|
511
|
-
|
|
512
|
-
return {
|
|
513
|
-
"success": True,
|
|
514
|
-
"library": library_name,
|
|
515
|
-
"components": components,
|
|
516
|
-
"showing": len(components),
|
|
517
|
-
"total_in_library": len(symbols),
|
|
518
|
-
"message": f"Showing {len(components)} of {len(symbols)} components in {library_name}"
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
except Exception as e:
|
|
522
|
-
return {
|
|
523
|
-
"success": False,
|
|
524
|
-
"message": f"Error browsing library: {str(e)}",
|
|
525
|
-
"errorDetails": traceback.format_exc()
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
@mcp.tool()
|
|
529
|
-
def list_component_categories() -> Dict[str, Any]:
|
|
530
|
-
"""List all component categories with counts."""
|
|
531
|
-
try:
|
|
532
|
-
search_index = get_search_index()
|
|
533
|
-
categories = search_index.get_categories()
|
|
534
|
-
|
|
535
|
-
return {
|
|
536
|
-
"success": True,
|
|
537
|
-
"categories": categories,
|
|
538
|
-
"total_categories": len(categories),
|
|
539
|
-
"message": f"Found {len(categories)} component categories"
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
except Exception as e:
|
|
543
|
-
return {
|
|
544
|
-
"success": False,
|
|
545
|
-
"message": f"Error listing categories: {str(e)}",
|
|
546
|
-
"errorDetails": traceback.format_exc()
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
@mcp.tool()
|
|
550
|
-
def get_search_stats() -> Dict[str, Any]:
|
|
551
|
-
"""Get search index statistics and performance info."""
|
|
552
|
-
try:
|
|
553
|
-
search_index = get_search_index()
|
|
554
|
-
stats = search_index.get_stats()
|
|
555
|
-
|
|
556
|
-
return {
|
|
557
|
-
"success": True,
|
|
558
|
-
"stats": stats,
|
|
559
|
-
"message": f"Search index contains {stats['total_components']} components from {stats['total_libraries']} libraries"
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
except Exception as e:
|
|
563
|
-
return {
|
|
564
|
-
"success": False,
|
|
565
|
-
"message": f"Error getting search stats: {str(e)}",
|
|
566
|
-
"errorDetails": traceback.format_exc()
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
@mcp.tool()
|
|
570
|
-
def add_hierarchical_sheet(name: str, filename: str, position: Tuple[float, float],
|
|
571
|
-
size: Tuple[float, float], project_name: str = "",
|
|
572
|
-
page_number: str = "2") -> Dict[str, Any]:
|
|
573
|
-
"""Add a hierarchical sheet to the current schematic.
|
|
574
|
-
|
|
575
|
-
HIERARCHICAL DESIGN WORKFLOW:
|
|
576
|
-
1. MAIN SCHEMATIC: Add this sheet to your main schematic
|
|
577
|
-
2. CREATE SUBCIRCUIT: Use create_schematic() for the sub-schematic
|
|
578
|
-
3. ADD SHEET PINS: Use add_sheet_pin() to create connection points on the sheet
|
|
579
|
-
4. ADD COMPONENTS: Switch to subcircuit, add components normally
|
|
580
|
-
5. ADD LABELS: In subcircuit, use add_hierarchical_label() on component pins
|
|
581
|
-
6. NAME MATCHING: Sheet pin names must exactly match hierarchical label names
|
|
582
|
-
|
|
583
|
-
SHEET SIZING GUIDELINES:
|
|
584
|
-
- Small: (60, 40) for 2-3 pins (power supplies, simple filters)
|
|
585
|
-
- Medium: (80, 50) for 4-6 pins (op-amp circuits, basic MCU interfaces)
|
|
586
|
-
- Large: (100, 60) for 7+ pins (complex subcircuits)
|
|
587
|
-
- Maximum: (120, 80) only for very complex sheets (avoid if possible)
|
|
588
|
-
|
|
589
|
-
CRITICAL: Sheets are currently being created too large! Use smaller sizes for cleaner schematics.
|
|
590
|
-
|
|
591
|
-
POSITIONING:
|
|
592
|
-
- Leave space around sheet for connections
|
|
593
|
-
- Align with main schematic grid (multiples of 25.4)
|
|
594
|
-
- Position where sheet pins will connect logically to main circuit
|
|
595
|
-
"""
|
|
596
|
-
try:
|
|
597
|
-
if not state.is_loaded():
|
|
598
|
-
return {
|
|
599
|
-
"success": False,
|
|
600
|
-
"message": "No schematic loaded. Use load_schematic() or create_schematic() first."
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
# Add the hierarchical sheet
|
|
604
|
-
sheet_uuid = state.current_schematic.add_sheet(
|
|
605
|
-
name=name,
|
|
606
|
-
filename=filename,
|
|
607
|
-
position=position,
|
|
608
|
-
size=size,
|
|
609
|
-
project_name=project_name,
|
|
610
|
-
page_number=page_number
|
|
611
|
-
)
|
|
612
|
-
|
|
613
|
-
return {
|
|
614
|
-
"success": True,
|
|
615
|
-
"message": f"Added hierarchical sheet '{name}' ({filename}) at {position}",
|
|
616
|
-
"sheet_uuid": sheet_uuid,
|
|
617
|
-
"name": name,
|
|
618
|
-
"filename": filename,
|
|
619
|
-
"position": position,
|
|
620
|
-
"size": size,
|
|
621
|
-
"help": "Use add_hierarchical_label() to add pins to connect this sheet to parent schematic"
|
|
622
|
-
}
|
|
623
|
-
except Exception as e:
|
|
624
|
-
return {
|
|
625
|
-
"success": False,
|
|
626
|
-
"message": f"Error adding hierarchical sheet: {str(e)}",
|
|
627
|
-
"errorDetails": traceback.format_exc()
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
@mcp.tool()
|
|
631
|
-
def add_sheet_pin(sheet_uuid: str, name: str, pin_type: str = "input",
|
|
632
|
-
position: Tuple[float, float] = (0, 0), rotation: float = 0,
|
|
633
|
-
size: float = 1.27) -> Dict[str, Any]:
|
|
634
|
-
"""Add a pin to a hierarchical sheet for connecting to parent schematic.
|
|
635
|
-
|
|
636
|
-
SHEET PIN POSITIONING (relative to sheet origin):
|
|
637
|
-
- LEFT EDGE: (0, Y) - for inputs entering the sheet
|
|
638
|
-
- RIGHT EDGE: (sheet_width, Y) - for outputs leaving the sheet
|
|
639
|
-
- TOP EDGE: (X, 0) - for control signals
|
|
640
|
-
- BOTTOM EDGE: (X, sheet_height) - for power/ground
|
|
641
|
-
|
|
642
|
-
CRITICAL NAMING RULE:
|
|
643
|
-
Sheet pin names MUST exactly match hierarchical label names in the subcircuit!
|
|
644
|
-
Example: Sheet pin "VCC" connects to hierarchical label "VCC"
|
|
645
|
-
|
|
646
|
-
PIN TYPES USAGE:
|
|
647
|
-
- input: Power coming in, control signals in
|
|
648
|
-
- output: Data/signals leaving the sheet
|
|
649
|
-
- bidirectional: I2C, SPI data lines
|
|
650
|
-
- passive: Ground connections, analog signals
|
|
651
|
-
|
|
652
|
-
POSITIONING EXAMPLES for 100×80 sheet:
|
|
653
|
-
- Power in: (0, 10) and (0, 20)
|
|
654
|
-
- Signals out: (100, 15) and (100, 25)
|
|
655
|
-
- Ground: (50, 80) - bottom center
|
|
656
|
-
"""
|
|
657
|
-
try:
|
|
658
|
-
if not state.is_loaded():
|
|
659
|
-
return {
|
|
660
|
-
"success": False,
|
|
661
|
-
"message": "No schematic loaded. Use load_schematic() or create_schematic() first."
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
# Validate pin_type
|
|
665
|
-
valid_pin_types = ["input", "output", "bidirectional", "tri_state", "passive"]
|
|
666
|
-
if pin_type not in valid_pin_types:
|
|
667
|
-
return {
|
|
668
|
-
"success": False,
|
|
669
|
-
"message": f"Invalid pin_type '{pin_type}'. Valid types: {valid_pin_types}",
|
|
670
|
-
"valid_types": valid_pin_types
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
# Add the sheet pin
|
|
674
|
-
pin_uuid = state.current_schematic.add_sheet_pin(
|
|
675
|
-
sheet_uuid=sheet_uuid,
|
|
676
|
-
name=name,
|
|
677
|
-
pin_type=pin_type,
|
|
678
|
-
position=position,
|
|
679
|
-
rotation=rotation,
|
|
680
|
-
size=size
|
|
681
|
-
)
|
|
682
|
-
|
|
683
|
-
return {
|
|
684
|
-
"success": True,
|
|
685
|
-
"message": f"Added sheet pin '{name}' ({pin_type}) to sheet {sheet_uuid}",
|
|
686
|
-
"pin_uuid": pin_uuid,
|
|
687
|
-
"name": name,
|
|
688
|
-
"pin_type": pin_type,
|
|
689
|
-
"position": position,
|
|
690
|
-
"help": "Create matching hierarchical_label in child schematic for connectivity"
|
|
691
|
-
}
|
|
692
|
-
except ValueError as e:
|
|
693
|
-
return {
|
|
694
|
-
"success": False,
|
|
695
|
-
"message": str(e)
|
|
696
|
-
}
|
|
697
|
-
except Exception as e:
|
|
698
|
-
return {
|
|
699
|
-
"success": False,
|
|
700
|
-
"message": f"Error adding sheet pin: {str(e)}",
|
|
701
|
-
"errorDetails": traceback.format_exc()
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
@mcp.tool()
|
|
705
|
-
def add_hierarchical_label(text: str, position: Tuple[float, float],
|
|
706
|
-
label_type: str = "input", rotation: float = 0,
|
|
707
|
-
size: float = 1.27) -> Dict[str, Any]:
|
|
708
|
-
"""Add a hierarchical label for connecting to parent schematic via sheet pins.
|
|
709
|
-
|
|
710
|
-
CRITICAL PLACEMENT RULES:
|
|
711
|
-
1. POSITION: Place labels directly on component pins, not floating
|
|
712
|
-
- Must touch the actual pin connection point
|
|
713
|
-
- Use component pin positions, not arbitrary locations
|
|
714
|
-
|
|
715
|
-
2. ROTATION: Labels must face AWAY from the component
|
|
716
|
-
- 0° = right-facing (for pins on left side of component)
|
|
717
|
-
- 180° = left-facing (for pins on right side of component)
|
|
718
|
-
- 90° = up-facing (for pins on bottom of component)
|
|
719
|
-
- 270° = down-facing (for pins on top of component)
|
|
720
|
-
|
|
721
|
-
3. NET NAMES: Use clear, descriptive names
|
|
722
|
-
- Power: VCC, 3V3, 5V, GND, VBAT
|
|
723
|
-
- Signals: SDA, SCL, TX, RX, CLK, DATA
|
|
724
|
-
- Custom: INPUT_A, OUTPUT_B, CTRL_SIGNAL
|
|
725
|
-
|
|
726
|
-
4. LABEL TYPES:
|
|
727
|
-
- input: Signal enters this sheet (power in, data in)
|
|
728
|
-
- output: Signal leaves this sheet (power out, data out)
|
|
729
|
-
- bidirectional: Signal goes both ways (I2C, SPI)
|
|
730
|
-
- passive: Non-directional (GND, analog)
|
|
731
|
-
"""
|
|
732
|
-
try:
|
|
733
|
-
if not state.is_loaded():
|
|
734
|
-
return {
|
|
735
|
-
"success": False,
|
|
736
|
-
"message": "No schematic loaded. Use load_schematic() or create_schematic() first."
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
# Validate label_type (hierarchical labels use same types as sheet pins)
|
|
740
|
-
valid_types = ["input", "output", "bidirectional", "tri_state", "passive"]
|
|
741
|
-
if label_type not in valid_types:
|
|
742
|
-
return {
|
|
743
|
-
"success": False,
|
|
744
|
-
"message": f"Invalid label_type '{label_type}'. Valid types: {valid_types}",
|
|
745
|
-
"valid_types": valid_types
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
# Add the hierarchical label
|
|
749
|
-
from ..core.types import HierarchicalLabelShape
|
|
750
|
-
|
|
751
|
-
# Map string types to enum
|
|
752
|
-
shape_map = {
|
|
753
|
-
"input": HierarchicalLabelShape.INPUT,
|
|
754
|
-
"output": HierarchicalLabelShape.OUTPUT,
|
|
755
|
-
"bidirectional": HierarchicalLabelShape.BIDIRECTIONAL,
|
|
756
|
-
"tri_state": HierarchicalLabelShape.TRISTATE,
|
|
757
|
-
"passive": HierarchicalLabelShape.PASSIVE
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
label_uuid = state.current_schematic.add_hierarchical_label(
|
|
761
|
-
text=text,
|
|
762
|
-
position=position,
|
|
763
|
-
shape=shape_map[label_type],
|
|
764
|
-
rotation=rotation,
|
|
765
|
-
size=size
|
|
766
|
-
)
|
|
767
|
-
|
|
768
|
-
return {
|
|
769
|
-
"success": True,
|
|
770
|
-
"message": f"Added hierarchical label '{text}' ({label_type}) at {position}",
|
|
771
|
-
"label_uuid": label_uuid,
|
|
772
|
-
"text": text,
|
|
773
|
-
"label_type": label_type,
|
|
774
|
-
"position": position,
|
|
775
|
-
"help": "This label connects to a matching sheet pin in the parent schematic"
|
|
776
|
-
}
|
|
777
|
-
except Exception as e:
|
|
778
|
-
return {
|
|
779
|
-
"success": False,
|
|
780
|
-
"message": f"Error adding hierarchical label: {str(e)}",
|
|
781
|
-
"errorDetails": traceback.format_exc()
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
@mcp.tool()
|
|
785
|
-
def list_hierarchical_sheets() -> Dict[str, Any]:
|
|
786
|
-
"""List all hierarchical sheets in the current schematic."""
|
|
787
|
-
try:
|
|
788
|
-
if not state.is_loaded():
|
|
789
|
-
return {
|
|
790
|
-
"success": False,
|
|
791
|
-
"message": "No schematic loaded"
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
sheets_data = state.current_schematic._data.get("sheets", [])
|
|
795
|
-
|
|
796
|
-
sheets = []
|
|
797
|
-
for sheet_data in sheets_data:
|
|
798
|
-
sheet_info = {
|
|
799
|
-
"uuid": sheet_data.get("uuid"),
|
|
800
|
-
"name": sheet_data.get("name"),
|
|
801
|
-
"filename": sheet_data.get("filename"),
|
|
802
|
-
"position": [sheet_data.get("position", {}).get("x", 0),
|
|
803
|
-
sheet_data.get("position", {}).get("y", 0)],
|
|
804
|
-
"size": [sheet_data.get("size", {}).get("width", 0),
|
|
805
|
-
sheet_data.get("size", {}).get("height", 0)],
|
|
806
|
-
"pin_count": len(sheet_data.get("pins", [])),
|
|
807
|
-
"pins": [
|
|
808
|
-
{
|
|
809
|
-
"uuid": pin.get("uuid"),
|
|
810
|
-
"name": pin.get("name"),
|
|
811
|
-
"pin_type": pin.get("pin_type"),
|
|
812
|
-
"position": [pin.get("position", {}).get("x", 0),
|
|
813
|
-
pin.get("position", {}).get("y", 0)]
|
|
814
|
-
}
|
|
815
|
-
for pin in sheet_data.get("pins", [])
|
|
816
|
-
]
|
|
817
|
-
}
|
|
818
|
-
sheets.append(sheet_info)
|
|
819
|
-
|
|
820
|
-
return {
|
|
821
|
-
"success": True,
|
|
822
|
-
"sheets": sheets,
|
|
823
|
-
"count": len(sheets),
|
|
824
|
-
"message": f"Found {len(sheets)} hierarchical sheets"
|
|
825
|
-
}
|
|
826
|
-
except Exception as e:
|
|
827
|
-
return {
|
|
828
|
-
"success": False,
|
|
829
|
-
"message": f"Error listing hierarchical sheets: {str(e)}",
|
|
830
|
-
"errorDetails": traceback.format_exc()
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
def _get_reference_prefix(lib_id: str) -> str:
|
|
834
|
-
"""Get the standard reference prefix for a component type."""
|
|
835
|
-
lib_id_lower = lib_id.lower()
|
|
836
|
-
|
|
837
|
-
# Common component prefixes based on IPC standards
|
|
838
|
-
if 'resistor' in lib_id_lower or ':r' in lib_id_lower or lib_id.endswith(':R'):
|
|
839
|
-
return 'R'
|
|
840
|
-
elif 'capacitor' in lib_id_lower or ':c' in lib_id_lower or lib_id.endswith(':C'):
|
|
841
|
-
return 'C'
|
|
842
|
-
elif 'inductor' in lib_id_lower or ':l' in lib_id_lower or lib_id.endswith(':L'):
|
|
843
|
-
return 'L'
|
|
844
|
-
elif 'diode' in lib_id_lower or ':d' in lib_id_lower or 'led' in lib_id_lower:
|
|
845
|
-
return 'D'
|
|
846
|
-
elif 'transistor' in lib_id_lower or ':q' in lib_id_lower or 'fet' in lib_id_lower:
|
|
847
|
-
return 'Q'
|
|
848
|
-
elif any(ic_type in lib_id_lower for ic_type in ['mcu', 'microcontroller', 'processor', 'amplifier', 'regulator', 'ic']):
|
|
849
|
-
return 'U'
|
|
850
|
-
elif 'crystal' in lib_id_lower or 'oscillator' in lib_id_lower:
|
|
851
|
-
return 'Y'
|
|
852
|
-
elif 'connector' in lib_id_lower or 'header' in lib_id_lower:
|
|
853
|
-
return 'J'
|
|
854
|
-
elif 'switch' in lib_id_lower or 'button' in lib_id_lower:
|
|
855
|
-
return 'SW'
|
|
856
|
-
elif 'fuse' in lib_id_lower:
|
|
857
|
-
return 'F'
|
|
858
|
-
else:
|
|
859
|
-
# Default fallback
|
|
860
|
-
return 'U'
|
|
861
|
-
|
|
862
|
-
def _generate_next_reference(schematic, prefix: str) -> str:
|
|
863
|
-
"""Generate the next available reference for a given prefix."""
|
|
864
|
-
try:
|
|
865
|
-
existing_refs = []
|
|
866
|
-
if hasattr(schematic, 'components'):
|
|
867
|
-
# Get all existing references with this prefix
|
|
868
|
-
for comp in schematic.components:
|
|
869
|
-
if hasattr(comp, 'reference') and comp.reference.startswith(prefix):
|
|
870
|
-
ref = comp.reference
|
|
871
|
-
# Extract number from reference (e.g., 'R1' -> 1)
|
|
872
|
-
try:
|
|
873
|
-
num_part = ref[len(prefix):]
|
|
874
|
-
if num_part.isdigit():
|
|
875
|
-
existing_refs.append(int(num_part))
|
|
876
|
-
except (ValueError, IndexError):
|
|
877
|
-
continue
|
|
878
|
-
|
|
879
|
-
# Find next available number
|
|
880
|
-
next_num = 1
|
|
881
|
-
while next_num in existing_refs:
|
|
882
|
-
next_num += 1
|
|
883
|
-
|
|
884
|
-
return f"{prefix}{next_num}"
|
|
885
|
-
except Exception:
|
|
886
|
-
# Fallback to R1, C1, etc.
|
|
887
|
-
return f"{prefix}1"
|
|
888
|
-
|
|
889
|
-
def _suggest_common_footprints(symbol) -> List[str]:
|
|
890
|
-
"""Suggest common footprints for a component type using SymbolDefinition."""
|
|
891
|
-
return _suggest_common_footprints_by_prefix(symbol.reference_prefix.upper())
|
|
892
|
-
|
|
893
|
-
def _suggest_common_footprints_by_prefix(prefix: str) -> List[str]:
|
|
894
|
-
"""Suggest common footprints based on reference prefix."""
|
|
895
|
-
footprint_map = {
|
|
896
|
-
"R": ["Resistor_SMD:R_0603_1608Metric", "Resistor_SMD:R_0805_2012Metric", "Resistor_SMD:R_1206_3216Metric"],
|
|
897
|
-
"C": ["Capacitor_SMD:C_0603_1608Metric", "Capacitor_SMD:C_0805_2012Metric", "Capacitor_SMD:C_1206_3216Metric"],
|
|
898
|
-
"L": ["Inductor_SMD:L_0603_1608Metric", "Inductor_SMD:L_0805_2012Metric"],
|
|
899
|
-
"D": ["Diode_SMD:D_SOD-323", "Diode_SMD:D_0603_1608Metric"],
|
|
900
|
-
"LED": ["LED_SMD:LED_0603_1608Metric", "LED_SMD:LED_0805_2012Metric"],
|
|
901
|
-
"Q": ["Package_TO_SOT_SMD:SOT-23", "Package_TO_SOT_SMD:SOT-23-3"],
|
|
902
|
-
"U": ["Package_SO:SOIC-8_3.9x4.9mm_P1.27mm", "Package_DFN_QFN:QFN-16-1EP_3x3mm_P0.5mm_EP1.75x1.75mm"],
|
|
903
|
-
"J": ["Connector_PinHeader_2.54mm:PinHeader_1x02_P2.54mm_Vertical", "Connector_JST:JST_XH_B2B-XH-A_1x02_P2.50mm_Vertical"]
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
return footprint_map.get(prefix.upper(), ["Package_SO:SOIC-8_3.9x4.9mm_P1.27mm"])
|
|
907
|
-
|
|
908
|
-
def _get_usage_context(symbol) -> str:
|
|
909
|
-
"""Get basic usage context for a component using SymbolDefinition."""
|
|
910
|
-
return _get_usage_context_by_prefix(symbol.reference_prefix.upper())
|
|
911
|
-
|
|
912
|
-
def _get_usage_context_by_prefix(prefix: str) -> str:
|
|
913
|
-
"""Get basic usage context based on reference prefix."""
|
|
914
|
-
context_map = {
|
|
915
|
-
"R": "Use R_0603 for <0.1W signals, R_0805 for 0.1-0.25W power",
|
|
916
|
-
"C": "Use 0603 for <10μF ceramic, 0805 for 10-100μF, larger for >100μF",
|
|
917
|
-
"L": "Use 0603 for <1μH, 0805 for 1-10μH, larger for >10μH",
|
|
918
|
-
"D": "Use SOD-323 for signal diodes, larger packages for power applications",
|
|
919
|
-
"LED": "Use 0603 for indicators, 0805+ for higher current applications",
|
|
920
|
-
"Q": "Use SOT-23 for small signal, larger packages for power switching",
|
|
921
|
-
"U": "Choose package based on pin count and thermal requirements",
|
|
922
|
-
"J": "Select connector based on current rating and mechanical requirements"
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
return context_map.get(prefix.upper(), "Select package based on electrical and mechanical requirements")
|
|
926
|
-
|
|
927
|
-
@mcp.resource("schematic://current")
|
|
928
|
-
def get_current_schematic() -> str:
|
|
929
|
-
"""Get current schematic state as text."""
|
|
930
|
-
if not state.is_loaded():
|
|
931
|
-
return "No schematic currently loaded"
|
|
932
|
-
|
|
933
|
-
try:
|
|
934
|
-
info = {
|
|
935
|
-
"file_path": state.current_file_path or "Unsaved",
|
|
936
|
-
"component_count": len(state.current_schematic.components) if hasattr(state.current_schematic, 'components') else 0,
|
|
937
|
-
"loaded": True
|
|
938
|
-
}
|
|
939
|
-
return f"Current schematic: {info}"
|
|
940
|
-
except Exception as e:
|
|
941
|
-
return f"Error getting current schematic info: {e}"
|
|
942
|
-
|
|
943
|
-
@mcp.resource("kicad://components/common-resistors")
|
|
944
|
-
def get_common_resistors() -> str:
|
|
945
|
-
"""Common resistor components with standard footprints."""
|
|
946
|
-
return """# Common Resistors
|
|
947
|
-
|
|
948
|
-
## Standard Values (E12 Series)
|
|
949
|
-
- Device:R with values: 1.0, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2
|
|
950
|
-
- Multiply by: 1Ω, 10Ω, 100Ω, 1kΩ, 10kΩ, 100kΩ, 1MΩ
|
|
951
|
-
|
|
952
|
-
## Recommended Footprints
|
|
953
|
-
- **R_0603_1608Metric**: For <0.1W applications (most common)
|
|
954
|
-
- **R_0805_2012Metric**: For 0.1-0.25W applications
|
|
955
|
-
- **R_1206_3216Metric**: For 0.25-0.5W applications
|
|
956
|
-
|
|
957
|
-
## Common Usage
|
|
958
|
-
```python
|
|
959
|
-
# Example usage:
|
|
960
|
-
add_component("Device:R", "R1", "10k", (100, 100), footprint="Resistor_SMD:R_0603_1608Metric")
|
|
961
|
-
```
|
|
962
|
-
|
|
963
|
-
## Popular Values
|
|
964
|
-
- 10kΩ (pull-up resistors)
|
|
965
|
-
- 1kΩ (current limiting)
|
|
966
|
-
- 100Ω (series termination)
|
|
967
|
-
- 4.7kΩ (I2C pull-up)
|
|
968
|
-
"""
|
|
969
|
-
|
|
970
|
-
@mcp.resource("kicad://components/common-capacitors")
|
|
971
|
-
def get_common_capacitors() -> str:
|
|
972
|
-
"""Common capacitor components with standard footprints."""
|
|
973
|
-
return """# Common Capacitors
|
|
974
|
-
|
|
975
|
-
## Ceramic Capacitors (Device:C)
|
|
976
|
-
- Values: 1pF-10µF typical range
|
|
977
|
-
- Common: 100nF (0.1µF), 1µF, 10µF, 22µF
|
|
978
|
-
|
|
979
|
-
## Recommended Footprints
|
|
980
|
-
- **C_0603_1608Metric**: For <10µF ceramic (most common)
|
|
981
|
-
- **C_0805_2012Metric**: For 10µF-100µF ceramic
|
|
982
|
-
- **C_1206_3216Metric**: For >100µF ceramic
|
|
983
|
-
|
|
984
|
-
## Electrolytic Capacitors (Device:CP)
|
|
985
|
-
- Values: 1µF-1000µF+ for bulk storage
|
|
986
|
-
- Common: 10µF, 100µF, 470µF
|
|
987
|
-
|
|
988
|
-
## Common Usage
|
|
989
|
-
```python
|
|
990
|
-
# Decoupling capacitor
|
|
991
|
-
add_component("Device:C", "C1", "100n", (50, 50), footprint="Capacitor_SMD:C_0603_1608Metric")
|
|
992
|
-
|
|
993
|
-
# Power supply bulk capacitor
|
|
994
|
-
add_component("Device:CP", "C2", "100u", (75, 75), footprint="Capacitor_SMD:C_0805_2012Metric")
|
|
995
|
-
```
|
|
996
|
-
|
|
997
|
-
## Popular Applications
|
|
998
|
-
- 100nF: MCU decoupling
|
|
999
|
-
- 1µF: Voltage regulator input/output
|
|
1000
|
-
- 10µF: Power supply filtering
|
|
1001
|
-
"""
|
|
1002
|
-
|
|
1003
|
-
@mcp.resource("kicad://components/common-ics")
|
|
1004
|
-
def get_common_ics() -> str:
|
|
1005
|
-
"""Common integrated circuit components and packages."""
|
|
1006
|
-
return """# Common ICs
|
|
1007
|
-
|
|
1008
|
-
## Voltage Regulators
|
|
1009
|
-
- **Regulator_Linear:AMS1117-3.3**: 3.3V LDO regulator
|
|
1010
|
-
- Footprint: Package_TO_SOT_SMD:SOT-223-3_TabPin2
|
|
1011
|
-
- **Regulator_Linear:LM7805_TO220**: 5V linear regulator
|
|
1012
|
-
- Footprint: Package_TO_SOT_THT:TO-220-3_Vertical
|
|
1013
|
-
|
|
1014
|
-
## Op-Amps
|
|
1015
|
-
- **Amplifier_Operational:LM358**: Dual op-amp
|
|
1016
|
-
- Footprint: Package_SO:SOIC-8_3.9x4.9mm_P1.27mm
|
|
1017
|
-
- **Amplifier_Operational:TL072**: JFET input dual op-amp
|
|
1018
|
-
- Footprint: Package_DIP:DIP-8_W7.62mm
|
|
1019
|
-
|
|
1020
|
-
## Microcontrollers
|
|
1021
|
-
- **MCU_ST_STM32F4:STM32F401RETx**: ARM Cortex-M4 MCU
|
|
1022
|
-
- Footprint: Package_QFP:LQFP-64_10x10mm_P0.5mm
|
|
1023
|
-
- **MCU_Microchip_ATmega:ATmega328P-PU**: Arduino-compatible MCU
|
|
1024
|
-
- Footprint: Package_DIP:DIP-28_W15.24mm
|
|
1025
|
-
|
|
1026
|
-
## Common Usage
|
|
1027
|
-
```python
|
|
1028
|
-
# 3.3V regulator
|
|
1029
|
-
add_component("Regulator_Linear:AMS1117-3.3", "U1", "AMS1117", (100, 100),
|
|
1030
|
-
footprint="Package_TO_SOT_SMD:SOT-223-3_TabPin2")
|
|
1031
|
-
```
|
|
1032
|
-
"""
|
|
1033
|
-
|
|
1034
|
-
@mcp.resource("kicad://footprints/common-packages")
|
|
1035
|
-
def get_common_packages() -> str:
|
|
1036
|
-
"""Common footprint packages and their applications."""
|
|
1037
|
-
return """# Common Footprint Packages
|
|
1038
|
-
|
|
1039
|
-
## Surface Mount Packages
|
|
1040
|
-
|
|
1041
|
-
### Resistors & Capacitors
|
|
1042
|
-
- **0603 (1608 Metric)**: 1.6mm x 0.8mm - Most common for passives
|
|
1043
|
-
- **0805 (2012 Metric)**: 2.0mm x 1.2mm - Easier hand soldering
|
|
1044
|
-
- **1206 (3216 Metric)**: 3.2mm x 1.6mm - Higher power rating
|
|
1045
|
-
|
|
1046
|
-
### ICs
|
|
1047
|
-
- **SOIC-8**: 8-pin small outline package - common op-amps
|
|
1048
|
-
- **SOT-23**: 3-pin small outline transistor package
|
|
1049
|
-
- **SOT-223**: Power regulator package with tab
|
|
1050
|
-
- **LQFP-64**: 64-pin low-profile quad flat pack - microcontrollers
|
|
1051
|
-
- **QFN-16**: 16-pin quad flat no-lead package - compact ICs
|
|
1052
|
-
|
|
1053
|
-
## Through-Hole Packages
|
|
1054
|
-
|
|
1055
|
-
### Resistors & Capacitors
|
|
1056
|
-
- **Axial_DIN0207**: Standard through-hole resistor
|
|
1057
|
-
- **Radial_D5.0mm**: Through-hole capacitor, 5mm diameter
|
|
1058
|
-
|
|
1059
|
-
### ICs
|
|
1060
|
-
- **DIP-8**: 8-pin dual in-line package - breadboard friendly
|
|
1061
|
-
- **TO-220**: Power transistor/regulator package
|
|
1062
|
-
|
|
1063
|
-
## Package Selection Guide
|
|
1064
|
-
- Use 0603 for most SMD passives (good balance of size vs assemblability)
|
|
1065
|
-
- Use SOIC over TSSOP for easier hand assembly
|
|
1066
|
-
- Use through-hole (DIP/TO-220) for prototyping and high power
|
|
1067
|
-
- Use QFN/BGA only when density is critical
|
|
1068
|
-
"""
|
|
1069
|
-
|
|
1070
|
-
@mcp.resource("kicad://circuits/common-patterns")
|
|
1071
|
-
def get_common_patterns() -> str:
|
|
1072
|
-
"""Common circuit patterns and component combinations."""
|
|
1073
|
-
return """# Common Circuit Patterns
|
|
1074
|
-
|
|
1075
|
-
## Voltage Divider
|
|
1076
|
-
```python
|
|
1077
|
-
# R1 and R2 create voltage divider: Vout = Vin * R2/(R1+R2)
|
|
1078
|
-
add_component("Device:R", "R1", "10k", (100, 100)) # Top resistor
|
|
1079
|
-
add_component("Device:R", "R2", "10k", (100, 120)) # Bottom resistor
|
|
1080
|
-
add_wire((100, 110), (100, 120)) # Connect R1 pin2 to R2 pin1
|
|
1081
|
-
```
|
|
1082
|
-
|
|
1083
|
-
## MCU Decoupling
|
|
1084
|
-
```python
|
|
1085
|
-
# Always add 100nF ceramic cap near MCU power pins
|
|
1086
|
-
add_component("Device:C", "C1", "100n", (mcu_x + 5, mcu_y))
|
|
1087
|
-
# Add bulk capacitor nearby
|
|
1088
|
-
add_component("Device:C", "C2", "10u", (mcu_x + 10, mcu_y))
|
|
1089
|
-
```
|
|
1090
|
-
|
|
1091
|
-
## LED Current Limiting
|
|
1092
|
-
```python
|
|
1093
|
-
# LED with current limiting resistor
|
|
1094
|
-
add_component("Device:LED", "D1", "LED", (50, 50))
|
|
1095
|
-
add_component("Device:R", "R1", "330", (50, 70)) # ~10mA @ 3.3V
|
|
1096
|
-
add_wire((50, 60), (50, 70)) # Connect LED cathode to resistor
|
|
1097
|
-
```
|
|
1098
|
-
|
|
1099
|
-
## Pull-up Resistor
|
|
1100
|
-
```python
|
|
1101
|
-
# I2C or digital input pull-up
|
|
1102
|
-
add_component("Device:R", "R1", "4k7", (100, 100)) # 4.7kΩ standard for I2C
|
|
1103
|
-
add_component("Device:R", "R2", "10k", (120, 100)) # 10kΩ standard for digital
|
|
1104
|
-
```
|
|
1105
|
-
|
|
1106
|
-
## Voltage Regulator Circuit
|
|
1107
|
-
```python
|
|
1108
|
-
# Linear regulator with input/output capacitors
|
|
1109
|
-
add_component("Regulator_Linear:AMS1117-3.3", "U1", "AMS1117", (100, 100))
|
|
1110
|
-
add_component("Device:C", "C1", "10u", (80, 100)) # Input cap
|
|
1111
|
-
add_component("Device:C", "C2", "10u", (120, 100)) # Output cap
|
|
1112
|
-
add_component("Device:C", "C3", "100n", (120, 110)) # Fast decoupling
|
|
1113
|
-
```
|
|
1114
|
-
"""
|
|
1115
|
-
|
|
1116
|
-
@mcp.prompt()
|
|
1117
|
-
def how_to_use_kicad_mcp() -> str:
|
|
1118
|
-
"""Learn how to use the KiCAD Schematic MCP Server effectively."""
|
|
1119
|
-
return """# How to Use KiCAD Schematic MCP Server
|
|
1120
|
-
|
|
1121
|
-
## Getting Started
|
|
1122
|
-
|
|
1123
|
-
### 1. Create a New Schematic
|
|
1124
|
-
```
|
|
1125
|
-
Create a new schematic called "MyCircuit"
|
|
1126
|
-
```
|
|
1127
|
-
|
|
1128
|
-
### 2. Search for Components
|
|
1129
|
-
```
|
|
1130
|
-
Search for resistor components
|
|
1131
|
-
Search for components in the Device library
|
|
1132
|
-
Find STM32 microcontrollers
|
|
1133
|
-
```
|
|
1134
|
-
|
|
1135
|
-
### 3. Add Components
|
|
1136
|
-
```
|
|
1137
|
-
Add a 10kΩ resistor at position (100, 100) with reference R1
|
|
1138
|
-
Add a 100nF capacitor next to the resistor
|
|
1139
|
-
Add an LED with a current limiting resistor
|
|
1140
|
-
```
|
|
1141
|
-
|
|
1142
|
-
### 4. Connect Components
|
|
1143
|
-
```
|
|
1144
|
-
Connect the resistor R1 pin 1 to the capacitor C1 pin 1
|
|
1145
|
-
Add a wire from (100, 100) to (120, 100)
|
|
1146
|
-
```
|
|
1147
|
-
|
|
1148
|
-
### 5. Save Your Work
|
|
1149
|
-
```
|
|
1150
|
-
Save the schematic to "MyCircuit.kicad_sch"
|
|
1151
|
-
```
|
|
1152
|
-
|
|
1153
|
-
## Pro Tips
|
|
1154
|
-
|
|
1155
|
-
### Component Discovery
|
|
1156
|
-
- Use `search_components("resistor")` to find available resistor symbols
|
|
1157
|
-
- Use `validate_component("Device:R")` to check if a component exists
|
|
1158
|
-
- Use `list_libraries()` to see all available symbol libraries
|
|
1159
|
-
|
|
1160
|
-
### Common Components
|
|
1161
|
-
- **Device:R** - Generic resistor (use with values like "10k", "1M")
|
|
1162
|
-
- **Device:C** - Generic ceramic capacitor
|
|
1163
|
-
- **Device:LED** - Light emitting diode
|
|
1164
|
-
- **power:GND** - Ground symbol
|
|
1165
|
-
- **power:+3V3** - 3.3V power symbol
|
|
1166
|
-
|
|
1167
|
-
### Hierarchical Design Tools
|
|
1168
|
-
For complex multi-sheet designs:
|
|
1169
|
-
- `add_hierarchical_sheet("Sheet Name", "filename.kicad_sch", position, size)` - Create hierarchical sheet
|
|
1170
|
-
- `add_sheet_pin(sheet_uuid, "NET_NAME", "input/output/bidirectional", position)` - Add sheet pin
|
|
1171
|
-
- `add_hierarchical_label("NET_NAME", position, "input/output/bidirectional")` - Connect to parent sheet
|
|
1172
|
-
- `list_hierarchical_sheets()` - Show all sheets and their pins
|
|
1173
|
-
|
|
1174
|
-
### Footprint Selection
|
|
1175
|
-
The server suggests appropriate footprints for each component:
|
|
1176
|
-
- Resistors: R_0603_1608Metric (most common)
|
|
1177
|
-
- Capacitors: C_0603_1608Metric (for <10µF)
|
|
1178
|
-
- ICs: Package varies by pin count and thermal needs
|
|
1179
|
-
|
|
1180
|
-
### Best Practices
|
|
1181
|
-
1. Always search for components before adding them
|
|
1182
|
-
2. Use standard component values (E12 series for resistors)
|
|
1183
|
-
3. Add decoupling capacitors near ICs
|
|
1184
|
-
4. Use proper footprints for your assembly method
|
|
1185
|
-
5. Validate your design before saving
|
|
1186
|
-
|
|
1187
|
-
The server will guide you with suggestions and error messages if components don't exist!
|
|
1188
|
-
"""
|
|
1189
|
-
|
|
1190
|
-
@mcp.prompt()
|
|
1191
|
-
def schematic_design_guidelines() -> str:
|
|
1192
|
-
"""Essential schematic design guidelines for AI agents creating professional KiCAD schematics."""
|
|
1193
|
-
return """# KiCAD Schematic Design Guidelines for AI Agents
|
|
1194
|
-
|
|
1195
|
-
## CRITICAL CONNECTION STRATEGY
|
|
1196
|
-
|
|
1197
|
-
### ⚠️ AVOID WIRES - USE LABELS INSTEAD!
|
|
1198
|
-
- **NEVER use add_wire() in hierarchical designs**
|
|
1199
|
-
- **ALWAYS use hierarchical labels for connections**
|
|
1200
|
-
- **DEFAULT to labels even for simple connections**
|
|
1201
|
-
- Wires create messy, hard-to-read schematics
|
|
1202
|
-
|
|
1203
|
-
### 1. Component References - NEVER USE "?"
|
|
1204
|
-
- **ALWAYS** assign proper references: R1, R2, C1, C2, U1, etc.
|
|
1205
|
-
- **NEVER** leave references as "?" - this creates invalid schematics
|
|
1206
|
-
- Use standard prefixes: R=resistor, C=capacitor, U=IC, D=diode, L=inductor, Q=transistor
|
|
1207
|
-
|
|
1208
|
-
### 2. Component Spacing & Grid Alignment
|
|
1209
|
-
- **Minimum spacing**: 50 units between components
|
|
1210
|
-
- **Grid alignment**: Use multiples of 25.4 (100mil) or 12.7 (50mil)
|
|
1211
|
-
- **Examples**: (100,100), (150,100), (100,150) - NOT (103,97) random positions
|
|
1212
|
-
|
|
1213
|
-
### 3. Hierarchical Labels - CRITICAL POSITIONING
|
|
1214
|
-
- **Must touch component pins directly** - not floating in space
|
|
1215
|
-
- **Must face away from component** using proper rotation:
|
|
1216
|
-
* 0° = right-facing (for left-side pins)
|
|
1217
|
-
* 180° = left-facing (for right-side pins)
|
|
1218
|
-
* 90° = up-facing (for bottom pins)
|
|
1219
|
-
* 270° = down-facing (for top pins)
|
|
1220
|
-
|
|
1221
|
-
### 4. Hierarchical Sheet Sizing - KEEP SMALL!
|
|
1222
|
-
- **Small sheets**: (60, 40) for 2-3 pins (power, simple circuits)
|
|
1223
|
-
- **Medium sheets**: (80, 50) for 4-6 pins (most subcircuits)
|
|
1224
|
-
- **Large sheets**: (100, 60) for 7+ pins (complex only)
|
|
1225
|
-
- **AVOID**: Sheets larger than (120, 80) - they're too big!
|
|
1226
|
-
|
|
1227
|
-
## HIERARCHICAL DESIGN WORKFLOW
|
|
1228
|
-
|
|
1229
|
-
### Step-by-Step Process:
|
|
1230
|
-
1. **Main schematic**: add_hierarchical_sheet() first
|
|
1231
|
-
2. **Create subcircuit**: create_schematic() for new sheet file
|
|
1232
|
-
3. **Add sheet pins**: add_sheet_pin() on the sheet rectangle
|
|
1233
|
-
4. **Switch to subcircuit**: load_schematic() the new file
|
|
1234
|
-
5. **Add components**: Normal component placement in subcircuit
|
|
1235
|
-
6. **Add hierarchical labels**: On component pins with EXACT same names as sheet pins
|
|
1236
|
-
7. **Save both**: Save main and subcircuit schematics
|
|
1237
|
-
|
|
1238
|
-
### Name Matching Rule:
|
|
1239
|
-
Sheet pin "VCC" ↔ Hierarchical label "VCC" (MUST match exactly!)
|
|
1240
|
-
|
|
1241
|
-
## COMMON DESIGN PATTERNS
|
|
1242
|
-
|
|
1243
|
-
### Power Distribution:
|
|
1244
|
-
```
|
|
1245
|
-
- VCC/3V3/5V labels on power input pins
|
|
1246
|
-
- GND labels on ground pins
|
|
1247
|
-
- Use "input" type for power coming into sheet
|
|
1248
|
-
- Use "passive" type for ground connections
|
|
1249
|
-
```
|
|
1250
|
-
|
|
1251
|
-
### Signal Routing:
|
|
1252
|
-
```
|
|
1253
|
-
- Clear signal names: CLK, DATA, TX, RX, CS, MOSI, MISO
|
|
1254
|
-
- Use "output" for signals leaving a sheet
|
|
1255
|
-
- Use "input" for signals entering a sheet
|
|
1256
|
-
- Use "bidirectional" for I2C (SDA/SCL), SPI data
|
|
1257
|
-
```
|
|
1258
|
-
|
|
1259
|
-
### Component Values:
|
|
1260
|
-
```
|
|
1261
|
-
- Standard resistor values: 1k, 10k, 100k, 1M (E12 series)
|
|
1262
|
-
- Standard capacitor values: 100nF, 1uF, 10uF, 100uF
|
|
1263
|
-
- Specify footprints: R_0603_1608Metric, C_0603_1608Metric
|
|
1264
|
-
```
|
|
1265
|
-
|
|
1266
|
-
## ERROR PREVENTION
|
|
1267
|
-
|
|
1268
|
-
### Before creating any schematic:
|
|
1269
|
-
1. Plan component references (R1, R2, C1, etc.) - never use "?"
|
|
1270
|
-
2. Calculate component positions with proper spacing
|
|
1271
|
-
3. Plan hierarchical label names to match sheet pins exactly
|
|
1272
|
-
4. Verify label positions are on actual component pins
|
|
1273
|
-
5. Check label rotations face away from components
|
|
1274
|
-
|
|
1275
|
-
### Testing Your Design:
|
|
1276
|
-
- Use list_components() to verify references are assigned
|
|
1277
|
-
- Use get_schematic_info() to check component count
|
|
1278
|
-
- Ensure hierarchical labels have matching sheet pins
|
|
1279
|
-
"""
|
|
1280
|
-
|
|
1281
|
-
@mcp.prompt()
|
|
1282
|
-
def common_circuit_examples() -> str:
|
|
1283
|
-
"""Examples of common circuits you can build with KiCAD MCP."""
|
|
1284
|
-
return """# Common Circuit Examples
|
|
1285
|
-
|
|
1286
|
-
## 1. Simple LED Blinker
|
|
1287
|
-
Create a basic LED circuit with current limiting:
|
|
1288
|
-
```
|
|
1289
|
-
Create a new schematic called "LED_Blinker"
|
|
1290
|
-
Add an LED at position (100, 100)
|
|
1291
|
-
Add a 330Ω resistor at position (100, 80) for current limiting
|
|
1292
|
-
Connect the LED anode to one end of the resistor
|
|
1293
|
-
Add power and ground symbols
|
|
1294
|
-
Save the schematic
|
|
1295
|
-
```
|
|
1296
|
-
|
|
1297
|
-
## 2. Voltage Divider
|
|
1298
|
-
Build a voltage divider for level shifting:
|
|
1299
|
-
```
|
|
1300
|
-
Add two 10kΩ resistors in series
|
|
1301
|
-
Connect them to create Vout = Vin/2
|
|
1302
|
-
Add input and output connectors
|
|
1303
|
-
```
|
|
1304
|
-
|
|
1305
|
-
## 3. MCU Power Supply
|
|
1306
|
-
Design power conditioning for a microcontroller:
|
|
1307
|
-
```
|
|
1308
|
-
Add a 3.3V voltage regulator (AMS1117-3.3)
|
|
1309
|
-
Add 10µF input and output capacitors
|
|
1310
|
-
Add 100nF decoupling capacitors
|
|
1311
|
-
Connect ground plane
|
|
1312
|
-
```
|
|
1313
|
-
|
|
1314
|
-
## 4. I2C Pull-up Network
|
|
1315
|
-
Create proper I2C bus conditioning:
|
|
1316
|
-
```
|
|
1317
|
-
Add 4.7kΩ pull-up resistors for SDA and SCL lines
|
|
1318
|
-
Connect to 3.3V supply
|
|
1319
|
-
Add I2C connector
|
|
1320
|
-
```
|
|
1321
|
-
|
|
1322
|
-
## 5. Crystal Oscillator Circuit
|
|
1323
|
-
Build timing reference for MCU:
|
|
1324
|
-
```
|
|
1325
|
-
Add crystal oscillator component
|
|
1326
|
-
Add two 22pF load capacitors to ground
|
|
1327
|
-
Connect to MCU clock inputs
|
|
1328
|
-
```
|
|
1329
|
-
|
|
1330
|
-
Each example includes component selection, footprint recommendations, and connection guidance!
|
|
1331
|
-
"""
|
|
1332
|
-
|
|
1333
|
-
@mcp.resource("kicad://hierarchy/workflow-guide")
|
|
1334
|
-
def get_hierarchical_workflow() -> str:
|
|
1335
|
-
"""Guide for creating hierarchical schematics with multiple sheets."""
|
|
1336
|
-
return """# Hierarchical Schematic Workflow
|
|
1337
|
-
|
|
1338
|
-
## Overview
|
|
1339
|
-
Hierarchical schematics organize complex designs into multiple sheets for better readability and modularity. Each hierarchical sheet represents a functional block.
|
|
1340
|
-
|
|
1341
|
-
## Step 1: Plan Your Hierarchy
|
|
1342
|
-
Break your design into logical functional blocks:
|
|
1343
|
-
- Power Supply
|
|
1344
|
-
- MCU Core
|
|
1345
|
-
- Communication (USB, WiFi, etc.)
|
|
1346
|
-
- Analog Frontend
|
|
1347
|
-
- LED Driver
|
|
1348
|
-
- Debug/Programming Interface
|
|
1349
|
-
|
|
1350
|
-
## Step 2: Create Main Schematic
|
|
1351
|
-
Start with the top-level schematic that shows the overall system architecture:
|
|
1352
|
-
|
|
1353
|
-
```python
|
|
1354
|
-
# Create main schematic
|
|
1355
|
-
create_schematic("Main_Board")
|
|
1356
|
-
|
|
1357
|
-
# Add hierarchical sheets for each subsystem
|
|
1358
|
-
add_hierarchical_sheet("Power Supply", "power.kicad_sch", (50, 50), (50, 30))
|
|
1359
|
-
add_hierarchical_sheet("MCU Core", "mcu.kicad_sch", (120, 50), (60, 40))
|
|
1360
|
-
add_hierarchical_sheet("USB Interface", "usb.kicad_sch", (200, 50), (40, 25))
|
|
1361
|
-
```
|
|
1362
|
-
|
|
1363
|
-
## Step 3: Add Sheet Pins
|
|
1364
|
-
Connect hierarchical sheets by adding pins for signals that cross sheet boundaries:
|
|
1365
|
-
|
|
1366
|
-
```python
|
|
1367
|
-
# Get sheet UUID from add_hierarchical_sheet() return value
|
|
1368
|
-
power_sheet_uuid = "sheet-uuid-from-previous-call"
|
|
1369
|
-
|
|
1370
|
-
# Add power output pins to power supply sheet
|
|
1371
|
-
add_sheet_pin(power_sheet_uuid, "3V3", "output", (50, 5))
|
|
1372
|
-
add_sheet_pin(power_sheet_uuid, "5V", "output", (50, 10))
|
|
1373
|
-
add_sheet_pin(power_sheet_uuid, "GND", "passive", (50, 15))
|
|
1374
|
-
|
|
1375
|
-
# Add power input pins to MCU sheet
|
|
1376
|
-
mcu_sheet_uuid = "mcu-sheet-uuid"
|
|
1377
|
-
add_sheet_pin(mcu_sheet_uuid, "3V3", "input", (0, 5))
|
|
1378
|
-
add_sheet_pin(mcu_sheet_uuid, "GND", "passive", (0, 10))
|
|
1379
|
-
|
|
1380
|
-
# Add communication signals between MCU and USB
|
|
1381
|
-
add_sheet_pin(mcu_sheet_uuid, "USB_DP", "bidirectional", (60, 20))
|
|
1382
|
-
add_sheet_pin(mcu_sheet_uuid, "USB_DN", "bidirectional", (60, 25))
|
|
1383
|
-
```
|
|
1384
|
-
|
|
1385
|
-
## Step 4: Connect Sheet Pins with Wires
|
|
1386
|
-
Wire the sheet pins together on the main schematic:
|
|
1387
|
-
|
|
1388
|
-
```python
|
|
1389
|
-
# Connect power distribution
|
|
1390
|
-
add_wire((100, 55), (120, 55)) # 3V3: Power sheet output to MCU sheet input
|
|
1391
|
-
add_wire((100, 60), (120, 60)) # GND: Power sheet to MCU sheet
|
|
1392
|
-
|
|
1393
|
-
# Connect USB signals
|
|
1394
|
-
add_wire((180, 70), (200, 70)) # USB_DP: MCU to USB sheet
|
|
1395
|
-
add_wire((180, 75), (200, 75)) # USB_DN: MCU to USB sheet
|
|
1396
|
-
```
|
|
1397
|
-
|
|
1398
|
-
## Step 5: Create Child Schematics
|
|
1399
|
-
Create separate schematic files for each hierarchical sheet:
|
|
1400
|
-
|
|
1401
|
-
```python
|
|
1402
|
-
# Create power supply schematic
|
|
1403
|
-
create_schematic("Power_Supply")
|
|
1404
|
-
save_schematic("power.kicad_sch")
|
|
1405
|
-
|
|
1406
|
-
# Add hierarchical labels that match the sheet pins
|
|
1407
|
-
add_hierarchical_label("3V3", (150, 50), "output")
|
|
1408
|
-
add_hierarchical_label("5V", (150, 60), "output")
|
|
1409
|
-
add_hierarchical_label("GND", (150, 70), "passive")
|
|
1410
|
-
|
|
1411
|
-
# Add actual power supply components
|
|
1412
|
-
add_component("Regulator_Linear:AMS1117-3.3", "U1", "AMS1117", (100, 60))
|
|
1413
|
-
# ... more components
|
|
1414
|
-
```
|
|
1415
|
-
|
|
1416
|
-
## Pin Type Guidelines
|
|
1417
|
-
|
|
1418
|
-
**Input**: Signal flows into the sheet (power inputs, control signals)
|
|
1419
|
-
**Output**: Signal flows out of the sheet (power outputs, status signals)
|
|
1420
|
-
**Bidirectional**: Signal flows both ways (I2C, SPI data lines)
|
|
1421
|
-
**Tri-state**: Three-state signals (data buses with enable)
|
|
1422
|
-
**Passive**: Non-directional connections (ground, analog signals)
|
|
1423
|
-
|
|
1424
|
-
## Best Practices
|
|
1425
|
-
|
|
1426
|
-
1. **Consistent Naming**: Use the same net names for sheet pins and hierarchical labels
|
|
1427
|
-
2. **Logical Grouping**: Group related signals together on sheet edges
|
|
1428
|
-
3. **Clear Pin Types**: Use correct pin types for signal direction
|
|
1429
|
-
4. **Good Placement**: Position sheet pins at logical connection points
|
|
1430
|
-
5. **Documentation**: Add descriptive names and organize sheets clearly
|
|
1431
|
-
|
|
1432
|
-
## Common Patterns
|
|
1433
|
-
|
|
1434
|
-
### Power Distribution
|
|
1435
|
-
```python
|
|
1436
|
-
# Power sheet outputs
|
|
1437
|
-
add_sheet_pin(power_uuid, "3V3", "output", (50, 5))
|
|
1438
|
-
add_sheet_pin(power_uuid, "GND", "passive", (50, 30))
|
|
1439
|
-
|
|
1440
|
-
# Consumer sheet inputs
|
|
1441
|
-
add_sheet_pin(mcu_uuid, "3V3", "input", (0, 5))
|
|
1442
|
-
add_sheet_pin(mcu_uuid, "GND", "passive", (0, 30))
|
|
1443
|
-
```
|
|
1444
|
-
|
|
1445
|
-
### Communication Bus
|
|
1446
|
-
```python
|
|
1447
|
-
# MCU sheet (bus master)
|
|
1448
|
-
add_sheet_pin(mcu_uuid, "I2C_SCL", "output", (60, 10))
|
|
1449
|
-
add_sheet_pin(mcu_uuid, "I2C_SDA", "bidirectional", (60, 15))
|
|
1450
|
-
|
|
1451
|
-
# Sensor sheet (bus slave)
|
|
1452
|
-
add_sheet_pin(sensor_uuid, "I2C_SCL", "input", (0, 10))
|
|
1453
|
-
add_sheet_pin(sensor_uuid, "I2C_SDA", "bidirectional", (0, 15))
|
|
1454
|
-
```
|
|
1455
|
-
|
|
1456
|
-
This hierarchical approach makes complex designs manageable and promotes reusable functional blocks.
|
|
1457
|
-
"""
|
|
1458
|
-
|
|
1459
|
-
def main():
|
|
1460
|
-
"""Run the MCP server in standard on-demand mode."""
|
|
1461
|
-
import argparse
|
|
1462
|
-
|
|
1463
|
-
parser = argparse.ArgumentParser(description="KiCAD Schematic MCP Server")
|
|
1464
|
-
parser.add_argument('--test', action='store_true', help='Run quick test and exit')
|
|
1465
|
-
parser.add_argument('--debug', action='store_true', help='Enable debug logging')
|
|
1466
|
-
parser.add_argument('--status', action='store_true', help='Show server status and exit')
|
|
1467
|
-
parser.add_argument('--version', action='store_true', help='Show version and exit')
|
|
1468
|
-
|
|
1469
|
-
args = parser.parse_args()
|
|
1470
|
-
|
|
1471
|
-
if args.debug:
|
|
1472
|
-
logging.getLogger().setLevel(logging.DEBUG)
|
|
1473
|
-
logger.debug("Debug logging enabled")
|
|
1474
|
-
|
|
1475
|
-
if args.version:
|
|
1476
|
-
print("KiCAD Schematic MCP Server v0.1.5")
|
|
1477
|
-
return
|
|
1478
|
-
|
|
1479
|
-
if args.status:
|
|
1480
|
-
print("KiCAD Schematic MCP Server Status:")
|
|
1481
|
-
print(f" Component discovery: {len(get_search_index().get_categories()) if get_search_index() else 0} categories")
|
|
1482
|
-
print(f" Symbol cache: {get_symbol_cache().get_performance_stats()['total_symbols_cached']} symbols")
|
|
1483
|
-
print(" Status: Ready")
|
|
1484
|
-
return
|
|
1485
|
-
|
|
1486
|
-
if args.test:
|
|
1487
|
-
logger.info("Running MCP server test...")
|
|
1488
|
-
try:
|
|
1489
|
-
# Quick initialization test
|
|
1490
|
-
ensure_index_built()
|
|
1491
|
-
cache = get_symbol_cache()
|
|
1492
|
-
stats = cache.get_performance_stats()
|
|
1493
|
-
print(f"✅ Test passed: {stats['total_symbols_cached']} symbols, {len(get_search_index().get_categories())} categories")
|
|
1494
|
-
return
|
|
1495
|
-
except Exception as e:
|
|
1496
|
-
print(f"❌ Test failed: {e}")
|
|
1497
|
-
sys.exit(1)
|
|
1498
|
-
|
|
1499
|
-
logger.info("Starting KiCAD Schematic MCP Server...")
|
|
1500
|
-
try:
|
|
1501
|
-
# Run normally - Claude Desktop will manage the process lifecycle
|
|
1502
|
-
mcp.run()
|
|
1503
|
-
except KeyboardInterrupt:
|
|
1504
|
-
logger.info("Server stopped by user")
|
|
1505
|
-
except Exception as e:
|
|
1506
|
-
logger.error(f"Server error: {e}")
|
|
1507
|
-
sys.exit(1)
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
if __name__ == "__main__":
|
|
1511
|
-
main()
|