Track H's foundation, landed on main before its three groups branch. - engineering/understand/snapshot.py: one bulk read of a drawing into frozen WCS records (OCS translated through backends/ocs.py, text decoded, an INSERT boxed by its block geometry without its attribute texts). A DXF is read with ezdxf after the size check; the headless engine's document is read under its own lock; the live engine exports the active document with Document.Export(<tmp>/snap, "DXF", <set of all>) - never renamed, deleted afterwards - and a DWG is opened read-only, exported and closed. Measured on AutoCAD 2026: ".dxf" is refused with Invalid argument, the file lands as snap.DXF, model space, paper space and unused block definitions all reach it, and the selection set does not narrow a DXF export. - engineering/understand/labels.py: %%c, \U+2205, the U+2300 sign, o-stroke and MTEXT format runs all read as one diameter spelling; sizes are kept as drawn and never equated; Cyrillic look-alike capitals fold into Latin tags; panels, wiring callouts and electrical data (kW, phases, volts, N, VFD) in EN / TR / RU / NL / DE. A power that is not written stays None. - engineering/understand/vocab.py: layer classification with a confidence and the deciding keyword, supply / return words, room labels and block kinds in five languages. A CIP layer without a direction word is not guessed. - MAX_DXF_BYTES defaults to 512 MB (a real layout was 188 MB); the refusal names the size and the variable. New `office` extra (openpyxl), also in `full`; uv.lock regenerated. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
164 lines
6.1 KiB
TOML
164 lines
6.1 KiB
TOML
[project]
|
|
name = "autocad-mcp-pro"
|
|
version = "1.5.1"
|
|
description = "Production-grade AutoCAD MCP server — COM API + ezdxf dual engine"
|
|
readme = "README.md"
|
|
requires-python = ">=3.11"
|
|
license = "MIT"
|
|
license-files = ["LICENSE"]
|
|
authors = [
|
|
{ name = "Umutcan Edizsalan" },
|
|
]
|
|
keywords = [
|
|
"autocad",
|
|
"mcp",
|
|
"model-context-protocol",
|
|
"cad",
|
|
"dxf",
|
|
"ezdxf",
|
|
"drafting",
|
|
"engineering-drawing",
|
|
]
|
|
classifiers = [
|
|
"Development Status :: 5 - Production/Stable",
|
|
"Intended Audience :: Developers",
|
|
"Intended Audience :: Manufacturing",
|
|
"Operating System :: OS Independent",
|
|
"Programming Language :: Python :: 3",
|
|
"Programming Language :: Python :: 3.11",
|
|
"Programming Language :: Python :: 3.12",
|
|
"Programming Language :: Python :: 3.13",
|
|
"Topic :: Scientific/Engineering",
|
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
]
|
|
dependencies = [
|
|
# These ranges are what an *installer* gets from PyPI, so they stay ranges:
|
|
# a library that pins its dependencies exactly is unco-installable. They are
|
|
# deliberately narrow because a range is all a `pip install` has to protect
|
|
# it. The floor of each range is the version this release is tested against.
|
|
#
|
|
# What CI and contributors get is different and exact: uv.lock pins the
|
|
# whole transitive graph (including the dev tools) to the set the suite was
|
|
# last run against, and every locked lane installs with `--locked` so an
|
|
# untested upstream cannot appear silently. Ranges guard users; the lock
|
|
# guards us. Regenerate the lock with `uv lock` whenever these change.
|
|
#
|
|
# Every ceiling is the next major. A tighter cap (e.g. `<3.5`) would block a
|
|
# user whose environment already holds a newer minor from installing us at
|
|
# all, and it buys nothing here: the lock is what protects our own runs, and
|
|
# `deps-floating.yml` runs the suite against unpinned latest on a schedule,
|
|
# so a breaking upstream minor surfaces as a failing scheduled job rather
|
|
# than as a silent CI-green-but-users-broken state.
|
|
#
|
|
# fastmcp 3.x moves fast and `fastmcp` is now a thin meta-package
|
|
# (Requires-Dist: fastmcp-slim[client,server]), so a new minor can reshape
|
|
# the API we build on. If deps-floating goes red, add a targeted `!=` for
|
|
# the bad version rather than lowering this ceiling.
|
|
"fastmcp>=3.4.5,<4",
|
|
"ezdxf>=1.4,<2",
|
|
"pydantic>=2.0,<3",
|
|
]
|
|
|
|
[project.urls]
|
|
Homepage = "https://github.com/U-C4N/Autocad-MCP"
|
|
Repository = "https://github.com/U-C4N/Autocad-MCP"
|
|
Issues = "https://github.com/U-C4N/Autocad-MCP/issues"
|
|
Changelog = "https://github.com/U-C4N/Autocad-MCP/blob/main/CHANGELOG.md"
|
|
|
|
[project.optional-dependencies]
|
|
# Rendering needs an extra — the core install cannot draw pixels.
|
|
# ezdxf.addons.drawing.frontend does an unconditional top-level `import
|
|
# PIL.Image`, so Pillow is required by *every* ezdxf render path (PNG
|
|
# screenshot and PDF export alike), not only by the COM window-capture path
|
|
# it is listed under below. `pip install autocad-mcp-pro` on its own therefore
|
|
# renders nothing. Install `[pdf]` (or `[full]`) for rendering on any platform:
|
|
# Pillow comes in transitively as a matplotlib dependency.
|
|
com = [
|
|
# pywin32 publishes Windows wheels and no sdist, so without this marker
|
|
# the extra cannot be resolved at all on Linux or macOS -- pip dies at
|
|
# "no matching distribution" before installing anything. The marker
|
|
# makes `pip install autocad-mcp-pro[com]` a no-op there instead of a
|
|
# hard failure, which is the honest outcome: the COM backend needs
|
|
# Windows, and the server already falls back to ezdxf.
|
|
'pywin32>=306; sys_platform == "win32"',
|
|
# Win32 window capture for the live-AutoCAD screenshot path.
|
|
"Pillow>=10.0",
|
|
]
|
|
pdf = [
|
|
# Also satisfies ezdxf's hard Pillow requirement, transitively.
|
|
"matplotlib>=3.8",
|
|
]
|
|
office = [
|
|
# XLSX output: the takeoff workbooks (pipe_takeoff / cable_takeoff) and
|
|
# data_extract(format="xlsx"). Without it every one of them still writes
|
|
# CSV and refuses XLSX with the `xlsx_write` capability, naming this extra.
|
|
"openpyxl>=3.1",
|
|
]
|
|
full = [
|
|
# Same marker as `com` above, and for the same reason: `[full]` is the
|
|
# extra this project recommends to contributors, and it was
|
|
# uninstallable on every non-Windows machine.
|
|
'pywin32>=306; sys_platform == "win32"',
|
|
"Pillow>=10.0",
|
|
"matplotlib>=3.8",
|
|
"openpyxl>=3.1",
|
|
]
|
|
|
|
[project.scripts]
|
|
autocad-mcp = "server:main"
|
|
# Alias matching the PyPI distribution name so `uvx autocad-mcp-pro` works
|
|
# without a --from indirection (MCP registry / uvx convention).
|
|
autocad-mcp-pro = "server:main"
|
|
|
|
# PEP 735 dependency group (was [tool.uv] dev-dependencies, which uv now warns
|
|
# is deprecated). The standard spelling is understood by uv *and* by pip >=25.1
|
|
# (`pip install --group dev`), so the dev toolchain has one definition whether
|
|
# or not you use uv.
|
|
#
|
|
# These are the exact gates CI runs, and uv.lock freezes them: `uv sync
|
|
# --locked` reproduces this list bit-for-bit, so a lint or test job can no
|
|
# longer be broken by a tool release nobody chose.
|
|
[dependency-groups]
|
|
dev = [
|
|
"pytest>=8.0",
|
|
"pytest-asyncio>=0.23",
|
|
"pytest-cov>=5.0",
|
|
# Exact pin *and* locked: the formatter's output changes between minors, so
|
|
# a floating ruff makes `ruff format --check .` fail on unchanged code. This
|
|
# is now the only place the version is written — the workflows install ruff
|
|
# from the lock rather than restating it.
|
|
"ruff==0.16.1",
|
|
]
|
|
|
|
[build-system]
|
|
requires = ["hatchling"]
|
|
build-backend = "hatchling.build"
|
|
|
|
[tool.hatch.build.targets.wheel]
|
|
# Flat layout: no directory matches the project name, so hatchling needs an
|
|
# explicit file selection (GH issue #2). server.py imports config + security.
|
|
only-include = [
|
|
"server.py",
|
|
"config.py",
|
|
"security.py",
|
|
"version.py",
|
|
"backends",
|
|
"engineering",
|
|
"discovery",
|
|
"templates",
|
|
]
|
|
|
|
[tool.ruff]
|
|
line-length = 100
|
|
target-version = "py311"
|
|
|
|
[tool.ruff.lint]
|
|
select = ["E", "F", "I", "W", "UP", "B"]
|
|
ignore = ["E501"]
|
|
|
|
[tool.ruff.format]
|
|
quote-style = "double"
|
|
|
|
[tool.pytest.ini_options]
|
|
asyncio_mode = "strict"
|