Initial commit
This commit is contained in:
@@ -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")
|
||||
```
|
||||
Reference in New Issue
Block a user