feat(selection): add selection_get — read live viewport pickfirst selection (#1)

New COM-only Entity Query tool that reads AutoCAD's implied ("pickfirst")
selection set and returns the selected entity handles + EntityInfo, so the AI
can scope work (e.g. dimension_auto) to exactly what the user picked in the
viewport instead of the whole drawing. Resolves #1 — the layer-juggling
workaround is no longer needed.

- backends/base.py: abstract selection_get() returning a status dict
- backends/com_backend.py: reads ActiveDocument.PickfirstSelectionSet, surfaces
  the PICKFIRST sysvar, handles the empty-selection case
- backends/ezdxf_backend.py: headless stub (ok=False, empty handles, same shape)
- server.py: @mcp.tool registration (readOnlyHint), SECTION 5 3 -> 4 tools
- README + CHANGELOG + pyproject: 110 -> 111 tools, v1.1.0 -> v1.1.1

Verified live on AutoCAD 2027 (COM): selection_get correctly returned the
user's grip selection across two distinct picks; 318 tests pass, ruff-clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Umutcan Edizsalan
2026-06-20 11:41:36 +03:00
co-authored by Claude Opus 4.8
parent 5583730a79
commit dfb21a734a
7 changed files with 151 additions and 7 deletions
+60
View File
@@ -1444,6 +1444,66 @@ class ComBackend(AutoCADBackend):
return results
return await self._run(_sync)
async def selection_get(self) -> dict:
def _sync():
doc = _acad_doc()
# PICKFIRST controls whether the noun/verb (grip) pre-selection is
# captured at all. Surface it so an empty result is explainable.
try:
pickfirst = bool(doc.GetVariable("PICKFIRST"))
except Exception as exc:
log.debug("selection_get: PICKFIRST read failed: %s", exc)
pickfirst = None
try:
ss = doc.PickfirstSelectionSet
except Exception as exc:
log.debug("selection_get: PickfirstSelectionSet unavailable: %s", exc)
return {
"ok": False,
"error": f"PickfirstSelectionSet unavailable: {exc}",
"count": 0,
"handles": [],
"entities": [],
"pickfirst": pickfirst,
}
try:
count = int(ss.Count)
except Exception as exc:
log.debug("selection_get: Count read failed: %s", exc)
count = 0
entities = []
for i in range(count):
try:
entities.append(_entity_info(ss.Item(i)))
except Exception as exc:
log.debug("selection_get: skip selection item %d: %s", i, exc)
continue
handles = [e.handle for e in entities]
result = {
"ok": True,
"count": len(handles),
"handles": handles,
"entities": entities, # _dc()-converted at the server layer
"pickfirst": pickfirst,
}
if not handles:
result["message"] = (
"No entities are pre-selected in the AutoCAD viewport. "
"Select entities (so grips appear) before calling, and "
"ensure PICKFIRST=1."
if pickfirst is not False
else "PICKFIRST is 0; the noun/verb grip selection is disabled. "
"Set PICKFIRST=1 (system_set_variable) and re-select."
)
return result
return await self._run(_sync)
# ── view / screenshot ──────────────────────────────────────────────────────
async def view_zoom_extents(self) -> dict: