@hybridlabor-api/bdb-hardware-pcb 0.1.0

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 (192) hide show
  1. package/README.md +116 -0
  2. package/config/mcp/antigravity.json +22 -0
  3. package/config/mcp/claude.json +22 -0
  4. package/config/mcp/codex.toml +25 -0
  5. package/docs/adr/ADR-001-KICAD-OPENSCAD-MCP-STRATEGY.md +334 -0
  6. package/docs/review_documentation.md +121 -0
  7. package/installer.js +358 -0
  8. package/mcp_servers/kicad-mcp-server/.env.example +22 -0
  9. package/mcp_servers/kicad-mcp-server/.github/workflows/ci.yml +36 -0
  10. package/mcp_servers/kicad-mcp-server/CLAUDE.md +487 -0
  11. package/mcp_servers/kicad-mcp-server/README.md +316 -0
  12. package/mcp_servers/kicad-mcp-server/docs/DEVICE_TREE.md +416 -0
  13. package/mcp_servers/kicad-mcp-server/docs/INSTALLATION.md +332 -0
  14. package/mcp_servers/kicad-mcp-server/docs/PIN_ANALYSIS.md +332 -0
  15. package/mcp_servers/kicad-mcp-server/docs/README.md +240 -0
  16. package/mcp_servers/kicad-mcp-server/docs/TESTING.md +613 -0
  17. package/mcp_servers/kicad-mcp-server/docs/VALIDATION.md +268 -0
  18. package/mcp_servers/kicad-mcp-server/pyproject.toml +96 -0
  19. package/mcp_servers/kicad-mcp-server/requirements-dev.txt +16 -0
  20. package/mcp_servers/kicad-mcp-server/requirements-test.txt +24 -0
  21. package/mcp_servers/kicad-mcp-server/requirements.txt +13 -0
  22. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/__init__.py +3 -0
  23. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/__main__.py +17 -0
  24. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/config.py +46 -0
  25. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/models/__init__.py +1 -0
  26. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/models/types.py +87 -0
  27. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/__init__.py +1 -0
  28. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/netlist_parser.py +234 -0
  29. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/pcb_parser.py +375 -0
  30. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/pcb_parser_kicad.py +327 -0
  31. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/parsers/schematic_parser.py +902 -0
  32. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/server.py +71 -0
  33. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/__init__.py +1 -0
  34. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/arduino/connectivity_test.cpp.j2 +189 -0
  35. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/atmega.dts.j2 +77 -0
  36. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/esp32.dts.j2 +77 -0
  37. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/nrf52.dts.j2 +77 -0
  38. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/device_tree/stm32f4.dts.j2 +89 -0
  39. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/esp_idf/test_suite.c.j2 +340 -0
  40. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/pytest/test_connectivity.py.j2 +147 -0
  41. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/st_hal/hal_test.c.j2 +313 -0
  42. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_gpio_test.py.j2 +99 -0
  43. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_i2c_test.py.j2 +117 -0
  44. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_pinmux_test.py.j2 +43 -0
  45. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/pytest_spi_test.py.j2 +94 -0
  46. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_gpio_test.c.j2 +113 -0
  47. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_i2c_test.c.j2 +101 -0
  48. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/tests/unity_spi_test.c.j2 +94 -0
  49. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/templates/unittest/test_schematic.py.j2 +172 -0
  50. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/__init__.py +36 -0
  51. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/device_tree.py +1187 -0
  52. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/hierarchical_analysis.py +211 -0
  53. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/netlist.py +320 -0
  54. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/parts_registry.py +142 -0
  55. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pcb.py +955 -0
  56. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pcb_layout.py +308 -0
  57. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/pin_analysis.py +765 -0
  58. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/project.py +196 -0
  59. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic.py +319 -0
  60. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic_editor.py +674 -0
  61. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/schematic_search.py +158 -0
  62. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/validation.py +866 -0
  63. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/tools/visualization.py +225 -0
  64. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/__init__.py +1 -0
  65. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/file_handlers.py +65 -0
  66. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/kicad_cli.py +103 -0
  67. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/kicad_version.py +103 -0
  68. package/mcp_servers/kicad-mcp-server/src/kicad_mcp_server/utils/parts_registry.py +197 -0
  69. package/mcp_servers/kicad-mcp-server/tests/__init__.py +1 -0
  70. package/mcp_servers/kicad-mcp-server/tests/examples/ESP32S3_TEST.md +219 -0
  71. package/mcp_servers/kicad-mcp-server/tests/fixtures/README.md +65 -0
  72. package/mcp_servers/kicad-mcp-server/tests/fixtures/__init__.py +1 -0
  73. package/mcp_servers/kicad-mcp-server/tests/fixtures/example_pcb.kicad_pcb +177 -0
  74. package/mcp_servers/kicad-mcp-server/tests/fixtures/example_schematic.kicad_sch +145 -0
  75. package/mcp_servers/kicad-mcp-server/tests/fixtures/hier/child.kicad_sch +24 -0
  76. package/mcp_servers/kicad-mcp-server/tests/fixtures/hier/root.kicad_sch +38 -0
  77. package/mcp_servers/kicad-mcp-server/tests/test_tools/__init__.py +1 -0
  78. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_hierarchical_labels.py +195 -0
  79. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_kicad_cli.py +116 -0
  80. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_netlist_cache_path.py +29 -0
  81. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_schematic.py +189 -0
  82. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_schematic_hierarchy.py +69 -0
  83. package/mcp_servers/kicad-mcp-server/tests/test_tools/test_visualization.py +136 -0
  84. package/mcp_servers/kicad-mcp-server/uv.lock +2873 -0
  85. package/mcp_servers/openscad-mcp-server/.dockerignore +9 -0
  86. package/mcp_servers/openscad-mcp-server/.github/workflows/test.yml +40 -0
  87. package/mcp_servers/openscad-mcp-server/Dockerfile +29 -0
  88. package/mcp_servers/openscad-mcp-server/LICENSE +21 -0
  89. package/mcp_servers/openscad-mcp-server/README.md +154 -0
  90. package/mcp_servers/openscad-mcp-server/docs/audit.md +56 -0
  91. package/mcp_servers/openscad-mcp-server/docs/docker.md +66 -0
  92. package/mcp_servers/openscad-mcp-server/docs/issue-followup.md +19 -0
  93. package/mcp_servers/openscad-mcp-server/docs/jetson.md +17 -0
  94. package/mcp_servers/openscad-mcp-server/glama.json +4 -0
  95. package/mcp_servers/openscad-mcp-server/legacy/README.md +15 -0
  96. package/mcp_servers/openscad-mcp-server/legacy/README.original.md +294 -0
  97. package/mcp_servers/openscad-mcp-server/legacy/implementation_plan.md +100 -0
  98. package/mcp_servers/openscad-mcp-server/legacy/old/download_sam2_checkpoint.py +115 -0
  99. package/mcp_servers/openscad-mcp-server/legacy/old/src/ai/sam_segmentation.py +209 -0
  100. package/mcp_servers/openscad-mcp-server/legacy/old/src/models/threestudio_generator.py +231 -0
  101. package/mcp_servers/openscad-mcp-server/legacy/old/src/workflow/image_to_model_pipeline.py +260 -0
  102. package/mcp_servers/openscad-mcp-server/legacy/old/test_sam2_segmentation.py +96 -0
  103. package/mcp_servers/openscad-mcp-server/legacy/requirements.txt +57 -0
  104. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/README.md +39 -0
  105. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/decisions/ai-driven-code-generation.md +122 -0
  106. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/decisions/export-formats.md +76 -0
  107. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/ai/ai_service.py.md +51 -0
  108. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/main.py.md +63 -0
  109. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/models/code_generator.py.md +63 -0
  110. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/files/src/nlp/parameter_extractor.py.md +63 -0
  111. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/ai/natural-language-processing.md +78 -0
  112. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/nlp/parameter-extraction.md +173 -0
  113. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/export-formats.md +91 -0
  114. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/openscad-basics.md +66 -0
  115. package/mcp_servers/openscad-mcp-server/legacy/rtfmd/knowledge/openscad/primitive-testing.md +79 -0
  116. package/mcp_servers/openscad-mcp-server/legacy/src/__init__.py +0 -0
  117. package/mcp_servers/openscad-mcp-server/legacy/src/ai/ai_service.py +257 -0
  118. package/mcp_servers/openscad-mcp-server/legacy/src/ai/gemini_api.py +161 -0
  119. package/mcp_servers/openscad-mcp-server/legacy/src/ai/venice_api.py +203 -0
  120. package/mcp_servers/openscad-mcp-server/legacy/src/config.py +121 -0
  121. package/mcp_servers/openscad-mcp-server/legacy/src/main.py +1456 -0
  122. package/mcp_servers/openscad-mcp-server/legacy/src/main.py.new +404 -0
  123. package/mcp_servers/openscad-mcp-server/legacy/src/main_remote.py +401 -0
  124. package/mcp_servers/openscad-mcp-server/legacy/src/models/__init__.py +0 -0
  125. package/mcp_servers/openscad-mcp-server/legacy/src/models/code_generator.py +321 -0
  126. package/mcp_servers/openscad-mcp-server/legacy/src/models/cuda_mvs.py +209 -0
  127. package/mcp_servers/openscad-mcp-server/legacy/src/models/scad_templates/basic_shapes.scad +144 -0
  128. package/mcp_servers/openscad-mcp-server/legacy/src/nlp/__init__.py +0 -0
  129. package/mcp_servers/openscad-mcp-server/legacy/src/nlp/parameter_extractor.py +388 -0
  130. package/mcp_servers/openscad-mcp-server/legacy/src/openscad_wrapper/__init__.py +0 -0
  131. package/mcp_servers/openscad-mcp-server/legacy/src/openscad_wrapper/wrapper.py +418 -0
  132. package/mcp_servers/openscad-mcp-server/legacy/src/printer_discovery/__init__.py +1 -0
  133. package/mcp_servers/openscad-mcp-server/legacy/src/printer_discovery/printer_discovery.py +471 -0
  134. package/mcp_servers/openscad-mcp-server/legacy/src/remote/connection_manager.py +537 -0
  135. package/mcp_servers/openscad-mcp-server/legacy/src/remote/cuda_mvs_client.py +435 -0
  136. package/mcp_servers/openscad-mcp-server/legacy/src/remote/cuda_mvs_server.py +787 -0
  137. package/mcp_servers/openscad-mcp-server/legacy/src/remote/error_handling.py +415 -0
  138. package/mcp_servers/openscad-mcp-server/legacy/src/testing/__init__.py +0 -0
  139. package/mcp_servers/openscad-mcp-server/legacy/src/testing/primitive_tester.py +203 -0
  140. package/mcp_servers/openscad-mcp-server/legacy/src/testing/test_primitives.py +98 -0
  141. package/mcp_servers/openscad-mcp-server/legacy/src/utils/__init__.py +1 -0
  142. package/mcp_servers/openscad-mcp-server/legacy/src/utils/cad_exporter.py +241 -0
  143. package/mcp_servers/openscad-mcp-server/legacy/src/utils/format_validator.py +206 -0
  144. package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_exporter.py +140 -0
  145. package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_repair.py +91 -0
  146. package/mcp_servers/openscad-mcp-server/legacy/src/utils/stl_validator.py +123 -0
  147. package/mcp_servers/openscad-mcp-server/legacy/src/visualization/__init__.py +0 -0
  148. package/mcp_servers/openscad-mcp-server/legacy/src/visualization/headless_renderer.py +52 -0
  149. package/mcp_servers/openscad-mcp-server/legacy/src/visualization/renderer.py +177 -0
  150. package/mcp_servers/openscad-mcp-server/legacy/src/visualization/web_interface.py +639 -0
  151. package/mcp_servers/openscad-mcp-server/legacy/src/workflow/image_approval.py +148 -0
  152. package/mcp_servers/openscad-mcp-server/legacy/src/workflow/multi_view_to_model_pipeline.py +338 -0
  153. package/mcp_servers/openscad-mcp-server/legacy/test_complete_workflow.py +374 -0
  154. package/mcp_servers/openscad-mcp-server/legacy/test_cuda_mvs.py +191 -0
  155. package/mcp_servers/openscad-mcp-server/legacy/test_gemini_api.py +168 -0
  156. package/mcp_servers/openscad-mcp-server/legacy/test_image_approval.py +192 -0
  157. package/mcp_servers/openscad-mcp-server/legacy/test_image_approval_workflow.py +251 -0
  158. package/mcp_servers/openscad-mcp-server/legacy/test_image_to_model_pipeline.py +145 -0
  159. package/mcp_servers/openscad-mcp-server/legacy/test_model_selection.py +41 -0
  160. package/mcp_servers/openscad-mcp-server/legacy/test_multi_view_pipeline.py +290 -0
  161. package/mcp_servers/openscad-mcp-server/legacy/test_primitives.sh +13 -0
  162. package/mcp_servers/openscad-mcp-server/legacy/test_rabbit_direct.py +71 -0
  163. package/mcp_servers/openscad-mcp-server/legacy/test_remote_cuda_mvs.py +283 -0
  164. package/mcp_servers/openscad-mcp-server/legacy/test_venice_example.py +69 -0
  165. package/mcp_servers/openscad-mcp-server/pyproject.toml +33 -0
  166. package/mcp_servers/openscad-mcp-server/requirements.txt +2 -0
  167. package/mcp_servers/openscad-mcp-server/scad/simple_cube.scad +2 -0
  168. package/mcp_servers/openscad-mcp-server/scripts/test_docker.py +220 -0
  169. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/__init__.py +3 -0
  170. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/__main__.py +3 -0
  171. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/engine.py +94 -0
  172. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/geometry.py +242 -0
  173. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/server.py +245 -0
  174. package/mcp_servers/openscad-mcp-server/src/openscad_mcp/service.py +190 -0
  175. package/mcp_servers/openscad-mcp-server/tests/conftest.py +17 -0
  176. package/mcp_servers/openscad-mcp-server/tests/test_engine.py +37 -0
  177. package/mcp_servers/openscad-mcp-server/tests/test_geometry.py +106 -0
  178. package/mcp_servers/openscad-mcp-server/tests/test_integration.py +172 -0
  179. package/mcp_servers/openscad-mcp-server/tests/test_transports.py +314 -0
  180. package/mcp_servers/openscad-mcp-server/uv.lock +1123 -0
  181. package/package.json +44 -0
  182. package/scripts/install_mcps.sh +86 -0
  183. package/scripts/openscad_wrapper.sh +62 -0
  184. package/scripts/run_kicad_mcp.sh +25 -0
  185. package/scripts/run_openscad_mcp.sh +41 -0
  186. package/scripts/test_mcp_connection.py +626 -0
  187. package/scripts/test_mcp_connection.sh +168 -0
  188. package/skills/code-first-hardware-design/SKILL.md +260 -0
  189. package/skills/pcb-constraint-definition/SKILL.md +236 -0
  190. package/skills/pcb-layout-routing-automation/SKILL.md +153 -0
  191. package/skills/pcb-validation-dfm-signoff/SKILL.md +194 -0
  192. package/skills/schematic-datasheet-analysis/SKILL.md +202 -0
