docs: release v1.1.0 — accurate counts, changelog, audit report

- pyproject: version 1.0.0 -> 1.1.0
- README: 110 tools / 5 resources (was 87 / 6), 318 tests, new "Closed-Loop
  Quality Gate" highlight, Engineering + Premium tool categories, refreshed
  roadmap (1.1 shipped; 1.2 closed-loop moat; 1.3 ISO production).
- CHANGELOG: comprehensive [1.1.0] entry (Added/Changed/Fixed/Security).
- CLAUDE.md: drop removed CommandResult dataclass; ~110 tool count.
- docs/analysis/03-AUDIT-VERIFICATION.md: the verified audit + fix plan.
This commit is contained in:
Umutcan Edizsalan
2026-06-19 08:37:08 +03:00
parent 32a68be919
commit 2c2fba1459
5 changed files with 317 additions and 35 deletions
+51 -25
View File
@@ -7,36 +7,62 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
_Next: 1.2 — closed-loop validation moat (scalar drawing-score, pre-plan clarification, iterative refiner + transaction-stack isolation)._
## [1.1.0] — 2026-06-19
Correctness, cross-backend parity, and an **enforced** quality gate, landed across four audited
sprints (see `docs/analysis/`). 318 tests, ruff-clean.
### Added
- Security module (`security.py`) with path traversal protection and command injection prevention
- Centralized configuration (`config.py`) with environment variable support
- `.env.example` for environment variable documentation
- Path validation for all file operations (`drawing_open`, `drawing_save`, `drawing_export_*`)
- Command sanitization for `system_run_command` and `system_run_lisp`
- Ruff linting configuration in `pyproject.toml`
- Pre-commit hooks (`.pre-commit-config.yaml`) for automated code quality
- pytest-cov for test coverage reporting
- Comprehensive test suite: 95 tests across 7 test files
- `test_entity_creation.py` — 13 entity creation tests
- `test_drawing.py` — 8 drawing operation tests
- `test_dimensions.py` — 5 dimension tests
- `test_layer_block.py` — 12 layer and block tests
- `test_analysis.py` — 12 analysis and modification tests
- `test_security.py` — 29 security tests
- GitHub Actions CI workflow (lint + test on Python 3.11/3.12)
- README.md with full documentation
- CHANGELOG.md
- `.gitignore`
- **Premium drafting workflow** (shared across both backends): `drawing_plan`, `drawing_critique`
(ISO-128 focuses), `point_from_snap`, `drawing_apply_iso_layers`, `dimension_auto`,
`entity_select_smart`, `construction_xline` / `construction_clear`.
- **Deterministic geometry** for exact OSNAP coordinates: `point_intersection`
(line/line, line/circle, circle/circle) and `point_tangent` (external point → circle).
- **Engineering / deterministic CAD layer**: involute gear front view + section A-A,
DIN 6885 keyed bore, ISO A3 title block, and the 8-step `drawing_finalize` validator.
- **HTTP bearer-token auth**: `StaticTokenVerifier` wired into FastMCP when `MCP_AUTH_TOKEN` is set.
- Mocked-COM test harness (`tests/test_com_backend.py` + Sprint-3/4 suites) — COM logic is now
regression-tested on Linux CI without a live AutoCAD.
- `entity_create_mtext` rotation parameter; `dimension_auto` layer override; `bounding_box` /
ARC `length` / TEXT-MTEXT `rotation`+`char_height` on both backends; `BlockInfo.description`.
- Security module (`security.py`), centralized config (`config.py`), `.env.example`, path
validation, command/LISP sanitization, ruff config, pre-commit hooks, pytest-cov, GitHub Actions CI.
### Changed
- All silent `except Exception: pass` blocks now include logging
- Removed unused imports across all source files
- Renamed ambiguous variable `l` to `lyr` in layer iteration code
- Import sorting organized with ruff isort rules
- **`drawing_finalize` now enforces the premium critique** in addition to the structural validator:
leftover construction geometry, non-ISO-128 lineweights, untrimmed corners, duplicate entities,
and dimension overlap block the gate (was advisory-only). `strict_critique=True` fails on warnings too.
- **`drawing_save_as` derives the on-disk format from the file extension** — `part.dxf` writes DXF,
not DWG. ezdxf refuses to mislabel a `.dwg` file.
- Dimension and construction layers resolve from the active layer set (iso13567 → `M-DIMEN-T-N` /
`M-CONST-E-N`, not the hardcoded `DIM` / `CONSTRUCTION`).
- Premium meta-tools lifted into the shared `AutoCADBackend` base class (single source of truth).
- Dead code removed (`section.py`, `generate_tooth_profile`, `CommandResult`, `set_layer_active`, …).
### Fixed
- `_detect_autocad_running` now logs detection failures instead of silently swallowing them
- Resource functions now log errors before returning error JSON
- ezdxf `dimension_aligned` / `dimension_angular` raised `TypeError` (wrong ezdxf 1.4 args) — fixed.
- Full-circle polar array placed a duplicate copy over the original — fixed (divisor = count).
- `entity_offset` ignored `side_x`/`side_y` on both backends — now honored (and COM no longer leaks extras).
- COM `entity_create_hatch` built an associative hatch then deleted its boundary — now non-associative.
- Lineweight mm-vs-hundredths truncation wiped ISO-128 weights — fixed via `normalize_lineweight`.
- **`view_screenshot` / `drawing_export_pdf` could SIGSEGV** (matplotlib GUI backend in a worker thread)
— now render headless via `Agg` (`Figure` + `FigureCanvasAgg`).
- `entity_select_smart` `length_range` silently rejected all ARCs (no `length`) — fixed.
- Gear tooth profile self-overlapped for high tooth counts (z ≥ ~42); section view drew duplicate
bore lines; validator keyway heuristic was a permanent false-negative — all fixed.
- COM `system_run_lisp` always reported `"nil"`; `system_set_variable` didn't coerce numeric sysvars
— fixed. `system_about` tool groups / `_registered_tool_count` no longer drift or surface `-1`.
- `drawing_new` bootstrap failures now surface as `degraded` instead of reporting success.
### Security
- HTTP remote-bind guard now fires on **every** launch path (including `fastmcp run server.py:mcp`),
not only the `__main__` block — closing an anonymous-remote-bind gap.
- AutoLISP allowlist bypass-vector regression tests (newline injection, symbol aliasing, `vla*`/`acet-*`,
`c:` custom commands).
- COM apartment leak bounded (`CoUninitialize` on teardown); transaction commit/rollback and
`system_run_lisp` now respect the CMDACTIVE guard.
## [1.0.0] — 2026-03-01
+2 -2
View File
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
AutoCAD MCP Pro is a FastMCP 3.0 server that exposes 87 tools, 6 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).
AutoCAD MCP Pro is a FastMCP 3.0 server that exposes ~110 tools, 5 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
@@ -53,7 +53,7 @@ Uses `pytest-asyncio` for async test support.
The server uses a strategy pattern with an abstract base:
- `backends/base.py` — `AutoCADBackend` ABC defining the full interface + shared dataclasses (`EntityInfo`, `LayerInfo`, `BlockInfo`, `DrawingInfo`, `CommandResult`)
- `backends/base.py` — `AutoCADBackend` ABC defining the full interface + shared dataclasses (`EntityInfo`, `LayerInfo`, `BlockInfo`, `DrawingInfo`)
- `backends/ezdxf_backend.py` — `EzdxfBackend`: file-based DXF operations using the `ezdxf` library. All sync ezdxf calls are wrapped with `asyncio.to_thread` via `_async()`. Transactions are implemented as full DXF snapshots on an `_undo_stack`.
- `backends/com_backend.py` — `ComBackend`: live AutoCAD control via `pywin32` COM. All COM calls are routed through a `ThreadPoolExecutor` with a single thread to satisfy AutoCAD's STA (Single-Threaded Apartment) COM requirement.
+16 -7
View File
@@ -3,7 +3,7 @@
**Production-grade AutoCAD automation for the Model Context Protocol.**
Dual-engine. Battle-tested. Model-agnostic.
87 tools · 6 resources · 5 prompt templates · COM + ezdxf backends · Python 3.11+ · MIT
v1.1.0 · 110 tools · 5 resources · 5 prompt templates · COM + ezdxf backends · Python 3.11+ · MIT
---
@@ -15,7 +15,7 @@ My day job at **Anka-Makine** revolves around intensive AutoCAD work — product
After months of leaning on this server inside my own daily workflow — and watching it shave hours off my week without a single misstep on critical drawings — I decided it deserved a public release. It works. It is fast. It stays out of the way.
The model on the other end of the wire does not matter. The Model Context Protocol is the contract; AutoCAD MCP Pro is one well-typed implementation of that contract. Any MCP-aware client — and the LLM behind it — sees the same 87 tools.
The model on the other end of the wire does not matter. The Model Context Protocol is the contract; AutoCAD MCP Pro is one well-typed implementation of that contract. Any MCP-aware client — and the LLM behind it — sees the same 110 tools.
I will keep this repository actively maintained as my own use of it evolves, and **a public benchmark suite is on the way** — I want to give you numbers, not adjectives.
@@ -63,7 +63,7 @@ AutoCAD MCP Pro takes a different stance:
- **ezdxf backend**: headless DXF file operations powered by [ezdxf](https://github.com/mozman/ezdxf). Works on every platform, ideal for batch workloads and CI pipelines.
- Automatic backend selection, with a clean override via `AUTOCAD_MCP_BACKEND`.
- **87 Tools, 11 Categories**
- **110 Tools, 12 Categories**
- Drawing management — `drawing_new`, `drawing_open`, `drawing_save`, `drawing_save_as`, `drawing_export_dxf`, `drawing_export_pdf`, `drawing_purge`, `drawing_audit`, `drawing_undo`, `drawing_redo`, `drawing_close`
- Entity creation — line, circle, arc, polyline, rectangle, text, mtext, hatch, spline, ellipse, point, block reference, batch create
- Dimensions — linear, aligned, angular, radius, diameter
@@ -75,14 +75,21 @@ AutoCAD MCP Pro takes a different stance:
- Transactions — begin, commit, rollback, with disk-backed snapshots
- System — status, get/set variables, run command, run AutoLISP, about
- Templates and validation — apply standard layer templates, validate drawings against rule sets
- Engineering / deterministic CAD — involute gear front view + section A-A, DIN 6885 keyed bore, ISO A3 title block, and the 8-step `drawing_finalize` gate
- Premium drafting workflow — `drawing_plan`, deterministic OSNAP (`point_from_snap` / `point_intersection` / `point_tangent`), `drawing_apply_iso_layers`, `dimension_auto`, `entity_select_smart`, `drawing_critique`, `construction_*`
- **Production-Grade Plumbing**
- FastMCP 3.0 lifespan-managed backend singleton
- Middleware stack: error handling, audit logging, timing, request logging
- Structured progress reports for long-running operations (`drawing_open`, exports, batch ops)
- 104 tests across drawing, entity, dimension, layer, block, analysis, batch/template, security suites
- 318 tests across drawing, entity, dimension, layer, block, analysis, batch/template, engineering, premium, mocked-COM, and security suites
- Ruff-clean codebase, GitHub Actions CI
- **Closed-Loop Quality Gate**
- `drawing_finalize` runs **both** the 8-step structural validator **and** the premium ISO-128 critique (lineweights, layer colors, untrimmed corners, duplicate entities, leftover construction, dimension overlap) — leftover scaffolding or a non-standard lineweight blocks the gate instead of slipping through.
- Deterministic geometry: `point_from_snap` / `point_intersection` / `point_tangent` give the agent exact OSNAP coordinates instead of guessed ones — the single biggest source of LLM drawing errors.
- Cross-backend parity: the same `EntityInfo.properties` contract, save-format-from-extension, offset side selection, and ARC length on **both** engines.
- **Security-First Defaults**
- Path traversal & system-directory protection
- AutoLISP allowlist with regression tests for known bypass patterns (`vla-SendCommand`, `acet-*`, alias tricks, `funcall` indirection, `c:` custom commands, error-handler hijacks)
@@ -117,7 +124,7 @@ pip install -e ".[full]"
python server.py
```
That is it. The server starts in STDIO mode and your MCP client will discover all 87 tools.
That is it. The server starts in STDIO mode and your MCP client will discover all 110 tools.
For headless / CI workflows:
@@ -363,8 +370,10 @@ autocad-mcp/
## Roadmap
- [x] **1.0** — Initial public release
- [x] **1.1** — Security hardening (LISP allowlist, transaction snapshots on disk, COM timeouts, HTTP bind guard, dynamic tool count)
- [ ] **1.2** — Public benchmark suite (`benchmarks/` with reproducible runner)
- [x] **1.1** — Correctness, cross-backend parity & the enforced quality gate: `drawing_finalize` now runs the premium ISO-128 critique; deterministic geometry (`point_intersection`/`point_tangent`); dimension/save-format/polar-array/offset fixes across both engines; COM robustness (CoUninitialize, transaction & LISP guards); security hardening (HTTP bind guard on every launch path, LISP-allowlist bypass tests); mocked-COM test harness. **318 tests.**
- [ ] **1.2** — Closed-loop validation moat: scalar drawing-score, pre-plan clarification pass, and an iterative critique→repair→re-critique refiner (with dedicated transaction-stack isolation)
- [ ] **1.3** — ISO production: 2D GD&T (ISO 1101) feature-control frames, ISO 129 dimension tolerances/fits, ISO-25 dimension styles
- [ ] **1.4** — Public benchmark suite (`benchmarks/` with reproducible runner)
- [ ] **1.3** — Backend `capabilities()` map and per-tool enablement (e.g. hide `system_run_command` automatically on ezdxf)
- [ ] **1.4** — Module split for `server.py` and a clean `services/` orchestration layer
- [ ] **1.5** — Windows CI matrix with mocked COM backend; full coverage report
+247
View File
@@ -0,0 +1,247 @@
<!--
Generated by the AUDIT-AND-PLAN workflow (wf_f688392c-339), 2026-06-18.
Phase 1 (11 finders) completed; Phase 2 (adversarial verify) + Phase 3 (synthesis) hit the
session token limit, so verification + synthesis were completed inline by the main agent,
with 6 of the highest-impact findings re-confirmed by direct code reading (100% confirm rate).
Pairs with 01-DIAGNOSTICS.md (the pre-Sprint-1 baseline) and degisilik.md (Sprint-1 changelog).
-->
# AUDIT + VERIFICATION — AutoCAD MCP Pro (post Sprint-1)
This audit (a) verifies the 4 Sprint-1 commits, (b) re-confirms which of the 40 baseline
diagnostics are still open after those commits, and (c) adds fresh-sweep findings — then
sequences everything into a fix plan. Line numbers reflect the **current** working tree
(branch `fix/http-auth`, 233 tests green, ruff clean).
---
## A. Verdict on the 4 Sprint-1 commits
| Commit | Item | Verdict |
|---|---|---|
| `2b80d00` I16 | **N1** COM hatch associativity→False | ✅ correct & complete |
| | **R9** polar-array 360° divisor | ✅ correct (divisor=count on full ring; matches COM total-instance count) |
| | **R12 / R19** ezdxf aligned/angular dim API | ✅ correct (matches ezdxf 1.4.3 sig; xfail removed, passes strict) |
| | dead-code removal (section.py, generate_tooth_profile, CommandResult, _ACI_NAMES, set_layer_active, ensure_linetypes, _batch_mode) | ✅ clean — zero dangling Python refs; only docs still mention them |
| | **N12 / N13 / N14** dead helpers | ✅ removed |
| | **R13** entity_offset side_x/side_y | ⚠️ **incomplete** — honored in ezdxf only; COM still ignores it |
| | **R18** drawing_new bootstrap surfacing | ⚠️ **incomplete** — only a *total* exception → `degraded`; per-item `failed:` swallowed → still `bootstrap.ok=True` |
| | **N2** save format | ⚠️ **partial** — `drawing_finalize` derives fmt from extension ✅, ezdxf refuses `dwg` ✅, but the standalone `drawing_save_as` tool still defaults `fmt='dwg'` and ignores the extension |
| `0f1f051` I9 | **point_intersection / point_tangent** | ✅ math fully correct (LINE-LINE, LINE-CIRCLE, CIRCLE-CIRCLE, tangent perpendicularity verified numerically); 13 tests assert real invariants |
| `82c9fe0` I12 | StaticTokenVerifier wiring | ✅ `auth=_auth` correctly passed; token never logged; reject works at runtime |
| | **Guard enforcement** | ⚠️ **gap** — `_validate_http_bind` runs only in `__main__`; the documented `fastmcp run` import path skips it (see NEW-AUTH-1) |
| `f2f3fa6` I19 | `_safe_send_command` coverage | ✅ meaningful (var-restore, timeout, new-handle faithful) |
| | normalize_lineweight table | ⚠️ omits the ambiguous bare ints `1`/`2`; COM transactions + CMDACTIVE guard still untested |
**Bottom line:** the Sprint-1 fixes that landed are correct where they landed. The recurring
defect pattern is **one-backend / partial fixes**: R13 (ezdxf-only), R18 (total-failure-only),
N2 (finalize-only), I12 guard (`__main__`-only). Cross-backend parity is the theme of the open work.
---
## B. Confirmed open issues (ranked)
Severity recalibrated by impact-on-common-path. `★` = re-confirmed by direct code read in this session.
### HIGH
- **NEW-screenshot-1 ★** — `view_screenshot` / `drawing_export_pdf` build a `matplotlib.pyplot`
figure **inside an `asyncio.to_thread` worker without forcing the `Agg` backend**
(`ezdxf_backend.py:1420-1444`). On a host whose default MPL backend is GUI (Qt/Tk) this can
**hard-crash the process (SIGSEGV)** — this is the real cause of the `pytest` warning
"Starting a Matplotlib GUI outside of the main thread will likely fail." `drawing_finalize`
calls `view_screenshot`, so finalize can take down the server.
**Fix:** render via `matplotlib.figure.Figure` + `FigureCanvasAgg` (no `pyplot` global state),
or `matplotlib.use("Agg")` before the pyplot import. Headless-testable.
- **I15 ★** — `drawing_finalize` (`server.py:2333`) runs **only** `DrawingValidator().run()` and
**never** `backend.drawing_critique` / `run_critique`. The 6 premium critique focuses
(iso128, layer_color, untrimmed_corner, duplicate_entities, construction_left, dim_overlap)
are **not part of the finalize gate** — a drawing passes finalize with untrimmed corners,
non-ISO-128 lineweights, leftover construction scaffolding, duplicate lines. The product's
entire differentiator ("critique-then-finalize", CLAUDE.md) is advisory, not enforced.
**Fix:** run `run_critique(focus=None)` inside `drawing_finalize`, merge into the report,
gate (or warn) on critique errors. This is the moat.
- **NEW-AUTH-1 ★** — `_validate_http_bind(host)` is called **only** in the
`if __name__ == "__main__"` block (`server.py:2940`). The documented launch command
`fastmcp run server.py:mcp --transport http --host 0.0.0.0` *imports* the module and never
runs `__main__`, so the remote-bind guards (refuse remote bind without `ALLOW_REMOTE_HTTP`,
refuse without `MCP_AUTH_TOKEN`) **never fire** under the recommended launch. With the token
unset, the server binds to `0.0.0.0` anonymously with nothing to stop it.
**Fix:** enforce the bind check at import time / in the FastMCP lifespan, not only in `__main__`.
- **NEW-gear-roottooth + R21 ★** — `generate_involute_flank` (`gear.py:40-50`) prepends
`(root_r, 0)` while the involute starts at `(base_r, 0)` — both on y=0, so for `root_r < base_r`
the root→base join is a **straight radial segment, not a trochoid** (R21). Worse, for
`root_r ≥ base_r` (teeth ≥ ~42 @20° PA, within the teeth≥6 API limit) no root point is added and
**the flank starts at `base_r` *inside* the root circle** → the tooth profile dips below the root
arc and the closed outline **self-overlaps** (z=50 reproduced: outline points inside root_r).
**Fix:** clamp the flank start to `max(base_r, root_r)`; skip/relocate the root arc when
`root_r ≥ base_r`; ideally a true trochoid below the base circle.
### MED
- **R4 ★** — `dim_overlap` critique is a **guaranteed no-op**: `_check_dim_overlap`
(`critique.py:199-201`) reads `text_position`/`defpoint`/`insertion_point` which **no backend
populates** for dimensions; it also filters `entity_list(layer_filter="DIM")` (`critique.py:192`)
which misses the iso13567 dim layer. **Fix:** populate a dim reference point in both extractors
(or fall back to bbox center), and resolve the dim layer from the active set.
- **N4** — `dimension_auto` hardcodes `layer="DIM"` (`base.py:948/962/970/977`); construction ops
hardcode `"CONSTRUCTION"` (`base.py:869/880`); `_check_construction_left` also hardcodes
`"CONSTRUCTION"` (`critique.py:99`). For the **iso13567** set (dim layer `M-DIMEN-T-N`,
construction `M-CONST-E-N`) dims land on the wrong layer and leftover scaffold is never caught.
**Fix:** resolve dim/construction layer from the active `LAYER_SET_REGISTRY` keyed on `PlanSpec`.
- **R13-COM / NEW-offset-1 ★** — `ComBackend.entity_offset` (`com_backend.py:860-874`) accepts
`side_x/side_y` but never reads them (`ent.Offset(distance)` only) → COM offsets to a
backend-decided side while ezdxf honors the hint. Same tool, opposite results.
Also: only `offset_ent[0]` is kept; extra entities `Offset` returns leak. **Fix:** honor side in COM.
- **N2 (tool)** — standalone `drawing_save_as` MCP tool defaults `fmt='dwg'` and doesn't derive from
the extension, so `drawing_save_as('part.dxf')` raises on ezdxf / writes DWG bytes into a `.dxf`
name on COM. **Fix:** derive fmt from the path extension in the tool too (mirror finalize).
- **R5** — COM executor calls `CoInitialize()` but **`CoUninitialize` appears nowhere**; on every
`SendCommand` timeout the stuck STA thread + its COM apartment leak (a fresh executor is built),
and `disconnect()` uses the same `wait=False`. Long live-AutoCAD sessions exhaust resources.
**Fix:** worker-side `try/finally` `CoUninitialize`; bound abandoned executors.
- **R16** — COM `transaction_begin/commit/rollback` set `_transaction_active` *after* the await
with no `try/finally` (`com_backend.py:1398/1407/1417`) → stale flag on error; rollback's raw
`_UNDO B` (`:1414`) bypasses the CMDACTIVE guard. **Fix:** `try/finally`; route via `_safe_send_command`.
- **R23 / N6** — `system_run_lisp` (`com_backend.py:1483-1488`) sends raw LISP with **no CMDACTIVE
guard** (deadlock on a prompting form) and captures the **void** `SendCommand` return so it
**always reports `result:"nil"`**. **Fix:** route through `_safe_send_command`; capture via
`(setvar "USERS1" …)` + `GetVariable`, or drop the bogus `result`.
- **N5** — `EntityInfo.properties` shape diverges: ezdxf never sets `bounding_box` (COM always does);
ezdxf TEXT sets `rotation` / MTEXT `char_height`, COM sets neither. Clients break on one backend.
**Fix:** pick one contract; enforce both ways.
- **N3** — `entity_select_smart` `length_range` rejects **all ARCs** (no extractor sets ARC `length`).
**Fix:** populate ARC `length = radius·|Δangle|` or compute on the fly.
- **NEW-dimauto-baseline-rotation** — `dimension_auto` baseline mode (`base.py:952-963`) offsets
perpendicular to a rotated baseline but omits `rotation=` → dim lines render horizontal.
- **NEW-dimauto-ordinate-misnomer** — `dimension_auto` "ordinate" (`base.py:965-979`) emits two
linear dims at a constant offset → overlap; not real ordinate dims.
- **NEW-com-chamfer-return** — `entity_chamfer` (`com_backend.py:1609-1629`) hunts for a new
`AcDbLine` that CHAMFER of two distinct lines never creates → almost always returns the wrong entity.
- **NEW-com-set-variable-coercion** — `system_set_variable` (`com_backend.py:1453-1458`) forwards the
raw value to `SetVariable` with no int/float coercion → type mismatch for numeric sysvars.
- **NEW-section-dupbore** — `gear_draw_section_aa` + keyway draws the two bore lines twice →
`duplicate_entities` critique fires and blocks the premium workflow.
- **NEW-validator-keyway** — validator step-4 keyway heuristic requires a closed polyline, but
`draw_keyed_bore` emits `closed=False` and the section keyway is 4 lines → permanent
false-negative (`keyway_section_unverified`) even when a keyway exists.
- **NEW-gear-baseconstr** — the gear base circle is placed on `CONSTRUCTION` and is silently deleted
by the **mandated** `construction_clear()` before finalize → the returned handle dangles.
- **R15** — `system_about` `tool_groups` static dict omits ~22 registered tools (all engineering +
premium + corner-ops + `drawing_close`) and misfiles `entity_delete_many`. **Fix:** derive groups
from tags dynamically.
- **NEW-test-com-1** — `normalize_lineweight` table never tests the ambiguous bare ints `1`/`2`
(`normalize_lineweight(2)==200`, i.e. 2.00 mm not 2 hundredths) — the exact P0 truncation hazard.
- **NEW-test-com-2** — COM `transaction_begin/commit/rollback` + the `system_run_command` CMDACTIVE
busy-guard are entirely untested (R6 residual).
- **NEW-offset-attrs (ezdxf)** — `entity_offset` copies only `layer`, dropping source color/linetype,
so offsetting a CENTER/colored line yields a Continuous ByLayer line (unlike copy/mirror).
### LOW
- **R22** — single `_undo_stack` shared by undo *and* transactions (no txn-id isolation);
`drawing_undo` can eat an open transaction's snapshot; reload leaves `_current_layer` stale;
`system_status` reports `len(_undo_stack)` *as* `transaction_depth`.
- **R24** — `_apply_attrs` (`ezdxf_backend.py:501-502`) assigns linetype without
`_ensure_linetype_loaded` → `entity_create_line(linetype="CENTER")` silently renders Continuous.
- **R26** — ezdxf `view_zoom_window` no-op returns `ok:True`; `view_zoom_and_screenshot` ignores the
window region (renders the whole modelspace).
- **R28** — COM screenshot HWND via title-substring `EnumWindows` instead of `_acad_app().HWND`.
- **R20** — `_registered_tool_count` reaches FastMCP private attrs with a silent `-1` fallback while
a public list API exists.
- **R18-residual** — per-item bootstrap failures swallowed (only total exception → `degraded`).
- **N7** — `point_from_snap` ARC `end`/`mid` subscript `p["start_angle"]` → opaque `KeyError`.
- **N8** — `_plan_spec` written, never read; the "replayed by drawing_critique" contract is unmet.
- **NEW-base-1** — `point_tangent` leaks raw `math domain error` ValueError for `from`-points in
`[r-1e-9, r)` (needs an `acos`-arg clamp).
- **NEW-snap-quad-arc / NEW-snap-near-arc** — `point_from_snap` `quad`/`near` on an ARC return
full-circle points that may not lie on the arc — defeats the no-guessing guarantee.
- **NEW-mtext-1** — `entity_create_mtext` ignores caller rotation and never reports it.
- **NEW-undo-1** — `drawing_undo` / `transaction_begin` mutate `_undo_stack` outside `self._lock`.
- **NEW-constclear-1** — `construction_clear` / `_ensure_layer` (base.py) swallow all per-entity
exceptions (silent-pass introduced by the meta-tool refactor).
- **NEW-AUTH-2** — no test asserts `mcp.auth is _auth` or reject-on-bad-token (only construction).
- **NEW-batch-1** — `entity_batch_create` docstring promises a `rectangle` type `create_map` lacks,
and pops `type` off the caller's dict.
- **NEW-test-com-3/4/5/6** — test looseness (vacuous polling assert, single-ESC vs 3, wrong boundary docstring).
### SMELL
- **S5** — two layer systems (`_LAYER_TEMPLATES` vs `LAYER_SET_REGISTRY`); mechanical HIDDEN/CENTER
ACI colors (1/3) swapped vs `ENGINEERING_LAYERS` (3/1) → `template_apply_layers('mechanical')`
fails the `layer_color` critique. (The "violates ISO-128" sub-claim is **refuted** — template
weights all pass.)
- **S3** — `BlockInfo.description` always empty.
- **R27** — validator step-5 SPUR/HELICAL substring match over all TEXT-layer text (no word boundary).
- **NEW-base-2** — `point_intersection` treats LINE as infinite line; undocumented.
- **NEW-base-3** — COM path of `point_*` untested.
- **NEW-claudemd-1 / doc drift** — CLAUDE.md/README still list the removed `CommandResult` dataclass.
- **NEW-titleblock-titlex** — long title x-position can overflow left of the block.
---
## C. Fix plan (dependency-ordered)
### Sprint 2 — Make the gate real + stop the crashes *(CI-verifiable, headless)* — ✅ DONE
Goal: the validation moat must actually run and must not crash the server.
Implemented on branch `fix/sprint2-gate-correctness` (tests 233 → 242, ruff clean).
All six items below landed + locked by `tests/test_sprint2_fixes.py`. See degisilik.md.
1. **NEW-screenshot-1** — force Agg rendering (Figure+FigureCanvasAgg). *(S, no deps)*
2. **I15** — wire `run_critique(focus=None)` into `drawing_finalize`; merge into the report; gate/warn. *(M, depends on #3 to be honest)*
3. **R4 + N4** — populate a dim reference point + resolve dim/construction layer from the active set; fixes both the no-op and the iso13567 mislayer. *(M)*
4. **NEW-AUTH-1** — enforce the remote-bind guard at import/lifespan, not only `__main__`. *(S)*
5. **NEW-gear-roottooth + R21** — clamp flank start to `max(base_r,root_r)`, fix root-arc stitch. *(M)*
6. **NEW-section-dupbore + NEW-validator-keyway + NEW-gear-baseconstr** — unblock the gear/keyway premium workflow end-to-end. *(M)*
### Sprint 3 — Cross-backend parity + COM robustness *(some need live AutoCAD)* — ✅ DONE
Implemented on `fix/sprint2-gate-correctness` (tests 242 → 266). COM items code-read + mocked-COM tests (`tests/test_com_sprint3.py`); ezdxf/shared in `tests/test_sprint3_fixes.py`; R8 in `tests/test_security.py`.
7. **R13-COM** offset side ✅ (+ NEW-com-offset-orphan); **N2** save_as tool fmt ✅; **N5** properties parity ✅; **N3** ARC length ✅.
8. **R5** COM CoUninitialize/leak ✅; **R16** txn try/finally + UNDO via _safe_send_command ✅; **R23/N6** lisp guard + value capture ✅. *(live-verify pending on real AutoCAD)*
9. **NEW-com-set-variable-coercion** ✅; **NEW-dimauto-baseline/ordinate** ✅; **NEW-com-chamfer-return** → REFUTED on review (CHAMFER does create the bevel AcDbLine; code is correct).
10. **NEW-test-com-1/2** ✅ + adversarial sanitizer tests (R8) ✅.
### Sprint 4 — Hygiene + lower-severity correctness — ✅ DONE (R22 deferred)
Implemented via a 5-agent parallel workflow on `fix/sprint2-gate-correctness` (tests 266 → 318).
Done: R24, R26, R28, R20, R15, R27, N7, N8, NEW-base-1, NEW-snap-quad-arc, NEW-snap-near-arc,
NEW-undo-1, NEW-mtext-1, S3, doc drift (CLAUDE.md CommandResult + counts).
New tests: `tests/test_sprint4_{ezdxf,base,server,validator,com}.py`.
**Deferred — R22** (undo/transaction stack isolation): the roadmap makes it the prerequisite for the
I1 refiner's rollback cycle, so it lands with Phase A (1.2), not as standalone hygiene.
Not pursued (low value / refuted): N8 implemented as docstring-fix + `get_plan_spec()` accessor;
S5 (parallel layer systems) and NEW-constclear-1 / NEW-batch-1 left as documented SMELL.
---
## D. Top 5 next actions
1. **NEW-screenshot-1** — `Agg` backend in `view_screenshot`/`drawing_export_pdf` (prevents SIGSEGV; screenshots feed the visual signal).
2. **I15** — run the premium critique inside `drawing_finalize` (the moat — currently advisory only).
3. **R4 + N4** — make `dim_overlap` fire and resolve dim/construction layers per active set (so the gate doesn't lie).
4. **NEW-AUTH-1** — enforce the remote-bind guard on the documented `fastmcp run` path (fail-open security gap).
5. **NEW-gear-roottooth + R21** — fix the self-overlapping tooth outline for teeth ≥ ~42.
## E. Risks / notes
- **Live-AutoCAD only:** R5, R16, R23/N6, R28, NEW-com-chamfer/set-variable can't be CI-verified
here — gate them behind mocked-COM tests + manual live check.
- **Cross-backend parity** is the dominant theme of the open work (Sprint-1 fixes were mostly
one-backend). Every new behavior should land on both backends or explicitly document the gap.
- **Security residual:** the `audit:security-hardening` finder was blocked by a provider
cyber-safety filter, so the deny-list bypass analysis (R7/R8) is **not** complete. The sanitizer
*is* wired (`server.py:2055/2072`) and `test_security.py` exists, but adversarial bypass coverage
remains an open defensive item — handle in Sprint 3 #10.
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "autocad-mcp-pro"
version = "1.0.0"
version = "1.1.0"
description = "Production-grade AutoCAD MCP server — COM API + ezdxf dual engine"
readme = "README.md"
requires-python = ">=3.11"