kicad-sch-api 0.3.0__py3-none-any.whl → 0.5.1__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.
Files changed (112) hide show
  1. kicad_sch_api/__init__.py +68 -3
  2. kicad_sch_api/cli/__init__.py +45 -0
  3. kicad_sch_api/cli/base.py +302 -0
  4. kicad_sch_api/cli/bom.py +164 -0
  5. kicad_sch_api/cli/erc.py +229 -0
  6. kicad_sch_api/cli/export_docs.py +289 -0
  7. kicad_sch_api/cli/kicad_to_python.py +169 -0
  8. kicad_sch_api/cli/netlist.py +94 -0
  9. kicad_sch_api/cli/types.py +43 -0
  10. kicad_sch_api/collections/__init__.py +36 -0
  11. kicad_sch_api/collections/base.py +604 -0
  12. kicad_sch_api/collections/components.py +1623 -0
  13. kicad_sch_api/collections/junctions.py +206 -0
  14. kicad_sch_api/collections/labels.py +508 -0
  15. kicad_sch_api/collections/wires.py +292 -0
  16. kicad_sch_api/core/__init__.py +37 -2
  17. kicad_sch_api/core/collections/__init__.py +5 -0
  18. kicad_sch_api/core/collections/base.py +248 -0
  19. kicad_sch_api/core/component_bounds.py +34 -7
  20. kicad_sch_api/core/components.py +213 -52
  21. kicad_sch_api/core/config.py +110 -15
  22. kicad_sch_api/core/connectivity.py +692 -0
  23. kicad_sch_api/core/exceptions.py +175 -0
  24. kicad_sch_api/core/factories/__init__.py +5 -0
  25. kicad_sch_api/core/factories/element_factory.py +278 -0
  26. kicad_sch_api/core/formatter.py +60 -9
  27. kicad_sch_api/core/geometry.py +94 -5
  28. kicad_sch_api/core/junctions.py +26 -75
  29. kicad_sch_api/core/labels.py +324 -0
  30. kicad_sch_api/core/managers/__init__.py +30 -0
  31. kicad_sch_api/core/managers/base.py +76 -0
  32. kicad_sch_api/core/managers/file_io.py +246 -0
  33. kicad_sch_api/core/managers/format_sync.py +502 -0
  34. kicad_sch_api/core/managers/graphics.py +580 -0
  35. kicad_sch_api/core/managers/hierarchy.py +661 -0
  36. kicad_sch_api/core/managers/metadata.py +271 -0
  37. kicad_sch_api/core/managers/sheet.py +492 -0
  38. kicad_sch_api/core/managers/text_elements.py +537 -0
  39. kicad_sch_api/core/managers/validation.py +476 -0
  40. kicad_sch_api/core/managers/wire.py +410 -0
  41. kicad_sch_api/core/nets.py +305 -0
  42. kicad_sch_api/core/no_connects.py +252 -0
  43. kicad_sch_api/core/parser.py +194 -970
  44. kicad_sch_api/core/parsing_utils.py +63 -0
  45. kicad_sch_api/core/pin_utils.py +103 -9
  46. kicad_sch_api/core/schematic.py +1328 -1079
  47. kicad_sch_api/core/texts.py +316 -0
  48. kicad_sch_api/core/types.py +159 -23
  49. kicad_sch_api/core/wires.py +27 -75
  50. kicad_sch_api/exporters/__init__.py +10 -0
  51. kicad_sch_api/exporters/python_generator.py +610 -0
  52. kicad_sch_api/exporters/templates/default.py.jinja2 +65 -0
  53. kicad_sch_api/geometry/__init__.py +38 -0
  54. kicad_sch_api/geometry/font_metrics.py +22 -0
  55. kicad_sch_api/geometry/routing.py +211 -0
  56. kicad_sch_api/geometry/symbol_bbox.py +608 -0
  57. kicad_sch_api/interfaces/__init__.py +17 -0
  58. kicad_sch_api/interfaces/parser.py +76 -0
  59. kicad_sch_api/interfaces/repository.py +70 -0
  60. kicad_sch_api/interfaces/resolver.py +117 -0
  61. kicad_sch_api/parsers/__init__.py +14 -0
  62. kicad_sch_api/parsers/base.py +145 -0
  63. kicad_sch_api/parsers/elements/__init__.py +22 -0
  64. kicad_sch_api/parsers/elements/graphics_parser.py +564 -0
  65. kicad_sch_api/parsers/elements/label_parser.py +216 -0
  66. kicad_sch_api/parsers/elements/library_parser.py +165 -0
  67. kicad_sch_api/parsers/elements/metadata_parser.py +58 -0
  68. kicad_sch_api/parsers/elements/sheet_parser.py +352 -0
  69. kicad_sch_api/parsers/elements/symbol_parser.py +485 -0
  70. kicad_sch_api/parsers/elements/text_parser.py +250 -0
  71. kicad_sch_api/parsers/elements/wire_parser.py +242 -0
  72. kicad_sch_api/parsers/registry.py +155 -0
  73. kicad_sch_api/parsers/utils.py +80 -0
  74. kicad_sch_api/symbols/__init__.py +18 -0
  75. kicad_sch_api/symbols/cache.py +467 -0
  76. kicad_sch_api/symbols/resolver.py +361 -0
  77. kicad_sch_api/symbols/validators.py +504 -0
  78. kicad_sch_api/utils/logging.py +555 -0
  79. kicad_sch_api/utils/logging_decorators.py +587 -0
  80. kicad_sch_api/utils/validation.py +16 -22
  81. kicad_sch_api/validation/__init__.py +25 -0
  82. kicad_sch_api/validation/erc.py +171 -0
  83. kicad_sch_api/validation/erc_models.py +203 -0
  84. kicad_sch_api/validation/pin_matrix.py +243 -0
  85. kicad_sch_api/validation/validators.py +391 -0
  86. kicad_sch_api/wrappers/__init__.py +14 -0
  87. kicad_sch_api/wrappers/base.py +89 -0
  88. kicad_sch_api/wrappers/wire.py +198 -0
  89. kicad_sch_api-0.5.1.dist-info/METADATA +540 -0
  90. kicad_sch_api-0.5.1.dist-info/RECORD +114 -0
  91. kicad_sch_api-0.5.1.dist-info/entry_points.txt +4 -0
  92. {kicad_sch_api-0.3.0.dist-info → kicad_sch_api-0.5.1.dist-info}/top_level.txt +1 -0
  93. mcp_server/__init__.py +34 -0
  94. mcp_server/example_logging_integration.py +506 -0
  95. mcp_server/models.py +252 -0
  96. mcp_server/server.py +357 -0
  97. mcp_server/tools/__init__.py +32 -0
  98. mcp_server/tools/component_tools.py +516 -0
  99. mcp_server/tools/connectivity_tools.py +532 -0
  100. mcp_server/tools/consolidated_tools.py +1216 -0
  101. mcp_server/tools/pin_discovery.py +333 -0
  102. mcp_server/utils/__init__.py +38 -0
  103. mcp_server/utils/logging.py +127 -0
  104. mcp_server/utils.py +36 -0
  105. kicad_sch_api/core/manhattan_routing.py +0 -430
  106. kicad_sch_api/core/simple_manhattan.py +0 -228
  107. kicad_sch_api/core/wire_routing.py +0 -380
  108. kicad_sch_api-0.3.0.dist-info/METADATA +0 -483
  109. kicad_sch_api-0.3.0.dist-info/RECORD +0 -31
  110. kicad_sch_api-0.3.0.dist-info/entry_points.txt +0 -2
  111. {kicad_sch_api-0.3.0.dist-info → kicad_sch_api-0.5.1.dist-info}/WHEEL +0 -0
  112. {kicad_sch_api-0.3.0.dist-info → kicad_sch_api-0.5.1.dist-info}/licenses/LICENSE +0 -0