@@ -0,0 +1,196 @@
1
+ """KiCad project creation tools for KiCad MCP Server - Updated for KiCad 9.0+
2
+
3
+ Uses KiCad's template projects to ensure 100% compatibility.
4
+ """
5
+
6
+ import json
7
+ import re
8
+ import shutil
9
+ import uuid
10
+ from datetime import datetime
11
+ from pathlib import Path
12
+
13
+ from ..server import mcp
14
+
15
+
16
+ def _find_kicad_template() -> Path | None:
17
+ """Find KiCad template directory, auto-detecting installed version."""
18
+ from ..utils.kicad_version import find_kicad_install
19
+
20
+ kicad = find_kicad_install()
21
+ if kicad:
22
+ install_path, _version = kicad
23
+ for template_name in ["Arduino_Mega", "EuroCard160mmX100mm"]:
24
+ p = install_path / "share" / "kicad" / "template" / template_name
25
+ if p.exists():
26
+ return p
27
+
28
+ # macOS fallback
29
+ mac_base = Path("/Applications/KiCad/KiCad.app/Contents/SharedSupport/template")
30
+ for t in ["Arduino_Mega", "EuroCard160mmX100mm"]:
31
+ if (mac_base / t).exists():
32
+ return mac_base / t
33
+
34
+ # Linux fallback
35
+ for base in [Path("/usr/share/kicad/template"), Path("/usr/local/share/kicad/template")]:
36
+ for t in ["Arduino_Mega", "EuroCard160mmX100mm"]:
37
+ if (base / t).exists():
38
+ return base / t
39
+
40
+ return None
41
+
42
+
43
+ def _get_date_string() -> str:
44
+ """Get current date in ISO format."""
45
+ return datetime.now().strftime("%Y-%m-%d")
46
+
47
+
48
+ @mcp.tool()
49
+ async def create_kicad_project(
50
+ project_path: str,
51
+ project_name: str,
52
+ title: str = "",
53
+ company: str = "",
54
+ ) -> str:
55
+ """Create a complete KiCad 9.0+ project by copying KiCad's template.
56
+
57
+ This method copies a KiCad template project and modifies it,
58
+ ensuring 100% compatibility with KiCad 9.0+.
59
+
60
+ Args:
61
+ project_path: Directory path for the project
62
+ project_name: Name of the project (without extension)
63
+ title: Optional project title
64
+ company: Optional company name
65
+
66
+ Returns:
67
+ Confirmation message with created files
68
+ """
69
+ try:
70
+ # Find KiCad template
71
+ template_path = _find_kicad_template()
72
+
73
+ if not template_path:
74
+ return """❌ KiCad template not found.
75
+
76
+ Please ensure KiCad is installed:
77
+ macOS: brew install --cask kicad
78
+ Linux: sudo apt install kicad
79
+ Windows: https://www.kicad.org/download/
80
+
81
+ After installation, this tool will use KiCad's template to create projects.
82
+ """
83
+
84
+ path = Path(project_path)
85
+ path.mkdir(parents=True, exist_ok=True)
86
+
87
+ date_str = _get_date_string()
88
+ title_text = title or project_name
89
+
90
+ # Copy all files from template
91
+ for file in template_path.glob("*"):
92
+ if file.is_file():
93
+ dest = path / file.name.replace("Arduino_Mega", project_name).replace("EuroCard160mmX100mm", project_name)
94
+ shutil.copy(file, dest)
95
+
96
+ # Rename files if needed
97
+ for old_file in path.glob("*"):
98
+ if "Arduino_Mega" in old_file.name or "EuroCard160mmX100mm" in old_file.name:
99
+ new_name = old_file.name.replace("Arduino_Mega", project_name).replace("EuroCard160mmX100mm", project_name)
100
+ new_path = old_file.parent / new_name
101
+ if old_file != new_path:
102
+ old_file.rename(new_path)
103
+
104
+ # Modify .kicad_pro file
105
+ pro_file = path / f"{project_name}.kicad_pro"
106
+
107
+ if pro_file.exists():
108
+ with open(pro_file) as f:
109
+ pro_data = json.load(f)
110
+
111
+ # Update metadata
112
+ pro_data["meta"]["filename"] = f"{project_name}.kicad_pro"
113
+
114
+ # Update sheets
115
+ if "sheets" in pro_data and pro_data["sheets"]:
116
+ new_uuid = str(uuid.uuid4())
117
+ pro_data["sheets"] = [[new_uuid, "Root"]]
118
+
119
+ with open(pro_file, 'w') as f:
120
+ json.dump(pro_data, f, indent=2)
121
+
122
+ # Modify .kicad_sch file
123
+ sch_file = path / f"{project_name}.kicad_sch"
124
+
125
+ if sch_file.exists():
126
+ content = sch_file.read_text()
127
+
128
+ # Update UUID
129
+ new_uuid = str(uuid.uuid4())
130
+ content = re.sub(r'\(uuid "([^"]*)"\)', f'(uuid "{new_uuid}")', content, count=1)
131
+
132
+ # Update title block. Templates may not contain (title "...") or
133
+ # (company "...") fields, so insert them into (title_block ...) when
134
+ # absent — otherwise the regex silently no-ops while the success
135
+ # message still claims the title/company was written.
136
+ if title_text:
137
+ if '(title "' in content:
138
+ content = re.sub(r'\(title "([^"]*)"\)', f'(title "{title_text}")', content, count=1)
139
+ else:
140
+ content = content.replace("(title_block", f'(title_block\n (title "{title_text}")', 1)
141
+ if '(date "' in content:
142
+ content = re.sub(r'\(date "([^"]*)"\)', f'(date "{date_str}")', content, count=1)
143
+ if company:
144
+ if '(company "' in content:
145
+ content = re.sub(r'\(company "([^"]*)"\)', f'(company "{company}")', content, count=1)
146
+ else:
147
+ content = content.replace("(title_block", f'(title_block\n (company "{company}")', 1)
148
+
149
+ sch_file.write_text(content)
150
+
151
+ # List files that were actually copied (templates vary — don't hardcode
152
+ # "fp-lib-table", which some templates don't ship).
153
+ created_files = sorted(p.name for p in path.iterdir() if p.is_file())
154
+ files_listing = "\n".join(f"- {n}" for n in created_files)
155
+
156
+ return f"""# ✅ KiCad 9.0+ Project Created Successfully!
157
+
158
+ **Project Path:** {path}
159
+ **Project Name:** {project_name}
160
+ **Title:** {title_text}
161
+ **Company:** {company or '(none)'}
162
+ **Template Used:** {template_path.name}
163
+
164
+ ## 📄 Files Created:
165
+
166
+ {files_listing}
167
+
168
+ ## 📖 How to Open in KiCad 9.0+:
169
+
170
+ 1. Open KiCad
171
+ 2. File → Open Project...
172
+ 3. Navigate to: {pro_file}
173
+ 4. Click Open
174
+
175
+ The project will open in KiCad 9.0+ **without** version warnings!
176
+
177
+ ## ✅ Compatibility:
178
+
179
+ - Created from KiCad's official template
180
+ - 100% compatible with KiCad 9.0+
181
+ - All settings and configurations are valid
182
+ - Ready for schematic design and PCB layout
183
+
184
+ ## 🔧 Next Steps:
185
+
186
+ 1. Open schematic editor to add components
187
+ 2. Use `add_component_from_library` to add parts
188
+ 3. Use `add_wire` and `add_global_label` for connections
189
+ 4. Update PCB from schematic when ready
190
+
191
+ Project is ready for KiCad 9.0+! 🚀
192
+ """
193
+
194
+ except Exception as e:
195
+ import traceback
196
+ return f"Error creating project: {e}\n\n{traceback.format_exc()}"
@@ -0,0 +1,319 @@
1
+ """Schematic analysis tools for KiCad MCP Server."""
2
+
3
+ from pathlib import Path
4
+
5
+ from ..parsers.schematic_parser import SchematicParser
6
+ from ..server import mcp
7
+
8
+
9
+ @mcp.tool()
10
+ async def list_schematic_components(
11
+ file_path: str,
12
+ filter_type: str | None = None,
13
+ filter_value: str | None = None,
14
+ filter_dnp: bool | None = None,
15
+ ) -> str:
16
+ """List all components in a KiCad schematic file.
17
+
18
+ Args:
19
+ file_path: Path to .kicad_sch file
20
+ filter_type: Optional filter by component type prefix (e.g., 'R', 'C', 'U', 'IC')
21
+ filter_value: Optional filter by component value (partial match)
22
+ filter_dnp: Optional filter by DNP flag (True=only DNP, False=only non-DNP)
23
+
24
+ Returns:
25
+ Formatted list of components with their properties
26
+ """
27
+ try:
28
+ parser = SchematicParser(file_path)
29
+ components = parser.get_components()
30
+
31
+ # Apply filters
32
+ if filter_type:
33
+ components = [c for c in components if c.reference.startswith(filter_type.upper())]
34
+
35
+ if filter_value:
36
+ components = [c for c in components if filter_value.lower() in c.value.lower()]
37
+
38
+ if filter_dnp is not None:
39
+ components = [c for c in components if c.flags.get("dnp", False) == filter_dnp]
40
+
41
+ if not components:
42
+ return "No components found matching the specified criteria."
43
+
44
+ # Check if any non-default flags exist
45
+ has_dnp = any(c.flags.get("dnp", False) for c in components)
46
+ has_not_in_bom = any(not c.flags.get("in_bom", True) for c in components)
47
+
48
+ # Format output
49
+ header = "| Reference | Value | Footprint | Library |"
50
+ separator = "|-----------|-------|-----------|---------|"
51
+ if has_dnp or has_not_in_bom:
52
+ header += " DNP | In BOM |"
53
+ separator += "-----|--------|"
54
+
55
+ lines = [
56
+ f"# Components in {file_path}",
57
+ f"Total: {len(components)} component(s)",
58
+ "",
59
+ header,
60
+ separator,
61
+ ]
62
+
63
+ for comp in components:
64
+ footprint = comp.footprint or "-"
65
+ library = comp.library_id.split(":")[-1] if ":" in comp.library_id else comp.library_id
66
+ row = f"| {comp.reference} | {comp.value} | {footprint} | {library} |"
67
+ if has_dnp or has_not_in_bom:
68
+ dnp = "Yes" if comp.flags.get("dnp", False) else ""
69
+ in_bom = "No" if not comp.flags.get("in_bom", True) else ""
70
+ row += f" {dnp} | {in_bom} |"
71
+ lines.append(row)
72
+
73
+ return "\n".join(lines)
74
+
75
+ except FileNotFoundError as e:
76
+ return f"Error: {e}"
77
+ except Exception as e:
78
+ return f"Error parsing schematic: {e}"
79
+
80
+
81
+ @mcp.tool()
82
+ async def get_symbol_details(file_path: str, reference: str) -> str:
83
+ """Get detailed information about a specific symbol/component.
84
+
85
+ Args:
86
+ file_path: Path to .kicad_sch file
87
+ reference: Component reference designator (e.g., 'R1', 'U1')
88
+
89
+ Returns:
90
+ Detailed component information including pins and properties
91
+ """
92
+ try:
93
+ parser = SchematicParser(file_path)
94
+ component = parser.get_component_by_reference(reference)
95
+
96
+ if not component:
97
+ return f"Component '{reference}' not found in schematic."
98
+
99
+ # Format output
100
+ lines = [
101
+ f"# Symbol Details: {component.reference}",
102
+ "",
103
+ f"**Value:** {component.value}",
104
+ f"**Library:** {component.library_id}",
105
+ f"**Footprint:** {component.footprint or 'Not assigned'}",
106
+ f"**Position:** ({component.position[0]:.2f}, {component.position[1]:.2f})",
107
+ ]
108
+
109
+ if component.unit:
110
+ lines.append(f"**Unit:** {component.unit}")
111
+
112
+ # Properties
113
+ if component.properties:
114
+ lines.append("")
115
+ lines.append("## Properties")
116
+ for key, value in sorted(component.properties.items()):
117
+ if key not in ("Value", "Footprint"):
118
+ lines.append(f"- {key}: {value}")
119
+
120
+ # Pins
121
+ if component.pins:
122
+ lines.append("")
123
+ lines.append("## Pins")
124
+ lines.append("| Pin | Name | Type |")
125
+ lines.append("|-----|------|------|")
126
+ for pin in component.pins:
127
+ pin_num = pin.get("number", "?")
128
+ pin_name = pin.get("name", "")
129
+ pin_type = pin.get("electrical_type", "")
130
+ lines.append(f"| {pin_num} | {pin_name} | {pin_type} |")
131
+
132
+ return "\n".join(lines)
133
+
134
+ except FileNotFoundError as e:
135
+ return f"Error: {e}"
136
+ except Exception as e:
137
+ return f"Error parsing schematic: {e}"
138
+
139
+
140
+ @mcp.tool()
141
+ async def search_symbols(
142
+ file_path: str,
143
+ pattern: str,
144
+ search_fields: str = "all",
145
+ ) -> str:
146
+ """Search for symbols/components matching a pattern.
147
+
148
+ Args:
149
+ file_path: Path to .kicad_sch file
150
+ pattern: Search pattern (supports regex)
151
+ search_fields: Fields to search: 'all', 'reference', 'value', 'library'
152
+
153
+ Returns:
154
+ List of matching components
155
+ """
156
+ try:
157
+ parser = SchematicParser(file_path)
158
+ components = parser.search_components(pattern)
159
+
160
+ if not components:
161
+ return f"No components found matching pattern: {pattern}"
162
+
163
+ # Format output
164
+ lines = [
165
+ f"# Search Results: '{pattern}'",
166
+ f"Found {len(components)} matching component(s)",
167
+ "",
168
+ "| Reference | Value | Library |",
169
+ "|-----------|-------|---------|",
170
+ ]
171
+
172
+ for comp in components[:50]: # Limit to 50 results
173
+ library = comp.library_id.split(":")[-1] if ":" in comp.library_id else comp.library_id
174
+ lines.append(f"| {comp.reference} | {comp.value} | {library} |")
175
+
176
+ if len(components) > 50:
177
+ lines.append(f"\n... and {len(components) - 50} more")
178
+
179
+ return "\n".join(lines)
180
+
181
+ except FileNotFoundError as e:
182
+ return f"Error: {e}"
183
+ except Exception as e:
184
+ return f"Error parsing schematic: {e}"
185
+
186
+
187
+ @mcp.tool()
188
+ async def list_schematic_nets(
189
+ file_path: str,
190
+ filter_power: bool = False,
191
+ ) -> str:
192
+ """List all nets in a KiCad schematic.
193
+
194
+ Args:
195
+ file_path: Path to .kicad_sch file
196
+ filter_power: If True, only show power nets (VCC, GND, etc.)
197
+
198
+ Returns:
199
+ Formatted list of nets
200
+ """
201
+ try:
202
+ parser = SchematicParser(file_path)
203
+ nets = parser.get_nets()
204
+
205
+ # Filter for power nets if requested
206
+ if filter_power:
207
+ power_keywords = ["gnd", "vcc", "vdd", "vss", "+", "-"]
208
+ nets = [n for n in nets if any(kw in n.name.lower() for kw in power_keywords)]
209
+
210
+ # Sub-sheet detection: unnamed nets can't be resolved without hierarchy
211
+ from .netlist import _find_root_schematic
212
+
213
+ sch_path = Path(file_path)
214
+ root_sch = _find_root_schematic(sch_path)
215
+ subsheet_note = ""
216
+ if root_sch:
217
+ subsheet_note = (
218
+ f"**Note:** `{sch_path.name}` is a sub-sheet. "
219
+ f"Unnamed wire-only nets cannot be resolved without the full "
220
+ f"hierarchy context and will not appear here. "
221
+ f"Use `generate_netlist` on the root schematic `{root_sch.name}` "
222
+ f"for complete net data."
223
+ )
224
+
225
+ if not nets:
226
+ if subsheet_note:
227
+ return f"No nets found.\n\n{subsheet_note}"
228
+ return "No nets found."
229
+
230
+ # Format output
231
+ lines = [
232
+ f"# Nets in {file_path}",
233
+ f"Total: {len(nets)} net(s)",
234
+ "",
235
+ "| Net Name | Type | Code |",
236
+ "|----------|------|------|",
237
+ ]
238
+
239
+ for net in sorted(nets, key=lambda n: n.name):
240
+ lines.append(f"| {net.name} | {net.type} | {net.code} |")
241
+
242
+ if subsheet_note:
243
+ lines.append("")
244
+ lines.append(subsheet_note)
245
+
246
+ return "\n".join(lines)
247
+
248
+ except FileNotFoundError as e:
249
+ return f"Error: {e}"
250
+ except Exception as e:
251
+ return f"Error parsing schematic: {e}"
252
+
253
+
254
+ @mcp.tool()
255
+ async def get_schematic_info(file_path: str) -> str:
256
+ """Get general information about a schematic file.
257
+
258
+ Args:
259
+ file_path: Path to .kicad_sch file
260
+
261
+ Returns:
262
+ Schematic metadata and statistics
263
+ """
264
+ try:
265
+ parser = SchematicParser(file_path)
266
+ title_block = parser.get_title_block()
267
+ components = parser.get_components()
268
+ nets = parser.get_nets()
269
+ sheets = parser.get_sheets()
270
+
271
+ # Count components by type
272
+ component_counts = {}
273
+ for comp in components:
274
+ prefix = "".join(c for c in comp.reference if c.isalpha())
275
+ component_counts[prefix] = component_counts.get(prefix, 0) + 1
276
+
277
+ # Format output
278
+ lines = [
279
+ f"# Schematic Information: {file_path}",
280
+ "",
281
+ "## Project Information",
282
+ ]
283
+
284
+ if title_block.get("title"):
285
+ lines.append(f"**Title:** {title_block['title']}")
286
+ if title_block.get("company"):
287
+ lines.append(f"**Company:** {title_block['company']}")
288
+ if title_block.get("date"):
289
+ lines.append(f"**Date:** {title_block['date']}")
290
+ if title_block.get("rev"):
291
+ lines.append(f"**Revision:** {title_block['rev']}")
292
+
293
+ lines.extend([
294
+ "",
295
+ "## Statistics",
296
+ f"**Total Components:** {len(components)}",
297
+ f"**Total Nets:** {len(nets)}",
298
+ f"**Hierarchical Sheets:** {len(sheets)}",
299
+ "",
300
+ "## Components by Type",
301
+ ])
302
+
303
+ for prefix in sorted(component_counts.keys()):
304
+ lines.append(f"- {prefix}: {component_counts[prefix]}")
305
+
306
+ if sheets:
307
+ lines.extend([
308
+ "",
309
+ "## Hierarchical Sheets",
310
+ ])
311
+ for sheet in sheets:
312
+ lines.append(f"- {sheet['name']}: {sheet['file']}")
313
+
314
+ return "\n".join(lines)
315
+
316
+ except FileNotFoundError as e:
317
+ return f"Error: {e}"
318
+ except Exception as e:
319
+ return f"Error parsing schematic: {e}"