acad-mcp 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- acad_mcp-0.2.0/PKG-INFO +173 -0
- acad_mcp-0.2.0/README.md +151 -0
- acad_mcp-0.2.0/pyproject.toml +52 -0
- acad_mcp-0.2.0/pyproject.toml.orig +43 -0
- acad_mcp-0.2.0/src/acad_mcp/__init__.py +7 -0
- acad_mcp-0.2.0/src/acad_mcp/__main__.py +6 -0
- acad_mcp-0.2.0/src/acad_mcp/acad/__init__.py +1 -0
- acad_mcp-0.2.0/src/acad_mcp/acad/capture.py +128 -0
- acad_mcp-0.2.0/src/acad_mcp/acad/commands.py +302 -0
- acad_mcp-0.2.0/src/acad_mcp/acad/errors.py +7 -0
- acad_mcp-0.2.0/src/acad_mcp/acad/fastpath.py +169 -0
- acad_mcp-0.2.0/src/acad_mcp/acad/msgfilter.py +105 -0
- acad_mcp-0.2.0/src/acad_mcp/acad/util.py +498 -0
- acad_mcp-0.2.0/src/acad_mcp/acad/winutil.py +112 -0
- acad_mcp-0.2.0/src/acad_mcp/acad/worker.py +142 -0
- acad_mcp-0.2.0/src/acad_mcp/config.py +69 -0
- acad_mcp-0.2.0/src/acad_mcp/i18n.py +19 -0
- acad_mcp-0.2.0/src/acad_mcp/lisp/mcp_core.lsp +124 -0
- acad_mcp-0.2.0/src/acad_mcp/lisp/routines/_common.lsp +50 -0
- acad_mcp-0.2.0/src/acad_mcp/lisp/routines/count_blocks.lsp +20 -0
- acad_mcp-0.2.0/src/acad_mcp/lisp/routines/export_attributes.lsp +23 -0
- acad_mcp-0.2.0/src/acad_mcp/lisp/routines/fill_title_block.lsp +23 -0
- acad_mcp-0.2.0/src/acad_mcp/lisp/routines/find_replace_text.lsp +34 -0
- acad_mcp-0.2.0/src/acad_mcp/lisp/routines/layer_standard.lsp +38 -0
- acad_mcp-0.2.0/src/acad_mcp/lisp/routines/plot_pdf.lsp +18 -0
- acad_mcp-0.2.0/src/acad_mcp/lisp/routines/purge_audit.lsp +12 -0
- acad_mcp-0.2.0/src/acad_mcp/selftest.py +36 -0
- acad_mcp-0.2.0/src/acad_mcp/server.py +128 -0
- acad_mcp-0.2.0/src/acad_mcp/standards/example.json +22 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/__init__.py +25 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/advanced.py +120 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/batch.py +351 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/common.py +85 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/documents.py +214 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/draw.py +595 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/edit.py +401 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/extract.py +196 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/files.py +337 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/layers.py +226 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/query.py +327 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/recreate.py +298 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/routines.py +164 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/standards.py +310 -0
- acad_mcp-0.2.0/src/acad_mcp/tools/view.py +98 -0
acad_mcp-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: acad-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: MCP server that lets Claude (and other MCP clients) drive a running AutoCAD on Windows via COM
|
|
5
|
+
Keywords: mcp,autocad,cad,claude,model-context-protocol,dwg,dxf
|
|
6
|
+
Author: thurdevv
|
|
7
|
+
Author-email: thurdevv <demonrspy@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering
|
|
12
|
+
Requires-Dist: ezdxf>=1.4.5
|
|
13
|
+
Requires-Dist: mcp[cli]>=2.3.0
|
|
14
|
+
Requires-Dist: opencv-python-headless>=5.0.0.93
|
|
15
|
+
Requires-Dist: pillow>=13.0.0
|
|
16
|
+
Requires-Dist: pywin32>=312
|
|
17
|
+
Requires-Python: >=3.13
|
|
18
|
+
Project-URL: Homepage, https://github.com/thurdevv/acad-mcp
|
|
19
|
+
Project-URL: Issues, https://github.com/thurdevv/acad-mcp/issues
|
|
20
|
+
Project-URL: Changelog, https://github.com/thurdevv/acad-mcp/blob/main/CHANGELOG.md
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# acad-mcp — let Claude drive AutoCAD
|
|
24
|
+
|
|
25
|
+
<!-- mcp-name: io.github.thurdevv/acad-mcp -->
|
|
26
|
+
|
|
27
|
+
**[Português](README.pt-BR.md)**
|
|
28
|
+
|
|
29
|
+
An [MCP](https://modelcontextprotocol.io) server that lets Claude (and other MCP clients) **draw, edit, read and
|
|
30
|
+
check drawings in the AutoCAD running on your Windows PC** — and process whole folders of DWGs without opening them.
|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
- **61 tools + a guided prompt**, every write is **one undo step** (Ctrl+Z).
|
|
35
|
+
- **Fast on large drawings**: reads run as LISP inside AutoCAD — 50,000 entities in ~15 s (vs ~22 min over COM).
|
|
36
|
+
- **Batch**: run a routine or a layer standard over a folder with AutoCAD's console engine (no window, in
|
|
37
|
+
parallel). **Originals are never modified** — copies go to an output folder.
|
|
38
|
+
- **Routines without AI**: built-in LISP routines (find/replace, fill title blocks, export attributes, count
|
|
39
|
+
blocks, layer standard from CSV, purge/audit, plot PDF) that a person can also type as `MCP_...` commands.
|
|
40
|
+
- **Recreate drawings**: import vector PDFs with real measurements, DXF/DWG, or trace scans/photos into lines;
|
|
41
|
+
a coordinate grid on screenshots lets the model compare and fix.
|
|
42
|
+
- **English and Portuguese** (`ACAD_MCP_LANG=pt`).
|
|
43
|
+
|
|
44
|
+
> AutoCAD is a registered trademark of Autodesk, Inc. This project is not affiliated with or endorsed by Autodesk.
|
|
45
|
+
|
|
46
|
+
## Requirements
|
|
47
|
+
|
|
48
|
+
- **Windows** and the **full AutoCAD** (uses COM/ActiveX). **AutoCAD LT is not supported** (no COM, no console).
|
|
49
|
+
Tested on **AutoCAD 2027**; earlier versions (2021+) are likely to work but are not verified.
|
|
50
|
+
- [uv](https://docs.astral.sh/uv/) (it installs Python 3.13 for you).
|
|
51
|
+
- Linux/macOS: not possible — AutoCAD's automation only exists on Windows.
|
|
52
|
+
|
|
53
|
+
## Install
|
|
54
|
+
|
|
55
|
+
### Claude Desktop — extension (easiest)
|
|
56
|
+
Download `acad-mcp-<version>.mcpb` from the Releases page and open it with Claude Desktop
|
|
57
|
+
(Settings → Extensions). Options: language, read-only, LISP on/off, auto-restore window.
|
|
58
|
+
|
|
59
|
+
### Claude Desktop — config file
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"mcpServers": {
|
|
63
|
+
"acad": { "command": "uvx", "args": ["acad-mcp"], "env": { "ACAD_MCP_LANG": "en" } }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
Edit the config **with Claude fully closed** (tray icon → Quit): Claude rewrites the file from memory when it
|
|
68
|
+
quits, which would drop your change.
|
|
69
|
+
|
|
70
|
+
### One-click install from this folder (Windows)
|
|
71
|
+
Double-click **`INSTALAR.bat`**: it checks AutoCAD, asks the language, closes Claude (after asking), installs uv
|
|
72
|
+
if missing, installs Python and the dependencies, configures Claude (with a backup), runs a self-test and reopens
|
|
73
|
+
Claude. A `.venv` copied from another PC is detected and rebuilt.
|
|
74
|
+
|
|
75
|
+
### Claude Code
|
|
76
|
+
```bash
|
|
77
|
+
claude mcp add --scope user acad -- uvx acad-mcp
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Other MCP clients
|
|
81
|
+
Any client that runs local stdio servers: command `uvx`, args `["acad-mcp"]`. For HTTP: `acad-mcp --http`
|
|
82
|
+
(listens on 127.0.0.1:8765/mcp only).
|
|
83
|
+
|
|
84
|
+
## Use
|
|
85
|
+
|
|
86
|
+
Open AutoCAD and keep its window **not minimized** (it may stay behind other windows). Then ask, e.g.:
|
|
87
|
+
|
|
88
|
+
- *"Create layers WALL (red, 0.35) and AXIS (green, CENTER) and draw a 4 × 3 m room with axes and dimensions."*
|
|
89
|
+
- *"Count the blocks and export their attributes to a CSV on my Desktop."*
|
|
90
|
+
- *"Check this drawing against the `example` standard and fix what you can."*
|
|
91
|
+
- *"In C:\Projects\Sheets, replace 'REV A' with 'REV B' in every drawing."* (batch, originals untouched)
|
|
92
|
+
- *"Recreate the attached part directly in AutoCAD with dimensions, then show me."*
|
|
93
|
+
|
|
94
|
+
## Tools
|
|
95
|
+
|
|
96
|
+
| Group | Tools |
|
|
97
|
+
|---|---|
|
|
98
|
+
| Document | `status`, `list_documents`, `new_drawing`, `open_drawing`, `activate_drawing`, `save_drawing`, `close_drawing` |
|
|
99
|
+
| Read | `drawing_info`, `list_layers`, `list_styles`, `list_blocks`, `query_entities`, `get_entity`, `get_selection` |
|
|
100
|
+
| View | `get_view_image` (screenshot, optional coordinate grid), `zoom_view` |
|
|
101
|
+
| Draw | `draw_line`, `draw_polyline`, `draw_rectangle`, `draw_circle`, `draw_arc`, `draw_text`, `draw_dimension`, `draw_hatch`, `draw_leader`, `draw_table`, `insert_block`, `create_block`, `draw_batch` |
|
|
102
|
+
| Edit | `transform_entities`, `array_entities`, `offset_entity`, `erase_entities`, `set_entity_properties`, `edit_text`, `set_block_attributes`, `modify_geometry`, `explode_entities` |
|
|
103
|
+
| Layers & styles | `manage_layer`, `manage_text_style`, `set_current`, `load_linetype` |
|
|
104
|
+
| Data | `quantities`, `block_report` (CSV) |
|
|
105
|
+
| Standards | `list_standards`, `check_standards`, `apply_standards`, `purge_and_audit` |
|
|
106
|
+
| Files & recreate | `import_file` (DXF, DWG, vector PDF, image), `list_received_files`, `trace_image` |
|
|
107
|
+
| Routines & batch | `list_routines`, `run_routine`, `save_routine`, `export_routines_for_autocad`, `batch_process`, `batch_status` |
|
|
108
|
+
| Escape hatches | `run_command`, `run_lisp`, `undo`, `cancel_command` |
|
|
109
|
+
|
|
110
|
+
Prompt: `recreate_drawing` — measurements first, ask for what is missing, draw, compare, fix.
|
|
111
|
+
|
|
112
|
+
## Configuration
|
|
113
|
+
|
|
114
|
+
| Variable | Default | Meaning |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| `ACAD_MCP_LANG` | `en` | `pt` for Portuguese descriptions and messages |
|
|
117
|
+
| `ACAD_MCP_READ_ONLY` | `0` | `1` = only reading tools are registered |
|
|
118
|
+
| `ACAD_MCP_ALLOW_LISP` | `1` | `0` disables `run_lisp`, routines and batch routines |
|
|
119
|
+
| `ACAD_MCP_ALLOW_COMMANDS` | `1` | `0` disables `run_command` |
|
|
120
|
+
| `ACAD_MCP_AUTO_RESTORE` | `0` | `1` restores a minimized AutoCAD when needed (AutoCAD may grab focus) |
|
|
121
|
+
| `ACAD_MCP_COMMAND_TIMEOUT` | `30` | seconds before a command waiting for input is cancelled |
|
|
122
|
+
| `ACAD_MCP_FAST_THRESHOLD` | `300` | entity count above which reads use in-process LISP |
|
|
123
|
+
| `ACAD_MCP_STANDARDS_DIR` | `Documents\AutoCAD MCP\standards` | your JSON standards |
|
|
124
|
+
| `ACAD_MCP_ROUTINES_DIR` | `Documents\AutoCAD MCP\routines` | your saved routines |
|
|
125
|
+
|
|
126
|
+
## How it works (and what was measured)
|
|
127
|
+
|
|
128
|
+
- One COM thread talks to AutoCAD; a **COM message filter** retries calls while AutoCAD is busy, so a tool is
|
|
129
|
+
never re-run (re-running duplicated saves during development).
|
|
130
|
+
- Commands/LISP go through `SendCommand` with a **watchdog**: if a command waits for input, ESC is sent to the
|
|
131
|
+
drawing area (ESC to the main window does not cancel). The command line rejects expressions over ~1–2 k
|
|
132
|
+
characters, so long LISP is written to a temp file and read+evaluated (reading text does not trigger
|
|
133
|
+
SECURELOAD, unlike `(load)`).
|
|
134
|
+
- With the window **minimized**, `SendCommand` gets queued and AutoCAD freezes when restored, so commands, LISP and
|
|
135
|
+
screenshots refuse to run minimized (unless auto-restore is on). Direct drawing/editing works minimized.
|
|
136
|
+
- COM selection sets leave internal "OLEAPPLICATION" undo records; `undo` skips them.
|
|
137
|
+
- **Batch console engine** (`accoreconsole.exe`): pure AutoLISP and `vlax-curve-*` work, **`vla-*` does not**;
|
|
138
|
+
plotting to PDF and saving work. Measured: 3 files in ~1.5 s with 3 workers. Use `engine="gui"` for
|
|
139
|
+
`vla-*` routines and JSON standards.
|
|
140
|
+
- **PDF import**: `-PDFIMPORT` runs without a dialog. PDFs plotted by AutoCAD came back at real size; PDFs
|
|
141
|
+
without CAD scale come in inches (1 pt = 1/72") — rescale and check one known measurement.
|
|
142
|
+
- **trace_image** is a draft: straight strokes within ~1 % on clean drawings in tests; curves and text are not
|
|
143
|
+
traced.
|
|
144
|
+
|
|
145
|
+
## Safety
|
|
146
|
+
|
|
147
|
+
- Never writes over an existing file without `overwrite=true`; closing with unsaved changes needs
|
|
148
|
+
`discard_changes=true`; batch only touches copies.
|
|
149
|
+
- `run_command` blocks SHELL, START, NETLOAD, APPLOAD, SCRIPT, QUIT and similar; file dialogs are off.
|
|
150
|
+
- `run_lisp` blocks `startapp`, file deletion/renaming, registry writes, etc. **It is not a sandbox** — LISP can
|
|
151
|
+
do a lot. Disable it with `ACAD_MCP_ALLOW_LISP=0` or use read-only mode.
|
|
152
|
+
- HTTP mode listens on localhost only. **Never expose it publicly without authentication**: whoever reaches it
|
|
153
|
+
controls your AutoCAD. See [SECURITY.md](SECURITY.md).
|
|
154
|
+
|
|
155
|
+
## ChatGPT
|
|
156
|
+
|
|
157
|
+
ChatGPT only talks to remote MCP servers. See [docs/chatgpt.md](docs/chatgpt.md) (OpenAI Secure MCP Tunnel; not
|
|
158
|
+
fully verified yet).
|
|
159
|
+
|
|
160
|
+
## Development
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
uv sync
|
|
164
|
+
uv run pytest -m "not live" # no AutoCAD needed
|
|
165
|
+
uv run pytest # also the live tests: AutoCAD open, window not minimized
|
|
166
|
+
uv build # wheel + sdist
|
|
167
|
+
uv run python scripts/build_mcpb.py # Claude Desktop extension (needs Node.js)
|
|
168
|
+
```
|
|
169
|
+
The live tests create their own drawings and close them without saving; they never touch yours.
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT — see [LICENSE](LICENSE).
|
acad_mcp-0.2.0/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# acad-mcp — let Claude drive AutoCAD
|
|
2
|
+
|
|
3
|
+
<!-- mcp-name: io.github.thurdevv/acad-mcp -->
|
|
4
|
+
|
|
5
|
+
**[Português](README.pt-BR.md)**
|
|
6
|
+
|
|
7
|
+
An [MCP](https://modelcontextprotocol.io) server that lets Claude (and other MCP clients) **draw, edit, read and
|
|
8
|
+
check drawings in the AutoCAD running on your Windows PC** — and process whole folders of DWGs without opening them.
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
- **61 tools + a guided prompt**, every write is **one undo step** (Ctrl+Z).
|
|
13
|
+
- **Fast on large drawings**: reads run as LISP inside AutoCAD — 50,000 entities in ~15 s (vs ~22 min over COM).
|
|
14
|
+
- **Batch**: run a routine or a layer standard over a folder with AutoCAD's console engine (no window, in
|
|
15
|
+
parallel). **Originals are never modified** — copies go to an output folder.
|
|
16
|
+
- **Routines without AI**: built-in LISP routines (find/replace, fill title blocks, export attributes, count
|
|
17
|
+
blocks, layer standard from CSV, purge/audit, plot PDF) that a person can also type as `MCP_...` commands.
|
|
18
|
+
- **Recreate drawings**: import vector PDFs with real measurements, DXF/DWG, or trace scans/photos into lines;
|
|
19
|
+
a coordinate grid on screenshots lets the model compare and fix.
|
|
20
|
+
- **English and Portuguese** (`ACAD_MCP_LANG=pt`).
|
|
21
|
+
|
|
22
|
+
> AutoCAD is a registered trademark of Autodesk, Inc. This project is not affiliated with or endorsed by Autodesk.
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
- **Windows** and the **full AutoCAD** (uses COM/ActiveX). **AutoCAD LT is not supported** (no COM, no console).
|
|
27
|
+
Tested on **AutoCAD 2027**; earlier versions (2021+) are likely to work but are not verified.
|
|
28
|
+
- [uv](https://docs.astral.sh/uv/) (it installs Python 3.13 for you).
|
|
29
|
+
- Linux/macOS: not possible — AutoCAD's automation only exists on Windows.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
### Claude Desktop — extension (easiest)
|
|
34
|
+
Download `acad-mcp-<version>.mcpb` from the Releases page and open it with Claude Desktop
|
|
35
|
+
(Settings → Extensions). Options: language, read-only, LISP on/off, auto-restore window.
|
|
36
|
+
|
|
37
|
+
### Claude Desktop — config file
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"acad": { "command": "uvx", "args": ["acad-mcp"], "env": { "ACAD_MCP_LANG": "en" } }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
Edit the config **with Claude fully closed** (tray icon → Quit): Claude rewrites the file from memory when it
|
|
46
|
+
quits, which would drop your change.
|
|
47
|
+
|
|
48
|
+
### One-click install from this folder (Windows)
|
|
49
|
+
Double-click **`INSTALAR.bat`**: it checks AutoCAD, asks the language, closes Claude (after asking), installs uv
|
|
50
|
+
if missing, installs Python and the dependencies, configures Claude (with a backup), runs a self-test and reopens
|
|
51
|
+
Claude. A `.venv` copied from another PC is detected and rebuilt.
|
|
52
|
+
|
|
53
|
+
### Claude Code
|
|
54
|
+
```bash
|
|
55
|
+
claude mcp add --scope user acad -- uvx acad-mcp
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Other MCP clients
|
|
59
|
+
Any client that runs local stdio servers: command `uvx`, args `["acad-mcp"]`. For HTTP: `acad-mcp --http`
|
|
60
|
+
(listens on 127.0.0.1:8765/mcp only).
|
|
61
|
+
|
|
62
|
+
## Use
|
|
63
|
+
|
|
64
|
+
Open AutoCAD and keep its window **not minimized** (it may stay behind other windows). Then ask, e.g.:
|
|
65
|
+
|
|
66
|
+
- *"Create layers WALL (red, 0.35) and AXIS (green, CENTER) and draw a 4 × 3 m room with axes and dimensions."*
|
|
67
|
+
- *"Count the blocks and export their attributes to a CSV on my Desktop."*
|
|
68
|
+
- *"Check this drawing against the `example` standard and fix what you can."*
|
|
69
|
+
- *"In C:\Projects\Sheets, replace 'REV A' with 'REV B' in every drawing."* (batch, originals untouched)
|
|
70
|
+
- *"Recreate the attached part directly in AutoCAD with dimensions, then show me."*
|
|
71
|
+
|
|
72
|
+
## Tools
|
|
73
|
+
|
|
74
|
+
| Group | Tools |
|
|
75
|
+
|---|---|
|
|
76
|
+
| Document | `status`, `list_documents`, `new_drawing`, `open_drawing`, `activate_drawing`, `save_drawing`, `close_drawing` |
|
|
77
|
+
| Read | `drawing_info`, `list_layers`, `list_styles`, `list_blocks`, `query_entities`, `get_entity`, `get_selection` |
|
|
78
|
+
| View | `get_view_image` (screenshot, optional coordinate grid), `zoom_view` |
|
|
79
|
+
| Draw | `draw_line`, `draw_polyline`, `draw_rectangle`, `draw_circle`, `draw_arc`, `draw_text`, `draw_dimension`, `draw_hatch`, `draw_leader`, `draw_table`, `insert_block`, `create_block`, `draw_batch` |
|
|
80
|
+
| Edit | `transform_entities`, `array_entities`, `offset_entity`, `erase_entities`, `set_entity_properties`, `edit_text`, `set_block_attributes`, `modify_geometry`, `explode_entities` |
|
|
81
|
+
| Layers & styles | `manage_layer`, `manage_text_style`, `set_current`, `load_linetype` |
|
|
82
|
+
| Data | `quantities`, `block_report` (CSV) |
|
|
83
|
+
| Standards | `list_standards`, `check_standards`, `apply_standards`, `purge_and_audit` |
|
|
84
|
+
| Files & recreate | `import_file` (DXF, DWG, vector PDF, image), `list_received_files`, `trace_image` |
|
|
85
|
+
| Routines & batch | `list_routines`, `run_routine`, `save_routine`, `export_routines_for_autocad`, `batch_process`, `batch_status` |
|
|
86
|
+
| Escape hatches | `run_command`, `run_lisp`, `undo`, `cancel_command` |
|
|
87
|
+
|
|
88
|
+
Prompt: `recreate_drawing` — measurements first, ask for what is missing, draw, compare, fix.
|
|
89
|
+
|
|
90
|
+
## Configuration
|
|
91
|
+
|
|
92
|
+
| Variable | Default | Meaning |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `ACAD_MCP_LANG` | `en` | `pt` for Portuguese descriptions and messages |
|
|
95
|
+
| `ACAD_MCP_READ_ONLY` | `0` | `1` = only reading tools are registered |
|
|
96
|
+
| `ACAD_MCP_ALLOW_LISP` | `1` | `0` disables `run_lisp`, routines and batch routines |
|
|
97
|
+
| `ACAD_MCP_ALLOW_COMMANDS` | `1` | `0` disables `run_command` |
|
|
98
|
+
| `ACAD_MCP_AUTO_RESTORE` | `0` | `1` restores a minimized AutoCAD when needed (AutoCAD may grab focus) |
|
|
99
|
+
| `ACAD_MCP_COMMAND_TIMEOUT` | `30` | seconds before a command waiting for input is cancelled |
|
|
100
|
+
| `ACAD_MCP_FAST_THRESHOLD` | `300` | entity count above which reads use in-process LISP |
|
|
101
|
+
| `ACAD_MCP_STANDARDS_DIR` | `Documents\AutoCAD MCP\standards` | your JSON standards |
|
|
102
|
+
| `ACAD_MCP_ROUTINES_DIR` | `Documents\AutoCAD MCP\routines` | your saved routines |
|
|
103
|
+
|
|
104
|
+
## How it works (and what was measured)
|
|
105
|
+
|
|
106
|
+
- One COM thread talks to AutoCAD; a **COM message filter** retries calls while AutoCAD is busy, so a tool is
|
|
107
|
+
never re-run (re-running duplicated saves during development).
|
|
108
|
+
- Commands/LISP go through `SendCommand` with a **watchdog**: if a command waits for input, ESC is sent to the
|
|
109
|
+
drawing area (ESC to the main window does not cancel). The command line rejects expressions over ~1–2 k
|
|
110
|
+
characters, so long LISP is written to a temp file and read+evaluated (reading text does not trigger
|
|
111
|
+
SECURELOAD, unlike `(load)`).
|
|
112
|
+
- With the window **minimized**, `SendCommand` gets queued and AutoCAD freezes when restored, so commands, LISP and
|
|
113
|
+
screenshots refuse to run minimized (unless auto-restore is on). Direct drawing/editing works minimized.
|
|
114
|
+
- COM selection sets leave internal "OLEAPPLICATION" undo records; `undo` skips them.
|
|
115
|
+
- **Batch console engine** (`accoreconsole.exe`): pure AutoLISP and `vlax-curve-*` work, **`vla-*` does not**;
|
|
116
|
+
plotting to PDF and saving work. Measured: 3 files in ~1.5 s with 3 workers. Use `engine="gui"` for
|
|
117
|
+
`vla-*` routines and JSON standards.
|
|
118
|
+
- **PDF import**: `-PDFIMPORT` runs without a dialog. PDFs plotted by AutoCAD came back at real size; PDFs
|
|
119
|
+
without CAD scale come in inches (1 pt = 1/72") — rescale and check one known measurement.
|
|
120
|
+
- **trace_image** is a draft: straight strokes within ~1 % on clean drawings in tests; curves and text are not
|
|
121
|
+
traced.
|
|
122
|
+
|
|
123
|
+
## Safety
|
|
124
|
+
|
|
125
|
+
- Never writes over an existing file without `overwrite=true`; closing with unsaved changes needs
|
|
126
|
+
`discard_changes=true`; batch only touches copies.
|
|
127
|
+
- `run_command` blocks SHELL, START, NETLOAD, APPLOAD, SCRIPT, QUIT and similar; file dialogs are off.
|
|
128
|
+
- `run_lisp` blocks `startapp`, file deletion/renaming, registry writes, etc. **It is not a sandbox** — LISP can
|
|
129
|
+
do a lot. Disable it with `ACAD_MCP_ALLOW_LISP=0` or use read-only mode.
|
|
130
|
+
- HTTP mode listens on localhost only. **Never expose it publicly without authentication**: whoever reaches it
|
|
131
|
+
controls your AutoCAD. See [SECURITY.md](SECURITY.md).
|
|
132
|
+
|
|
133
|
+
## ChatGPT
|
|
134
|
+
|
|
135
|
+
ChatGPT only talks to remote MCP servers. See [docs/chatgpt.md](docs/chatgpt.md) (OpenAI Secure MCP Tunnel; not
|
|
136
|
+
fully verified yet).
|
|
137
|
+
|
|
138
|
+
## Development
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
uv sync
|
|
142
|
+
uv run pytest -m "not live" # no AutoCAD needed
|
|
143
|
+
uv run pytest # also the live tests: AutoCAD open, window not minimized
|
|
144
|
+
uv build # wheel + sdist
|
|
145
|
+
uv run python scripts/build_mcpb.py # Claude Desktop extension (needs Node.js)
|
|
146
|
+
```
|
|
147
|
+
The live tests create their own drawings and close them without saving; they never touch yours.
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "acad-mcp"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "MCP server that lets Claude (and other MCP clients) drive a running AutoCAD on Windows via COM"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
keywords = [
|
|
9
|
+
"mcp",
|
|
10
|
+
"autocad",
|
|
11
|
+
"cad",
|
|
12
|
+
"claude",
|
|
13
|
+
"model-context-protocol",
|
|
14
|
+
"dwg",
|
|
15
|
+
"dxf",
|
|
16
|
+
]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Operating System :: Microsoft :: Windows",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Scientific/Engineering",
|
|
21
|
+
]
|
|
22
|
+
dependencies = [
|
|
23
|
+
"ezdxf>=1.4.5",
|
|
24
|
+
"mcp[cli]>=2.3.0",
|
|
25
|
+
"opencv-python-headless>=5.0.0.93",
|
|
26
|
+
"pillow>=13.0.0",
|
|
27
|
+
"pywin32>=312",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[[project.authors]]
|
|
31
|
+
name = "thurdevv"
|
|
32
|
+
email = "demonrspy@gmail.com"
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/thurdevv/acad-mcp"
|
|
36
|
+
Issues = "https://github.com/thurdevv/acad-mcp/issues"
|
|
37
|
+
Changelog = "https://github.com/thurdevv/acad-mcp/blob/main/CHANGELOG.md"
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
acad-mcp = "acad_mcp:main"
|
|
41
|
+
|
|
42
|
+
[build-system]
|
|
43
|
+
requires = ["uv_build>=0.13.8,<0.14.0"]
|
|
44
|
+
build-backend = "uv_build"
|
|
45
|
+
|
|
46
|
+
[dependency-groups]
|
|
47
|
+
dev = ["pytest>=9.2.0"]
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
markers = ["live: needs a running AutoCAD (window not minimized)"]
|
|
51
|
+
testpaths = ["tests"]
|
|
52
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "acad-mcp"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "MCP server that lets Claude (and other MCP clients) drive a running AutoCAD on Windows via COM"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
authors = [{ name = "thurdevv", email = "demonrspy@gmail.com" }]
|
|
9
|
+
keywords = ["mcp", "autocad", "cad", "claude", "model-context-protocol", "dwg", "dxf"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Operating System :: Microsoft :: Windows",
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"Topic :: Scientific/Engineering",
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"ezdxf>=1.4.5",
|
|
17
|
+
"mcp[cli]>=2.3.0",
|
|
18
|
+
"opencv-python-headless>=5.0.0.93",
|
|
19
|
+
"pillow>=13.0.0",
|
|
20
|
+
"pywin32>=312",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://github.com/thurdevv/acad-mcp"
|
|
25
|
+
Issues = "https://github.com/thurdevv/acad-mcp/issues"
|
|
26
|
+
Changelog = "https://github.com/thurdevv/acad-mcp/blob/main/CHANGELOG.md"
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
acad-mcp = "acad_mcp:main"
|
|
30
|
+
|
|
31
|
+
[build-system]
|
|
32
|
+
requires = ["uv_build>=0.13.8,<0.14.0"]
|
|
33
|
+
build-backend = "uv_build"
|
|
34
|
+
|
|
35
|
+
[dependency-groups]
|
|
36
|
+
dev = [
|
|
37
|
+
"pytest>=9.2.0",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[tool.pytest.ini_options]
|
|
41
|
+
markers = ["live: needs a running AutoCAD (window not minimized)"]
|
|
42
|
+
testpaths = ["tests"]
|
|
43
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""AutoCAD access through COM (single thread), commands, fast LISP reads and screen capture."""
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Image of the AutoCAD drawing area (PrintWindow; works even when the window is covered)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ctypes
|
|
6
|
+
import io
|
|
7
|
+
import math
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import win32gui
|
|
11
|
+
import win32ui
|
|
12
|
+
from PIL import Image, ImageDraw, ImageFont
|
|
13
|
+
|
|
14
|
+
from ..i18n import t as tr
|
|
15
|
+
from . import commands
|
|
16
|
+
from .errors import AcadError
|
|
17
|
+
from .winutil import active_view_hwnd, is_minimized
|
|
18
|
+
|
|
19
|
+
PW_RENDERFULLCONTENT = 2
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def capture_view_png(app: Any, max_width: int = 1600, crop_to_view: bool = True,
|
|
23
|
+
grid: dict[str, Any] | None = None) -> tuple[bytes, dict]:
|
|
24
|
+
"""grid = {"center": VIEWCTR, "view_height": VIEWSIZE} to overlay coordinates."""
|
|
25
|
+
main = int(app.HWND)
|
|
26
|
+
if is_minimized(main):
|
|
27
|
+
if not commands.AUTO_RESTORE:
|
|
28
|
+
raise AcadError(tr(
|
|
29
|
+
"The AutoCAD window is minimized, so no image can be captured. "
|
|
30
|
+
"Ask the user to restore it (it may stay behind other windows).",
|
|
31
|
+
"A janela do AutoCAD está minimizada; não dá para capturar a imagem. "
|
|
32
|
+
"Peça ao usuário para restaurar a janela (pode ficar atrás de outras).",
|
|
33
|
+
))
|
|
34
|
+
commands.restore_behind(main)
|
|
35
|
+
l, t, r, b = win32gui.GetWindowRect(main)
|
|
36
|
+
w, h = r - l, b - t
|
|
37
|
+
hdc = win32gui.GetWindowDC(main)
|
|
38
|
+
mfc = win32ui.CreateDCFromHandle(hdc)
|
|
39
|
+
mem = mfc.CreateCompatibleDC()
|
|
40
|
+
bmp = win32ui.CreateBitmap()
|
|
41
|
+
try:
|
|
42
|
+
bmp.CreateCompatibleBitmap(mfc, w, h)
|
|
43
|
+
mem.SelectObject(bmp)
|
|
44
|
+
if not ctypes.windll.user32.PrintWindow(main, mem.GetSafeHdc(), PW_RENDERFULLCONTENT):
|
|
45
|
+
raise AcadError(tr("Could not capture the AutoCAD window (PrintWindow).",
|
|
46
|
+
"Falha ao capturar a janela do AutoCAD (PrintWindow)."))
|
|
47
|
+
info = bmp.GetInfo()
|
|
48
|
+
img = Image.frombuffer(
|
|
49
|
+
"RGB", (info["bmWidth"], info["bmHeight"]), bmp.GetBitmapBits(True), "raw", "BGRX", 0, 1
|
|
50
|
+
)
|
|
51
|
+
finally:
|
|
52
|
+
win32gui.DeleteObject(bmp.GetHandle())
|
|
53
|
+
mem.DeleteDC()
|
|
54
|
+
mfc.DeleteDC()
|
|
55
|
+
win32gui.ReleaseDC(main, hdc)
|
|
56
|
+
|
|
57
|
+
meta: dict[str, Any] = {"window_size": [w, h]}
|
|
58
|
+
cropped = False
|
|
59
|
+
if crop_to_view:
|
|
60
|
+
view = active_view_hwnd(main)
|
|
61
|
+
if view:
|
|
62
|
+
vl, vt, vr, vb = win32gui.GetWindowRect(view)
|
|
63
|
+
box = (max(vl - l, 0), max(vt - t, 0), min(vr - l, w), min(vb - t, h))
|
|
64
|
+
if box[2] - box[0] > 50 and box[3] - box[1] > 50:
|
|
65
|
+
img = img.crop(box)
|
|
66
|
+
cropped = True
|
|
67
|
+
meta["cropped_to_drawing_area"] = True
|
|
68
|
+
if grid is not None and cropped:
|
|
69
|
+
meta["coordinates"] = draw_grid(img, grid["center"], grid["view_height"])
|
|
70
|
+
scale = 1.0
|
|
71
|
+
if img.width > max_width:
|
|
72
|
+
scale = max_width / img.width
|
|
73
|
+
img = img.resize((max_width, int(img.height * scale)), Image.LANCZOS)
|
|
74
|
+
if "coordinates" in meta:
|
|
75
|
+
c = meta["coordinates"]
|
|
76
|
+
c["units_per_pixel"] = c["units_per_pixel"] / scale
|
|
77
|
+
meta["image_size"] = [img.width, img.height]
|
|
78
|
+
buf = io.BytesIO()
|
|
79
|
+
img.save(buf, format="PNG", optimize=True)
|
|
80
|
+
return buf.getvalue(), meta
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _nice_step(span: float, target_lines: int = 8) -> float:
|
|
84
|
+
raw = span / max(target_lines, 1)
|
|
85
|
+
mag = 10 ** math.floor(math.log10(raw)) if raw > 0 else 1
|
|
86
|
+
for m in (1, 2, 5, 10):
|
|
87
|
+
if raw <= m * mag:
|
|
88
|
+
return m * mag
|
|
89
|
+
return 10 * mag
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def draw_grid(img: Image.Image, center: list[float], view_height: float) -> dict[str, Any]:
|
|
93
|
+
"""Draw a labelled coordinate grid on the image (plan view in WCS assumed).
|
|
94
|
+
|
|
95
|
+
Returns the pixel <-> drawing mapping so the model can read coordinates off the picture.
|
|
96
|
+
"""
|
|
97
|
+
W, H = img.size
|
|
98
|
+
upp = view_height / H # drawing units per pixel
|
|
99
|
+
x0 = center[0] - W / 2 * upp
|
|
100
|
+
y_top = center[1] + H / 2 * upp
|
|
101
|
+
step = _nice_step(max(W, H) * upp)
|
|
102
|
+
d = ImageDraw.Draw(img, "RGBA")
|
|
103
|
+
font = ImageFont.load_default()
|
|
104
|
+
color, label = (0, 200, 255, 90), (0, 220, 255, 255)
|
|
105
|
+
|
|
106
|
+
def fmt(v: float) -> str:
|
|
107
|
+
return f"{v:.0f}" if step >= 1 else f"{v:.3g}"
|
|
108
|
+
|
|
109
|
+
gx = math.ceil(x0 / step) * step
|
|
110
|
+
while gx <= x0 + W * upp:
|
|
111
|
+
px = (gx - x0) / upp
|
|
112
|
+
d.line([(px, 0), (px, H)], fill=color, width=1)
|
|
113
|
+
d.text((px + 2, H - 12), fmt(gx), fill=label, font=font)
|
|
114
|
+
gx += step
|
|
115
|
+
y_bottom = y_top - H * upp
|
|
116
|
+
gy = math.ceil(y_bottom / step) * step
|
|
117
|
+
while gy <= y_top:
|
|
118
|
+
py = (y_top - gy) / upp
|
|
119
|
+
d.line([(0, py), (W, py)], fill=color, width=1)
|
|
120
|
+
d.text((2, py + 2), fmt(gy), fill=label, font=font)
|
|
121
|
+
gy += step
|
|
122
|
+
return {
|
|
123
|
+
"grid_step": step,
|
|
124
|
+
"units_per_pixel": upp,
|
|
125
|
+
"top_left": [x0, y_top],
|
|
126
|
+
"how_to_read": tr("x = top_left.x + px * units_per_pixel; y = top_left.y - py * units_per_pixel",
|
|
127
|
+
"x = top_left.x + px * units_per_pixel; y = top_left.y - py * units_per_pixel"),
|
|
128
|
+
}
|