Initial commit
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
---
|
||||
name: autocad-mcp
|
||||
description: |
|
||||
Automate AutoCAD drawing operations through AutoCAD MCP Pro server (FastMCP 3.0).
|
||||
86 tools for drawings, entities, layers, blocks, dimensions, analysis, batch ops,
|
||||
templates, and validation. Dual backends: COM (live AutoCAD) and ezdxf (headless DXF).
|
||||
|
||||
Trigger when: (1) User mentions AutoCAD, DXF, DWG, or CAD drawings,
|
||||
(2) User wants to create/modify engineering drawings programmatically,
|
||||
(3) Working with tools prefixed drawing_, entity_, layer_, block_, dimension_,
|
||||
analysis_, view_, system_, template_, or validation_,
|
||||
(4) User asks about CAD automation, drafting workflows, or MCP server setup.
|
||||
---
|
||||
|
||||
# AutoCAD MCP Pro
|
||||
|
||||
## Contents
|
||||
|
||||
- [Quick Start](#quick-start)
|
||||
- [Backend Selection](#backend-selection)
|
||||
- [Key Concepts](#key-concepts)
|
||||
- [Tool Groups](#tool-groups)
|
||||
- [Common Workflows](#common-workflows)
|
||||
- [Security Rules](#security-rules)
|
||||
- [Architecture](#architecture)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Examples](#examples)
|
||||
|
||||
## Quick Start
|
||||
|
||||
Standard workflow for any drawing task:
|
||||
|
||||
```
|
||||
1. drawing_new() # or drawing_open(path)
|
||||
2. layer_create(name, color, linetype) # set up layers
|
||||
3. entity_create_*(...) # draw geometry
|
||||
4. dimension_*(...) # add dimensions
|
||||
5. analysis_entity_stats() # verify drawing
|
||||
6. drawing_save() # persist changes
|
||||
```
|
||||
|
||||
Use `transaction_begin()` before complex operations. If something goes wrong, `transaction_rollback()` reverts all changes since begin.
|
||||
|
||||
For batch operations, use `entity_batch_create(entities)` with a list of entity definitions — handles are returned for each created entity.
|
||||
|
||||
## Backend Selection
|
||||
|
||||
| Environment Variable | Behavior |
|
||||
|---------------------|----------|
|
||||
| `AUTOCAD_MCP_BACKEND=auto` | Try COM first, fall back to ezdxf (default) |
|
||||
| `AUTOCAD_MCP_BACKEND=com` | Force COM — requires running AutoCAD on Windows |
|
||||
| `AUTOCAD_MCP_BACKEND=ezdxf` | Force ezdxf — headless DXF ops, no AutoCAD needed |
|
||||
|
||||
COM-only features: `system_run_command`, `system_run_lisp`, `view_zoom_extents/window` (view ops are no-ops in ezdxf). Screenshots: COM uses Win32 capture, ezdxf uses matplotlib render.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
**Entity handles**: Hex strings (e.g. `"1A2B"`) returned by all create/copy operations. Required for modify/query operations (`entity_get`, `entity_move`, `entity_delete`, etc.).
|
||||
|
||||
**Coordinates**: Drawing units (mm default). Angles in degrees, counter-clockwise from X axis.
|
||||
|
||||
**ACI colors**: 1–255 = specific colors, 256 = ByLayer, 0 = ByBlock. Common: 1=red, 2=yellow, 3=green, 4=cyan, 5=blue, 6=magenta, 7=white.
|
||||
|
||||
**Transactions**: `transaction_begin` → operations → `transaction_commit` or `transaction_rollback`. ezdxf uses full DXF snapshots; COM uses native undo marks.
|
||||
|
||||
## Tool Groups
|
||||
|
||||
| Group | Count | Key Tools | Details |
|
||||
|-------|-------|-----------|---------|
|
||||
| Drawing Management | 11 | `drawing_new`, `drawing_open`, `drawing_save`, `drawing_export_*` | [tools-drawing.md](references/tools-drawing.md) |
|
||||
| Entity Creation | 13 | `entity_create_line/circle/arc/polyline/text/hatch/...` | [tools-entity-creation.md](references/tools-entity-creation.md) |
|
||||
| Entity Modification | 13 | `entity_move/copy/rotate/scale/mirror/offset/delete/...` | [tools-entity-modification.md](references/tools-entity-modification.md) |
|
||||
| Dimensions | 5 | `dimension_linear/aligned/angular/radius/diameter` | [tools-dimensions.md](references/tools-dimensions.md) |
|
||||
| Layer Management | 12 | `layer_create/delete/freeze/thaw/lock/unlock/isolate/...` | [tools-layers.md](references/tools-layers.md) |
|
||||
| Block Operations | 7 | `block_insert/explode/create_from_entities/attributes/...` | [tools-blocks.md](references/tools-blocks.md) |
|
||||
| Analysis | 9 | `analysis_entity_stats/find_in_region/measure_*/bounding_box` | [tools-analysis.md](references/tools-analysis.md) |
|
||||
| Batch & Templates | 4 | `entity_batch_create/modify`, `template_apply_layers/list` | [tools-batch-template.md](references/tools-batch-template.md) |
|
||||
| Validation | 1 | `validation_check` | [tools-batch-template.md](references/tools-batch-template.md) |
|
||||
| View & Screenshot | 5 | `view_zoom_extents/window/screenshot/zoom_and_screenshot` | [tools-view-system.md](references/tools-view-system.md) |
|
||||
| Transactions | 3 | `transaction_begin/commit/rollback` | [tools-view-system.md](references/tools-view-system.md) |
|
||||
| System | 6 | `system_status/get_variable/run_command/run_lisp/about` | [tools-view-system.md](references/tools-view-system.md) |
|
||||
|
||||
**Resources** (6): `autocad://drawing`, `autocad://layers`, `autocad://blocks`, `autocad://stats`, `autocad://status`, `autocad://entities/{layer_name}`
|
||||
|
||||
**Prompt templates** (5): `prompt_floor_plan`, `prompt_pid_diagram`, `prompt_electrical_schematic`, `prompt_mechanical_drawing`, `prompt_quick_drawing`
|
||||
|
||||
## Common Workflows
|
||||
|
||||
**New drawing from scratch**: Set up layers first, then draw entities, add dimensions, save.
|
||||
See [workflows.md](references/workflows.md) for step-by-step checklists.
|
||||
|
||||
**Edit existing DXF**: `drawing_open` → `entity_list` to find entities → modify/delete → save.
|
||||
|
||||
**Batch operations**: `entity_batch_create([{type:"line", ...}, {type:"circle", ...}])` — single call, multiple entities.
|
||||
|
||||
**Template-based**: `template_apply_layers("architectural")` creates standard layer sets, then add content.
|
||||
|
||||
**Quality check**: `validation_check(["empty_layers", "zero_length", "duplicate_entities"])` reports issues.
|
||||
|
||||
## Security Rules
|
||||
|
||||
All file paths pass through `validate_path()`:
|
||||
- Path traversal blocked (`../` patterns rejected)
|
||||
- `ALLOWED_PATHS` env restricts accessible directories
|
||||
- Write operations require `allow_write=True`
|
||||
|
||||
Commands pass through `sanitize_command()`:
|
||||
- Dangerous patterns blocked: ERASE ALL, DELETE, PURGE, QUIT
|
||||
- LISP sanitized: `startapp`, `vl-file-delete`, `dos_*` rejected
|
||||
- Override: `DANGEROUS_COMMANDS_ENABLED=true`
|
||||
|
||||
Details: [security.md](references/security.md)
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
server.py FastMCP 3.0 server (86 tools, 6 resources, 5 prompts)
|
||||
├── config.py Centralized pydantic-settings configuration
|
||||
├── security.py Path validation + command sanitization
|
||||
└── backends/
|
||||
├── base.py AutoCADBackend ABC + data models
|
||||
├── ezdxf_backend.py Headless DXF via ezdxf (async via _async wrapper)
|
||||
└── com_backend.py Live AutoCAD via pywin32 COM (STA single-thread)
|
||||
```
|
||||
|
||||
Middleware stack: ErrorHandling → AuditLog → Timing → Logging
|
||||
|
||||
Adding a new tool: (1) abstract method in `base.py` → (2) implement in `ezdxf_backend.py` → (3) implement in `com_backend.py` → (4) register in `server.py` with `@mcp.tool` → (5) test
|
||||
|
||||
Details: [architecture.md](references/architecture.md)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Error | Solution |
|
||||
|-------|----------|
|
||||
| "Backend unavailable" | Call `drawing_new()` or `drawing_open(path)` first |
|
||||
| "Entity handle not found" | Use `entity_list()` to get valid handles |
|
||||
| "Layer not found" | `layer_list()` to see existing, `layer_create()` to add |
|
||||
| "Path validation failed" | Use absolute paths within `ALLOWED_PATHS` |
|
||||
| "Command rejected" | Check `DANGEROUS_COMMANDS_ENABLED` setting |
|
||||
| "COM backend failed" | Verify AutoCAD is running, or switch to ezdxf |
|
||||
| "View ops are no-ops" | View/zoom only work with COM backend |
|
||||
|
||||
Full list: [troubleshooting.md](references/troubleshooting.md)
|
||||
|
||||
## Examples
|
||||
|
||||
- [Basic drawing workflow](examples/basic-workflow.md) — Room with walls, door, dimensions
|
||||
- [Floor plan](examples/floor-plan.md) — Multi-room apartment layout
|
||||
- [Mechanical part](examples/mechanical-part.md) — Flange with bolt pattern
|
||||
|
||||
## Utility Scripts
|
||||
|
||||
**Setup check**: `python scripts/check_setup.py` — Verify dependencies and configuration
|
||||
|
||||
**Drawing validation**: `python scripts/validate_drawing.py` — Check for common drawing issues
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"architectural": {
|
||||
"description": "Standard architectural drawing layers",
|
||||
"layers": [
|
||||
{"name": "WALLS", "color": 7, "linetype": "Continuous", "description": "Wall outlines"},
|
||||
{"name": "DOORS", "color": 3, "linetype": "Continuous", "description": "Door swings and frames"},
|
||||
{"name": "WINDOWS", "color": 5, "linetype": "Continuous", "description": "Window openings"},
|
||||
{"name": "DIMENSIONS", "color": 1, "linetype": "Continuous", "description": "Dimension annotations"},
|
||||
{"name": "TEXT", "color": 2, "linetype": "Continuous", "description": "Text labels and notes"},
|
||||
{"name": "FURNITURE", "color": 8, "linetype": "Continuous", "description": "Furniture outlines"},
|
||||
{"name": "PLUMBING", "color": 4, "linetype": "Continuous", "description": "Plumbing fixtures"},
|
||||
{"name": "HVAC", "color": 6, "linetype": "Continuous", "description": "HVAC equipment and ducts"}
|
||||
]
|
||||
},
|
||||
"mechanical": {
|
||||
"description": "Standard mechanical drawing layers",
|
||||
"layers": [
|
||||
{"name": "OUTLINE", "color": 7, "linetype": "Continuous", "description": "Part outlines"},
|
||||
{"name": "CENTERLINE", "color": 1, "linetype": "CENTER", "description": "Center lines and axes"},
|
||||
{"name": "HIDDEN", "color": 8, "linetype": "HIDDEN", "description": "Hidden edges"},
|
||||
{"name": "DIMENSIONS", "color": 5, "linetype": "Continuous", "description": "Dimensions"},
|
||||
{"name": "NOTES", "color": 2, "linetype": "Continuous", "description": "Notes and callouts"},
|
||||
{"name": "SECTION", "color": 3, "linetype": "Continuous", "description": "Section lines and hatching"},
|
||||
{"name": "DETAIL", "color": 4, "linetype": "Continuous", "description": "Detail views"}
|
||||
]
|
||||
},
|
||||
"electrical": {
|
||||
"description": "Standard electrical drawing layers",
|
||||
"layers": [
|
||||
{"name": "POWER", "color": 1, "linetype": "Continuous", "description": "Power circuits"},
|
||||
{"name": "LIGHTING", "color": 2, "linetype": "Continuous", "description": "Lighting circuits"},
|
||||
{"name": "DATA", "color": 3, "linetype": "Continuous", "description": "Data and communication"},
|
||||
{"name": "FIRE_ALARM", "color": 4, "linetype": "Continuous", "description": "Fire alarm system"},
|
||||
{"name": "HVAC_CTRL", "color": 5, "linetype": "Continuous", "description": "HVAC controls"},
|
||||
{"name": "PANELS", "color": 7, "linetype": "Continuous", "description": "Panel boards and equipment"},
|
||||
{"name": "CONDUIT", "color": 8, "linetype": "Continuous", "description": "Conduit runs"}
|
||||
]
|
||||
},
|
||||
"piping": {
|
||||
"description": "Standard piping/P&ID drawing layers",
|
||||
"layers": [
|
||||
{"name": "PROCESS", "color": 7, "linetype": "Continuous", "description": "Process piping"},
|
||||
{"name": "UTILITY", "color": 3, "linetype": "Continuous", "description": "Utility piping"},
|
||||
{"name": "INSTRUMENTS", "color": 1, "linetype": "Continuous", "description": "Instrumentation"},
|
||||
{"name": "EQUIPMENT", "color": 5, "linetype": "Continuous", "description": "Equipment outlines"},
|
||||
{"name": "VALVES", "color": 2, "linetype": "Continuous", "description": "Valves and fittings"},
|
||||
{"name": "LABELS", "color": 4, "linetype": "Continuous", "description": "Labels and tags"}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
# Example: Basic Room Drawing
|
||||
|
||||
Create a simple room with walls, a door opening, dimensions, and labels.
|
||||
|
||||
## Setup
|
||||
|
||||
```
|
||||
drawing_new()
|
||||
layer_create("WALLS", color=7)
|
||||
layer_create("DOORS", color=3)
|
||||
layer_create("DIMENSIONS", color=1)
|
||||
layer_create("TEXT", color=2)
|
||||
```
|
||||
|
||||
## Draw Walls (5m x 4m room)
|
||||
|
||||
```
|
||||
layer_set_current("WALLS")
|
||||
|
||||
# Outer walls as rectangle
|
||||
entity_create_rectangle(x1=0, y1=0, x2=5000, y2=4000)
|
||||
# Returns: {handle: "A1", type: "LWPOLYLINE", ...}
|
||||
```
|
||||
|
||||
## Add Door Opening (0.9m wide on south wall)
|
||||
|
||||
```
|
||||
layer_set_current("DOORS")
|
||||
|
||||
# Door opening represented as an arc (90° swing)
|
||||
entity_create_arc(cx=2000, cy=0, radius=900, start_angle=0, end_angle=90)
|
||||
# Returns: {handle: "B2", type: "ARC", ...}
|
||||
|
||||
# Door frame lines
|
||||
entity_create_line(x1=2000, y1=0, x2=2000, y2=100)
|
||||
entity_create_line(x1=2900, y1=0, x2=2900, y2=100)
|
||||
```
|
||||
|
||||
## Add Dimensions
|
||||
|
||||
```
|
||||
layer_set_current("DIMENSIONS")
|
||||
|
||||
# Room width
|
||||
dimension_linear(x1=0, y1=0, x2=5000, y2=0, dim_x=2500, dim_y=-500)
|
||||
|
||||
# Room height
|
||||
dimension_linear(x1=5000, y1=0, x2=5000, y2=4000, dim_x=5500, dim_y=2000)
|
||||
|
||||
# Door width
|
||||
dimension_linear(x1=2000, y1=0, x2=2900, y2=0, dim_x=2450, dim_y=-300)
|
||||
```
|
||||
|
||||
## Add Labels
|
||||
|
||||
```
|
||||
layer_set_current("TEXT")
|
||||
entity_create_text(text="BEDROOM", x=2500, y=2000, height=200)
|
||||
entity_create_text(text="5000", x=2500, y=-800, height=150)
|
||||
```
|
||||
|
||||
## Verify and Save
|
||||
|
||||
```
|
||||
analysis_entity_stats()
|
||||
# Expected: ~8 entities across 4 layers
|
||||
|
||||
validation_check(["empty_layers"])
|
||||
# Should report no issues
|
||||
|
||||
drawing_save("C:/drawings/room.dxf")
|
||||
```
|
||||
@@ -0,0 +1,100 @@
|
||||
# Example: Apartment Floor Plan
|
||||
|
||||
Create a 3-room apartment: living room, bedroom, kitchen with doors and windows.
|
||||
|
||||
## Setup
|
||||
|
||||
```
|
||||
drawing_new()
|
||||
template_apply_layers("architectural")
|
||||
# Creates: WALLS, DOORS, WINDOWS, DIMENSIONS, TEXT, FURNITURE, PLUMBING, HVAC
|
||||
```
|
||||
|
||||
## Draw Exterior Walls (12m x 8m)
|
||||
|
||||
```
|
||||
layer_set_current("WALLS")
|
||||
|
||||
# Exterior boundary
|
||||
entity_create_rectangle(x1=0, y1=0, x2=12000, y2=8000)
|
||||
```
|
||||
|
||||
## Draw Interior Walls
|
||||
|
||||
```
|
||||
# Living/bedroom divider (vertical at x=5000)
|
||||
entity_create_line(x1=5000, y1=0, x2=5000, y2=8000)
|
||||
|
||||
# Kitchen divider (horizontal at y=4000, from x=5000)
|
||||
entity_create_line(x1=5000, y1=4000, x2=12000, y2=4000)
|
||||
```
|
||||
|
||||
## Add Doors (0.9m openings)
|
||||
|
||||
```
|
||||
layer_set_current("DOORS")
|
||||
|
||||
# Living room door (south wall)
|
||||
entity_create_arc(cx=2000, cy=0, radius=900, start_angle=0, end_angle=90)
|
||||
|
||||
# Bedroom door (on divider wall)
|
||||
entity_create_arc(cx=5000, cy=2000, radius=900, start_angle=90, end_angle=180)
|
||||
|
||||
# Kitchen door (on divider wall)
|
||||
entity_create_arc(cx=7000, cy=4000, radius=900, start_angle=0, end_angle=90)
|
||||
```
|
||||
|
||||
## Add Windows (1.5m wide)
|
||||
|
||||
```
|
||||
layer_set_current("WINDOWS")
|
||||
|
||||
# Living room window (north wall)
|
||||
entity_create_line(x1=1500, y1=8000, x2=3000, y2=8000, color=5)
|
||||
|
||||
# Bedroom window (east wall)
|
||||
entity_create_line(x1=12000, y1=5500, x2=12000, y2=7000, color=5)
|
||||
|
||||
# Kitchen window (east wall)
|
||||
entity_create_line(x1=12000, y1=1500, x2=12000, y2=3000, color=5)
|
||||
```
|
||||
|
||||
## Add Dimensions
|
||||
|
||||
```
|
||||
layer_set_current("DIMENSIONS")
|
||||
|
||||
# Overall width
|
||||
dimension_linear(x1=0, y1=0, x2=12000, y2=0, dim_x=6000, dim_y=-800)
|
||||
|
||||
# Overall height
|
||||
dimension_linear(x1=0, y1=0, x2=0, y2=8000, dim_x=-800, dim_y=4000)
|
||||
|
||||
# Room widths
|
||||
dimension_linear(x1=0, y1=8000, x2=5000, y2=8000, dim_x=2500, dim_y=8500)
|
||||
dimension_linear(x1=5000, y1=8000, x2=12000, y2=8000, dim_x=8500, dim_y=8500)
|
||||
```
|
||||
|
||||
## Add Room Labels
|
||||
|
||||
```
|
||||
layer_set_current("TEXT")
|
||||
|
||||
entity_create_text(text="LIVING ROOM", x=2500, y=4000, height=300)
|
||||
entity_create_mtext(text="12.0 m²", x=2500, y=3500, width=2000, height=200)
|
||||
|
||||
entity_create_text(text="BEDROOM", x=8500, y=6000, height=300)
|
||||
entity_create_mtext(text="28.0 m²", x=8500, y=5500, width=2000, height=200)
|
||||
|
||||
entity_create_text(text="KITCHEN", x=8500, y=2000, height=300)
|
||||
entity_create_mtext(text="28.0 m²", x=8500, y=1500, width=2000, height=200)
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
```
|
||||
analysis_entity_stats()
|
||||
analysis_layer_stats()
|
||||
validation_check(["empty_layers", "zero_length"])
|
||||
drawing_save("C:/drawings/apartment.dxf")
|
||||
```
|
||||
@@ -0,0 +1,105 @@
|
||||
# Example: Flange with Bolt Pattern
|
||||
|
||||
Create a circular flange plate with bolt holes using polar array.
|
||||
|
||||
## Setup
|
||||
|
||||
```
|
||||
drawing_new()
|
||||
template_apply_layers("mechanical")
|
||||
# Creates: OUTLINE, CENTERLINE, HIDDEN, DIMENSIONS, NOTES, SECTION, DETAIL
|
||||
```
|
||||
|
||||
## Draw Flange Outline
|
||||
|
||||
```
|
||||
layer_set_current("OUTLINE")
|
||||
|
||||
# Outer flange circle (diameter 200mm)
|
||||
entity_create_circle(cx=0, cy=0, radius=100)
|
||||
# Returns: {handle: "F1", ...}
|
||||
|
||||
# Inner bore (diameter 50mm)
|
||||
entity_create_circle(cx=0, cy=0, radius=25)
|
||||
# Returns: {handle: "F2", ...}
|
||||
```
|
||||
|
||||
## Add Centerlines
|
||||
|
||||
```
|
||||
layer_set_current("CENTERLINE")
|
||||
|
||||
# Horizontal centerline
|
||||
entity_create_line(x1=-120, y1=0, x2=120, y2=0, linetype="CENTER")
|
||||
|
||||
# Vertical centerline
|
||||
entity_create_line(x1=0, y1=-120, x2=0, y2=120, linetype="CENTER")
|
||||
```
|
||||
|
||||
## Create Bolt Hole Pattern
|
||||
|
||||
```
|
||||
layer_set_current("OUTLINE")
|
||||
|
||||
# First bolt hole on bolt circle (PCD = 150mm diameter, so radius = 75mm)
|
||||
entity_create_circle(cx=75, cy=0, radius=6)
|
||||
# Returns: {handle: "B1", ...}
|
||||
|
||||
# Create polar array: 6 holes evenly spaced
|
||||
entity_array_polar(handle="B1", center_x=0, center_y=0, count=6, angle=360)
|
||||
# Returns: list of 5 new handles (original + 5 copies = 6 total)
|
||||
```
|
||||
|
||||
## Add Dimensions
|
||||
|
||||
```
|
||||
layer_set_current("DIMENSIONS")
|
||||
|
||||
# Outer diameter
|
||||
dimension_diameter(x1=-100, y1=0, x2=100, y2=0)
|
||||
|
||||
# Inner bore diameter
|
||||
dimension_diameter(x1=-25, y1=0, x2=25, y2=0)
|
||||
|
||||
# Bolt circle diameter
|
||||
dimension_radius(center_x=0, center_y=0, radius=75, angle=45)
|
||||
|
||||
# Bolt hole diameter
|
||||
dimension_diameter(x1=69, y1=0, x2=81, y2=0)
|
||||
```
|
||||
|
||||
## Add Notes
|
||||
|
||||
```
|
||||
layer_set_current("NOTES")
|
||||
|
||||
entity_create_mtext(
|
||||
text="FLANGE PLATE\P6x M12 HOLES ON PCD 150\PMATERIAL: SS304",
|
||||
x=-120, y=-140,
|
||||
width=240, height=5
|
||||
)
|
||||
```
|
||||
|
||||
## Create Block from Bolt Hole
|
||||
|
||||
```
|
||||
# Select all bolt hole entities and create a reusable block
|
||||
# (Get handles from the array result)
|
||||
block_create_from_entities(
|
||||
name="BOLT_PATTERN_6x150",
|
||||
handles=["B1", ...], # all bolt hole handles
|
||||
base_x=0, base_y=0
|
||||
)
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
```
|
||||
analysis_entity_stats()
|
||||
# Expected: circles, lines, dimensions, mtext
|
||||
|
||||
analysis_bounding_box()
|
||||
# Expected: approximately -120 to 120 in both axes
|
||||
|
||||
drawing_save("C:/drawings/flange.dxf")
|
||||
```
|
||||
@@ -0,0 +1,60 @@
|
||||
# Architecture
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
server.py FastMCP 3.0 server — all tool/resource/prompt registrations
|
||||
├── config.py Settings class (pydantic-settings, env vars)
|
||||
├── security.py validate_path() + sanitize_command() + sanitize_lisp()
|
||||
└── backends/
|
||||
├── base.py AutoCADBackend ABC + data models
|
||||
├── ezdxf_backend.py Headless DXF ops via ezdxf library
|
||||
└── com_backend.py Live AutoCAD control via pywin32 COM
|
||||
```
|
||||
|
||||
## Dual-Backend Strategy Pattern
|
||||
|
||||
`AutoCADBackend` (in `base.py`) is an abstract base class. Both backends implement the full interface. The server creates exactly one backend at startup via `_make_backend()`:
|
||||
|
||||
1. Check `AUTOCAD_MCP_BACKEND` env var
|
||||
2. If `auto` or `com` on Windows: try COM → fall back to ezdxf
|
||||
3. If non-Windows or `ezdxf`: use ezdxf
|
||||
|
||||
## Data Models
|
||||
|
||||
Defined in `base.py`:
|
||||
|
||||
- **EntityInfo**: `{handle, type, layer, color, linetype, visible, properties}`
|
||||
- **LayerInfo**: `{name, color, linetype, lineweight, is_on, is_frozen, is_locked, is_current}`
|
||||
- **BlockInfo**: `{name, origin, attribute_count, entity_count, is_xref, description}`
|
||||
- **DrawingInfo**: `{name, full_path, saved, entity_count, layer_count, block_count, extents_min, extents_max, units, version, backend}`
|
||||
- **CommandResult**: `{ok, payload, error}`
|
||||
|
||||
## Backend Differences
|
||||
|
||||
| Feature | ezdxf | COM |
|
||||
|---------|-------|-----|
|
||||
| Platform | Any | Windows only |
|
||||
| AutoCAD needed | No | Yes (running) |
|
||||
| View/zoom | No-op | Full control |
|
||||
| Screenshots | matplotlib render | Win32 window capture |
|
||||
| Commands/LISP | Not supported | Full support |
|
||||
| Undo | DXF snapshot stack | Native AutoCAD undo |
|
||||
| Threading | `asyncio.to_thread` via `_async()` | `ThreadPoolExecutor` (single thread, STA) |
|
||||
| File formats | DXF only | DWG + DXF |
|
||||
|
||||
## FastMCP Server
|
||||
|
||||
The `mcp` object is configured with:
|
||||
- **Lifespan**: `autocad_lifespan` — initializes backend, stores in `ctx.lifespan_context["backend"]`
|
||||
- **Middleware**: ErrorHandling → AuditLog (custom timing) → Timing → Logging
|
||||
- **`_backend(ctx)`**: Helper that retrieves backend from context, raises `ToolError` if unavailable
|
||||
|
||||
## Adding a New Tool
|
||||
|
||||
1. Add abstract method to `AutoCADBackend` in `backends/base.py`
|
||||
2. Implement in `EzdxfBackend` — wrap sync calls with `_async(func)`
|
||||
3. Implement in `ComBackend` — wrap COM calls with `self._com(func)`
|
||||
4. Register in `server.py`: `@mcp.tool(...)` calling `_backend(ctx).method(...)`
|
||||
5. Use `_dc(result)` to convert dataclass returns to dicts
|
||||
6. Write tests in `tests/`
|
||||
@@ -0,0 +1,53 @@
|
||||
# Security
|
||||
|
||||
## Path Validation
|
||||
|
||||
All file-path tools pass through `validate_path()` in `security.py`.
|
||||
|
||||
**How it works**:
|
||||
- Resolves path via `Path.resolve()` (removes `..`, symlinks)
|
||||
- Checks against `ALLOWED_PATHS` env var (comma-separated directories)
|
||||
- If `ALLOWED_PATHS` is empty → all paths allowed (backward compat)
|
||||
- Separate `allow_write` flag for read vs write operations
|
||||
|
||||
**Tools with path validation**:
|
||||
- `drawing_open` (read)
|
||||
- `drawing_save`, `drawing_save_as` (write)
|
||||
- `drawing_export_dxf`, `drawing_export_pdf` (write)
|
||||
- `drawing_new` with template (read)
|
||||
|
||||
**Blocked examples**:
|
||||
- `../../../etc/passwd` → rejected (traversal)
|
||||
- `C:\Windows\System32\...` → rejected if not in ALLOWED_PATHS
|
||||
- Relative paths with `..` → always rejected
|
||||
|
||||
## Command Sanitization
|
||||
|
||||
`sanitize_command()` checks `system_run_command` input:
|
||||
|
||||
**Blocked patterns**: `ERASE`, `DELETE`, `PURGE`, `QUIT`, `CLOSE`, `_QUIT`, shell-related commands
|
||||
|
||||
**Override**: Set `DANGEROUS_COMMANDS_ENABLED=true` to bypass.
|
||||
|
||||
## LISP Sanitization
|
||||
|
||||
`sanitize_lisp()` checks `system_run_lisp` input:
|
||||
|
||||
**Blocked functions**: `startapp`, `vl-file-delete`, `vl-file-rename`, `dos_*` (file system access), `command` with dangerous args
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `ALLOWED_PATHS` | (empty) | Comma-separated allowed directories. Empty = all allowed |
|
||||
| `DANGEROUS_COMMANDS_ENABLED` | `false` | Allow blocked commands |
|
||||
| `AUTOCAD_MCP_BACKEND` | `auto` | Backend selection |
|
||||
| `MAX_UNDO_STACK` | `50` | Max undo snapshots (ezdxf) |
|
||||
| `LOG_LEVEL` | `INFO` | Logging level |
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Always set `ALLOWED_PATHS` in production to restrict file access
|
||||
- Never pass raw user input to `system_run_command` or `system_run_lisp`
|
||||
- Use `transaction_begin/rollback` for destructive batch operations
|
||||
- Review audit logs for suspicious tool calls (AuditMiddleware logs all calls)
|
||||
@@ -0,0 +1,73 @@
|
||||
# Analysis Tools (9)
|
||||
|
||||
Tools for querying and measuring drawing contents.
|
||||
|
||||
---
|
||||
|
||||
## analysis_entity_stats
|
||||
No parameters. Returns entity counts grouped by type and by layer.
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 150,
|
||||
"by_type": {"LINE": 50, "CIRCLE": 20, "TEXT": 30, ...},
|
||||
"by_layer": {"WALLS": 40, "DOORS": 10, ...}
|
||||
}
|
||||
```
|
||||
|
||||
## analysis_find_in_region
|
||||
Find entities within a rectangular region.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `x1, y1` | `float` | Region minimum corner |
|
||||
| `x2, y2` | `float` | Region maximum corner |
|
||||
| `type_filter` | `str \| null` | Filter by entity type |
|
||||
|
||||
Returns list of entity infos within the bounding box.
|
||||
|
||||
## analysis_measure_distance
|
||||
Measure distance between two points.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `x1, y1` | `float` | Point 1 |
|
||||
| `x2, y2` | `float` | Point 2 |
|
||||
|
||||
Returns `{distance, dx, dy, angle}`.
|
||||
|
||||
## analysis_measure_area
|
||||
Calculate area of a polygon.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `points` | `list[list[float]]` | Polygon vertices (min 3 points) |
|
||||
|
||||
Returns `{area, perimeter, centroid}`.
|
||||
|
||||
## analysis_bounding_box
|
||||
No parameters. Returns `{min_x, min_y, max_x, max_y, width, height}` for all entities.
|
||||
|
||||
## analysis_select_by_layer
|
||||
Get all entities on a specific layer.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `layer_name` | `str` | Layer to select from |
|
||||
|
||||
Returns list of entity infos.
|
||||
|
||||
## analysis_select_by_type
|
||||
Get all entities of a specific type.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `entity_type` | `str` | Type: LINE, CIRCLE, ARC, LWPOLYLINE, TEXT, MTEXT, INSERT, HATCH, SPLINE, ELLIPSE |
|
||||
|
||||
Returns list of entity infos.
|
||||
|
||||
## analysis_layer_stats
|
||||
No parameters. Returns per-layer statistics: entity count, types present, color, state.
|
||||
|
||||
## analysis_entities_in_region
|
||||
Alias/variant of `analysis_find_in_region` — check both for availability.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Batch, Template & Validation Tools (5)
|
||||
|
||||
## Batch Operations
|
||||
|
||||
### entity_batch_create
|
||||
Create multiple entities in a single call. Reports progress.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `entities` | `list[dict]` | Entity definitions (see format below) |
|
||||
|
||||
Each dict requires `type` and type-specific params:
|
||||
|
||||
```json
|
||||
[
|
||||
{"type": "line", "x1": 0, "y1": 0, "x2": 100, "y2": 0},
|
||||
{"type": "circle", "cx": 50, "cy": 50, "radius": 25},
|
||||
{"type": "text", "text": "Hello", "x": 10, "y": 10, "height": 5},
|
||||
{"type": "rectangle", "x1": 0, "y1": 0, "x2": 200, "y2": 100},
|
||||
{"type": "point", "x": 75, "y": 75},
|
||||
{"type": "arc", "cx": 50, "cy": 0, "radius": 10, "start_angle": 0, "end_angle": 180},
|
||||
{"type": "polyline", "points": [[0,0],[10,10],[20,0]], "closed": true}
|
||||
]
|
||||
```
|
||||
|
||||
Optional per-entity: `layer`, `color`, `linetype`.
|
||||
|
||||
Returns `{created: [...handles], errors: [...]}`. Partial success is possible.
|
||||
|
||||
### entity_batch_modify
|
||||
Apply multiple modifications in a single call. Reports progress.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `operations` | `list[dict]` | Operation definitions |
|
||||
|
||||
Each dict requires `handle` and `action`:
|
||||
|
||||
```json
|
||||
[
|
||||
{"handle": "1A", "action": "move", "dx": 10, "dy": 20},
|
||||
{"handle": "2B", "action": "rotate", "base_x": 0, "base_y": 0, "angle_deg": 45},
|
||||
{"handle": "3C", "action": "scale", "base_x": 0, "base_y": 0, "factor": 2.0},
|
||||
{"handle": "4D", "action": "delete"},
|
||||
{"handle": "5E", "action": "set_properties", "layer": "WALLS", "color": 1}
|
||||
]
|
||||
```
|
||||
|
||||
Returns results per operation.
|
||||
|
||||
---
|
||||
|
||||
## Templates
|
||||
|
||||
### template_apply_layers
|
||||
Apply a standard layer set to the current drawing.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `template` | `str` | Template name: `architectural`, `mechanical`, `electrical`, `piping` |
|
||||
|
||||
Creates predefined layers with standard names, colors, and linetypes.
|
||||
|
||||
### template_list
|
||||
No parameters. Returns all available templates and their layer definitions.
|
||||
|
||||
---
|
||||
|
||||
## Validation
|
||||
|
||||
### validation_check
|
||||
Run quality checks on the drawing.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `checks` | `list[str] \| null` | Check types (default: all) |
|
||||
|
||||
Available checks:
|
||||
- `empty_layers` — Layers with no entities
|
||||
- `zero_length` — Lines/arcs with zero length
|
||||
- `duplicate_entities` — Overlapping identical entities
|
||||
|
||||
Returns `{issues: [{check, severity, message, details}, ...], summary}`.
|
||||
@@ -0,0 +1,68 @@
|
||||
# Block Operations Tools (7)
|
||||
|
||||
Blocks are reusable named groups of entities. A **block definition** is the template; a **block reference** (INSERT entity) is a placed instance with position, scale, and rotation.
|
||||
|
||||
---
|
||||
|
||||
## block_list
|
||||
No parameters. Returns all block definitions: `{name, origin, attribute_count, entity_count, is_xref, description}`.
|
||||
|
||||
## block_insert
|
||||
Place a block reference in the drawing.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | Block definition name (must exist) |
|
||||
| `x, y` | `float` | Insertion point |
|
||||
| `x_scale` | `float` | X scale factor (default: 1.0) |
|
||||
| `y_scale` | `float` | Y scale factor (default: 1.0) |
|
||||
| `rotation` | `float` | Rotation in degrees (default: 0) |
|
||||
| `layer` | `str \| null` | Target layer |
|
||||
|
||||
Returns entity info with handle (type: INSERT).
|
||||
|
||||
## block_explode
|
||||
Decompose a block reference into individual entities.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Block reference (INSERT entity) handle |
|
||||
|
||||
Returns list of new entity handles created from explosion.
|
||||
|
||||
## block_get_attributes
|
||||
Read attribute values from a block reference.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Block reference handle |
|
||||
|
||||
Returns `{tag: value}` dict of all attributes.
|
||||
|
||||
## block_set_attributes
|
||||
Write attribute values to a block reference.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Block reference handle |
|
||||
| `attributes` | `dict` | `{tag: value}` pairs to set |
|
||||
|
||||
## block_create_from_entities
|
||||
Create a new block definition from existing entities.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | New block definition name |
|
||||
| `handles` | `list[str]` | Entity handles to include |
|
||||
| `base_x, base_y` | `float` | Block base/insertion point |
|
||||
|
||||
Entities are moved into the block definition.
|
||||
|
||||
## block_find_references
|
||||
Find all INSERT entities that reference a specific block.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | Block definition name |
|
||||
|
||||
Returns list of block reference entity infos.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Dimension Tools (5)
|
||||
|
||||
All dimension tools return entity info with handle. Dimensions are placed on the current layer unless `layer` is specified.
|
||||
|
||||
---
|
||||
|
||||
## dimension_linear
|
||||
Linear dimension between two points (horizontal or vertical measurement).
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `x1, y1` | `float` | First extension line origin |
|
||||
| `x2, y2` | `float` | Second extension line origin |
|
||||
| `dim_x, dim_y` | `float` | Dimension line position (controls placement) |
|
||||
| `layer` | `str \| null` | Target layer |
|
||||
|
||||
## dimension_aligned
|
||||
Aligned dimension — measures true distance between points regardless of angle.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `x1, y1` | `float` | First point |
|
||||
| `x2, y2` | `float` | Second point |
|
||||
| `distance` | `float` | Offset distance for dimension line |
|
||||
| `layer` | `str \| null` | Target layer |
|
||||
|
||||
## dimension_angular
|
||||
Angular dimension between two lines meeting at a vertex.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `vertex_x, vertex_y` | `float` | Angle vertex point |
|
||||
| `start_x, start_y` | `float` | First line endpoint |
|
||||
| `end_x, end_y` | `float` | Second line endpoint |
|
||||
| `dim_x, dim_y` | `float` | Dimension arc position |
|
||||
| `layer` | `str \| null` | Target layer |
|
||||
|
||||
## dimension_radius
|
||||
Radius dimension for circles and arcs.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `center_x, center_y` | `float` | Circle/arc center |
|
||||
| `radius` | `float` | Radius value |
|
||||
| `angle` | `float` | Leader angle in degrees (where arrow points) |
|
||||
| `layer` | `str \| null` | Target layer |
|
||||
|
||||
## dimension_diameter
|
||||
Diameter dimension across a circle.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `x1, y1` | `float` | First point on diameter |
|
||||
| `x2, y2` | `float` | Opposite point on diameter |
|
||||
| `layer` | `str \| null` | Target layer |
|
||||
@@ -0,0 +1,103 @@
|
||||
# Drawing Management Tools (11)
|
||||
|
||||
## Contents
|
||||
- [drawing_info](#drawing_info)
|
||||
- [drawing_new](#drawing_new)
|
||||
- [drawing_open](#drawing_open)
|
||||
- [drawing_save](#drawing_save)
|
||||
- [drawing_save_as](#drawing_save_as)
|
||||
- [drawing_export_dxf](#drawing_export_dxf)
|
||||
- [drawing_export_pdf](#drawing_export_pdf)
|
||||
- [drawing_purge](#drawing_purge)
|
||||
- [drawing_audit](#drawing_audit)
|
||||
- [drawing_undo / drawing_redo](#drawing_undo--drawing_redo)
|
||||
|
||||
---
|
||||
|
||||
## drawing_info
|
||||
Read-only. Returns comprehensive metadata for the current drawing.
|
||||
|
||||
**Returns**: `{name, full_path, saved, entity_count, layer_count, block_count, extents_min, extents_max, units, version, backend}`
|
||||
|
||||
---
|
||||
|
||||
## drawing_new
|
||||
Create a new empty drawing, optionally from a template.
|
||||
|
||||
| Param | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `template` | `str \| null` | No | Path to `.dwt` template file |
|
||||
|
||||
Path validation applied to template. Returns drawing info dict.
|
||||
|
||||
---
|
||||
|
||||
## drawing_open
|
||||
Open an existing DWG or DXF file. Reports progress.
|
||||
|
||||
| Param | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `path` | `str` | Yes | Full path to `.dwg` or `.dxf` file |
|
||||
|
||||
Path validation: `allow_write=False`. Returns drawing info dict.
|
||||
|
||||
---
|
||||
|
||||
## drawing_save
|
||||
Save current drawing. Uses current path if omitted.
|
||||
|
||||
| Param | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `path` | `str \| null` | No | Optional save path |
|
||||
|
||||
Path validation: `allow_write=True`.
|
||||
|
||||
---
|
||||
|
||||
## drawing_save_as
|
||||
Save drawing to a new path.
|
||||
|
||||
| Param | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `path` | `str` | Yes | Full destination path with extension |
|
||||
|
||||
Path validation: `allow_write=True`.
|
||||
|
||||
---
|
||||
|
||||
## drawing_export_dxf
|
||||
Export drawing as DXF file.
|
||||
|
||||
| Param | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `path` | `str` | Yes | Output `.dxf` file path |
|
||||
| `version` | `str` | No | DXF version: `R2010`, `R2013`, `R2018` (default: `R2018`) |
|
||||
|
||||
---
|
||||
|
||||
## drawing_export_pdf
|
||||
Export drawing as PDF. Requires `matplotlib`.
|
||||
|
||||
| Param | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `path` | `str` | Yes | Output `.pdf` file path |
|
||||
|
||||
---
|
||||
|
||||
## drawing_purge
|
||||
Remove all unused objects (layers, blocks, linetypes, styles). No parameters.
|
||||
|
||||
---
|
||||
|
||||
## drawing_audit
|
||||
Run integrity audit — detect and fix drawing errors. No parameters.
|
||||
|
||||
---
|
||||
|
||||
## drawing_undo / drawing_redo
|
||||
Undo/redo last operation.
|
||||
|
||||
- **ezdxf**: Snapshot-based (restores full DXF state from `_undo_stack`)
|
||||
- **COM**: Native AutoCAD undo/redo
|
||||
|
||||
**Note**: For multi-step undo protection, use `transaction_begin/rollback` instead.
|
||||
@@ -0,0 +1,96 @@
|
||||
# Entity Creation Tools (13)
|
||||
|
||||
All creation tools return `{handle, type, layer, color, linetype, visible, properties}`. The `handle` (hex string) is required for all subsequent operations on that entity.
|
||||
|
||||
Common optional params on all creation tools:
|
||||
|
||||
| Param | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `layer` | `str` | Current layer | Target layer name |
|
||||
| `color` | `int` | 256 (ByLayer) | ACI color (1-255, 256=ByLayer, 0=ByBlock) |
|
||||
| `linetype` | `str` | `"ByLayer"` | Linetype name |
|
||||
|
||||
---
|
||||
|
||||
## entity_create_line
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `x1, y1` | `float` | Start point |
|
||||
| `x2, y2` | `float` | End point |
|
||||
|
||||
## entity_create_circle
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `cx, cy` | `float` | Center point |
|
||||
| `radius` | `float` | Radius |
|
||||
|
||||
## entity_create_arc
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `cx, cy` | `float` | Center point |
|
||||
| `radius` | `float` | Radius |
|
||||
| `start_angle` | `float` | Start angle (degrees, CCW from X) |
|
||||
| `end_angle` | `float` | End angle (degrees) |
|
||||
|
||||
## entity_create_polyline
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `points` | `list[list[float]]` | `[[x,y], [x,y], ...]` coordinate pairs |
|
||||
| `closed` | `bool` | Close the polyline (default: false) |
|
||||
|
||||
## entity_create_rectangle
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `x1, y1` | `float` | First corner |
|
||||
| `x2, y2` | `float` | Opposite corner |
|
||||
|
||||
Creates an LWPOLYLINE (closed).
|
||||
|
||||
## entity_create_text
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `text` | `str` | Text content |
|
||||
| `x, y` | `float` | Insertion point |
|
||||
| `height` | `float` | Text height (default: 2.5) |
|
||||
| `rotation` | `float` | Rotation angle in degrees (default: 0) |
|
||||
|
||||
## entity_create_mtext
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `text` | `str` | Content (`\P` for paragraph break, `{\H...;}` for formatting) |
|
||||
| `x, y` | `float` | Insertion point |
|
||||
| `width` | `float` | Text box width (default: 100) |
|
||||
| `height` | `float` | Character height (default: 2.5) |
|
||||
|
||||
## entity_create_hatch
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `pattern` | `str` | Pattern name: `SOLID`, `ANSI31`, `ANSI32`, `STEEL`, `GRAVEL`, etc. |
|
||||
| `boundary_points` | `list[list[float]]` | Closed boundary polygon `[[x,y], ...]` |
|
||||
| `scale` | `float` | Pattern scale (default: 1.0) |
|
||||
| `angle` | `float` | Pattern angle (default: 0) |
|
||||
|
||||
## entity_create_spline
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `fit_points` | `list[list[float]]` | Points the spline passes through |
|
||||
|
||||
## entity_create_ellipse
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `cx, cy` | `float` | Center point |
|
||||
| `major_axis_x, major_axis_y` | `float` | Major axis endpoint (relative to center) |
|
||||
| `ratio` | `float` | Minor/major axis ratio (0.0–1.0) |
|
||||
|
||||
## entity_create_point
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `x, y` | `float` | Point coordinates |
|
||||
|
||||
## entity_create_block_ref
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | Block definition name (must exist) |
|
||||
| `x, y` | `float` | Insertion point |
|
||||
| `x_scale, y_scale` | `float` | Scale factors (default: 1.0) |
|
||||
| `rotation` | `float` | Rotation in degrees (default: 0) |
|
||||
@@ -0,0 +1,111 @@
|
||||
# Entity Modification & Query Tools (13)
|
||||
|
||||
All modify tools require a `handle` (hex string). Get handles from `entity_list()` or any `entity_create_*` return value.
|
||||
|
||||
---
|
||||
|
||||
## Modification Tools (10)
|
||||
|
||||
### entity_move
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Entity handle |
|
||||
| `dx, dy` | `float` | Displacement vector |
|
||||
|
||||
### entity_copy
|
||||
Returns new entity info with a **new handle**.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Entity to copy |
|
||||
| `dx, dy` | `float` | Offset for the copy |
|
||||
|
||||
### entity_rotate
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Entity handle |
|
||||
| `base_x, base_y` | `float` | Rotation base point |
|
||||
| `angle_degrees` | `float` | Rotation angle (CCW) |
|
||||
|
||||
### entity_scale
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Entity handle |
|
||||
| `base_x, base_y` | `float` | Scale base point |
|
||||
| `factor` | `float` | Scale factor |
|
||||
|
||||
### entity_mirror
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Entity handle |
|
||||
| `x1, y1` | `float` | Mirror line point 1 |
|
||||
| `x2, y2` | `float` | Mirror line point 2 |
|
||||
|
||||
### entity_offset
|
||||
Works on line, circle, polyline.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Entity handle |
|
||||
| `distance` | `float` | Offset distance (positive = outward) |
|
||||
|
||||
### entity_delete
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Entity to delete |
|
||||
|
||||
### entity_array_rectangular
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Entity to array |
|
||||
| `rows` | `int` | Number of rows |
|
||||
| `cols` | `int` | Number of columns |
|
||||
| `row_spacing` | `float` | Row spacing |
|
||||
| `col_spacing` | `float` | Column spacing |
|
||||
|
||||
Returns list of new entity handles.
|
||||
|
||||
### entity_array_polar
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Entity to array |
|
||||
| `center_x, center_y` | `float` | Array center point |
|
||||
| `count` | `int` | Number of copies |
|
||||
| `angle` | `float` | Total angle to fill (degrees, default: 360) |
|
||||
|
||||
### entity_set_properties
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Entity handle |
|
||||
| `layer` | `str \| null` | Move to layer |
|
||||
| `color` | `int \| null` | Change ACI color |
|
||||
| `linetype` | `str \| null` | Change linetype |
|
||||
| `lineweight` | `float \| null` | Change lineweight |
|
||||
|
||||
All property params are optional — only provided ones are changed.
|
||||
|
||||
---
|
||||
|
||||
## Query Tools (3)
|
||||
|
||||
### entity_get
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handle` | `str` | Entity handle |
|
||||
|
||||
Returns full entity info: `{handle, type, layer, color, linetype, visible, properties}`.
|
||||
|
||||
### entity_list
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `type_filter` | `str \| null` | Filter: `LINE`, `CIRCLE`, `ARC`, `LWPOLYLINE`, `TEXT`, `MTEXT`, `INSERT`, `HATCH`, etc. |
|
||||
| `layer_filter` | `str \| null` | Filter by layer name |
|
||||
| `limit` | `int` | Max results (default: 100) |
|
||||
| `offset` | `int` | Skip first N results (default: 0) |
|
||||
|
||||
### entity_delete_many
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `handles` | `list[str]` | List of handles to delete |
|
||||
|
||||
Reports progress. Returns `{deleted, failed, errors}`.
|
||||
@@ -0,0 +1,63 @@
|
||||
# Layer Management Tools (12)
|
||||
|
||||
Layers organize entities by category (walls, doors, dimensions, etc.). Each layer has: name, color (ACI), linetype, lineweight, and states (on/off, frozen/thawed, locked/unlocked).
|
||||
|
||||
---
|
||||
|
||||
## layer_list
|
||||
No parameters. Returns list of all layers with properties: `{name, color, linetype, lineweight, is_on, is_frozen, is_locked, is_current}`.
|
||||
|
||||
## layer_create
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | New layer name |
|
||||
| `color` | `int` | ACI color (default: 7=white) |
|
||||
| `linetype` | `str` | Linetype name (default: `"Continuous"`) |
|
||||
|
||||
## layer_delete
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | Layer to delete (must be empty, cannot be layer "0") |
|
||||
|
||||
## layer_set_current
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | Layer to make current (new entities go here) |
|
||||
|
||||
## layer_modify
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | Layer to modify |
|
||||
| `color` | `int \| null` | New color |
|
||||
| `linetype` | `str \| null` | New linetype |
|
||||
| `lineweight` | `float \| null` | New lineweight |
|
||||
|
||||
## layer_freeze / layer_thaw
|
||||
Freeze hides and excludes from regeneration (faster). Thaw restores.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | Layer name |
|
||||
|
||||
## layer_lock / layer_unlock
|
||||
Lock prevents editing but keeps visible. Unlock restores editing.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | Layer name |
|
||||
|
||||
## layer_hide / layer_show
|
||||
Hide turns layer off (invisible). Show turns it on.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | Layer name |
|
||||
|
||||
## layer_isolate
|
||||
Shows only the specified layer, hides all others.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | Layer to keep visible |
|
||||
|
||||
To restore: `layer_show` each hidden layer, or reopen the drawing.
|
||||
@@ -0,0 +1,95 @@
|
||||
# View, Transaction & System Tools (14)
|
||||
|
||||
## View & Screenshot (5)
|
||||
|
||||
### view_zoom_extents
|
||||
Zoom to show all entities. No parameters. **COM only** (no-op in ezdxf).
|
||||
|
||||
### view_zoom_window
|
||||
Zoom to a specific rectangular area. **COM only**.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `x1, y1` | `float` | Window corner 1 |
|
||||
| `x2, y2` | `float` | Window corner 2 |
|
||||
|
||||
### view_screenshot
|
||||
Capture drawing as image. Returns base64-encoded image.
|
||||
|
||||
- **COM**: Win32 window capture (requires Pillow)
|
||||
- **ezdxf**: matplotlib render (requires matplotlib)
|
||||
|
||||
### view_zoom_and_screenshot
|
||||
Combined zoom + screenshot in one call.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `x1, y1` | `float \| null` | Optional zoom window corner 1 |
|
||||
| `x2, y2` | `float \| null` | Optional zoom window corner 2 |
|
||||
|
||||
If coordinates omitted, captures full extents.
|
||||
|
||||
### view_set_view
|
||||
Set named view. Parameters depend on backend.
|
||||
|
||||
---
|
||||
|
||||
## Transactions (3)
|
||||
|
||||
Use transactions to group operations with undo support.
|
||||
|
||||
```
|
||||
transaction_begin() # Save checkpoint
|
||||
... make changes ...
|
||||
transaction_commit() # Accept changes
|
||||
# or
|
||||
transaction_rollback() # Revert to checkpoint
|
||||
```
|
||||
|
||||
### transaction_begin
|
||||
Start a transaction. ezdxf saves full DXF snapshot; COM creates undo mark.
|
||||
|
||||
### transaction_commit
|
||||
Accept all changes since `transaction_begin`.
|
||||
|
||||
### transaction_rollback
|
||||
Revert all changes since `transaction_begin`. ezdxf restores snapshot; COM uses native undo.
|
||||
|
||||
---
|
||||
|
||||
## System (6)
|
||||
|
||||
### system_status
|
||||
No parameters. Returns server status: `{backend, drawing_loaded, entity_count, uptime, ...}`.
|
||||
|
||||
### system_get_variable
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | Variable name: `DIMSCALE`, `LTSCALE`, `INSUNITS`, `CLAYER`, `MEASUREMENT` |
|
||||
|
||||
### system_set_variable
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | `str` | Variable name |
|
||||
| `value` | `any` | New value |
|
||||
|
||||
### system_run_command
|
||||
Execute raw AutoCAD command. **COM only**. Sanitized by `sanitize_command()`.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `command` | `str` | Command string (e.g. `_ZOOM E`, `_REGEN`) |
|
||||
|
||||
Dangerous commands blocked unless `DANGEROUS_COMMANDS_ENABLED=true`.
|
||||
|
||||
### system_run_lisp
|
||||
Evaluate AutoLISP expression. **COM only**. Sanitized by `sanitize_lisp()`.
|
||||
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `expression` | `str` | LISP expression (e.g. `(command "ZOOM" "E")`) |
|
||||
|
||||
Dangerous functions blocked: `startapp`, `vl-file-delete`, `dos_*`.
|
||||
|
||||
### system_about
|
||||
No parameters. Returns server capabilities, tool count, version, and feature list.
|
||||
@@ -0,0 +1,79 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Common Errors
|
||||
|
||||
### "AutoCAD backend unavailable"
|
||||
**Cause**: No drawing is loaded — backend needs an active drawing context.
|
||||
**Fix**: Call `drawing_new()` or `drawing_open(path)` before any other operations.
|
||||
|
||||
### "Entity handle 'XXXX' not found"
|
||||
**Cause**: Handle doesn't exist, was deleted, or drawing was reloaded.
|
||||
**Fix**: Use `entity_list()` to get current valid handles. Handles are session-specific in ezdxf; persist by saving/reopening.
|
||||
|
||||
### "Layer 'X' not found"
|
||||
**Cause**: Layer doesn't exist in the drawing.
|
||||
**Fix**: `layer_list()` to see existing layers. `layer_create("X")` to create it.
|
||||
|
||||
### "Cannot delete layer '0'"
|
||||
**Cause**: Layer "0" is the default layer and cannot be removed.
|
||||
**Fix**: Move entities to another layer instead. Layer "0" always exists.
|
||||
|
||||
### "Path validation failed"
|
||||
**Cause**: Path contains traversal (`..`) or is outside `ALLOWED_PATHS`.
|
||||
**Fix**: Use absolute paths within configured allowed directories. Check `ALLOWED_PATHS` env var.
|
||||
|
||||
### "Command rejected by sanitizer"
|
||||
**Cause**: `system_run_command` detected a dangerous pattern (ERASE, DELETE, etc.).
|
||||
**Fix**: Use specific tools instead (e.g., `entity_delete` instead of `ERASE` command). Or set `DANGEROUS_COMMANDS_ENABLED=true` if needed.
|
||||
|
||||
### "LISP expression rejected"
|
||||
**Cause**: `system_run_lisp` detected dangerous function (startapp, file ops).
|
||||
**Fix**: Use MCP tools instead of LISP for file operations.
|
||||
|
||||
### "COM backend initialization failed"
|
||||
**Cause**: AutoCAD not running, not installed, or COM registration issues.
|
||||
**Fix**: Start AutoCAD, then retry. Or switch to ezdxf: `AUTOCAD_MCP_BACKEND=ezdxf`.
|
||||
|
||||
### "View operations are no-ops"
|
||||
**Cause**: Using ezdxf backend — view/zoom commands only work with COM.
|
||||
**Fix**: Switch to COM backend if live viewport control is needed. Screenshots still work via matplotlib in ezdxf.
|
||||
|
||||
### "Screenshot failed"
|
||||
**Cause**: Missing dependency (Pillow for COM, matplotlib for ezdxf).
|
||||
**Fix**: `pip install Pillow` (COM) or `pip install matplotlib` (ezdxf).
|
||||
|
||||
### "PDF export failed"
|
||||
**Cause**: matplotlib not installed.
|
||||
**Fix**: `pip install matplotlib` or `pip install -e ".[pdf]"`.
|
||||
|
||||
### "Transaction not active"
|
||||
**Cause**: `transaction_commit/rollback` called without `transaction_begin`.
|
||||
**Fix**: Always call `transaction_begin()` first.
|
||||
|
||||
### "Block 'X' not found"
|
||||
**Cause**: Block definition doesn't exist.
|
||||
**Fix**: `block_list()` to see available blocks. Create a block with `block_create_from_entities()`.
|
||||
|
||||
---
|
||||
|
||||
## Backend-Specific Issues
|
||||
|
||||
### ezdxf Backend
|
||||
- **No DWG write support**: ezdxf reads DWG but can only write DXF. Use `drawing_export_dxf()`.
|
||||
- **Undo stack memory**: Full DXF snapshots stored. `MAX_UNDO_STACK` env limits count (default: 50).
|
||||
- **Text rendering**: Depends on system fonts. MathTex/complex formatting may not render in screenshots.
|
||||
|
||||
### COM Backend
|
||||
- **STA threading**: All COM calls go through a single thread. Batch operations are sequential.
|
||||
- **AutoCAD version**: Tested with AutoCAD 2020+. Older versions may have COM API differences.
|
||||
- **Window capture**: Screenshots capture the AutoCAD window as-is. Minimize overlapping windows.
|
||||
|
||||
---
|
||||
|
||||
## Diagnostic Steps
|
||||
|
||||
1. `system_status()` — Check backend type, connection, drawing state
|
||||
2. `system_about()` — See available tools and capabilities
|
||||
3. `drawing_info()` — Verify drawing is loaded and get metadata
|
||||
4. `analysis_entity_stats()` — Quick overview of drawing contents
|
||||
5. Check logs: `LOG_LEVEL=DEBUG` for verbose output
|
||||
@@ -0,0 +1,166 @@
|
||||
# Common Workflows
|
||||
|
||||
## 1. New Drawing from Scratch
|
||||
|
||||
```
|
||||
Progress:
|
||||
- [ ] Step 1: Create drawing
|
||||
- [ ] Step 2: Set up layers
|
||||
- [ ] Step 3: Draw entities
|
||||
- [ ] Step 4: Add dimensions
|
||||
- [ ] Step 5: Add text annotations
|
||||
- [ ] Step 6: Verify and save
|
||||
```
|
||||
|
||||
**Step 1**: `drawing_new()` — creates empty drawing
|
||||
|
||||
**Step 2**: Create layers for organization:
|
||||
```
|
||||
layer_create("WALLS", color=7, linetype="Continuous")
|
||||
layer_create("DOORS", color=3)
|
||||
layer_create("WINDOWS", color=5)
|
||||
layer_create("DIMENSIONS", color=1)
|
||||
layer_create("TEXT", color=2)
|
||||
```
|
||||
Or use `template_apply_layers("architectural")` for standard sets.
|
||||
|
||||
**Step 3**: Draw geometry on appropriate layers:
|
||||
```
|
||||
layer_set_current("WALLS")
|
||||
entity_create_rectangle(x1=0, y1=0, x2=6000, y2=4000)
|
||||
entity_create_line(x1=3000, y1=0, x2=3000, y2=4000)
|
||||
```
|
||||
|
||||
**Step 4**: Add dimensions:
|
||||
```
|
||||
layer_set_current("DIMENSIONS")
|
||||
dimension_linear(x1=0, y1=0, x2=6000, y2=0, dim_x=3000, dim_y=-500)
|
||||
```
|
||||
|
||||
**Step 5**: Add text labels:
|
||||
```
|
||||
layer_set_current("TEXT")
|
||||
entity_create_text(text="Living Room", x=1500, y=2000, height=200)
|
||||
```
|
||||
|
||||
**Step 6**: Verify and save:
|
||||
```
|
||||
analysis_entity_stats() # check entity counts
|
||||
validation_check() # run quality checks
|
||||
drawing_save("/path/to/drawing.dxf")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Edit Existing DXF
|
||||
|
||||
```
|
||||
Progress:
|
||||
- [ ] Step 1: Open file
|
||||
- [ ] Step 2: Explore contents
|
||||
- [ ] Step 3: Make changes (with transaction)
|
||||
- [ ] Step 4: Verify and save
|
||||
```
|
||||
|
||||
**Step 1**: `drawing_open("/path/to/file.dxf")`
|
||||
|
||||
**Step 2**: Explore:
|
||||
```
|
||||
analysis_entity_stats() # overview
|
||||
layer_list() # see layers
|
||||
entity_list(type_filter="LINE", limit=20) # browse entities
|
||||
```
|
||||
|
||||
**Step 3**: Begin transaction, then modify:
|
||||
```
|
||||
transaction_begin()
|
||||
entity_move(handle="1A2B", dx=100, dy=0)
|
||||
entity_set_properties(handle="3C4D", color=1, layer="WALLS")
|
||||
entity_delete(handle="5E6F")
|
||||
transaction_commit() # or transaction_rollback() to undo all
|
||||
```
|
||||
|
||||
**Step 4**: `drawing_save()`
|
||||
|
||||
---
|
||||
|
||||
## 3. Batch Entity Creation
|
||||
|
||||
```
|
||||
Progress:
|
||||
- [ ] Step 1: Prepare entity list
|
||||
- [ ] Step 2: Batch create
|
||||
- [ ] Step 3: Verify results
|
||||
```
|
||||
|
||||
**Step 1**: Build entity definitions:
|
||||
```json
|
||||
[
|
||||
{"type": "line", "x1": 0, "y1": 0, "x2": 100, "y2": 0, "layer": "WALLS"},
|
||||
{"type": "line", "x1": 100, "y1": 0, "x2": 100, "y2": 100, "layer": "WALLS"},
|
||||
{"type": "circle", "cx": 50, "cy": 50, "radius": 10, "layer": "DETAILS"},
|
||||
{"type": "text", "text": "A1", "x": 50, "y": -20, "height": 5, "layer": "TEXT"}
|
||||
]
|
||||
```
|
||||
|
||||
**Step 2**: `entity_batch_create(entities=[...])` — returns handles for all created entities
|
||||
|
||||
**Step 3**: `analysis_entity_stats()` to verify counts
|
||||
|
||||
---
|
||||
|
||||
## 4. Drawing Analysis
|
||||
|
||||
```
|
||||
Progress:
|
||||
- [ ] Step 1: Open drawing
|
||||
- [ ] Step 2: Get overview stats
|
||||
- [ ] Step 3: Examine specific areas
|
||||
- [ ] Step 4: Measure and report
|
||||
```
|
||||
|
||||
**Step 1**: `drawing_open(path)` or `drawing_info()` if already open
|
||||
|
||||
**Step 2**:
|
||||
```
|
||||
analysis_entity_stats() # by type and layer
|
||||
analysis_layer_stats() # per-layer details
|
||||
analysis_bounding_box() # drawing extents
|
||||
```
|
||||
|
||||
**Step 3**: Region queries:
|
||||
```
|
||||
analysis_find_in_region(x1=0, y1=0, x2=1000, y2=1000, type_filter="LINE")
|
||||
analysis_select_by_layer(layer_name="WALLS")
|
||||
analysis_select_by_type(entity_type="CIRCLE")
|
||||
```
|
||||
|
||||
**Step 4**: Measurements:
|
||||
```
|
||||
analysis_measure_distance(x1=0, y1=0, x2=100, y2=100)
|
||||
analysis_measure_area(points=[[0,0],[100,0],[100,100],[0,100]])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Template-Based Drawing
|
||||
|
||||
```
|
||||
Progress:
|
||||
- [ ] Step 1: Create drawing with template
|
||||
- [ ] Step 2: Apply layer standards
|
||||
- [ ] Step 3: Add content
|
||||
- [ ] Step 4: Validate and save
|
||||
```
|
||||
|
||||
**Step 1**: `drawing_new(template="/path/to/template.dwt")` (or `drawing_new()` without template)
|
||||
|
||||
**Step 2**: `template_apply_layers("architectural")` — creates standard layers
|
||||
|
||||
**Step 3**: Draw content on appropriate layers
|
||||
|
||||
**Step 4**:
|
||||
```
|
||||
validation_check(["empty_layers", "zero_length"])
|
||||
drawing_save("/path/to/output.dxf")
|
||||
```
|
||||
@@ -0,0 +1,79 @@
|
||||
"""Check AutoCAD MCP Pro setup and dependencies."""
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def check(name: str, condition: bool, fix: str = ""):
|
||||
status = "OK" if condition else "MISSING"
|
||||
symbol = "[+]" if condition else "[-]"
|
||||
line = f" {symbol} {name}: {status}"
|
||||
if not condition and fix:
|
||||
line += f" -> {fix}"
|
||||
print(line)
|
||||
return condition
|
||||
|
||||
|
||||
def main():
|
||||
print("AutoCAD MCP Pro — Setup Check\n")
|
||||
ok_count = 0
|
||||
total = 0
|
||||
|
||||
print("Python:")
|
||||
total += 1
|
||||
v = sys.version_info
|
||||
ok_count += check(
|
||||
f"Python {v.major}.{v.minor}.{v.micro}",
|
||||
v >= (3, 11),
|
||||
"Requires Python 3.11+",
|
||||
)
|
||||
|
||||
print("\nRequired packages:")
|
||||
for pkg, install in [
|
||||
("fastmcp", "pip install fastmcp"),
|
||||
("ezdxf", "pip install ezdxf"),
|
||||
("pydantic", "pip install pydantic"),
|
||||
]:
|
||||
total += 1
|
||||
try:
|
||||
importlib.import_module(pkg)
|
||||
ok_count += check(pkg, True)
|
||||
except ImportError:
|
||||
ok_count += check(pkg, False, install)
|
||||
|
||||
print("\nOptional packages:")
|
||||
for pkg, install, purpose in [
|
||||
("win32com", "pip install pywin32", "COM backend"),
|
||||
("PIL", "pip install Pillow", "Screenshots (COM)"),
|
||||
("matplotlib", "pip install matplotlib", "PDF export + screenshots (ezdxf)"),
|
||||
]:
|
||||
total += 1
|
||||
try:
|
||||
importlib.import_module(pkg)
|
||||
ok_count += check(f"{pkg} ({purpose})", True)
|
||||
except ImportError:
|
||||
ok_count += check(f"{pkg} ({purpose})", False, install)
|
||||
|
||||
print("\nEnvironment variables:")
|
||||
for var, default in [
|
||||
("AUTOCAD_MCP_BACKEND", "auto"),
|
||||
("ALLOWED_PATHS", "(empty = all paths allowed)"),
|
||||
("MAX_UNDO_STACK", "50"),
|
||||
("LOG_LEVEL", "INFO"),
|
||||
("DANGEROUS_COMMANDS_ENABLED", "false"),
|
||||
]:
|
||||
val = os.environ.get(var, "")
|
||||
total += 1
|
||||
ok_count += check(var, True, "")
|
||||
if val:
|
||||
print(f" Current value: {val}")
|
||||
else:
|
||||
print(f" Default: {default}")
|
||||
|
||||
print(f"\nResult: {ok_count}/{total} checks passed")
|
||||
return 0 if ok_count >= 4 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,91 @@
|
||||
"""Validate drawing quality — run after creating/editing a drawing.
|
||||
|
||||
Usage: python scripts/validate_drawing.py [drawing_path]
|
||||
|
||||
This script checks for common drawing issues:
|
||||
- Empty layers (layers with no entities)
|
||||
- Entities on layer "0" (should use named layers)
|
||||
- Entity count summary
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
def validate_stats(stats: dict) -> list[str]:
|
||||
"""Validate entity statistics. Pass the result of analysis_entity_stats()."""
|
||||
issues = []
|
||||
|
||||
if stats.get("total", 0) == 0:
|
||||
issues.append("WARNING: Drawing has no entities")
|
||||
|
||||
by_layer = stats.get("by_layer", {})
|
||||
if "0" in by_layer and by_layer["0"] > 0:
|
||||
issues.append(
|
||||
f"INFO: {by_layer['0']} entities on default layer '0' "
|
||||
"(consider moving to named layers)"
|
||||
)
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def validate_layers(layers: list[dict]) -> list[str]:
|
||||
"""Validate layer configuration. Pass the result of layer_list()."""
|
||||
issues = []
|
||||
|
||||
for layer in layers:
|
||||
name = layer.get("name", "")
|
||||
if name == "0":
|
||||
continue
|
||||
if layer.get("color", 7) == 7 and name != "0":
|
||||
issues.append(
|
||||
f"INFO: Layer '{name}' uses default color (white/7). "
|
||||
"Consider assigning a distinct color."
|
||||
)
|
||||
|
||||
return issues
|
||||
|
||||
|
||||
def main():
|
||||
print("Drawing Validation Tool")
|
||||
print("=" * 40)
|
||||
print()
|
||||
print("This tool validates output from MCP analysis tools.")
|
||||
print("Pipe JSON from analysis_entity_stats() or layer_list().")
|
||||
print()
|
||||
print("Usage with MCP:")
|
||||
print(" 1. Call analysis_entity_stats() → copy JSON output")
|
||||
print(" 2. Call layer_list() → copy JSON output")
|
||||
print(" 3. Review results for issues")
|
||||
print()
|
||||
print("Checks performed:")
|
||||
print(" - Empty drawing detection")
|
||||
print(" - Entities on default layer '0'")
|
||||
print(" - Layers with default color")
|
||||
print()
|
||||
|
||||
if not sys.stdin.isatty():
|
||||
try:
|
||||
data = json.load(sys.stdin)
|
||||
if "total" in data:
|
||||
issues = validate_stats(data)
|
||||
elif isinstance(data, list):
|
||||
issues = validate_layers(data)
|
||||
else:
|
||||
issues = ["ERROR: Unrecognized input format"]
|
||||
|
||||
if issues:
|
||||
for issue in issues:
|
||||
print(f" {issue}")
|
||||
else:
|
||||
print(" OK — No issues found")
|
||||
except json.JSONDecodeError:
|
||||
print(" ERROR: Invalid JSON input")
|
||||
else:
|
||||
print("No stdin input. Run interactively or pipe JSON data.")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user