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:
+51
-25
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user