Build123d Parametric Mounting Bracket
Source: examples/mcp/build123d_parametric_mounting_bracket.py
Introduction
Inspect and exercise the MCP-backed Build123d mounting-bracket problem.
Technical Implementation
This page is generated from the top-of-file module docstring and the example source code. The full script is included below for direct inspection.
1from __future__ import annotations
2
3import asyncio
4import json
5from collections.abc import Sequence
6from textwrap import dedent
7from typing import TYPE_CHECKING, Any, cast
8
9import design_research_problems as derp
10
11if TYPE_CHECKING:
12 from mcp.server.fastmcp import FastMCP
13
14PROBLEM_ID = "mcp_build123d_parametric_mounting_bracket"
15SERVER_NAME = "build123d-mounting-bracket-demo"
16STARTER_SCRIPT = dedent(
17 """
18 from build123d import Align, BuildPart, Box, Cylinder, Location, Locations, Mode, fillet
19
20 WIDTH = 80.0
21 DEPTH = 40.0
22 BASE_THICKNESS = 6.0
23 FLANGE_HEIGHT = 40.0
24 FLANGE_THICKNESS = 6.0
25 HOLE_DIAMETER = 6.0
26
27 with BuildPart() as part:
28 Box(WIDTH, DEPTH, BASE_THICKNESS, align=(Align.CENTER, Align.CENTER, Align.MIN))
29 flange_center_y = -DEPTH / 2.0 + FLANGE_THICKNESS / 2.0
30 with Locations((0.0, flange_center_y, BASE_THICKNESS)):
31 Box(WIDTH, FLANGE_THICKNESS, FLANGE_HEIGHT, align=(Align.CENTER, Align.CENTER, Align.MIN))
32
33 for x_mm, y_mm in ((-30.0, -10.0), (30.0, -10.0), (-30.0, 10.0), (30.0, 10.0)):
34 with Locations((x_mm, y_mm, 0.0)):
35 Cylinder(
36 radius=HOLE_DIAMETER / 2.0,
37 height=BASE_THICKNESS,
38 align=(Align.CENTER, Align.CENTER, Align.MIN),
39 mode=Mode.SUBTRACT,
40 )
41
42 for z_mm in (16.0, 36.0):
43 with Locations(Location((0.0, flange_center_y, z_mm), (90.0, 0.0, 0.0))):
44 Cylinder(
45 radius=HOLE_DIAMETER / 2.0,
46 height=FLANGE_THICKNESS,
47 align=(Align.CENTER, Align.CENTER, Align.CENTER),
48 mode=Mode.SUBTRACT,
49 )
50
51 target_y = -DEPTH / 2.0 + FLANGE_THICKNESS
52 inner_edges = [
53 edge
54 for edge in part.edges()
55 if abs(edge.center().Y - target_y) <= 1e-6 and abs(edge.center().Z - BASE_THICKNESS) <= 1e-6
56 ]
57 radius = 4.0
58 while inner_edges and radius >= 0.5:
59 try:
60 fillet(inner_edges, radius)
61 break
62 except ValueError:
63 radius -= 0.5
64
65 result = part.part
66 """
67).strip()
68
69
70def _extract_structured_payload(payload: object) -> dict[str, object]:
71 """Normalize one FastMCP ``call_tool`` payload into a dictionary.
72
73 Args:
74 payload: Raw payload returned by ``server.call_tool``.
75
76 Returns:
77 Parsed dictionary payload when available.
78 """
79 if isinstance(payload, tuple):
80 _, structured = payload
81 if isinstance(structured, dict):
82 return cast(dict[str, object], structured)
83 return {}
84
85 content = cast(Sequence[Any], payload)
86 if not content:
87 return {}
88 text = getattr(content[0], "text", None)
89 if not isinstance(text, str):
90 return {}
91 parsed = json.loads(text)
92 if isinstance(parsed, dict):
93 return cast(dict[str, object], parsed)
94 return {}
95
96
97def create_server() -> FastMCP:
98 """Create the MCP proxy server for this packaged problem.
99
100 Returns:
101 Configured FastMCP server instance.
102 """
103 problem = derp.get_problem(PROBLEM_ID)
104 return problem.to_mcp_server(server_name=SERVER_NAME, include_citation=False)
105
106
107def _is_expected_build123d_runtime_unavailable_error(exc: BaseException) -> bool:
108 """Return whether one exception indicates optional build123d runtime absence.
109
110 Args:
111 exc: Exception raised while exercising the Build123d MCP tools.
112
113 Returns:
114 ``True`` when the error matches expected optional-backend absence paths.
115 """
116 message = str(exc).lower()
117 return (
118 "build123d is not installed" in message
119 or "tcl wasn't installed properly" in message
120 or "upstream mcp tool 'evaluate_scripted_part' failed" in message
121 )
122
123
124async def run_summary(server: FastMCP) -> dict[str, object]:
125 """Collect a short runtime summary from the wrapped MCP server.
126
127 Returns:
128 Summary payload with discovered tools, resources, and final-answer echo.
129 """
130 tools = await server.list_tools()
131 resources = await server.list_resources()
132
133 try:
134 status_payload = await server.call_tool("backend_status", {})
135 status = _extract_structured_payload(status_payload)
136 eval_payload = await server.call_tool(
137 "evaluate_scripted_part",
138 {"script": STARTER_SCRIPT, "result_name": "result", "include_script": False},
139 )
140 evaluation = _extract_structured_payload(eval_payload)
141 last_payload = await server.call_tool("describe_last_script_result", {"include_script": False})
142 last_result = _extract_structured_payload(last_payload)
143 submit_final_payload = await server.call_tool(
144 "submit_final",
145 {"answer": "Submitted a Build123d script that generates and validates the bracket geometry."},
146 )
147 submit_final = _extract_structured_payload(submit_final_payload)
148 finally:
149 close_upstream = getattr(server, "aclose_upstream_session", None)
150 if callable(close_upstream):
151 await close_upstream()
152
153 return {
154 "problem_id": PROBLEM_ID,
155 "tool_count": len(tools),
156 "tool_names": sorted(tool.name for tool in tools),
157 "resource_uris": sorted(str(resource.uri) for resource in resources),
158 "backend_available": status.get("available", False),
159 "result_is_valid": evaluation.get("is_valid"),
160 "volume_mm3": evaluation.get("volume_mm3"),
161 "matches_nominal_envelope": cast(dict[str, object], evaluation.get("constraint_checks", {})).get(
162 "matches_nominal_envelope"
163 ),
164 "last_result_name": last_result.get("result_name"),
165 "submit_final": submit_final.get("answer", "<missing>"),
166 }
167
168
169def main() -> int:
170 """Run the Build123d MCP wrapper demo.
171
172 Returns:
173 Process exit code.
174 """
175 problem = derp.get_problem(PROBLEM_ID)
176 print("Problem id:", problem.metadata.problem_id)
177 print("Upstream command:", problem.command)
178
179 try:
180 server = create_server()
181 summary = asyncio.run(run_summary(server))
182 except (derp.MissingOptionalDependencyError, ModuleNotFoundError) as exc:
183 print(exc)
184 print("Install the optional MCP dependency with: pip install design-research-problems[mcp]")
185 return 0
186 except (FileNotFoundError, RuntimeError) as exc:
187 print(f"build123d backend startup failed: {exc}")
188 return 0
189 except Exception as exc:
190 if _is_expected_build123d_runtime_unavailable_error(exc):
191 print(f"build123d backend startup failed: {exc}")
192 return 0
193 raise
194
195 print("Tool count:", summary["tool_count"])
196 print("Tools:", ", ".join(cast(list[str], summary["tool_names"])))
197 print("Resources:", ", ".join(cast(list[str], summary["resource_uris"])))
198 print("Backend available:", summary["backend_available"])
199 print("Result valid:", summary["result_is_valid"])
200 print("Volume (mm^3):", summary["volume_mm3"])
201 print("Matches nominal envelope:", summary["matches_nominal_envelope"])
202 print("Last result name:", summary["last_result_name"])
203 print("submit_final answer:", summary["submit_final"])
204 return 0
205
206
207if __name__ == "__main__":
208 raise SystemExit(main())
Expected Results
Run Command
PYTHONPATH=src python3 examples/mcp/build123d_parametric_mounting_bracket.py
Run the command shown below from repository root. Output should summarize the problem setup, a baseline solution, or diagnostic values relevant to this example.