The evidence for track F, in the shape tracks A, E and B + G used. - benchmarks/tasks_v6.py: 19 tasks, DEFAULT_MATRIX = "v6"; v5 to v2 stay addressable. arch_roundtrip draws the example plan with arch_plan_from_spec and reads it back without the spec: rooms_detect must find each labelled room within 0.1 % of its label and of the net floor computed by hand (4825 x 5750 and 3825 x 5750), the schedules must list D1, D2, W1, W2, and the plan is gated on a zero-issue critique and a finalize score of at least 90. A test removes one room label and the gate fails naming arch_room_unlabelled. - Four correctness checks, every number worked out by hand in the docstring: L / T / X junction corners and wall areas, a 4 x 5 m room labelled 20.00 m2 rather than its 21.84 m2 axis area, a door interrupting both faces, and a plan of plain lines read as 20 and 15 m2 at confidence 0.6. The published A/B report is re-run over 43 checks: 26 / 43 -> 43 / 43, zero regressed. - scripts/smoke_arch_com.py runs the whole plan once on a live seat and closes every document it opens; the CHANGELOG records what it returned. - The token suite measures a TOOL_PACKS=core,arch row; check_doc_numbers derives the task count from the v6 matrix; CLAUDE.md, both READMEs and the spec status line say what shipped. - fix(com): the first live smoke exited 1 - the room reader found 0 wall segments. Measured on AutoCAD 2026 (2026-09-23): with a makepy cache, ModelSpace.Item(i) returns the gen_py IAcadEntity wrapper (type(item).__name__ == "IAcadEntity") and item.StartPoint on an AcDbLine raises AttributeError, so every entity_list row lost its type-specific properties (28 wall LINEs, 0 with start/end). _entity_info now re-dispatches exactly that wrapper through _com_unnarrow; after the fix 38 of 53 rows carry start/end and the smoke exits 0 with both rooms at 27 743 750 / 21 993 750 mm2 and 0 critique issues. Re-dispatch measured at ~1.7 ms per entity (dynamic vs gencache alike). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
28 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
AutoCAD MCP Pro is a FastMCP 3.0 server that exposes ~166 tools, 8 resources, and 5 prompt templates for AutoCAD automation. (The exact tool count is reported dynamically by system_status / system_about — never hardcode it.) It runs with a dual-engine architecture: a live COM backend (Windows/AutoCAD required) and a headless ezdxf backend (works anywhere).
Running the Server
# STDIO mode (default, for MCP clients like Claude Desktop)
python server.py
# HTTP mode
fastmcp run server.py:mcp --transport http --port 8000
# Force a specific backend
AUTOCAD_MCP_BACKEND=ezdxf python server.py # headless file ops
AUTOCAD_MCP_BACKEND=com python server.py # live AutoCAD (Windows only)
AUTOCAD_MCP_BACKEND=auto python server.py # auto-detect (default)
Installation
# Core (ezdxf backend only)
pip install -e .
# With COM backend (Windows + AutoCAD)
pip install -e ".[com]"
# With PDF/screenshot support via matplotlib
pip install -e ".[pdf]"
# Everything
pip install -e ".[full]"
Running Tests
pytest
pytest tests/test_specific.py::test_name # single test
pytest -x # stop on first failure
Uses pytest-asyncio for async test support.
Architecture
Dual-Backend Pattern
The server uses a strategy pattern with an abstract base:
backends/contracts/— the interface, one module per domain (drawing.py,layouts.py,entity_creation.py, …). Split in v1.5.0 out of a single 1854-line ABC; some slices (premium, gdt, settings) also carry shared concrete implementations, so they are mixins rather than pure interfaces.backends/capability.py— the refusal vocabulary (UnsupportedCapabilityError) plus@capability(key, reason=...), which marks a contract method optional and gives it a default that raises that refusal. Use it only for genuine engine boundaries, never for an unfinished method;@abstractmethodstays the default. Every key used must be declared in both capability maps —tests/test_capability_contract.pyfails otherwise, which is what stops the decorator becoming a way to ship undeclared holes.backends/base.py— shared dataclasses (EntityInfo,LayerInfo,BlockInfo,DrawingInfo,CapabilityMap) and theAutoCADBackendcomposition of every contract. Import surface is unchanged:from backends.base import AutoCADBackend, UnsupportedCapabilityErrorstill works.backends/ezdxf_backend.py—EzdxfBackend: file-based DXF operations using theezdxflibrary. All sync ezdxf calls are wrapped withasyncio.to_threadvia_async(). Transactions are implemented as full DXF snapshots on an_undo_stack.backends/com_backend.py—ComBackend: live AutoCAD control viapywin32COM. All COM calls are routed through aThreadPoolExecutorwith a single thread to satisfy AutoCAD's STA (Single-Threaded Apartment) COM requirement.
Backend selection at startup (in server.py::_make_backend):
- Check
AUTOCAD_MCP_BACKENDenv var - On Windows with
auto/com: try COM first, fall back to ezdxf - On non-Windows: always use ezdxf
FastMCP Server Structure (server.py)
The mcp FastMCP instance is configured with:
- Lifespan (
autocad_lifespan): initializes the backend singleton; stores it inctx.lifespan_context["backend"] - Middleware stack:
ErrorHandlingMiddleware→AuditMiddleware(custom timing/audit log) →TimingMiddleware→LoggingMiddleware _backend(ctx)helper: retrieves the backend from lifespan context, raisesToolErrorif not ready
Tools are organized into sections (counts are indicative — system_about is authoritative):
- Drawing Management (12 tools):
drawing_*(includesdrawing_redo) - Entity Creation (14 tools):
entity_create_*(includesentity_create_table,leader_create_mleader) - Dimensions (5 tools):
dimension_* - Entity Modification (16 tools):
entity_move/copy/rotate/scale/mirror/offset/delete/array_*, corner ops (entity_trim/extend/fillet/chamfer), plus in-placeentity_edit_text(TEXT/MTEXT content/height/rotation) andentity_edit_geometry(CIRCLE/LINE/ARC — center/radius/endpoints/angles), both handle-preserving - Entity Query (9 tools):
entity_get,entity_list,entity_delete_many,selection_get,selection_window/polygon/filter, andentity_get_xdata/entity_set_xdata(typed by group code) - Layer Management (14 tools incl. 2 linetype_*):
layer_*,linetype_list,linetype_load - Block Operations (8 tools):
block_*— includesblock_define(a block from typed primitive specs + ATTDEFs, no source entities needed) - Analysis & Query (8 tools):
analysis_*— plus Batch (2), Templates (2), Validation (1) - View & Screenshot (4 tools — includes
view_zoom_and_screenshot):view_* - Transactions (3 tools):
transaction_begin/commit/rollback - System (8 tools):
system_status/get_variable/set_variable/run_command/run_lisp/about/capabilities, plusdrawing_settings(friendly units/scale/precision/osnap facade over system variables; the facade now also covers limits, grid/snap/ortho/polar, PSLTSCALE, annotation scale ("1:50"), linear/angular unit formats and the current dimstyle/textstyle, with unknown keys refused and every key reported on read) - Engineering / Deterministic CAD (8 tools):
gear_draw_*,keyway_draw_*,titleblock_apply_iso_a3,drawing_finalize - Premium meta-tools (12):
drawing_preflight,drawing_plan,drawing_critique,drawing_refine,drawing_deliver,point_from_snap/intersection/tangent,construction_*,drawing_apply_iso_layers,dimension_auto,entity_select_smart - GD&T (ISO 1101 / ASME Y14.5):
gd_frame(feature control frames),datum_feature— enforced by thegdtcritique focus - Layouts & Paper Space (12 tools): tab lifecycle
layout_list/create/set_current/delete/rename/copy, viewportsviewport_create/list/set_scale/lock/delete, andentity_change_space(CHSPACE — taggedlayoutandmodify, andlayoutwins the group priority, so it files here).drawing_export_pdftakes an optionallayoutparam and does project model content through viewports on both engines (viewport_render— v1.4 declared this COM-only and was wrong; the headless renderer only omits the viewport borders).entity_change_spaceis the mirror case — ezdxf-only (chspace), because ActiveX exposes no change-space member and the COM route is unverified. - 3D Solids (5 tools, opt-in via
ENABLE_3D=true, COM only):solid_box/cylinder/extrude/revolve/boolean— hidden from discovery and rejected while disabled; ezdxf reportssolid_3das unsupported (no headless ACIS). - P&ID (9 tools, pack
pid):pid_symbol_list/insert(ISO 10628-2 + ISA-5.1 catalogue authored in code, real blocks with TAG attributes, named ports, ACADMCP_PID XDATA),pid_line_draw(port-to-port orthogonal routing, ISA-5.1 line classes → layer/linetype/marker blocks, line numbers),pid_tag_parse(ISA-5.1 grammar), the readerpid_graphand its deliverablespid_instrument_index/line_list/equipment_list, andpid_from_spec(whole sheet, one transaction). Sixpid_*critique focuses (pid_dangling_line,pid_duplicate_tag,pid_incompatible_connection,pid_untagged_instrument,pid_illegal_tag,pid_unconnected_equipment) run underfocus=Noneand insidedrawing_finalize; they return nothing on a drawing without P&ID nodes. Two resources (autocad://pid/symbols,autocad://standards/isa51) carry the catalogue and the ISA-5.1 letter tables. Both engines implement every P&ID contract; the COM paths were executed live once byscripts/smoke_pid_com.py(AutoCAD 2026). - Styles (10 tools, pack
core):dimstyle_list/create/modify/set_current(authored ISO-25 / ANSI presets fromengineering/standards/dimstyles.py;dimstyle_modifyaccepts onlyDIM_VARIABLE_WHITELISTnames (33) and refuses out-of-range values by name before writing;changedlists only variables that moved, andrerender_required: truesays a headless DIMENSION keeps its rendered block until redrawn),textstyle_list/create/set_current(font_resolved: falseis reported, never refused — DXF stores only the name),mleaderstyle_list/create(both engines: ezdxf ondoc.mleader_styles, live through theACAD_MLEADERSTYLEdictionary — ActiveX has no MLeaderStyle collection, which is not an engine boundary, so nomleaderstylecapability key exists), anddrawing_apply_standard("iso"|"ansi")— dimstyle + text style + units + themechlayer set in one call, each item reportingcreatedor already present. - Page setup & templates (6 tools, pack
core):page_setup_list/apply(ISO 216 / ANSI Y14.1 sizes fromPAPER_SIZES; an unknown ctb is written and reportedplot_style_known: false),plot_style_list(catalog + the files actually installed on a live seat),batch_plot(every paper layout throughdrawing_export_pdf(layout=);mediabox_mmis read back from the PDF's own/MediaBox, so the sheet size is verified, not assumed),drawing_template_list(the five bundled templates:iso_a3_mech,iso_a1_arch,iso_a3_pid,ansi_b_mech,ansi_d_arch— DXF headless,.dwttwin on COM,scripts/build_templates.py --checkproves the DXFs reproducible),drawing_template_save(a.dwtrequest headlessly is refused with capabilitydwt_writenaming the DXF route).drawing_new(template=)resolves a catalog name to the bundled file and reportstemplate: {name, source}. - Environment (22 tools, pack
settings): documentsdocument_list/activate/close(both engines — the headless backend keeps a document registry;document_closerefuses to discard unsaved work unlessdiscard=True), layer stateslayer_state_save/restore/list/delete(portable: JSON chunks in anACADMCP_LAYERSTATESXRECORD that travels with the file and does not appear in AutoCAD's Layer States Manager —system_capabilities.layer_statessays so), named viewsview_named_save/restore/list(headless restore writes$VIEWCTR/$VIEWSIZEand reportsapplied: "header_only"), UCSucs_list/set/restore(non-orthogonal axes refused with the measured angle; tool coordinates stay WCS — a UCS is stored for the operator, no tool interprets input in it),system_variable_describe(the 90-entrySYSVAR_CATALOG; an unknown name answersknown: falseplus nearest names, never an error;system_set_variablenow refuses an out-of-range value for a catalogued name before writing),drawing_properties_get/set(custom properties on both engines through$CUSTOMPROPERTYTAG; the five summary fields are COM-only, capabilitydwgprops), and the live-only groupsystem_launch(live_application),system_preferences_get/set(preferences; a whitelist ofPreferences.*keys with ranges, read-only keys refused by name, writes reportold/new),user_pick_point/select,system_prompt_message(interactive_prompt; a cancelled pick returns{cancelled: true}, aCOM_CALL_TIMEOUToverrun reportstimed_out). The COM paths were executed live once byscripts/smoke_settings_com.py(AutoCAD 2026), which also built the.dwttwins. - Mechanical parts (6 tools, pack
mech):mech_part_draw,mech_view_add,mech_dimension_part,mech_hole_pattern,mech_part_from_spec,mech_part_inspect. One part model — a profile (RevolvedPartsegments or aPrismaticPartoutline) plus typed features — and one view engine that turns it into front / side / top / section / detail views with hidden lines, cut faces and ISO 129 dimensions. The model is written onto the drawing asACADMCP_MECHXDATA, somech_view_addcan add a view months later without the caller re-describing the part. A feature that cannot be projected into a view is listed inomitted: [{feature, reason}]— never dropped. - Standard parts (3 tools, pack
mech):std_part_list,std_part_insert,std_feature_draw. ISO 4014 / 4017 / 4032 / 7089 / 4762 fasteners, ISO 15 bearings with ISO 8826-1 representation, DIN 471/472 retaining-ring grooves, DIN 509 undercuts, DIN 332 centre holes and ISO 3601-2 O-ring grooves — authored tables, inserted as real blocks with attributes andACADMCP_MECHXDATA, which is what makes the parts list a read of the drawing. A size outside a table's transcribed coverage is refused by name, never interpolated. - Mechanical annotation (5 tools, pack
mech):surface_texture(ISO 21920-1, ISO 1302's older Ra form accepted),weld_symbol(ISO 2553),centre_marks(ISO 128-23),section_line(ISO 128-40 — it returns the planemech_view_add(kind="section")consumes, so the label and the view cannot disagree),hatch_material(the ISO 128-50 map; an unknown material is refused with the list, never hatched as steel). - Sheet and delivery (11 tools, pack
core):sheet_frame(ISO 5457 A4-A0 — 20 mm filing margin, 10 mm elsewhere on A3, zone grid, centring and trimming marks),titleblock_apply(ISO 7200 for every size;titleblock_apply_iso_a3is now a thin alias),revision_add,bom_extract(never modifies the drawing),bom_table(ISO 7573),balloon_add(ISO 6433, linked by XDATA so a rerun renumbers instead of duplicating),data_extract(CSV always; XLSX refused withxlsx_writewhenopenpyxlis absent),xref_attach,xref_manage,image_attach,drawing_export_dwg(COMSaveAsnatively; headless only with the ODA File Converter, otherwisedwg_writerefuses and names the install route). The COM paths of SECTIONS 21-24 were executed live once byscripts/smoke_mech_com.py(AutoCAD 2026): part, section, dimensions, an ISO 4014 block with its XDATA, the parts list read off the drawing, a linked balloon, an xref attached and detached, a real.dwg, anddrawing_critique(focus=None)clean. - Architecture (12 tools, pack
arch):arch_wall,arch_opening,arch_stair,arch_room,arch_rooms_detect,arch_schedule,arch_grid,arch_symbol,arch_catalogue_list,arch_catalogue_insert,arch_dimension_chains,arch_plan_from_spec. One plan model — a wall network (axis polyline, thickness, justification, material) with hosted doors and windows — and one engine: L / T / X junctions resolved, openings cut, cut faces hatched by material (engineering/arch/materials.py: ISO 128-50 general hatching for brick, the JIS A 0150 RC / stone / wood symbols, AutoCAD's AR-CONC / AR-SAND, AAC as a declared convention), and anything it cannot place listed inomitted: [{element, reason}], never dropped. Rooms are computed: a label's area is the planar face its point lies in, andarch_rooms_detectreads rooms from any plan (confidence 1.0 for our walls, 0.6 for plain lines) without modifying it. Schedules are a read of theACADMCP_ARCHrecords, drawn as real TABLEs. Labels and schedules speak English, or Turkish withlang="tr"(decimal comma). The COM paths were executed live once byscripts/smoke_arch_com.py(AutoCAD 2026).
Tool profiles: TOOL_PROFILE=lean|full (default full) controls the advertised surface — lean = 64 curated drafting tools (54 with TOOL_PACKS=core, which drops the three pid_*, the three mech_* and the four arch_* lean members; the five settings essentials — drawing_apply_standard, dimstyle_set_current, textstyle_set_current, page_setup_apply, batch_plot — are core-pack tools and stay; including cad_batch, since a client with a tight tool cap is exactly the client paying most per turn). Applied in the lifespan; reported by system_about. The core profile was removed in v1.5.0 (the discovery layer replaced it); TOOL_PROFILE=core falls back to full with a warning.
Tool packs: TOOL_PACKS=all|core,pid,settings,mech,arch (default all) advertises only the vertical packs a client uses; core is always on and is everything no other pack claims; settings is the 22 SECTION 20 environment tools, pid the nine P&ID tools, mech the 14 SECTION 21-23 mechanical tools, arch the 12 SECTION 25 architectural tools; SECTION 18/19/24 are core; unknown names are ignored with a warning; a lean profile intersects with the enabled packs. system_about reports tool_packs.
Tool discovery: DISCOVERY_MODE=off|search (default off). search replaces the advertised catalog with search_tools + call_tool, ranking hits over an AutoCAD command / synonym corpus (discovery/aliases.py) layered on fastmcp's BM25 index.
ISO 129 tolerances: dimension_linear / dimension_radius / dimension_diameter take tol_upper / tol_lower / tol_mode (symmetric ± / deviation +a/-b / limit / basic) and text_override (e.g. ⌀20 H7). Prefer these over hand-drawn tolerance text.
ISO 286 fits: the same dimension tools take fit="H7" (mutually exclusive with tol_*) — deviations are resolved from authored ISO 286 tables (engineering/fits.py; shafts d/e/f/g/h/js/k/m/n/p, holes D/E/F/G/H/JS, sizes 1–500 mm) against the measured nominal and the fit code is appended to the dimension text.
Drawing score: drawing_finalize returns payload["score"] — a 0-100 scalar + invalidity_ratio + A-F grade over the validator + critique union. Use it as the objective quality metric.
Measuring: never read vertices back and shoelace them — that loses 28.2% of the area on a semicircular edge, silently. analysis_measure_entity(handle) reads the real geometry, and its payload states its own accuracy: exact, flatten_tolerance, assumed_closed, self_intersecting. A HATCH reports the area it fills (outer loops minus islands) plus hatch_style; on a curved hatch edge flatten_tolerance is not the accuracy knob, because ezdxf hands boundaries over as cubic Beziers whose ~0.028% circle error is already baked in. boundary_trace / boundary_from_entities report the same number analysis_measure_entity gives for the polyline they just drew — if those ever diverge, one of them is approximating and neither says so.
Benchmark matrix: benchmarks/tasks_v6.py is the current set (19 tasks; --matrix v5 / v4 / v3 / v2 reproduce the earlier sets). The published competitor reports are pinned v1.4 runs against v2 and are never back-filled for the five v3 tasks, the v4 pid_roundtrip / page_setup_truth, the v5 mech_assembly (a flange-coupling sheet gated on a zero-issue drawing_critique(focus=None) and a finalize score of at least 90) or the v6 arch_roundtrip (a two-room plan drawn by arch_plan_from_spec, its rooms read back by arch_rooms_detect within 0.1 % of the label and of the hand-computed net floor, the schedules listing the drawn tags, gated like mech_assembly) — the chart shows them not run, not zero. benchmarks/correctness_suite.py (43 checks) is the release A/B gate: python benchmarks/compare_versions.py v1.5.1 (the published report is benchmarks/results/published/ab-v1.5.1-vs-v1.6.0-dev.json; scripts/check_doc_numbers.py names it in AB_REPORT).
Adding a New Tool
- Add the method to the matching module in
backends/contracts/—@abstractmethodif both engines must have it,@capability("key", reason=...)only if it is a real engine boundary (and then declarekeyin both capability maps, or the gate fails) - Implement it in
EzdxfBackend(backends/ezdxf_backend.py) using the_async(func)wrapper - Implement it in
ComBackend(backends/com_backend.py) usingself._com(func) - Register the tool in
server.pywith@mcp.tool(...)calling_backend(ctx).your_method(...) - Use
_dc(result)to convert dataclass returns to dicts
Key Conventions
- Entity handles: hex strings (e.g.
"1A2B"). Always returned from create/copy operations; required by all modify/query operations. - Coordinates: drawing units (mm by default); angles in degrees, counter-clockwise from X axis.
Every coordinate in or out of a tool is WCS on both engines. DXF stores
CIRCLE/ARC/LWPOLYLINE/TEXT/INSERT geometry in a per-entity frame (
extrusion);backends/ocs.pytranslates at the backend boundary, so OCS never reaches a tool. An entity in a plane tilted out of WCS XY reportsplane_normaland omits the fields xy cannot express; writing an xy into such an entity is refused withcapability: "ocs_tilted_plane"— useentity_moveinstead. - ACI colors: 1–255 for specific colors, 256=ByLayer, 0=ByBlock.
- COM backend only:
system_run_command,system_run_lisp, andview_zoom_extents/window(view ops are no-ops in ezdxf). - Screenshot: COM uses Win32 window capture (Pillow required); ezdxf renders via matplotlib.
_dc(obj): converts dataclasses to dicts recursively for JSON serialization.- Engineering layer scaffold:
drawing_newauto-bootstraps standard linetypes (CENTER, HIDDEN, PHANTOM) and engineering layers (GEOMETRY, DIM, CENTER, HIDDEN, PHANTOM, HATCH, TEXT, TITLEBLOCK). Passbootstrap=Falseto opt out. - Generic contracts added in 1.6:
block_define(name, entities, attdefs)(typed primitives + ATTDEFs, both engines;overwritereplaces contents so INSERTs keep the name) andentity_get/set_xdata(typed by group code; 255-char strings, 16 KB per entity, enforced before writing). - P&ID reader rules:
pid_graphnever modifies the drawing, reads geometry from the drawing (an INSERT's real rotation/scale) rather than the catalogue, and reportssource/confidenceper node (catalog 1.0, xdata 0.95, heuristic 0.6, inferred 0.3) — never raised by agreement between heuristics. Readstats.confidence_minbefore trusting a foreign drawing. TOOL_PACKS=all|core,pid,settings,mech,archadvertises only the packs a client uses;coreis always on;system_aboutreportstool_packs.- Layer states are ours, not AutoCAD's.
layer_state_*stores a portable snapshot in anACADMCP_LAYERSTATESXRECORD; it survives save/reopen on both engines and travels with the file, and it is not listed in AutoCAD's Layer States Manager. Said in the tool docstring, insystem_capabilities.layer_states(mode: "xrecord", reasonportable_acadmcp_xrecord;not_listed_in_autocad_layer_states_manager) and in the README. - Page-setup evidence is the PDF.
batch_plotreportsmediabox_mmparsed from the file's/MediaBox(engineering/standards/pdfinfo.py); a page setup is verified by what a printer would get, not by the setter's return value. A style or setting write reportschanged— re-setting a value to itself is not a change. - Nothing interprets coordinates in UCS.
ucs_set/ucs_restorestore and activate a UCS for the operator's benefit; every tool input and output stays WCS (the repository rule above). - The part model, not primitives: a machine part is
mech_part_drawwith a segment list or an outline plus typed features — never hand-drawn lines. The same rule as gears and keyways, for the same reason. - Standards tables are transcribed, never derived: every row under
engineering/mech/standards/carries aSOURCEnaming standard, edition and table, and a size outside the coverage is refused by name.engineering/mech/standards/materials.pystates which of its rows are ISO 128-50, which are ASME Y14.2M viaacadiso.pat, and which (concrete, wood) have no mechanical standard at all. - Six
mech_*critique focuses (mech_missing_centreline,mech_unhatched_section,mech_view_misaligned,mech_duplicate_dimension,mech_thread_unrepresented,mech_bom_balloon_mismatch) run underfocus=Noneand insidedrawing_finalize; they return nothing on a drawing with noACADMCP_MECHpayload.mech_view_misalignedchecks the projection axis and not the quadrant, andmech_duplicate_dimensioncatches two dimensions in the same place rather than a semantic restatement — both narrowings are in the module docstring, because the payload carries no projection angle and no engine exposes what a dimension measured. - Live ActiveX will not create a layer on assignment (
entity.Layer = "X"on a missing layer raisesKey not found; ezdxf creates it silently). Every mechanical/annotation draw path ensures its target layers before the first entity — a new path must do the same, or it passes every headless test and dies live. - A diameter's text stands beyond the FIRST chord point on both engines (the ActiveX
AddDimDiametricrule, measured);dimension_diameter(x1, y1, x2, y2)annotates on the(x1, y1)side. - The plan model, not lines: a wall is
arch_wall(an axis, a thickness, a justification, a material) and a door or window is hosted in it byarch_opening— never hand-drawn lines. The engine resolves the junctions and cuts the openings; what it cannot place is inomitted. - Areas are measured, never typed: a room's area is the planar face its label point lies in, written into the label and the
roomrecord byarch_room; no tool accepts an area from its caller, andarch_plan_from_specrefuses a typedareaby path. - Plan text follows the plot scale:
scaleis a denominator (50 = 1:50) and a paper heighthis drawn ath × scaledrawing units — no annotative objects.lang="tr"switches words and the decimal separator; any other language is refused with the list. - Three
arch_*critique focuses (arch_room_unlabelled,arch_wall_gap— two wall ends within 50 mm that do not join,arch_opening_clash) run underfocus=Noneand insidedrawing_finalize; they return nothing on a drawing with noACADMCP_ARCHrecord. - Nominal, not standard: the furniture and sanitary blocks carry nominal catalogue dimensions and say so; the stair tool reports the Blondel relation (2R + G within 600–650 mm) as a design rule, never as a building code; the
archlayer colours are a repository convention (ISO 13567 fixes names, not colours). - Production drawings: For real engineering output, use the
engineering/package primitives via thegear_*/keyway_*/titleblock_*MCP tools — do NOT hand-draw teeth/keyways/sections with rawentity_create_*calls. Always end withdrawing_finalizefor the 8-step validator.
Premium Drawing Rules
These rules are non-negotiable for production engineering output.
- Plan before draw: Call
drawing_plan(intent, scale, sheet)before anyentity_create_*. The returned PlanSpec is held by the backend and replayed bydrawing_critiqueat finalize time. - No coordinate guessing: Never compute snap points (endpoints, midpoints,
intersections, perpendicular feet) from memory. Use
point_from_snap(handle, "end"|"mid"|"center"|"quad"|"perp"|"near", ref_x, ref_y). - Layer discipline: All geometry on engineering layers (GEOMETRY, HIDDEN,
CENTER, ...). Construction geometry on
CONSTRUCTIONlayer (color 250, lightest weight); wipe withconstruction_clear()before finalize. - Lineweights are ISO 128: 0.13/0.18/0.25/0.35/0.50/0.70/1.00/1.40/2.00 mm
only. Use
drawing_apply_iso_layers("mech"|"pid"|"iso13567")to bootstrap correct lineweights per layer; never set lineweight manually outside this set. - Corners must be explicit: Two intersecting lines that should meet at a
sharp corner must use
entity_trim(withkeep_x/keep_y) — leaving overshoot is reported asuntrimmed_cornerbydrawing_critique. Rounded/beveled corners useentity_fillet/entity_chamfer. - Dimensions via auto: Prefer
dimension_auto(handles, "chain"|"baseline"| "ordinate")over individualdimension_linearcalls. Manual dimensions only for special cases (leader notes, ordinate origins). - Critique-then-finalize:
drawing_critique(focus=[...])must return zero issues beforedrawing_finalize. Available focuses (closed enum):iso128,layer_color,dim_overlap,untrimmed_corner,duplicate_entities,construction_left. Passfocus=Nonefor all. - Meta-tools over raw: When a meta-tool exists for a workflow, use it; do not reimplement with low-level primitives. Same rule as the engineering- primitives line above.
Standard Premium Workflow (template)
1. plan = drawing_plan(intent, sheet_size="A3", scale=1.0)
2. drawing_apply_iso_layers("mech") # or "pid", "iso13567"
3. construction_xline(...) # scaffolding (optional)
4. entity_create_line / circle / ... # main geometry on engineering layers
5. entity_trim / fillet / chamfer # close corners explicitly
6. handles = entity_select_smart({...}) # avoid handle-memorization
7. dimension_auto(handles, style="chain") # ISO 129 dims
8. issues = drawing_critique(focus=None) # must be []
9. construction_clear() # wipe scaffolding
10. drawing_finalize(save_path=..., screenshot_path=...)