sw-mcp 0.1.0__tar.gz

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 (68) hide show
  1. sw_mcp-0.1.0/LICENSE +22 -0
  2. sw_mcp-0.1.0/PKG-INFO +445 -0
  3. sw_mcp-0.1.0/README.md +420 -0
  4. sw_mcp-0.1.0/pyproject.toml +50 -0
  5. sw_mcp-0.1.0/setup.cfg +4 -0
  6. sw_mcp-0.1.0/src/sw_mcp/__init__.py +8 -0
  7. sw_mcp-0.1.0/src/sw_mcp/advanced.py +470 -0
  8. sw_mcp-0.1.0/src/sw_mcp/binding.py +96 -0
  9. sw_mcp-0.1.0/src/sw_mcp/com_worker.py +99 -0
  10. sw_mcp-0.1.0/src/sw_mcp/constants.py +350 -0
  11. sw_mcp-0.1.0/src/sw_mcp/drawing.py +2522 -0
  12. sw_mcp-0.1.0/src/sw_mcp/drawing_layout.py +512 -0
  13. sw_mcp-0.1.0/src/sw_mcp/errors.py +9 -0
  14. sw_mcp-0.1.0/src/sw_mcp/families.py +234 -0
  15. sw_mcp-0.1.0/src/sw_mcp/free_sketch.py +353 -0
  16. sw_mcp-0.1.0/src/sw_mcp/gears.py +133 -0
  17. sw_mcp-0.1.0/src/sw_mcp/guide.md +439 -0
  18. sw_mcp-0.1.0/src/sw_mcp/image_sheet.py +71 -0
  19. sw_mcp-0.1.0/src/sw_mcp/material_ids.json +222 -0
  20. sw_mcp-0.1.0/src/sw_mcp/mesh_tools.py +242 -0
  21. sw_mcp-0.1.0/src/sw_mcp/region_tools.py +209 -0
  22. sw_mcp-0.1.0/src/sw_mcp/selftest.py +256 -0
  23. sw_mcp-0.1.0/src/sw_mcp/server.py +1429 -0
  24. sw_mcp-0.1.0/src/sw_mcp/session.py +7058 -0
  25. sw_mcp-0.1.0/src/sw_mcp/sketch_constraints.py +412 -0
  26. sw_mcp-0.1.0/src/sw_mcp/units.py +21 -0
  27. sw_mcp-0.1.0/src/sw_mcp.egg-info/PKG-INFO +445 -0
  28. sw_mcp-0.1.0/src/sw_mcp.egg-info/SOURCES.txt +66 -0
  29. sw_mcp-0.1.0/src/sw_mcp.egg-info/dependency_links.txt +1 -0
  30. sw_mcp-0.1.0/src/sw_mcp.egg-info/entry_points.txt +2 -0
  31. sw_mcp-0.1.0/src/sw_mcp.egg-info/requires.txt +7 -0
  32. sw_mcp-0.1.0/src/sw_mcp.egg-info/top_level.txt +1 -0
  33. sw_mcp-0.1.0/tests/test_advanced.py +134 -0
  34. sw_mcp-0.1.0/tests/test_assembly.py +451 -0
  35. sw_mcp-0.1.0/tests/test_binding.py +84 -0
  36. sw_mcp-0.1.0/tests/test_bodies.py +65 -0
  37. sw_mcp-0.1.0/tests/test_corner_radii.py +130 -0
  38. sw_mcp-0.1.0/tests/test_documents.py +80 -0
  39. sw_mcp-0.1.0/tests/test_drawing.py +342 -0
  40. sw_mcp-0.1.0/tests/test_drawing_families.py +133 -0
  41. sw_mcp-0.1.0/tests/test_drawing_layout.py +229 -0
  42. sw_mcp-0.1.0/tests/test_edges.py +117 -0
  43. sw_mcp-0.1.0/tests/test_equations.py +103 -0
  44. sw_mcp-0.1.0/tests/test_families.py +142 -0
  45. sw_mcp-0.1.0/tests/test_features.py +1246 -0
  46. sw_mcp-0.1.0/tests/test_free_sketch.py +191 -0
  47. sw_mcp-0.1.0/tests/test_gears.py +67 -0
  48. sw_mcp-0.1.0/tests/test_guard.py +85 -0
  49. sw_mcp-0.1.0/tests/test_history.py +198 -0
  50. sw_mcp-0.1.0/tests/test_import.py +124 -0
  51. sw_mcp-0.1.0/tests/test_joints.py +391 -0
  52. sw_mcp-0.1.0/tests/test_logic.py +1031 -0
  53. sw_mcp-0.1.0/tests/test_mesh_tools.py +159 -0
  54. sw_mcp-0.1.0/tests/test_mirror.py +114 -0
  55. sw_mcp-0.1.0/tests/test_moulded.py +95 -0
  56. sw_mcp-0.1.0/tests/test_packaging.py +46 -0
  57. sw_mcp-0.1.0/tests/test_part_scripts.py +201 -0
  58. sw_mcp-0.1.0/tests/test_person_part.py +192 -0
  59. sw_mcp-0.1.0/tests/test_placement.py +34 -0
  60. sw_mcp-0.1.0/tests/test_printability.py +78 -0
  61. sw_mcp-0.1.0/tests/test_region_tools.py +65 -0
  62. sw_mcp-0.1.0/tests/test_selftest.py +126 -0
  63. sw_mcp-0.1.0/tests/test_server_portability.py +123 -0
  64. sw_mcp-0.1.0/tests/test_shape_tools.py +207 -0
  65. sw_mcp-0.1.0/tests/test_sketch_constraints.py +154 -0
  66. sw_mcp-0.1.0/tests/test_sketching.py +144 -0
  67. sw_mcp-0.1.0/tests/test_text.py +90 -0
  68. sw_mcp-0.1.0/tests/test_units.py +28 -0