mcp_server/models.py ADDED
@@ -0,0 +1,252 @@
1
+ """
2
+ Pydantic models for MCP server API responses.
3
+
4
+ Provides type-safe data models for MCP tools that interact with the schematic API,
5
+ ensuring consistent serialization and validation across all endpoints.
6
+ """
7
+
8
+ from typing import List, Optional
9
+ from pydantic import BaseModel, ConfigDict, Field
10
+
11
+
12
+ class PointModel(BaseModel):
13
+ """2D point coordinates."""
14
+
15
+ model_config = ConfigDict(
16
+ json_schema_extra={
17
+ "example": {"x": 100.0, "y": 100.0},
18
+ "description": "Position in KiCAD schematic coordinates",
19
+ }
20
+ )
21
+
22
+ x: float = Field(..., description="X coordinate in mm")
23
+ y: float = Field(..., description="Y coordinate in mm")
24
+
25
+
26
+ class PinInfoOutput(BaseModel):
27
+ """
28
+ Complete pin information for MCP clients.
29
+
30
+ Provides comprehensive pin metadata including position, electrical type,
31
+ graphical representation, and unique identification.
32
+ """
33
+
34
+ model_config = ConfigDict(
35
+ json_schema_extra={
36
+ "example": {
37
+ "number": "1",
38
+ "name": "~",
39
+ "position": {"x": 100.33, "y": 104.14},
40
+ "electrical_type": "passive",
41
+ "shape": "line",
42
+ "length": 2.54,
43
+ "orientation": 0.0,
44
+ "uuid": "1f8ab1be-1ad8-469d-8ba9-667910bdee9e:1",
45
+ }
46
+ }
47
+ )
48
+
49
+ number: str = Field(
50
+ ...,
51
+ description="Pin number or designator (e.g., '1', '2', 'A1')",
52
+ examples=["1", "2", "A1", "CLK"],
53
+ )
54
+ name: str = Field(
55
+ ...,
56
+ description="Pin name or signal designation (e.g., 'VCC', 'GND', 'CLK')",
57
+ examples=["VCC", "GND", "CLK", "D0"],
58
+ )
59
+ position: PointModel = Field(
60
+ ...,
61
+ description="Absolute position in schematic coordinates (mm), "
62
+ "accounting for component rotation and mirroring",
63
+ )
64
+ electrical_type: str = Field(
65
+ ...,
66
+ description="Pin electrical type (input, output, passive, power_in, power_out, bidirectional, etc.)",
67
+ examples=["passive", "input", "output", "power_in", "power_out"],
68
+ )
69
+ shape: str = Field(
70
+ ...,
71
+ description="Pin graphical shape (line, inverted, clock, inverted_clock, input_low, etc.)",
72
+ examples=["line", "inverted", "clock"],
73
+ )
74
+ length: float = Field(
75
+ ...,
76
+ description="Pin length in mm (typically 2.54 for standard pins)",
77
+ examples=[2.54],
78
+ )
79
+ orientation: float = Field(
80
+ ...,
81
+ description="Pin orientation in degrees (0, 90, 180, or 270)",
82
+ examples=[0.0, 90.0, 180.0, 270.0],
83
+ )
84
+ uuid: str = Field(
85
+ ...,
86
+ description="Unique identifier for this pin instance (composite: component_uuid:pin_number)",
87
+ examples=["1f8ab1be-1ad8-469d-8ba9-667910bdee9e:1"],
88
+ )
89
+
90
+
91
+ class ComponentPinsOutput(BaseModel):
92
+ """
93
+ Output model for get_component_pins MCP tool.
94
+
95
+ Returns all pins for a specified component with complete metadata.
96
+ """
97
+
98
+ model_config = ConfigDict(
99
+ json_schema_extra={
100
+ "example": {
101
+ "reference": "R1",
102
+ "lib_id": "Device:R",
103
+ "pins": [
104
+ {
105
+ "number": "1",
106
+ "name": "~",
107
+ "position": {"x": 100.33, "y": 104.14},
108
+ "electrical_type": "passive",
109
+ "shape": "line",
110
+ "length": 2.54,
111
+ "orientation": 0.0,
112
+ "uuid": "1f8ab1be-1ad8-469d-8ba9-667910bdee9e:1",
113
+ },
114
+ {
115
+ "number": "2",
116
+ "name": "~",
117
+ "position": {"x": 100.33, "y": 95.86},
118
+ "electrical_type": "passive",
119
+ "shape": "line",
120
+ "length": 2.54,
121
+ "orientation": 0.0,
122
+ "uuid": "1f8ab1be-1ad8-469d-8ba9-667910bdee9e:2",
123
+ },
124
+ ],
125
+ "pin_count": 2,
126
+ "success": True,
127
+ }
128
+ }
129
+ )
130
+
131
+ reference: str = Field(
132
+ ...,
133
+ description="Component reference designator (e.g., 'R1', 'U2', 'C1')",
134
+ examples=["R1", "U2", "C1"],
135
+ )
136
+ lib_id: str = Field(
137
+ ...,
138
+ description="Library identifier (e.g., 'Device:R', 'Amplifier_Operational:TL072')",
139
+ examples=["Device:R", "Amplifier_Operational:TL072", "LED:LED"],
140
+ )
141
+ pins: List[PinInfoOutput] = Field(
142
+ ...,
143
+ description="List of all pins for this component with complete metadata",
144
+ )
145
+ pin_count: int = Field(
146
+ ...,
147
+ description="Total number of pins",
148
+ examples=[2, 8, 14, 48],
149
+ )
150
+ success: bool = Field(
151
+ default=True,
152
+ description="Whether the operation was successful",
153
+ )
154
+ message: Optional[str] = Field(
155
+ default=None,
156
+ description="Optional message or error description",
157
+ )
158
+
159
+
160
+ class ComponentInfoOutput(BaseModel):
161
+ """
162
+ Component information output model.
163
+
164
+ Provides comprehensive component metadata including position, properties,
165
+ and unique identification.
166
+ """
167
+
168
+ model_config = ConfigDict(
169
+ json_schema_extra={
170
+ "example": {
171
+ "reference": "R1",
172
+ "lib_id": "Device:R",
173
+ "value": "10k",
174
+ "position": {"x": 100.0, "y": 100.0},
175
+ "rotation": 0.0,
176
+ "footprint": "Resistor_SMD:R_0603_1608Metric",
177
+ "uuid": "1f8ab1be-1ad8-469d-8ba9-667910bdee9e",
178
+ "success": True,
179
+ }
180
+ }
181
+ )
182
+
183
+ reference: str = Field(
184
+ ...,
185
+ description="Component reference designator (e.g., 'R1', 'U2', 'C1')",
186
+ examples=["R1", "U2", "C1"],
187
+ )
188
+ lib_id: str = Field(
189
+ ...,
190
+ description="Library identifier (e.g., 'Device:R', 'Amplifier_Operational:TL072')",
191
+ examples=["Device:R", "Amplifier_Operational:TL072", "LED:LED"],
192
+ )
193
+ value: str = Field(
194
+ ...,
195
+ description="Component value or part description",
196
+ examples=["10k", "100nF", "TL072"],
197
+ )
198
+ position: PointModel = Field(
199
+ ...,
200
+ description="Component position in schematic coordinates (mm)",
201
+ )
202
+ rotation: float = Field(
203
+ ...,
204
+ description="Component rotation in degrees (0, 90, 180, or 270)",
205
+ examples=[0.0, 90.0, 180.0, 270.0],
206
+ )
207
+ footprint: Optional[str] = Field(
208
+ default=None,
209
+ description="PCB footprint identifier",
210
+ examples=["Resistor_SMD:R_0603_1608Metric", "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm"],
211
+ )
212
+ uuid: str = Field(
213
+ ...,
214
+ description="Unique identifier for this component instance",
215
+ examples=["1f8ab1be-1ad8-469d-8ba9-667910bdee9e"],
216
+ )
217
+ success: bool = Field(
218
+ default=True,
219
+ description="Whether the operation was successful",
220
+ )
221
+ message: Optional[str] = Field(
222
+ default=None,
223
+ description="Optional message or error description",
224
+ )
225
+
226
+
227
+ class ErrorOutput(BaseModel):
228
+ """Standard error response model."""
229
+
230
+ model_config = ConfigDict(
231
+ json_schema_extra={
232
+ "example": {
233
+ "success": False,
234
+ "error": "COMPONENT_NOT_FOUND",
235
+ "message": "Component 'R999' not found in schematic",
236
+ }
237
+ }
238
+ )
239
+
240
+ success: bool = Field(
241
+ default=False,
242
+ description="Operation success status",
243
+ )
244
+ error: str = Field(
245
+ ...,
246
+ description="Error type or code",
247
+ examples=["COMPONENT_NOT_FOUND", "LIBRARY_ERROR"],
248
+ )
249
+ message: str = Field(
250
+ ...,
251
+ description="Detailed error message",
252
+ )
mcp_server/server.py ADDED
@@ -0,0 +1,357 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ MCP server main entry point for kicad-sch-api.
4
+
5
+ Provides schematic management and pin discovery tools via the Model Context Protocol.
6
+ """
7
+
8
+ import logging
9
+ import sys
10
+ from pathlib import Path
11
+ from typing import Optional
12
+
13
+ from fastmcp import FastMCP
14
+
15
+ import kicad_sch_api as ksa
16
+ from mcp_server.models import ErrorOutput
17
+ from mcp_server.tools.pin_discovery import (
18
+ find_pins_by_name,
19
+ find_pins_by_type,
20
+ get_component_pins,
21
+ get_current_schematic,
22
+ set_current_schematic,
23
+ )
24
+ from mcp_server.tools.component_tools import (
25
+ add_component,
26
+ list_components,
27
+ update_component,
28
+ remove_component,
29
+ filter_components,
30
+ )
31
+ from mcp_server.tools.connectivity_tools import (
32
+ add_wire,
33
+ add_label,
34
+ add_junction,
35
+ connect_components,
36
+ )
37
+ from mcp_server.tools.consolidated_tools import (
38
+ manage_schematic,
39
+ manage_components,
40
+ manage_wires,
41
+ manage_labels,
42
+ manage_text_boxes,
43
+ manage_power,
44
+ manage_sheets,
45
+ manage_global_labels,
46
+ )
47
+
48
+
49
+ # Configure logging
50
+ logging.basicConfig(
51
+ level=logging.INFO,
52
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
53
+ stream=sys.stderr,
54
+ )
55
+ logger = logging.getLogger(__name__)
56
+
57
+
58
+ # Initialize FastMCP server
59
+ mcp = FastMCP("kicad-sch-api")
60
+
61
+
62
+ # ========== Schematic Management Tools ==========
63
+
64
+
65
+ @mcp.tool()
66
+ async def create_schematic(name: str) -> dict:
67
+ """
68
+ Create a new blank KiCAD schematic.
69
+
70
+ Args:
71
+ name: Project name for the schematic
72
+
73
+ Returns:
74
+ Dictionary with success status and schematic information
75
+
76
+ Examples:
77
+ >>> result = await create_schematic("MyProject")
78
+ >>> print(result['message'])
79
+ Created new schematic: MyProject
80
+ """
81
+ logger.info(f"[MCP] create_schematic called with name: {name}")
82
+
83
+ try:
84
+ # Create new schematic
85
+ schematic = ksa.create_schematic(name)
86
+
87
+ # Set as current working schematic
88
+ set_current_schematic(schematic)
89
+
90
+ logger.info(f"[MCP] Created and set schematic: {name}")
91
+ return {
92
+ "success": True,
93
+ "message": f"Created new schematic: {name}",
94
+ "project_name": name,
95
+ "uuid": str(schematic.uuid),
96
+ }
97
+
98
+ except Exception as e:
99
+ logger.error(f"[MCP] Error creating schematic: {e}", exc_info=True)
100
+ return {
101
+ "success": False,
102
+ "error": "CREATION_ERROR",
103
+ "message": f"Failed to create schematic: {str(e)}",
104
+ }
105
+
106
+
107
+ @mcp.tool()
108
+ async def load_schematic(file_path: str) -> dict:
109
+ """
110
+ Load an existing KiCAD schematic file.
111
+
112
+ Args:
113
+ file_path: Absolute path to .kicad_sch file
114
+
115
+ Returns:
116
+ Dictionary with success status and schematic information
117
+
118
+ Examples:
119
+ >>> result = await load_schematic("/path/to/project.kicad_sch")
120
+ >>> print(f"Loaded: {result['project_name']}")
121
+ """
122
+ logger.info(f"[MCP] load_schematic called with path: {file_path}")
123
+
124
+ try:
125
+ # Validate path
126
+ path = Path(file_path)
127
+ if not path.exists():
128
+ logger.error(f"[MCP] File not found: {file_path}")
129
+ return {
130
+ "success": False,
131
+ "error": "FILE_NOT_FOUND",
132
+ "message": f"Schematic file not found: {file_path}",
133
+ }
134
+
135
+ if not path.suffix == ".kicad_sch":
136
+ logger.error(f"[MCP] Invalid file extension: {file_path}")
137
+ return {
138
+ "success": False,
139
+ "error": "INVALID_FILE",
140
+ "message": "File must have .kicad_sch extension",
141
+ }
142
+
143
+ # Load schematic
144
+ schematic = ksa.Schematic.load(str(path))
145
+
146
+ # Set as current working schematic
147
+ set_current_schematic(schematic)
148
+
149
+ # Count components
150
+ component_count = len(list(schematic.components.all()))
151
+
152
+ logger.info(
153
+ f"[MCP] Loaded schematic: {schematic.title_block.title} "
154
+ f"({component_count} components)"
155
+ )
156
+ return {
157
+ "success": True,
158
+ "message": f"Loaded schematic: {path.name}",
159
+ "file_path": str(path),
160
+ "project_name": schematic.title_block.title,
161
+ "uuid": str(schematic.uuid),
162
+ "component_count": component_count,
163
+ }
164
+
165
+ except Exception as e:
166
+ logger.error(f"[MCP] Error loading schematic: {e}", exc_info=True)
167
+ return {
168
+ "success": False,
169
+ "error": "LOAD_ERROR",
170
+ "message": f"Failed to load schematic: {str(e)}",
171
+ }
172
+
173
+
174
+ @mcp.tool()
175
+ async def save_schematic(file_path: Optional[str] = None) -> dict:
176
+ """
177
+ Save the current schematic to disk.
178
+
179
+ Args:
180
+ file_path: Optional path to save to. If not provided, saves to original location.
181
+
182
+ Returns:
183
+ Dictionary with success status
184
+
185
+ Examples:
186
+ >>> # Save to original location
187
+ >>> result = await save_schematic()
188
+
189
+ >>> # Save to new location
190
+ >>> result = await save_schematic("/path/to/new_location.kicad_sch")
191
+ """
192
+ logger.info(f"[MCP] save_schematic called with path: {file_path}")
193
+
194
+ # Check if schematic is loaded
195
+ schematic = get_current_schematic()
196
+ if schematic is None:
197
+ logger.error("[MCP] No schematic loaded")
198
+ return {
199
+ "success": False,
200
+ "error": "NO_SCHEMATIC_LOADED",
201
+ "message": "No schematic is currently loaded. Please load or create a schematic first.",
202
+ }
203
+
204
+ try:
205
+ if file_path:
206
+ # Save to specified path
207
+ path = Path(file_path)
208
+ if not path.suffix == ".kicad_sch":
209
+ logger.error(f"[MCP] Invalid file extension: {file_path}")
210
+ return {
211
+ "success": False,
212
+ "error": "INVALID_FILE",
213
+ "message": "File must have .kicad_sch extension",
214
+ }
215
+ schematic.save(str(path))
216
+ logger.info(f"[MCP] Saved schematic to: {path}")
217
+ return {
218
+ "success": True,
219
+ "message": f"Saved schematic to: {path.name}",
220
+ "file_path": str(path),
221
+ }
222
+ else:
223
+ # Save to original location
224
+ schematic.save()
225
+ logger.info("[MCP] Saved schematic to original location")
226
+ return {
227
+ "success": True,
228
+ "message": "Saved schematic to original location",
229
+ }
230
+
231
+ except Exception as e:
232
+ logger.error(f"[MCP] Error saving schematic: {e}", exc_info=True)
233
+ return {
234
+ "success": False,
235
+ "error": "SAVE_ERROR",
236
+ "message": f"Failed to save schematic: {str(e)}",
237
+ }
238
+
239
+
240
+ @mcp.tool()
241
+ async def get_schematic_info() -> dict:
242
+ """
243
+ Get information about the currently loaded schematic.
244
+
245
+ Returns:
246
+ Dictionary with schematic metadata
247
+
248
+ Examples:
249
+ >>> result = await get_schematic_info()
250
+ >>> print(f"Components: {result['component_count']}")
251
+ """
252
+ logger.info("[MCP] get_schematic_info called")
253
+
254
+ # Check if schematic is loaded
255
+ schematic = get_current_schematic()
256
+ if schematic is None:
257
+ logger.error("[MCP] No schematic loaded")
258
+ return {
259
+ "success": False,
260
+ "error": "NO_SCHEMATIC_LOADED",
261
+ "message": "No schematic is currently loaded",
262
+ }
263
+
264
+ try:
265
+ # Collect schematic information
266
+ components = list(schematic.components.all())
267
+ component_refs = [c.reference for c in components]
268
+
269
+ info = {
270
+ "success": True,
271
+ "project_name": schematic.title_block.title,
272
+ "uuid": str(schematic.uuid),
273
+ "component_count": len(components),
274
+ "component_references": component_refs,
275
+ "lib_symbols_count": len(schematic.lib_symbols),
276
+ }
277
+
278
+ logger.info(
279
+ f"[MCP] Schematic info: {info['project_name']} ({info['component_count']} components)"
280
+ )
281
+ return info
282
+
283
+ except Exception as e:
284
+ logger.error(f"[MCP] Error getting schematic info: {e}", exc_info=True)
285
+ return {
286
+ "success": False,
287
+ "error": "INTERNAL_ERROR",
288
+ "message": f"Failed to get schematic info: {str(e)}",
289
+ }
290
+
291
+
292
+ # ========== Register Pin Discovery Tools ==========
293
+
294
+ # Register the pin discovery tools from pin_discovery.py
295
+ mcp.tool()(get_component_pins)
296
+ mcp.tool()(find_pins_by_name)
297
+ mcp.tool()(find_pins_by_type)
298
+
299
+
300
+ # ========== Register Component Management Tools ==========
301
+
302
+ # Register the component management tools from component_tools.py
303
+ mcp.tool()(add_component)
304
+ mcp.tool()(list_components)
305
+ mcp.tool()(update_component)
306
+ mcp.tool()(remove_component)
307
+ mcp.tool()(filter_components)
308
+
309
+
310
+ # ========== Register Connectivity Tools ==========
311
+
312
+ # Register the connectivity tools from connectivity_tools.py
313
+ mcp.tool()(add_wire)
314
+ mcp.tool()(add_label)
315
+ mcp.tool()(add_junction)
316
+ mcp.tool()(connect_components)
317
+
318
+
319
+ # ========== Register Consolidated Tools ==========
320
+
321
+ # Register the 8 consolidated CRUD tools for schematic management
322
+ # These tools consolidate all operations by entity type with action parameters
323
+ mcp.tool()(manage_schematic) # Schematic: create, read, save, load
324
+ mcp.tool()(manage_components) # Components: add, list, get_pins, update, remove
325
+ mcp.tool()(manage_wires) # Wires: add, remove
326
+ mcp.tool()(manage_labels) # Labels: add, remove
327
+ mcp.tool()(manage_text_boxes) # TextBoxes: add, update, remove
328
+ mcp.tool()(manage_power) # Power: add, list, remove
329
+ mcp.tool()(manage_sheets) # Sheets: add, set_context, list, remove
330
+ mcp.tool()(manage_global_labels) # GlobalLabels: add, remove
331
+
332
+
333
+ # ========== Server Entry Point ==========
334
+
335
+
336
+ def main() -> None:
337
+ """
338
+ Main entry point for the MCP server.
339
+
340
+ Starts the FastMCP server with STDIO transport for Claude Desktop integration.
341
+ """
342
+ logger.info("Starting kicad-sch-api MCP server...")
343
+ logger.info(f"kicad-sch-api version: {ksa.__version__}")
344
+
345
+ try:
346
+ # Run the MCP server with STDIO transport
347
+ mcp.run(transport="stdio")
348
+ except KeyboardInterrupt:
349
+ logger.info("Server shutdown requested")
350
+ sys.exit(0)
351
+ except Exception as e:
352
+ logger.error(f"Server error: {e}", exc_info=True)
353
+ sys.exit(1)
354
+
355
+
356
+ if __name__ == "__main__":
357
+ main()
@@ -0,0 +1,32 @@
1
+ """
2
+ MCP tools for kicad-sch-api.
3
+
4
+ Provides tool implementations for pin discovery, component manipulation,
5
+ and connectivity operations.
6
+ """
7
+
8
+ from mcp_server.tools.connectivity_tools import (
9
+ add_junction,
10
+ add_label,
11
+ add_wire,
12
+ connect_components,
13
+ )
14
+ from mcp_server.tools.pin_discovery import (
15
+ find_pins_by_name,
16
+ find_pins_by_type,
17
+ get_component_pins,
18
+ get_current_schematic,
19
+ set_current_schematic,
20
+ )
21
+
22
+ __all__ = [
23
+ "get_component_pins",
24
+ "find_pins_by_name",
25
+ "find_pins_by_type",
26
+ "get_current_schematic",
27
+ "set_current_schematic",
28
+ "add_wire",
29
+ "add_label",
30
+ "add_junction",
31
+ "connect_components",
32
+ ]