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/`
|
||||
Reference in New Issue
Block a user