solidworks-mcp-python 1.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- solidworks_mcp/__init__.py +57 -0
- solidworks_mcp/adapters/__init__.py +40 -0
- solidworks_mcp/adapters/base.py +1882 -0
- solidworks_mcp/adapters/circuit_breaker.py +2052 -0
- solidworks_mcp/adapters/com_executor.py +252 -0
- solidworks_mcp/adapters/complexity_analyzer.py +272 -0
- solidworks_mcp/adapters/connection_pool.py +1884 -0
- solidworks_mcp/adapters/factory.py +366 -0
- solidworks_mcp/adapters/intelligent_router.py +218 -0
- solidworks_mcp/adapters/mock_adapter.py +4016 -0
- solidworks_mcp/adapters/pywin32_adapter.py +2842 -0
- solidworks_mcp/adapters/solidworks/__init__.py +17 -0
- solidworks_mcp/adapters/solidworks/bitmap.py +180 -0
- solidworks_mcp/adapters/solidworks/com_utils.py +56 -0
- solidworks_mcp/adapters/solidworks/dimension_layout.py +753 -0
- solidworks_mcp/adapters/solidworks/documentation.py +1476 -0
- solidworks_mcp/adapters/solidworks/drawing_standard.py +1993 -0
- solidworks_mcp/adapters/solidworks/face_selection.py +392 -0
- solidworks_mcp/adapters/solidworks/features.py +3362 -0
- solidworks_mcp/adapters/solidworks/io.py +3649 -0
- solidworks_mcp/adapters/solidworks/rebuild_diagnostics.py +269 -0
- solidworks_mcp/adapters/solidworks/rebuild_inspector.py +146 -0
- solidworks_mcp/adapters/solidworks/selection.py +308 -0
- solidworks_mcp/adapters/solidworks/sketch.py +2545 -0
- solidworks_mcp/adapters/sw_feature_errors.py +66 -0
- solidworks_mcp/adapters/sw_type_info.py +317 -0
- solidworks_mcp/adapters/vba_adapter.py +318 -0
- solidworks_mcp/adapters/vba_macro_executor.py +215 -0
- solidworks_mcp/agents/__init__.py +68 -0
- solidworks_mcp/agents/design_knowledge/assembly_patterns.md +127 -0
- solidworks_mcp/agents/design_knowledge/hinges.md +118 -0
- solidworks_mcp/agents/design_knowledge/snap_fits.md +93 -0
- solidworks_mcp/agents/design_knowledge/solidworks_basics.md +3 -0
- solidworks_mcp/agents/design_knowledge/tolerancing.md +128 -0
- solidworks_mcp/agents/harness.py +216 -0
- solidworks_mcp/agents/history_db.py +1508 -0
- solidworks_mcp/agents/retrieval_index.py +164 -0
- solidworks_mcp/agents/schemas.py +165 -0
- solidworks_mcp/agents/smoke_test.py +285 -0
- solidworks_mcp/agents/soc_exporter.py +650 -0
- solidworks_mcp/agents/soc_pickup.py +332 -0
- solidworks_mcp/agents/soc_rewind.py +221 -0
- solidworks_mcp/agents/vector_rag.py +665 -0
- solidworks_mcp/cache/__init__.py +5 -0
- solidworks_mcp/cache/response_cache.py +164 -0
- solidworks_mcp/config.py +729 -0
- solidworks_mcp/exceptions.py +37 -0
- solidworks_mcp/security/__init__.py +68 -0
- solidworks_mcp/security/auth.py +128 -0
- solidworks_mcp/security/cors.py +27 -0
- solidworks_mcp/security/rate_limiting.py +90 -0
- solidworks_mcp/security/runtime.py +155 -0
- solidworks_mcp/server.py +777 -0
- solidworks_mcp/server_cli_fixed.py +120 -0
- solidworks_mcp/setup/__init__.py +1 -0
- solidworks_mcp/setup/cli.py +399 -0
- solidworks_mcp/setup/clients.py +157 -0
- solidworks_mcp/setup/config_file.py +230 -0
- solidworks_mcp/setup/handshake.py +202 -0
- solidworks_mcp/setup/launcher.py +102 -0
- solidworks_mcp/setup/legacy.py +100 -0
- solidworks_mcp/setup/registration.py +193 -0
- solidworks_mcp/setup/solidworks.py +49 -0
- solidworks_mcp/tools/__init__.py +64 -0
- solidworks_mcp/tools/analysis.py +723 -0
- solidworks_mcp/tools/automation.py +578 -0
- solidworks_mcp/tools/docs_discovery.py +1476 -0
- solidworks_mcp/tools/drawing.py +1015 -0
- solidworks_mcp/tools/drawing_analysis.py +456 -0
- solidworks_mcp/tools/drawing_automation.py +627 -0
- solidworks_mcp/tools/engineering_data.py +466 -0
- solidworks_mcp/tools/error_classifier.py +232 -0
- solidworks_mcp/tools/export.py +833 -0
- solidworks_mcp/tools/file_facts.py +40 -0
- solidworks_mcp/tools/file_management.py +1473 -0
- solidworks_mcp/tools/input_compat.py +17 -0
- solidworks_mcp/tools/macro_recording.py +464 -0
- solidworks_mcp/tools/modeling.py +2360 -0
- solidworks_mcp/tools/sketching.py +1422 -0
- solidworks_mcp/tools/template_management.py +367 -0
- solidworks_mcp/tools/tool_boundary.py +280 -0
- solidworks_mcp/tools/vba_generation.py +1145 -0
- solidworks_mcp/ui/__init__.py +9 -0
- solidworks_mcp/ui/local_llm.py +725 -0
- solidworks_mcp/ui/routers/__init__.py +10 -0
- solidworks_mcp/ui/routers/checkpoint.py +24 -0
- solidworks_mcp/ui/routers/docs.py +47 -0
- solidworks_mcp/ui/routers/llm.py +83 -0
- solidworks_mcp/ui/routers/local_model.py +69 -0
- solidworks_mcp/ui/routers/model.py +75 -0
- solidworks_mcp/ui/routers/preview.py +38 -0
- solidworks_mcp/ui/routers/session.py +161 -0
- solidworks_mcp/ui/routers/viewer.py +154 -0
- solidworks_mcp/ui/schemas.py +502 -0
- solidworks_mcp/ui/server.py +298 -0
- solidworks_mcp/ui/service.py +481 -0
- solidworks_mcp/ui/services/__init__.py +136 -0
- solidworks_mcp/ui/services/_utils.py +958 -0
- solidworks_mcp/ui/services/checkpoint_service.py +1110 -0
- solidworks_mcp/ui/services/docs_service.py +282 -0
- solidworks_mcp/ui/services/llm_service.py +956 -0
- solidworks_mcp/ui/services/model_service.py +459 -0
- solidworks_mcp/ui/services/parameter_repair_service.py +314 -0
- solidworks_mcp/ui/services/preview_service.py +489 -0
- solidworks_mcp/ui/services/session_service.py +1389 -0
- solidworks_mcp/utils/__init__.py +9 -0
- solidworks_mcp/utils/feature_tree_classifier.py +342 -0
- solidworks_mcp/utils/logging.py +76 -0
- solidworks_mcp/utils/validation.py +77 -0
- solidworks_mcp/version.py +8 -0
- solidworks_mcp_python-1.5.0.dist-info/METADATA +410 -0
- solidworks_mcp_python-1.5.0.dist-info/RECORD +115 -0
- solidworks_mcp_python-1.5.0.dist-info/WHEEL +4 -0
- solidworks_mcp_python-1.5.0.dist-info/entry_points.txt +3 -0
- solidworks_mcp_python-1.5.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
"""Drawing-automation tools: model dimensions on drawing views, laid out without overlaps.
|
|
2
|
+
|
|
3
|
+
The tools call real adapter methods (``adapters/solidworks/documentation.py``
|
|
4
|
+
and ``adapters/solidworks/drawing_standard.py``); the layout geometry is in
|
|
5
|
+
``adapters/solidworks/dimension_layout.py``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Literal
|
|
11
|
+
|
|
12
|
+
from fastmcp import FastMCP
|
|
13
|
+
from pydantic import Field, field_validator
|
|
14
|
+
|
|
15
|
+
from ..adapters.base import SolidWorksAdapter
|
|
16
|
+
from .input_compat import CompatInput
|
|
17
|
+
from .tool_boundary import (
|
|
18
|
+
normalize_input,
|
|
19
|
+
require_capability,
|
|
20
|
+
require_success,
|
|
21
|
+
with_tool_boundary,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class InsertModelDimensionsInput(CompatInput):
|
|
26
|
+
"""Input schema for importing model dimensions into drawing views.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
view_name (str | None): One view, or every view when omitted.
|
|
30
|
+
include_hole_wizard (bool): Also import Hole Wizard location dims.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
view_name: str | None = Field(
|
|
34
|
+
default=None,
|
|
35
|
+
description="One view, e.g. 'Drawing View1'; every orthographic view "
|
|
36
|
+
"when omitted. Find names with list_drawing_views.",
|
|
37
|
+
)
|
|
38
|
+
include_hole_wizard: bool = Field(
|
|
39
|
+
default=True, description="Also import Hole Wizard location dimensions"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ArrangeDimensionsInput(CompatInput):
|
|
44
|
+
"""Input schema for arranging drawing dimensions.
|
|
45
|
+
|
|
46
|
+
Attributes:
|
|
47
|
+
view_name (str | None): One view, or every view when omitted.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
view_name: str | None = Field(
|
|
51
|
+
default=None,
|
|
52
|
+
description="Only move this view's dimensions, e.g. 'Drawing View1'; "
|
|
53
|
+
"every view when omitted. The overlap check always covers the whole sheet.",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class InsertBomTableInput(CompatInput):
|
|
58
|
+
"""Input schema for inserting a BOM table.
|
|
59
|
+
|
|
60
|
+
Attributes:
|
|
61
|
+
view_name (str): Assembly drawing view the BOM is attached to.
|
|
62
|
+
template_path (str | None): .sldbomtbt template.
|
|
63
|
+
bom_type (str): BOM type.
|
|
64
|
+
x_mm (float | None): Table X position.
|
|
65
|
+
y_mm (float | None): Table Y position.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
view_name: str = Field(description="A view of an assembly, e.g. 'Drawing View1'")
|
|
69
|
+
template_path: str | None = Field(
|
|
70
|
+
default=None, description=".sldbomtbt template; bom-standard when omitted"
|
|
71
|
+
)
|
|
72
|
+
bom_type: Literal["top_level_only", "parts_only", "indented"] = Field(
|
|
73
|
+
default="top_level_only", description="BOM structure"
|
|
74
|
+
)
|
|
75
|
+
x_mm: float | None = Field(
|
|
76
|
+
default=None,
|
|
77
|
+
description="Table X on the sheet (mm); the sheet's BOM anchor when omitted",
|
|
78
|
+
)
|
|
79
|
+
y_mm: float | None = Field(default=None, description="Table Y on the sheet (mm)")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AddGtolInput(CompatInput):
|
|
83
|
+
"""Input schema for a GD&T feature control frame.
|
|
84
|
+
|
|
85
|
+
Attributes:
|
|
86
|
+
characteristic (str): The geometric characteristic.
|
|
87
|
+
tolerance (float): Tolerance value in drawing units.
|
|
88
|
+
diameter_zone (bool): Ø before the tolerance.
|
|
89
|
+
material_condition (str): none, mmc or lmc.
|
|
90
|
+
datums (list[str]): Datum letters, primary first.
|
|
91
|
+
x_mm (float): Frame position on the sheet.
|
|
92
|
+
y_mm (float): Frame position on the sheet.
|
|
93
|
+
attach_x_mm (float | None): A point on the edge the leader points to.
|
|
94
|
+
attach_y_mm (float | None): A point on the edge the leader points to.
|
|
95
|
+
dimension_view (str | None): View holding the dimension to attach to.
|
|
96
|
+
dimension_name (str | None): Model dimension to attach to.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
characteristic: Literal[
|
|
100
|
+
"position",
|
|
101
|
+
"flatness",
|
|
102
|
+
"perpendicularity",
|
|
103
|
+
"parallelism",
|
|
104
|
+
"profile_surface",
|
|
105
|
+
"concentricity",
|
|
106
|
+
] = Field(description="Geometric characteristic")
|
|
107
|
+
tolerance: float = Field(gt=0, description="Tolerance value, e.g. 0.2")
|
|
108
|
+
diameter_zone: bool = Field(
|
|
109
|
+
default=False, description="Cylindrical zone: Ø before the tolerance"
|
|
110
|
+
)
|
|
111
|
+
material_condition: Literal["none", "mmc", "lmc"] = Field(
|
|
112
|
+
default="none", description="Material condition modifier"
|
|
113
|
+
)
|
|
114
|
+
datums: list[str] = Field(
|
|
115
|
+
default_factory=list, max_length=3, description="Datum letters, primary first"
|
|
116
|
+
)
|
|
117
|
+
x_mm: float = Field(
|
|
118
|
+
default=0.0,
|
|
119
|
+
description="Frame X on the sheet (mm); not used when on a dimension",
|
|
120
|
+
)
|
|
121
|
+
y_mm: float = Field(
|
|
122
|
+
default=0.0,
|
|
123
|
+
description="Frame Y on the sheet (mm); not used when on a dimension",
|
|
124
|
+
)
|
|
125
|
+
attach_x_mm: float | None = Field(
|
|
126
|
+
default=None, description="X of a point on the edge the leader points to"
|
|
127
|
+
)
|
|
128
|
+
attach_y_mm: float | None = Field(
|
|
129
|
+
default=None, description="Y of a point on the edge the leader points to"
|
|
130
|
+
)
|
|
131
|
+
dimension_view: str | None = Field(
|
|
132
|
+
default=None, description="View holding the dimension to attach to"
|
|
133
|
+
)
|
|
134
|
+
dimension_name: str | None = Field(
|
|
135
|
+
default=None,
|
|
136
|
+
description="Model dimension to attach to, e.g. 'D3@Sketch1' (see "
|
|
137
|
+
"list_model_dimensions); the frame sits under its text",
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def attach_point(self) -> tuple[float, float] | None:
|
|
141
|
+
"""The leader's attach point, when both coordinates are given."""
|
|
142
|
+
if self.attach_x_mm is None or self.attach_y_mm is None:
|
|
143
|
+
return None
|
|
144
|
+
return (self.attach_x_mm, self.attach_y_mm)
|
|
145
|
+
|
|
146
|
+
def attach_dimension(self) -> tuple[str, str] | None:
|
|
147
|
+
"""``(view, dimension)`` to attach to, when both are given."""
|
|
148
|
+
if not self.dimension_view or not self.dimension_name:
|
|
149
|
+
return None
|
|
150
|
+
return (self.dimension_view, self.dimension_name)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class AddDatumTagInput(CompatInput):
|
|
154
|
+
"""Input schema for a datum feature symbol.
|
|
155
|
+
|
|
156
|
+
Attributes:
|
|
157
|
+
label (str): The datum letter.
|
|
158
|
+
attach_x_mm (float): A point on the edge it marks.
|
|
159
|
+
attach_y_mm (float): A point on the edge it marks.
|
|
160
|
+
x_mm (float): Tag position on the sheet.
|
|
161
|
+
y_mm (float): Tag position on the sheet.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
label: str = Field(min_length=1, max_length=1, description="Datum letter, e.g. 'A'")
|
|
165
|
+
attach_x_mm: float = Field(description="X of a point on the edge it marks (mm)")
|
|
166
|
+
attach_y_mm: float = Field(description="Y of a point on the edge it marks (mm)")
|
|
167
|
+
x_mm: float = Field(description="Tag X on the sheet (mm)")
|
|
168
|
+
y_mm: float = Field(description="Tag Y on the sheet (mm)")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class SetSheetScaleInput(CompatInput):
|
|
172
|
+
"""Input schema for the sheet scale.
|
|
173
|
+
|
|
174
|
+
Attributes:
|
|
175
|
+
numerator (float): Scale numerator.
|
|
176
|
+
denominator (float): Scale denominator.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
numerator: float = Field(gt=0, description="e.g. 1 for 1:2")
|
|
180
|
+
denominator: float = Field(gt=0, description="e.g. 2 for 1:2")
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class GenerateDrawingPackageInput(CompatInput):
|
|
184
|
+
"""Input schema for the standard A3 drawing generator.
|
|
185
|
+
|
|
186
|
+
Attributes:
|
|
187
|
+
model_path (str): Part or assembly to draw.
|
|
188
|
+
output_dir (str | None): Folder for the .SLDDRW and PDF.
|
|
189
|
+
title (str | None): Title block TITLE (short).
|
|
190
|
+
subtitle (str | None): Sheet title.
|
|
191
|
+
drawing_number (str | None): DWG NO. and file name.
|
|
192
|
+
author (str): DRAWN name.
|
|
193
|
+
revision (str): Revision letter.
|
|
194
|
+
company (str): Company name field.
|
|
195
|
+
notes (list[str]): Extra note lines.
|
|
196
|
+
include_isometric (bool): Add the isometric view.
|
|
197
|
+
write_model_properties (bool): Write title-block properties into the model.
|
|
198
|
+
title_block_text_height_mm (float | None): Cap on DWG NO./TITLE text height.
|
|
199
|
+
include_bom (bool): Put a BOM above the isometric view (assemblies).
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
model_path: str = Field(description="Full path to the .SLDPRT or .SLDASM")
|
|
203
|
+
output_dir: str | None = Field(
|
|
204
|
+
default=None,
|
|
205
|
+
description="Where to save the drawing; the model's folder when omitted",
|
|
206
|
+
)
|
|
207
|
+
title: str | None = Field(
|
|
208
|
+
default=None,
|
|
209
|
+
description="Short title, e.g. 'Mounting Plate - Rev A'; the file name when omitted",
|
|
210
|
+
)
|
|
211
|
+
subtitle: str | None = Field(
|
|
212
|
+
default=None, description="Sheet title, e.g. 'Base support'"
|
|
213
|
+
)
|
|
214
|
+
drawing_number: str | None = Field(
|
|
215
|
+
default=None,
|
|
216
|
+
description="DWG NO., also the drawing file name, e.g. 'MP-RevA-01'",
|
|
217
|
+
)
|
|
218
|
+
author: str = Field(default="", description="DRAWN name")
|
|
219
|
+
revision: str = Field(default="A", description="Revision letter")
|
|
220
|
+
company: str = Field(default="", description="Company name")
|
|
221
|
+
notes: list[str] = Field(
|
|
222
|
+
default_factory=list,
|
|
223
|
+
description="Extra note lines (material, process, connections); 3-7 lines in total",
|
|
224
|
+
)
|
|
225
|
+
include_isometric: bool = Field(default=True, description="Add an isometric view")
|
|
226
|
+
write_model_properties: bool = Field(
|
|
227
|
+
default=True,
|
|
228
|
+
description="Write Description/Material/Weight into the model so the title block shows them, and save it",
|
|
229
|
+
)
|
|
230
|
+
title_block_text_height_mm: float | None = Field(
|
|
231
|
+
default=None,
|
|
232
|
+
gt=0,
|
|
233
|
+
description="Largest text height (mm) for the title block's DWG NO. and "
|
|
234
|
+
"TITLE, e.g. 3 for smaller text. By default they keep the sheet "
|
|
235
|
+
"format's height and are only shrunk to fit their boxes.",
|
|
236
|
+
)
|
|
237
|
+
include_bom: bool = Field(
|
|
238
|
+
default=False,
|
|
239
|
+
description="Assemblies only: put a bill of materials in the top of the "
|
|
240
|
+
"isometric view's cell, with the isometric fitted below it",
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
@field_validator("model_path")
|
|
244
|
+
@classmethod
|
|
245
|
+
def _part_or_assembly(cls, value: str) -> str:
|
|
246
|
+
"""Only parts and assemblies can be drawn."""
|
|
247
|
+
if not value.strip().lower().endswith((".sldprt", ".sldasm")):
|
|
248
|
+
raise ValueError(
|
|
249
|
+
"model_path must be a part (.SLDPRT) or assembly (.SLDASM)"
|
|
250
|
+
)
|
|
251
|
+
return value
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
async def register_drawing_automation_tools(
|
|
255
|
+
mcp: FastMCP, adapter: SolidWorksAdapter, config: dict[str, Any]
|
|
256
|
+
) -> int:
|
|
257
|
+
"""Register drawing-automation tools with FastMCP.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
mcp (FastMCP): The MCP server.
|
|
261
|
+
adapter (SolidWorksAdapter): Adapter used by every tool.
|
|
262
|
+
config (dict[str, Any]): Server configuration (unused).
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
int: Number of tools registered.
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
mcp = with_tool_boundary(mcp)
|
|
269
|
+
|
|
270
|
+
def _supports(method: str) -> None:
|
|
271
|
+
require_capability(
|
|
272
|
+
adapter,
|
|
273
|
+
method,
|
|
274
|
+
f"The active adapter does not support {method}; nothing was done.",
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
@mcp.tool()
|
|
278
|
+
async def insert_model_dimensions(
|
|
279
|
+
input_data: InsertModelDimensionsInput | None = None,
|
|
280
|
+
) -> dict[str, Any]:
|
|
281
|
+
"""Bring the model's dimensions into the active drawing's views.
|
|
282
|
+
|
|
283
|
+
Imports the model's "marked for drawing" dimensions view by view, each
|
|
284
|
+
dimension shown once on the sheet (a repeat in a later view is
|
|
285
|
+
deleted). Pictorial views (isometric, dimetric, trimetric) are skipped
|
|
286
|
+
unless named. Falls back to SolidWorks AutoDimension only when nothing
|
|
287
|
+
could be imported. SolidWorks places imported dimensions inside the
|
|
288
|
+
views, so run ``arrange_dimensions`` afterwards.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
input_data (InsertModelDimensionsInput | None): ``view_name`` for one
|
|
292
|
+
view and ``include_hole_wizard``; omit for every view.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
dict[str, Any]: ``data`` has per-view ``views`` (before/after counts
|
|
296
|
+
and ``duplicates_removed``), ``dimensions_added``,
|
|
297
|
+
``unique_dimensions``, ``skipped_pictorial_views`` and the ``method``
|
|
298
|
+
used. ``not_found`` for an unknown view; ``wrong_document_type``
|
|
299
|
+
unless a drawing is active.
|
|
300
|
+
|
|
301
|
+
Example:
|
|
302
|
+
```python
|
|
303
|
+
await insert_model_dimensions()
|
|
304
|
+
await insert_model_dimensions({"view_name": "Drawing View1"})
|
|
305
|
+
```
|
|
306
|
+
"""
|
|
307
|
+
input_data = normalize_input(input_data, InsertModelDimensionsInput)
|
|
308
|
+
_supports("insert_model_dimensions")
|
|
309
|
+
result = await adapter.insert_model_dimensions(
|
|
310
|
+
input_data.view_name, input_data.include_hole_wizard
|
|
311
|
+
)
|
|
312
|
+
require_success(result, context="Failed to insert model dimensions")
|
|
313
|
+
data = result.data or {}
|
|
314
|
+
return {
|
|
315
|
+
"status": "success",
|
|
316
|
+
"message": (
|
|
317
|
+
f"Added {data.get('dimensions_added', 0)} dimension(s) to "
|
|
318
|
+
f"{len(data.get('views', []))} view(s) ({data.get('method')})"
|
|
319
|
+
),
|
|
320
|
+
"data": data,
|
|
321
|
+
"execution_time": result.execution_time,
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
@mcp.tool()
|
|
325
|
+
async def arrange_dimensions(
|
|
326
|
+
input_data: ArrangeDimensionsInput | None = None,
|
|
327
|
+
) -> dict[str, Any]:
|
|
328
|
+
"""Move drawing dimensions so nothing overlaps, then check the real result.
|
|
329
|
+
|
|
330
|
+
Dimensions go outside their views in rows (first 8 mm from the
|
|
331
|
+
outline, rows 7 mm apart, columns 9 mm apart), shortest span nearest
|
|
332
|
+
the view, each on the side its extension lines start from. One with no
|
|
333
|
+
room on that side stays put and is listed in ``unplaced``. After moving
|
|
334
|
+
them it re-reads every dimension's real text box and lines and checks
|
|
335
|
+
the whole sheet: no text on text, no line through text, no line across
|
|
336
|
+
another view or across its own view, nothing on a view or outside the
|
|
337
|
+
sheet.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
input_data (ArrangeDimensionsInput | None): ``view_name`` to move
|
|
341
|
+
only one view's dimensions; omit for every view.
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
dict[str, Any]: ``data`` has ``dimensions``, ``moved``,
|
|
345
|
+
``overlaps_before``, ``overlaps_after`` and ``unplaced``. When any
|
|
346
|
+
overlap remains the call is an error whose ``data`` lists each one
|
|
347
|
+
in ``remaining_overlaps``; try a smaller sheet scale.
|
|
348
|
+
|
|
349
|
+
Example:
|
|
350
|
+
```python
|
|
351
|
+
await insert_model_dimensions()
|
|
352
|
+
await arrange_dimensions()
|
|
353
|
+
```
|
|
354
|
+
"""
|
|
355
|
+
input_data = normalize_input(input_data, ArrangeDimensionsInput)
|
|
356
|
+
_supports("arrange_dimensions")
|
|
357
|
+
result = await adapter.arrange_dimensions(input_data.view_name)
|
|
358
|
+
require_success(result, context="Failed to arrange dimensions", with_data=True)
|
|
359
|
+
data = result.data or {}
|
|
360
|
+
return {
|
|
361
|
+
"status": "success",
|
|
362
|
+
"message": (
|
|
363
|
+
f"Arranged {data.get('dimensions', 0)} dimension(s): "
|
|
364
|
+
f"{data.get('overlaps_before', 0)} overlap(s) before, "
|
|
365
|
+
f"{data.get('overlaps_after', 0)} after"
|
|
366
|
+
),
|
|
367
|
+
"data": data,
|
|
368
|
+
"execution_time": result.execution_time,
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
@mcp.tool()
|
|
372
|
+
async def insert_bom_table(
|
|
373
|
+
input_data: InsertBomTableInput | None = None,
|
|
374
|
+
) -> dict[str, Any]:
|
|
375
|
+
"""Insert a bill-of-materials table for an assembly view on the active drawing.
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
input_data (InsertBomTableInput | None): ``view_name`` (a view of an
|
|
379
|
+
assembly), optional ``template_path``, ``bom_type`` and table
|
|
380
|
+
position ``x_mm``/``y_mm`` (the sheet's BOM anchor when omitted).
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
dict[str, Any]: ``data`` has ``view``, ``bom_type``, ``template`` and
|
|
384
|
+
``row_count``. ``not_found`` for an unknown view (the message lists
|
|
385
|
+
the views); ``wrong_document_type`` unless a drawing is active.
|
|
386
|
+
|
|
387
|
+
Example:
|
|
388
|
+
```python
|
|
389
|
+
await insert_bom_table({"view_name": "Drawing View1"})
|
|
390
|
+
await insert_bom_table({"view_name": "Drawing View1", "bom_type": "indented"})
|
|
391
|
+
```
|
|
392
|
+
"""
|
|
393
|
+
_supports("insert_bom_table")
|
|
394
|
+
input_data = normalize_input(input_data, InsertBomTableInput)
|
|
395
|
+
result = await adapter.insert_bom_table(
|
|
396
|
+
input_data.view_name,
|
|
397
|
+
input_data.template_path,
|
|
398
|
+
input_data.bom_type,
|
|
399
|
+
input_data.x_mm,
|
|
400
|
+
input_data.y_mm,
|
|
401
|
+
)
|
|
402
|
+
require_success(result, context="Failed to insert the BOM table")
|
|
403
|
+
return {
|
|
404
|
+
"status": "success",
|
|
405
|
+
"message": f"Inserted a {input_data.bom_type} BOM on {input_data.view_name}",
|
|
406
|
+
"data": result.data,
|
|
407
|
+
"execution_time": result.execution_time,
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
@mcp.tool()
|
|
411
|
+
async def add_gtol(input_data: AddGtolInput | None = None) -> dict[str, Any]:
|
|
412
|
+
"""Add a GD&T feature control frame, e.g. position Ø0.2 (M) to A|B|C.
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
input_data (AddGtolInput | None): ``characteristic`` (position,
|
|
416
|
+
flatness, perpendicularity, ...), ``tolerance``,
|
|
417
|
+
``diameter_zone``, ``material_condition``, up to three
|
|
418
|
+
``datums``, and what it is on: a drawing dimension
|
|
419
|
+
(``dimension_view`` + ``dimension_name``; the frame sits under
|
|
420
|
+
its text - the usual place for a hole's position tolerance),
|
|
421
|
+
or ``x_mm``/``y_mm`` on the sheet with an optional leader to
|
|
422
|
+
the edge at ``attach_x_mm``/``attach_y_mm``.
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
dict[str, Any]: ``data`` is the frame as read back from SolidWorks.
|
|
426
|
+
Bad inputs are ``invalid_input``; no edge at the attach point is
|
|
427
|
+
``not_found``; anything other than a drawing is
|
|
428
|
+
``wrong_document_type``.
|
|
429
|
+
|
|
430
|
+
Example:
|
|
431
|
+
```python
|
|
432
|
+
await add_gtol({
|
|
433
|
+
"characteristic": "position", "tolerance": 0.2,
|
|
434
|
+
"diameter_zone": True, "material_condition": "mmc",
|
|
435
|
+
"datums": ["A", "B", "C"],
|
|
436
|
+
"dimension_view": "Drawing View2", "dimension_name": "D3@Sketch1",
|
|
437
|
+
})
|
|
438
|
+
await add_gtol({
|
|
439
|
+
"characteristic": "flatness", "tolerance": 0.05,
|
|
440
|
+
"x_mm": 60, "y_mm": 90, "attach_x_mm": 80, "attach_y_mm": 107,
|
|
441
|
+
})
|
|
442
|
+
```
|
|
443
|
+
"""
|
|
444
|
+
_supports("add_gtol")
|
|
445
|
+
input_data = normalize_input(input_data, AddGtolInput)
|
|
446
|
+
attach = input_data.attach_point()
|
|
447
|
+
result = await adapter.add_gtol(
|
|
448
|
+
input_data.characteristic,
|
|
449
|
+
input_data.tolerance,
|
|
450
|
+
input_data.x_mm,
|
|
451
|
+
input_data.y_mm,
|
|
452
|
+
input_data.diameter_zone,
|
|
453
|
+
input_data.material_condition,
|
|
454
|
+
input_data.datums,
|
|
455
|
+
attach,
|
|
456
|
+
input_data.attach_dimension(),
|
|
457
|
+
)
|
|
458
|
+
require_success(result, context="Failed to add the feature control frame")
|
|
459
|
+
return {
|
|
460
|
+
"status": "success",
|
|
461
|
+
"message": (
|
|
462
|
+
f"Added a {input_data.characteristic} frame "
|
|
463
|
+
f"({input_data.tolerance:g}"
|
|
464
|
+
+ (f" to {'|'.join(input_data.datums)}" if input_data.datums else "")
|
|
465
|
+
+ ")"
|
|
466
|
+
),
|
|
467
|
+
"data": result.data,
|
|
468
|
+
"execution_time": result.execution_time,
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
@mcp.tool()
|
|
472
|
+
async def add_datum_tag(
|
|
473
|
+
input_data: AddDatumTagInput | None = None,
|
|
474
|
+
) -> dict[str, Any]:
|
|
475
|
+
"""Add a datum feature symbol (A, B, C, ...) on a drawing edge.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
input_data (AddDatumTagInput | None): ``label``, a point on the
|
|
479
|
+
edge it marks (``attach_x_mm``/``attach_y_mm``) and where the
|
|
480
|
+
tag goes (``x_mm``/``y_mm``), all on the sheet in mm.
|
|
481
|
+
|
|
482
|
+
Returns:
|
|
483
|
+
dict[str, Any]: ``data`` has the ``label`` as read back. A label
|
|
484
|
+
that is not one letter is ``invalid_input``; no edge at the point
|
|
485
|
+
is ``not_found``; anything other than a drawing is
|
|
486
|
+
``wrong_document_type``.
|
|
487
|
+
|
|
488
|
+
Example:
|
|
489
|
+
```python
|
|
490
|
+
await add_datum_tag({
|
|
491
|
+
"label": "A", "attach_x_mm": 120, "attach_y_mm": 95,
|
|
492
|
+
"x_mm": 120, "y_mm": 85,
|
|
493
|
+
})
|
|
494
|
+
```
|
|
495
|
+
"""
|
|
496
|
+
_supports("add_datum_tag")
|
|
497
|
+
input_data = normalize_input(input_data, AddDatumTagInput)
|
|
498
|
+
result = await adapter.add_datum_tag(
|
|
499
|
+
input_data.label,
|
|
500
|
+
(input_data.attach_x_mm, input_data.attach_y_mm),
|
|
501
|
+
input_data.x_mm,
|
|
502
|
+
input_data.y_mm,
|
|
503
|
+
)
|
|
504
|
+
require_success(result, context="Failed to add the datum tag")
|
|
505
|
+
return {
|
|
506
|
+
"status": "success",
|
|
507
|
+
"message": f"Added datum {input_data.label.strip().upper()}",
|
|
508
|
+
"data": result.data,
|
|
509
|
+
"execution_time": result.execution_time,
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
@mcp.tool()
|
|
513
|
+
async def set_sheet_scale(
|
|
514
|
+
input_data: SetSheetScaleInput | None = None,
|
|
515
|
+
) -> dict[str, Any]:
|
|
516
|
+
"""Set the active drawing sheet's scale; views using the sheet scale follow.
|
|
517
|
+
|
|
518
|
+
Args:
|
|
519
|
+
input_data (SetSheetScaleInput | None): ``numerator`` and
|
|
520
|
+
``denominator``, e.g. 1 and 2 for 1:2.
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
dict[str, Any]: ``data`` has the ``sheet`` name and new ``scale``.
|
|
524
|
+
Zero or negative values are ``invalid_input``; anything other than a
|
|
525
|
+
drawing is ``wrong_document_type``.
|
|
526
|
+
|
|
527
|
+
Example:
|
|
528
|
+
```python
|
|
529
|
+
await set_sheet_scale({"numerator": 1, "denominator": 2})
|
|
530
|
+
```
|
|
531
|
+
"""
|
|
532
|
+
_supports("set_sheet_scale")
|
|
533
|
+
input_data = normalize_input(input_data, SetSheetScaleInput)
|
|
534
|
+
result = await adapter.set_sheet_scale(
|
|
535
|
+
input_data.numerator, input_data.denominator
|
|
536
|
+
)
|
|
537
|
+
require_success(result, context="Failed to set the sheet scale")
|
|
538
|
+
return {
|
|
539
|
+
"status": "success",
|
|
540
|
+
"message": f"Sheet scale set to {(result.data or {}).get('scale')}",
|
|
541
|
+
"data": result.data,
|
|
542
|
+
"execution_time": result.execution_time,
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
@mcp.tool()
|
|
546
|
+
async def generate_drawing_package(
|
|
547
|
+
input_data: GenerateDrawingPackageInput | None = None,
|
|
548
|
+
) -> dict[str, Any]:
|
|
549
|
+
"""Create a complete A3 drawing of a part or assembly in one call.
|
|
550
|
+
|
|
551
|
+
A3 ISO frame with a filled title block (title, drawing number, material,
|
|
552
|
+
weight, drawn by/date, revision); front, plan, side and isometric views
|
|
553
|
+
in cells sized to fill the sheet at one standard scale (never stepped
|
|
554
|
+
down); model dimensions shown once and laid out in rows; bold view-name
|
|
555
|
+
captions; a NOTES block; nothing overlapping, checked on the read-back
|
|
556
|
+
geometry. Saves ``<drawing_number>.SLDDRW`` and a PDF.
|
|
557
|
+
|
|
558
|
+
The DWG NO. and TITLE are shrunk to fit their title-block boxes. For
|
|
559
|
+
smaller title-block text, pass ``title_block_text_height_mm`` (e.g.
|
|
560
|
+
``3``); a field that cannot fit even at 2 mm is reported as an
|
|
561
|
+
overlap, with that hint.
|
|
562
|
+
|
|
563
|
+
Args:
|
|
564
|
+
input_data (GenerateDrawingPackageInput | None): ``model_path`` plus
|
|
565
|
+
optional title-block fields, ``notes`` and output folder.
|
|
566
|
+
|
|
567
|
+
Returns:
|
|
568
|
+
dict[str, Any]: ``data`` has ``scale``, ``views``, ``dimensions``,
|
|
569
|
+
``overlap_check``, ``title_block`` (whether the DWG NO. and TITLE
|
|
570
|
+
fit their boxes), ``drawing_path``, ``pdf_path`` and ``steps``. The
|
|
571
|
+
call is an error, with that data, when any overlap remains. A
|
|
572
|
+
missing model is ``not_found``; a file that is not a part or
|
|
573
|
+
assembly is ``invalid_input``.
|
|
574
|
+
|
|
575
|
+
Example:
|
|
576
|
+
```python
|
|
577
|
+
await generate_drawing_package({
|
|
578
|
+
"model_path": "C:/parts/Mounting Plate.SLDPRT",
|
|
579
|
+
"title": "Mounting Plate",
|
|
580
|
+
"drawing_number": "MP-01",
|
|
581
|
+
"author": "J. Smith",
|
|
582
|
+
})
|
|
583
|
+
# Smaller DWG NO. and TITLE text in the title block:
|
|
584
|
+
await generate_drawing_package({
|
|
585
|
+
"model_path": "C:/parts/Mounting Plate.SLDPRT",
|
|
586
|
+
"title_block_text_height_mm": 3,
|
|
587
|
+
})
|
|
588
|
+
```
|
|
589
|
+
"""
|
|
590
|
+
require_capability(
|
|
591
|
+
adapter,
|
|
592
|
+
"create_standard_drawing",
|
|
593
|
+
"The active adapter does not support generate_drawing_package; "
|
|
594
|
+
"nothing was done.",
|
|
595
|
+
)
|
|
596
|
+
input_data = normalize_input(input_data, GenerateDrawingPackageInput)
|
|
597
|
+
result = await adapter.create_standard_drawing(
|
|
598
|
+
input_data.model_path,
|
|
599
|
+
input_data.output_dir,
|
|
600
|
+
input_data.title,
|
|
601
|
+
input_data.subtitle,
|
|
602
|
+
input_data.drawing_number,
|
|
603
|
+
input_data.author,
|
|
604
|
+
input_data.revision,
|
|
605
|
+
input_data.company,
|
|
606
|
+
None,
|
|
607
|
+
input_data.notes,
|
|
608
|
+
input_data.include_isometric,
|
|
609
|
+
input_data.write_model_properties,
|
|
610
|
+
input_data.title_block_text_height_mm,
|
|
611
|
+
input_data.include_bom,
|
|
612
|
+
)
|
|
613
|
+
require_success(
|
|
614
|
+
result, context="Failed to create the standard drawing", with_data=True
|
|
615
|
+
)
|
|
616
|
+
data = result.data or {}
|
|
617
|
+
return {
|
|
618
|
+
"status": "success",
|
|
619
|
+
"message": (
|
|
620
|
+
f"Created {data.get('drawing_path')} at {data.get('scale')} "
|
|
621
|
+
f"with PDF {data.get('pdf_path')}"
|
|
622
|
+
),
|
|
623
|
+
"data": data,
|
|
624
|
+
"execution_time": result.execution_time,
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return 7
|