sw_mcp-0.1.0/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Henk Jan Baard
4
+ Copyright (c) 2026 Nejc Dvorsek (sw-mcp fork)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
sw_mcp-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,445 @@
1
+ Metadata-Version: 2.4
2
+ Name: sw-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server that drives a local SolidWorks instance over COM for parametric part modelling.
5
+ Author: Nejc Dvorsek, Henk Jan Baard
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/NejcDvorsek/sw-mcp
8
+ Project-URL: Repository, https://github.com/NejcDvorsek/sw-mcp
9
+ Project-URL: Changelog, https://github.com/NejcDvorsek/sw-mcp/blob/main/CHANGELOG.md
10
+ Project-URL: Issues, https://github.com/NejcDvorsek/sw-mcp/issues
11
+ Keywords: solidworks,mcp,cad,com,parametric,3d-printing
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Scientific/Engineering
17
+ Requires-Python: >=3.11
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: pywin32>=306; sys_platform == "win32"
21
+ Requires-Dist: mcp<2,>=1.26
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=8; extra == "dev"
24
+ Dynamic: license-file
25
+
26
+ # sw-mcp
27
+
28
+ [![PyPI](https://img.shields.io/pypi/v/sw-mcp)](https://pypi.org/project/sw-mcp/)
29
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/NejcDvorsek/sw-mcp/blob/main/LICENSE)
30
+
31
+ Let an AI agent (Claude, or any other MCP client) model real parametric parts and
32
+ assemblies in **your own SolidWorks** — and **check its own work**. Every
33
+ modelling call returns the measured volume, mass and bounding box, so the agent
34
+ can compare the result with the spec and correct itself instead of guessing that
35
+ it "looks about right".
36
+
37
+ sw-mcp is a fork of [SolidWorks-MCP](https://github.com/hjbaard/SolidWorks-MCP)
38
+ by Henk Jan Baard (`solidworks-mcp` 0.16.3 on PyPI). It adds manufacturing
39
+ drawings, lofts and reference axes, `run_script`, and merges related tools into
40
+ families so a client loads fewer of them; see
41
+ [Differences from upstream](#differences-from-upstream). It is the SolidWorks
42
+ connector of [Jig](https://github.com/NejcDvorsek/jig), and works on its own
43
+ with any MCP client.
44
+
45
+ <!-- mcp-name: io.github.NejcDvorsek/sw-mcp -->
46
+
47
+ <p align="center">
48
+ <img src="https://raw.githubusercontent.com/NejcDvorsek/sw-mcp/main/Docs/images/bracket.png" width="32%" alt="3D-print mounting bracket with counterbored holes, bolt circle and cable slot">
49
+ <img src="https://raw.githubusercontent.com/NejcDvorsek/sw-mcp/main/Docs/images/thread.png" width="32%" alt="M10 bolt with a real, printable ISO metric thread">
50
+ <img src="https://raw.githubusercontent.com/NejcDvorsek/sw-mcp/main/Docs/images/vase.png" width="32%" alt="Revolved and shelled vase">
51
+ </p>
52
+ <p align="center">
53
+ <img src="https://raw.githubusercontent.com/NejcDvorsek/sw-mcp/main/Docs/images/pipe.png" width="32%" alt="Swept pipe with rounded bends">
54
+ <img src="https://raw.githubusercontent.com/NejcDvorsek/sw-mcp/main/Docs/images/loft.png" width="32%" alt="Lofted and shelled horn">
55
+ </p>
56
+ <p align="center"><sub>Built by the tools themselves: a mounting bracket (every step checked against a hand calculation), an M10 bolt with a real thread, a revolved + shelled vase, a swept pipe, a lofted + shelled horn.</sub></p>
57
+
58
+ ## Why this server
59
+
60
+ - **It verifies, not just generates.** Features report measured geometry;
61
+ dimensions and mates are measured back after the rebuild.
62
+ - **Fully defined sketches.** Every sketch is constrained the way a designer
63
+ would: dimensions from the origin, relations only where the geometry is
64
+ exactly horizontal, vertical or on the origin. Tools return their dimensions
65
+ by role (`width@Sketch1`), so the part stays editable, in SolidWorks or
66
+ through the agent.
67
+ - **Real CAD, not just primitives.** Extrude, revolve, sweep, loft and splines;
68
+ holes, counterbores, slots and pockets on any face; rounded polygon corners; ISO holes from the Hole Wizard; real ISO metric threads;
69
+ fillets, chamfers, shells, patterns, mirrors, ribs, equations and materials. Assemblies
70
+ with mates and interference checks. STEP/STL/3MF export and screenshots.
71
+ Engraved and embossed text. Work on existing parts: list, delete, suppress and reorder features; read, extrude and cut a person's sketches; import STEP. Colours per part or component. Joints with concentric and angle mates, checked over their range. Printability checks. Manufacturing drawings: ISO sheets with a title block, section and detail views, dimensions placed from model references (also baseline, chain and ordinate), counted hole callouts, surface finish, datum and geometric tolerance symbols, tables and computed gear data tables, notes, automatic layout, a sheet check, and drawings built from a spec. Planes at any offset or angle, or through a point square to any direction; axes anywhere, and circular patterns about them; lofts and loft cuts between sketches on any planes. Several bodies per part, combined and split. Variable fillets and full rounds. Free sketches with tangent arcs and splines. 91 tools in total, and `run_script` for what none of them covers (off unless allowed).
72
+ - **It fails loud.** A call that cannot do what was asked returns
73
+ `{ok: false, error}` with the cause, never silently wrong geometry.
74
+ - **A fixed, typed tool surface.** The agent can only do what the tools allow.
75
+ `run_script`, which runs arbitrary Python, is off unless you start the server
76
+ with `--allow-scripts`.
77
+ - **Tested against real SolidWorks.** 697 tests; each feature's integration test
78
+ compares the result with a hand calculation.
79
+ - **Local.** It talks to your running SolidWorks over COM; the server itself
80
+ makes no network calls.
81
+
82
+ ## Quickstart
83
+
84
+ 1. Install [uv](https://docs.astral.sh/uv/getting-started/installation/).
85
+ 2. **Start SolidWorks** and leave it open (the server attaches to the running
86
+ instance — it does not launch one).
87
+ 3. Register the server with your MCP client.
88
+
89
+ **Claude Code:**
90
+
91
+ ```bash
92
+ claude mcp add solidworks -- uvx sw-mcp
93
+ ```
94
+
95
+ **Claude Desktop** (`claude_desktop_config.json`) or any other client:
96
+
97
+ ```json
98
+ {
99
+ "mcpServers": {
100
+ "solidworks": {
101
+ "command": "uvx",
102
+ "args": ["sw-mcp"]
103
+ }
104
+ }
105
+ }
106
+ ```
107
+
108
+ **With Jig** there is nothing to register: the plugin starts sw-mcp itself.
109
+
110
+ 4. Ask for a part, for example:
111
+
112
+ > Design a 100 × 80 × 8 mm mounting plate with a Ø16 mm centre bore, four
113
+ > counterbored M5 holes 12 mm from the corners and R5 corners. Check the
114
+ > volume against your own calculation, then export a fine STL.
115
+
116
+ ## Guidelines for agents
117
+
118
+ The server hands every MCP client short modelling guidelines when it connects
119
+ (conventions, verify each step, known pitfalls). The full guide is the resource
120
+ `solidworks://guide`: recipes for holes, ribs, threads and assemblies, 3D-print
121
+ advice, and how to reverse-engineer a part from a mesh (STL/3MF). The same text
122
+ is in [`src/sw_mcp/guide.md`](https://github.com/NejcDvorsek/sw-mcp/blob/main/src/sw_mcp/guide.md).
123
+
124
+ ## Requirements and compatibility
125
+
126
+ - Windows, with SolidWorks installed, licensed and **running**.
127
+ - Python 3.11+ (uv fetches one if needed).
128
+ - A **Y-up part template**, SolidWorks' standard. Since SOLIDWORKS 2025 new
129
+ parts can also be Z-up; the tools build in the Y-up frame and refuse a Z-up
130
+ part with a message that says so.
131
+ - **Tested on SOLIDWORKS 2026** (3DEXPERIENCE R2026x). The API calls it uses
132
+ exist since SOLIDWORKS 2020 SP2, so 2020–2025 should work, but that is
133
+ **untested**. To check your installation, run this with SolidWorks open:
134
+
135
+ ```bash
136
+ uvx sw-mcp --selftest
137
+ ```
138
+
139
+ It builds a few small parts, compares each with a hand calculation and
140
+ closes them unsaved. Please paste its output in a
141
+ [compatibility report](https://github.com/NejcDvorsek/sw-mcp/issues/new?template=compatibility.yml),
142
+ whether it worked or not.
143
+
144
+ **Status: early (0.x).** It works end-to-end, but tool names and conventions
145
+ may still change. Versions restart at 0.1.0 for the fork. See [CHANGELOG.md](https://github.com/NejcDvorsek/sw-mcp/blob/main/CHANGELOG.md).
146
+
147
+ ## Troubleshooting
148
+
149
+ - **Start with the selftest**: `uvx sw-mcp --selftest` shows your
150
+ SolidWorks release, language and templates, and which tool areas work. Add
151
+ its output to a [bug report](https://github.com/NejcDvorsek/sw-mcp/issues/new?template=bug_report.yml).
152
+ - **"No running SolidWorks found" / connection fails** — SolidWorks must be
153
+ *running* before you start the server or run a script; it attaches to the active
154
+ instance via `GetActiveObject` and does not launch one.
155
+ - **First call is slow or `EnsureModule` errors** — the first COM call generates the
156
+ makepy typelib wrappers under your temp `gen_py` folder. Let it finish; if it gets
157
+ into a bad state, delete the `gen_py` cache and retry. Early binding is mandatory on
158
+ this build (see [Architecture](#architecture)).
159
+ - **A feature returns `{ok: false, error: ...}`** — that is by design: every tool
160
+ fails loud with a readable message rather than silently producing wrong
161
+ geometry. Read the message; it names the likely cause.
162
+ - **Only tested against SOLIDWORKS 2026 (3DEXPERIENCE R2026x).** On other builds the
163
+ verified enum values or method signatures may differ — re-run
164
+ `scripts/introspect_api.py` to inspect your installed typelib.
165
+
166
+ ## Development
167
+
168
+ Clone the repository, then install it editable into a venv:
169
+
170
+ ```powershell
171
+ python -m venv .venv
172
+ .\.venv\Scripts\python.exe -m pip install -e .[dev]
173
+ ```
174
+
175
+ To run the MCP server from this checkout instead of via uvx, point your client
176
+ at the venv's Python:
177
+
178
+ ```json
179
+ {
180
+ "mcpServers": {
181
+ "solidworks": {
182
+ "command": "C:\\path\\to\\sw-mcp\\.venv\\Scripts\\python.exe",
183
+ "args": ["-m", "sw_mcp.server"]
184
+ }
185
+ }
186
+ }
187
+ ```
188
+
189
+ Add `"--allow-scripts"` to `args` to switch on `run_script`, which runs any
190
+ Python it is given on this computer.
191
+
192
+ ### Run the verification scripts
193
+
194
+ With SolidWorks open:
195
+
196
+ ```powershell
197
+ .\.venv\Scripts\python.exe scripts\probe_connection.py # M0
198
+ .\.venv\Scripts\python.exe scripts\m1_block.py # M1
199
+ .\.venv\Scripts\python.exe scripts\m2_parametric.py # M2
200
+ .\.venv\Scripts\python.exe scripts\test_mcp_server.py # M3 (full MCP loop over stdio)
201
+ .\.venv\Scripts\python.exe scripts\m5_demo_bracket.py # M5 (3D-print bracket, every step verified)
202
+ ```
203
+
204
+ `scripts/introspect_api.py` regenerates/inspects the installed typelib and prints
205
+ verified enum values — run it if SolidWorks is upgraded and signatures change.
206
+
207
+ ### Tests
208
+
209
+ ```powershell
210
+ .\.venv\Scripts\python.exe -m pytest # all tests
211
+ .\.venv\Scripts\python.exe -m pytest -m "not solidworks" # fast unit layer, no SolidWorks
212
+ ```
213
+
214
+ Two layers: **pure unit tests** (units, selector/direction parsing, polygon
215
+ cleaning, the component-placement maths, and that every MCP tool forwards its
216
+ arguments to the right session method) run anywhere; **integration tests**
217
+ (`solidworks` marker) drive a running SolidWorks and verify each feature's
218
+ volume — or each component's placement — against a hand calc. They auto-skip if
219
+ SolidWorks isn't reachable.
220
+
221
+ ## Tools
222
+
223
+ The server speaks MCP over **stdio**.
224
+
225
+ ### Part tools
226
+
227
+ | Tool | Purpose |
228
+ |---|---|
229
+ | `get_status` | Is SolidWorks reachable? revision, server version, active/current part |
230
+ | `new_document(kind)` | Create a new empty part, or an assembly with `kind='assembly'` (becomes current) |
231
+ | `add_box(width_mm, height_mm, depth_mm, name)` | Sketch rectangle + extrude; returns mass properties |
232
+ | `add_cylinder(diameter_mm, height_mm, name)` | Cylinder by revolving a profile 360° about an axis (Y axis) |
233
+ | `add_disc(diameter_mm, thickness_mm, name, x_mm, y_mm)` | Disc/puck/flange: circle extruded along +Z, centred at (x, y) (holes/patterns compose) |
234
+ | `add_cone(bottom_diameter_mm, top_diameter_mm, height_mm, name)` | Cone/frustum by revolve (top Ø = 0 → full cone) |
235
+ | `add_revolved_profile(profile_mm, angle_deg, name, corner_radii_mm, axis_mm)` | Revolve any closed `(radius, height)` profile about the Y axis (shafts, vases, rings), or `[x, y]` points about any line `axis_mm` on the Front plane; `corner_radii_mm` rounds its edges |
236
+ | `add_swept_pipe(path_mm, diameter_mm, bend_radius_mm, name, smooth)` | Sweep a round profile along a 2D path with rounded bends (pipes, tubes, rods), or along a spline through the points with `smooth=True` |
237
+ | `add_swept_profile(profile_mm, path_mm, bend_radius_mm, name, corner_radii_mm)` | Sweep any closed cross-section along a 2D path (rails, gaskets, trim, channels); `corner_radii_mm` rounds the cross-section |
238
+ | `add_rib(start_mm, end_mm, toward_mm, thickness_mm, z_mm, name)` | Straight rib / gusset in a plane parallel to Front at `z_mm`, grown toward `toward_mm` until it meets the part (L-bracket gussets) |
239
+ | `add_extruded_profile(points_mm, depth_mm, face, plane, reverse, name, corner_radii_mm, rotate_deg, about_mm, draft_deg, merge, spline)` | Extrude any closed polygon `[[x,y],…]` (brackets, sections), or a pad grown out of ANY planar face (`face`, 3D points on it), or a polygon on any plane (`plane`); `corner_radii_mm` rounds its corners with real sketch fillets, each radius a dimension; `rotate_deg` turns it about a pivot; `draft_deg` tapers its walls; `merge=False` keeps a separate body; `spline=True` runs a smooth closed spline through the points |
240
+ | `add_plane(base, offset_mm, angle_deg, about, name, normal, through)` | A reference plane offset from a plane, turned about a model axis, or through a point square to any direction; gives its origin and axes |
241
+ | `add_axis(through, planes, name)` | A reference axis through a point along a direction, or where two planes meet |
242
+ | `add_loft(sketches, cut, profiles_mm, heights_mm, name)` | Loft, or loft cut, through closed sketches by name on any planes (a bevel gear's tooth gap); or blend 2+ polygon or round `profiles_mm` stacked at `heights_mm` (transitions, adapters, a leg thick at the knee) |
243
+ | `list_bodies()` | The part's solid bodies: name, volume, bounding box |
244
+ | `combine_bodies(operation, main, tools, name)` | Add, subtract or intersect bodies of the part (`add` / `subtract` / `common`) |
245
+ | `split_body(plane, name)` | Split the part into two bodies along a plane |
246
+ | `add_hole(x_mm, y_mm, z_mm, diameter_mm, face, depth_mm, cbore_diameter_mm, cbore_depth_mm, iso, size, fit, thread, name)` | Round hole: through at (x, y); on ANY planar face at a 3D point, through or blind (`face`, `depth_mm`); a counterbore from +Z (`cbore_*`); or an ISO hole from SolidWorks' Hole Wizard tables (`iso` clearance / counterbore / countersink / tapped, `size` 'M6'; `thread="modeled"` cuts a real, printable thread) |
247
+ | `add_thread(size, x_mm, y_mm, z_mm, length_mm, internal, name)` | Real, printable ISO metric thread (e.g. `M10x1.5`) from a rod's end edge or a hole's mouth, via SolidWorks' Thread feature; the size is checked against the thread profiles. Internal: drill the basic minor diameter first (M10x1.5 → Ø8.376) |
248
+ | `add_boss_on_face(diameter_mm, face, x_mm, y_mm, z_mm, height_mm, name)` | Round boss (standoff, peg) grown out of ANY planar face |
249
+ | `add_text_on_face(text, face, x_mm, y_mm, z_mm, height_mm, depth_mm, emboss, font, name)` | Engrave text into ANY planar face, or emboss it: labels, version numbers; the position is two dimensions |
250
+ | `cut_profile(points_mm, depth_mm, face, plane, name, corner_radii_mm, rotate_deg, about_mm, keep_inside)` | Cut a polygon pocket/slot from the +Z face (blind or through, optionally turned about a pivot), on ANY face (`face`, 3D points on it), or drawn on a plane (`plane`: through all both ways or `depth_mm` centred on it; wedges, side windows; `keep_inside` keeps the profile instead, so two views make a 3D shape) |
251
+ | `cut_offset_pocket(face, x_mm, y_mm, z_mm, rim_mm, depth_mm, name)` | Pocket a face leaving a rim along its outline (I-beam web, tray, frame); rim and depth are dimensions |
252
+ | `cut_slot(length_mm, width_mm, x_mm, y_mm, angle_deg, depth_mm, name)` | Cut a straight slotted hole (obround) on the +Z face at any angle |
253
+ | `add_extruded_slot(start_mm, end_mm, width_mm, depth_mm, name)` | Extrude a stadium (rounded tab, lug, link) between two points, the round ends centred on them |
254
+ | `add_fillet(radius_mm, edges, name, radii_at_mm, skip_shorter_mm)` | Round edges (`edges`: `all`, axis `x`/`y`/`z`, a face outline `"+z:outline"`, one feature's edges `"feature:Boss"`, or indices `"2,5"`); `radii_at_mm` varies the radius from end to end; a refusal names the edges that do not fit |
255
+ | `add_full_round(face, x_mm, y_mm, z_mm, name)` | Round a rib's top off completely between its two closest opposite sides |
256
+ | `add_chamfer(distance_mm, edges, name)` | Chamfer edges at 45° (`edges`: `all`, axis, a face outline, a feature's edges, or indices) |
257
+ | `add_shell(thickness_mm, open_face)` | Hollow to a wall thickness; open a face (`+z`/…) or `none` |
258
+ | `add_linear_pattern(count, spacing_mm, direction, feature_name)` | Repeat a feature N times along `+x`/`-x`/… |
259
+ | `add_circular_pattern(count, center_x_mm, center_y_mm, feature_name, axis, angle_deg)` | Repeat a feature N times around a hole's axis (bolt circle), a model axis (`axis='y'`: revolved parts) or a reference axis by name; all round or over `angle_deg` |
260
+ | `add_mirror(plane, offset_mm, features, name)` | Mirror features (copies follow their seeds) or the whole body about the Front/Top/Right plane, or a plane by name, moved `offset_mm`; fails when a copy would land outside the part |
261
+ | `set_dimension(dimension_name, value_mm)` | Change a named driving dim (e.g. `D1@BlockExtrude`, or any name a tool returned in `dimensions`), rebuild, remeasure; an angle in degrees |
262
+ | `set_equation(equation)` | Add a global equation or variable linking dims (e.g. `"W" = 40`, then `"width@Sketch1" = "W"`), or replace the one that sets the same name; a list sets several with one rebuild; names the features that fail to rebuild |
263
+ | `list_equations` / `delete_equation(equation)` | List the equations (value, global, broken) / delete one by name or index |
264
+ | `slice_mesh(path, axis, heights_mm, frame)` | Cross-sections of an STL/3MF mesh as polygon loops, ready to use as profiles |
265
+ | `compare_with_mesh(path, axis, heights_mm, frame, offset_mm)` | Compare the part's cross-sections with a reference mesh (area and extent differences) |
266
+ | `read_sketch(name)` | A sketch read back in model coordinates: lines, arcs, circles, splines, its dimensions, fully defined or not |
267
+ | `add_sketch(plane, start_mm, segments, name, fixed)` | Sketch an outline of lines, arcs and splines on any plane, fully defined: tangent where it flows, dimensioned from the origin; `fixed=True` freezes computed curves (involutes, cams) instead |
268
+ | `run_script(code, path, image)` | Python against the live SolidWorks on the server's COM thread, with its helpers: a whole part in one call from a script file (`path`), its helper modules reloaded every run. One report: features added, failing features, bodies, clashes between bodies, under-defined sketches, mass, and one PNG of four views. Rolled back when it raises. Off unless the server runs with `--allow-scripts` (or `SOLIDWORKS_MCP_ALLOW_SCRIPTS=1`) |
269
+ | `extrude_sketch(sketch, depth_mm, reverse, name, up_to)` / `cut_sketch(sketch, depth_mm, reverse, name)` | Build on an existing sketch by name (one a person drew): extrude it blind or up to the next face, or cut it blind or through all |
270
+ | `list_dimensions()` | Every dimension in the part: name (for `set_dimension`), feature, value, unit |
271
+ | `list_features()` | The part's history in tree order (name, type, suppressed); flags features that fail to rebuild and sketches that are not fully defined |
272
+ | `delete_feature(name, with_children, with_equations)` | Undo a step: delete a feature with its sketch; refuses (and names them) while other features depend on it, unless `with_children`; names the equations it breaks, or deletes them with `with_equations` |
273
+ | `suppress_feature(name, suppress)` | Take a feature out but keep it and its dimensions (try a variant); `suppress=False` brings it back with its dependents |
274
+ | `set_material(name, database, density_kg_m3)` | Assign a material (e.g. `6061 Alloy`) so mass/density are real; one SolidWorks lacks (e.g. `TPU`) comes with its density |
275
+ | `rebuild(top_only)` | Force rebuild, report errors |
276
+ | `get_mass_properties` | Volume, mass, density, surface area, centre of mass, bounding box |
277
+ | `get_bounding_box` | Tight bounding box of the part, or of the whole assembly (min/max/size, mm) |
278
+ | `list_faces(component)` / `list_edges(face, feature, within_mm, min_length_mm)` | Inspect faces (normal/area/centre; a cylinder's axis, radius and centre, also of a component in an assembly) and edges (type/length/ends/axis) by index, narrowed to a face, a feature, a box or a length |
279
+ | `check_printability(up, overhang_deg, min_wall_mm)` | For a print direction: overhanging faces (area, worst lean, centre), bed contact, height; with `min_wall_mm` the walls thinner than that |
280
+ | `export(path, file_format, quality, deviation_mm, angle_deg, per_component)` | STEP/STL/IGES/Parasolid/3MF (silent; verifies file). STL/3MF tessellation: `quality` `coarse`/`fine`, or explicit `deviation_mm`+`angle_deg`; an assembly's STL as one file or one per component |
281
+ | `screenshot(path, view, zoom_mm, show_planes, from_dir)` | PNG/JPG/TIF from a standard view (`iso`, `front`, `top`, …) or any direction, zoomed to fit or onto a region; planes and axes left out unless asked for |
282
+ | `open_document(path, kind)` / `save_document(path)` | Open a native `.sldprt` / `.sldasm`, or import STEP, IGES and Parasolid as a part or an assembly (parts as components in place) / save the current one, to a path or over its own file |
283
+ | `close_document(title, save)` | Close any open document by title (part, assembly or drawing), or the current one; `save=True` saves it first, otherwise unsaved changes are dropped and reported |
284
+ | `list_documents` / `activate_document(title)` | The open documents (path, type, unsaved changes) / make one current again, also one never saved |
285
+
286
+ ### Drawing tools
287
+
288
+ Sheet millimetres, origin at the sheet's bottom-left corner. Annotations are
289
+ named `<name>@<view>`. Each family of drawing operations is one tool with a
290
+ `kind` / `type` / `action` / `style` switch, so a client loads 16 drawing tools. Where geometry matters, tools take references into a
291
+ view: `edge:-x` (the part's face facing -x, seen edge-on), `hole:<feature>`,
292
+ `feature:<name>`, or a model point `[x, y, z]`.
293
+
294
+ | Tool | Purpose |
295
+ |---|---|
296
+ | `make_drawing(path, sheet, scale, keep_open)` | The whole drawing in one call: front, top and side views (first angle) plus iso on an ISO sheet with SolidWorks' ISO title block, at the largest standard scale that leaves room for dimensions; model dimensions once each, one counted hole callout per set of identical Hole Wizard holes (`4X ...`), their centres dimensioned, material and mass in the title block; returns `problems`. PDF, `.slddrw`, DXF or DWG |
297
+ | `build_drawing(spec, path)` | A drawing from a JSON spec (views, layout, imports, dimensions, callouts, symbols, notes, tables, title block), built from scratch each time; a failing entry is named |
298
+ | `drawing_file(action, path, sheet, part)` | The current drawing, kept apart from the current part: `new` on an ISO sheet, `open`, `save`, `export` (PDF, DXF, DWG or a picture of the sheet), `close` |
299
+ | `set_drawing_properties(properties)` | Title block through its linked properties: description, material and weight (`'from part'` links them to the model) on the part; drawn by, dates, revision, company on the drawing |
300
+ | `add_view(kind, …)` | A `model` view (front, top, iso, …), or a `projected`, `section` (through a model point, square to an axis) or `detail` view of a parent; placed at a sheet point or beside another view, past views in the way |
301
+ | `edit_view(name, …)` | Move a view (absolute or beside another), change its scale, display, tangent edges or name, or delete it |
302
+ | `list_views(view)` | Frame and title block; each view's outline, geometry box, model-to-sheet mapping and annotations (ids, texts, values, boxes) |
303
+ | `import_dimensions(view, kinds)` | Model dimensions (once each), counted hole callouts, hole centre locations, centre marks and lines |
304
+ | `add_dimension(view, ref, to, style, direction, side, offset_mm)` | Dimensions between references, stacked on free rows; `single`, `baseline`, `chain` or `ordinate` |
305
+ | `add_symbol(type, view, ref, …)` | On an edge: a `hole_callout` linked to the model and counting identical holes, an ISO 1302 `surface_finish`, a `datum`, or an ISO 1101 `gtol` frame |
306
+ | `add_note(text, at, height_mm)` | A note on the sheet |
307
+ | `add_table(rows, gear, at)` | A table on the sheet, or the gear data table of a spur or straight bevel gear, computed (from the part's custom properties when not given) |
308
+ | `edit_annotation(annotation_id, …, delete)` | Prefix, suffix, precision, tolerance, fit, text, place; or delete it |
309
+ | `auto_layout(scale, main, arrange_dimensions)` | Lay all views out again around the main one, at the largest standard scale that leaves room for dimensions; optionally SolidWorks' auto arrange of the dimensions |
310
+ | `check_drawing()` / `render_drawing(path, zoom_mm)` | The sheet's problems (outside the frame, over the title block, overlapping text, crossing leaders, small text, duplicate dimensions, undimensioned features); a picture of the sheet or a region |
311
+
312
+ ### Assembly tools
313
+
314
+ | Tool | Purpose |
315
+ |---|---|
316
+ | `insert_component(path, x_mm, y_mm, z_mm, fixed)` | Insert a part, or a `.sldasm` as a sub-assembly, with its **origin** at (x, y, z); the first component is fixed by default |
317
+ | `list_components(name, mates, brief)` | Name, path, fixed, position, rotation and bounding box of every component (its own extent, also when turned), and the mates with the components they hold, their dimensions and errors. `name` narrows a big assembly (`'Bolt*'`, or a part of the name) to those components and their mates; `mates=False` and `brief=True` shorten it |
318
+ | `delete_component(component)` | Remove a component from the assembly, with its mates |
319
+ | `replace_component(component, path)` | Swap a component for another part or sub-assembly in the same place; reports the mates kept, and for each broken one what it held and candidate faces on the new part |
320
+ | `set_component_transform(name, x_mm, y_mm, z_mm, rx_deg, ry_deg, rz_deg)` | Move/rotate a component; the transform is read back and verified |
321
+ | `add_mate(comp_a, face_a, comp_b, face_b, mate_type, distance_mm, angle_deg, flip, mates, stop_on_error)` | Mate two faces (by direction, or `#index` from `list_faces`): `coincident`, `distance`, `parallel`, `perpendicular`, `angle`, or `concentric` between cylinders — measured back afterwards; a refused mate is removed. A distance or angle mate returns its dimension: a joint angle as one number. `mates=[{...}, ...]` adds several in one call |
322
+ | `check_motion(dimension_name, values, distances)` | Step a joint through its range: overlapping pairs and chosen distances per step, the smallest distance and where it occurs, the components that moved |
323
+ | `swept_region(component, dimension, values, heights_mm, axis, margin_mm, tolerance_mm, frame)` | What a component covers in a plane over a joint's range: its outline, with a margin, in the coordinates of the part to cut from |
324
+ | `delete_mate(name)` / `suppress_mate(name, suppress)` | Delete a mate, or a list of them at once, or suppress one (and bring it back) |
325
+ | `check_interference` | Component pairs whose solids overlap, with the volume in mm³ (touching faces don't count) |
326
+ | `measure_distance(component_a, component_b, point_mm, axis_mm)` | Smallest distance between two components, to a point (with the nearest point, and whether the point is in the material), or to an axis |
327
+
328
+ `export` and `screenshot` work on assemblies too.
329
+
330
+ Faces are selected by direction in the component's **own** frame (`+x`, `-z`, …),
331
+ so a selector keeps meaning the same face however the component is turned. Add
332
+ `:inner` (e.g. `+y:inner`) for the cavity side of a hollow part — the inside of a
333
+ room wall instead of its outer skin.
334
+
335
+ All linear dimensions are **millimetres**; the server converts to/from the
336
+ SolidWorks-internal metre/radian units at the boundary.
337
+
338
+ ## Architecture
339
+
340
+ ```
341
+ src/sw_mcp/
342
+ binding.py early-binding plumbing (wrap raw dispatches in generated classes)
343
+ com_worker.py one dedicated STA thread; all COM calls serialised through it
344
+ session.py SolidWorks operations (must run on the COM thread)
345
+ server.py FastMCP tools that delegate to session via the worker
346
+ families.py one tool per family: picks the session method from the arguments
347
+ advanced.py axes, lofts, planes by normal, run_script
348
+ drawing.py drawing tools (views, dimensions, symbols, tables, checks)
349
+ drawing_layout.py view and dimension placement (pure, no COM)
350
+ image_sheet.py several views on one PNG (run_script's picture)
351
+ constants.py enum values read from the installed typelib (verified)
352
+ units.py mm<->m, deg<->rad
353
+ errors.py SolidWorksError -> agent-facing {ok:false,error}
354
+ ```
355
+
356
+ Two non-obvious design decisions, both load-bearing:
357
+
358
+ 1. **Early binding is mandatory.** On this build `GetActiveObject` returns a
359
+ dispatch whose `GetTypeInfo()` fails, so `EnsureDispatch`/`CastTo` cannot infer
360
+ types and pure late binding breaks (`IModelDoc2.FirstFeature` →
361
+ `DISP_E_MEMBERNOTFOUND`). We generate makepy wrappers from the installed
362
+ typelib and wrap each raw dispatch in the right interface class; calls then go
363
+ by dispid via `InvokeTypes`, bypassing name resolution. See `binding.py`.
364
+
365
+ 2. **A dedicated COM thread.** COM is STA and thread-affine. The MCP server runs
366
+ on asyncio, so all COM work is pinned to one worker thread (`com_worker.py`)
367
+ that handlers post to and await — actively enforcing the "one COM session,
368
+ single-threaded" rule that does not hold automatically in an async server.
369
+
370
+ ## Status and roadmap
371
+
372
+ Proven end-to-end against **SOLIDWORKS 2026 (3DEXPERIENCE R2026x)**:
373
+
374
+ | Milestone | What it proves | State |
375
+ |---|---|---|
376
+ | M0 | COM connection to a running SolidWorks | ✅ |
377
+ | M1 | new part → sketch rectangle → extrude → mass properties (volume matches hand calc) | ✅ |
378
+ | M2 | change a named dimension → rebuild → volume changes predictably | ✅ |
379
+ | M3 | full agent loop via the MCP server: build → measure → correct → export STEP/STL + screenshot | ✅ |
380
+ | M4 | revolve, sweep, loft, profiles, holes/pockets/counterbores, slots, fillet/chamfer, shell, patterns, equations, materials, save/open | 🚧 ongoing |
381
+ | M5 | end-to-end 3D-print part: build a functional mounting bracket through the full loop → verify every dimension → export a fine STL ([scripts/m5_demo_bracket.py](https://github.com/NejcDvorsek/sw-mcp/blob/main/scripts/m5_demo_bracket.py)) | ✅ |
382
+ | M6 | assemblies: insert and position components, mate them, check interference — every placement and mate measured back ([tests/test_assembly.py](https://github.com/NejcDvorsek/sw-mcp/blob/main/tests/test_assembly.py)) | ✅ |
383
+
384
+ See [Docs/PROGRESS.md](https://github.com/NejcDvorsek/sw-mcp/blob/main/Docs/PROGRESS.md) for the detailed log and roadmap.
385
+ Feedback and contributions are welcome.
386
+
387
+ ## Known limitations
388
+
389
+ - Geometry so far: **boxes**, **cylinders/cones** (revolve), **arbitrary
390
+ extruded profiles**, **holes**, **polygon pockets/slots** (`cut_profile`),
391
+ **fillets**, **chamfers**, **shells**, **linear + circular patterns** (bolt
392
+ circles); plus **equations**, **materials**, geometry **inspection**, and
393
+ **save/open** of `.sldprt`, **holes + pockets on any planar face**
394
+ (model→sketch transform), **round flanges** (disc + bore + bolt circle), and
395
+ **slotted holes** (`cut_slot`, obround at any angle — the first arc-based sketch),
396
+ **general revolves** (`add_revolved_profile`: any `(r,z)` profile → shafts,
397
+ vases, rings), **swept pipes/tubes** (`add_swept_pipe`: a round profile along
398
+ a rounded 2D path), and **lofts** (`add_lofted_solid`: blend stacked polygon
399
+ profiles → transitions/adapters), **free-form extrusions**
400
+ (`add_extruded_spline`: a smooth closed spline → organic/aesthetic outlines), and
401
+ **non-circular sweeps** (`add_swept_profile`: any cross-section along a path →
402
+ rails, gaskets, trim), and **mirrors** (`add_mirror`: features or the whole
403
+ body about a plane through the part).
404
+ - Selection: plane walk, face-by-normal/direction (`_planar_face_by_normal`,
405
+ `+z`/…, with `:inner` for the cavity side of a hollow part), and edge selection
406
+ by axis **or explicit index** (`_select_edges`). `list_faces`/`list_edges` let
407
+ an agent inspect geometry before selecting.
408
+ - Assemblies (M6): components, transforms, mates and interference detection.
409
+ Component patterns, in-context features, configurations and Simulation (FEA)
410
+ are out of scope.
411
+ - `list_faces`, `list_edges` and `add_fillet` see only the first body of a
412
+ multibody part.
413
+
414
+ ## Differences from upstream
415
+
416
+ Forked from `hjbaard/SolidWorks-MCP` at 0.16.3. Added since (details in
417
+ [CHANGELOG.md](https://github.com/NejcDvorsek/sw-mcp/blob/main/CHANGELOG.md)):
418
+
419
+ - **Manufacturing drawings**: `make_drawing` on an ISO sheet with title block,
420
+ counted hole callouts and hole positions, plus 15 drawing tools (views,
421
+ sections, details, dimensions, hole callouts, surface finish, datums, GD&T,
422
+ tables and gear data tables, notes, layout, a sheet check, `build_drawing`
423
+ from a spec).
424
+ - **Modelling**: `add_axis`, `add_loft` (also loft cuts), circular patterns
425
+ about any axis, planes through a point square to any direction,
426
+ `add_sketch(fixed=True)`, several mates in one call, `replace_component`.
427
+ - **`run_script`** (off unless allowed): a whole part from a script file in one
428
+ call, with one report and a four-view picture.
429
+ - **Tool families**: related tools merged into one with a switch (`add_hole`,
430
+ `add_extruded_profile`, `cut_profile`, `new_document`, ...), 91 tools in
431
+ place of 106.
432
+ - Different names: the package is `sw-mcp`, the module `sw_mcp`.
433
+
434
+ Fixes from upstream are merged in from time to time. In a clone:
435
+
436
+ ```bash
437
+ git remote add upstream https://github.com/hjbaard/SolidWorks-MCP.git
438
+ git fetch upstream && git merge upstream/main
439
+ ```
440
+
441
+ ## Credits and license
442
+
443
+ MIT, see [LICENSE](LICENSE). The original server is by Henk Jan Baard
444
+ ([hjbaard/SolidWorks-MCP](https://github.com/hjbaard/SolidWorks-MCP)); this
445
+ fork is maintained by Nejc Dvorsek.