Auditoría de text-to-cad: 17.978 estrellas, un servidor MCP con dos herramientas y los 822 MB de runtime detrás de los «superpoderes CAD»

text-to-cad promete superpoderes CAD para agentes de código y, con 17.978 estrellas, es el proyecto más visible de la categoría. Instalé cadgen 0.7.15 y medí lo que cuesta la promesa: una rueda de 11 MB que se convierte en 822 MB de site-packages, un primer snapshot que tarda 9,7 segundos a través de un Chromium headless y un demonio de compilación que deja 1,65 GiB residentes en cuatro procesos durante una hora después de construir un modelo que tardó 1,5 segundos. La superficie MCP son dos herramientas —cad_show y cad_analytics— porque el modelado real ocurre en 12 skills de nivel prompt que invocan una CLI, no en herramientas del protocolo. Las 19 leyes de diseño que el proyecto publica en PyPI explican el resto, incluida la ley 10: fallo ruidoso o salida correcta, nada intermedio. Esa ley se detiene en el prompt.

«Give your agent CAD superpowers» es todo el argumento: 17.978 estrellas dicen que funcionó, y la presencia en la mitad alta de GitHub Trending —junto con un hilo de Hacker News que llegó a 186 puntos el 2026-05-01— dice que la gente se lo cree. Así que lo instalé, construí una pieza real y medí todo aquello que el README no cuantifica.

La versión corta: es uno de los repositorios de tooling para agentes más disciplinados que se publican hoy (76 releases en 129 días, ninguno sin notas, licencia MIT, determinismo de bytes como promesa documentada), y lo interesante es exactamente dónde se detienen sus garantías. El proyecto es fanático con la capa que una máquina puede verificar —código fuente hasta bytes, geometría hasta veredictos— y guarda silencio sobre la capa donde vive de verdad la ambigüedad: tu frase describiendo la pieza que quieres.

Los números que se reproducen

Todo lo siguiente sale de la API de GitHub, la API JSON de PyPI y pypistats, no del README.

MétricaValor
Estrellas / forks / watchers17.978 / 1.804 / 93
Proporción de watchers / de forks0,52 % / 10,0 %
Releases (2026-05-30 → 2026-10-06)76, última v0.7.15
Notas de release76 de 76 no vacías, intervalo mediano de 16,5 horas, 24 en los últimos 30 días
Colaboradores18 cuentas, 1.391 commits
Concentración de commitsearthtojake 1.110 (79,8 %), github-actions[bot] 102 (7,3 %), claude 87 (6,3 %)
Tamaño del repositorio2.902 archivos, 34,5 MB de blobs, 1.436 archivos Python
Versiones de cadgen en PyPI53 desde el 2026-08-11
Descargas de cadgen42.213 en 56 días; 23.603 en los últimos 30 (≈787/día)

Dos de esas filas merecen una segunda lectura.

93 watchers frente a 17.978 estrellas. Los watchers son quienes pidieron que se les notificara cada cambio; un 0,52 % es bajo incluso para los estándares de un repositorio viral. Los forks, en cambio, son el 10,0 %: alto, lo que encaja con un proyecto cuya instalación en una de las cinco apps soportadas empieza con un git clone.

claude con 87 commits, más 102 de github-actions[bot]. La interfaz de GitHub muestra commits firmados por «earthtojake and claude», y la cuenta bot se encarga del andamiaje de publicación. Sumando ambas, cerca del 13,6 % de los commits los escribe una máquina; el mantenedor aporta el 79,8 %. Es un factor bus de uno con cola automatizada — conviene saberlo antes de montar una cadena de producción encima.

Qué recibe realmente el agente

Aquí es donde el vocabulario de marketing y la arquitectura se separan. «Plugin» y «servidor MCP» sugieren una superficie de operaciones CAD. Hablé el protocolo directamente —initialize de JSON-RPC y luego tools/list sobre stdio contra cadgen mcp— y esto fue lo que devolvió:

CapaQué expone
Herramientas MCP2: cad_show (un argumento, path), cad_analytics (un argumento, action)
Recursos MCP1: ui://cad/…/app.html, text/html;profile=mcp-app
Skills (nivel prompt)12, coincidiendo exactamente con el árbol del repositorio
CLIMás de 30 verbos (step build, stl build, step snapshot, dxf snapshot, urdf validate, store why, …)

El servidor MCP es un visor, no una API de modelado. Su trabajo es renderizar un archivo que el agente le señala (tarjetas de visor en el chat, una pestaña lateral en Codex) y encender o apagar la analítica anónima. El trabajo CAD propiamente dicho ocurre en doce paquetes de skills —CAD, step.parts, engineering-drawing, DXF, URDF, SRDF, SDF, SendCutSend, DfAM check, DFM, G-code y Bambu Labs— que son instrucciones en markdown que le dicen al agente que escriba un archivo Python y llame a cadgen.

La arquitectura es defendible (la CLI es la misma puerta para todos los agentes y las 12 skills se versionan junto a la rueda), pero tiene una consecuencia que hay que presupuestar: la capacidad CAD del agente es un contrato de prompt, no una superficie tipada de herramientas. Nada valida la geometría que el agente afirma haber producido, salvo las comprobaciones que el texto de la skill le pide ejecutar.

La factura del runtime, medida

La rueda cadgen-0.7.15-py3-none-any.whl pesa 11,01 MB. Esto es lo que produjo instalarla en un entorno virtual limpio de uv en esta máquina:

ElementoMedido
Rueda11,01 MB
site-packages instalado822 MB (75× la rueda)
OCP (binding de OpenCascade)158 MB
cadquery_ocp_novtk.libs95 MB
playwright (fijado en ==1.63.0)137 MB
scipy + scikit-learn + numpy + sympy + matplotlib≈195 MB
Tiempo de instalación12,5 s (caché de paquetes caliente)
Runtime incluido en la rueda22 MB: cliente del visor 14 MB, paquete de render en navegador 1,4 MB, Node 0,4 MB

La lista de dependencias es corta y en su mayoría inevitable: build123d>=0.11.1,<0.12, cadquery-ocp-novtk>=7.9,<8, ezdxf, shapely, matplotlib, pillow y ese Playwright fijado al bit. Dos decisiones deliberadas se leen en ella: la variante -novtk de OCP existe para evitar VTK, y Playwright está pinneado exacto en lugar de por rango. El render es genuinamente un problema de navegador: snapshot_core.py describe «the headless browser driver» y carga render.html más snapshot-render.js, con los umbrales de teselado replicados entre el renderizador Python y packages/core/src/common/source.js y cubiertos por una prueba de paridad.

Así que la cifra honesta de la primera ejecución es: 822 MB de entorno Python, más un Chromium headless en el primer snapshot (261 MB para el build chromium_headless_shell en la caché de Playwright de esta máquina; el snapshot funcionó porque ya había un navegador compatible). El README dice «Its first start downloads CAD’s runtime» sin dar tamaño. Es una instalación de portátil, no un curl | sh.

El demonio que nadie pidió

Después de un solo python src/mount_plate.py —una placa de 60 × 40 × 5 mm con cuatro agujeros de paso M3, escrita en once líneas de build123d— la compilación tardó 1,55 segundos de reloj de pared, y 0,19 segundos en la segunda pasada, que respondió correctamente «STEP/mount_plate.step is current; not rebuilt».

Luego miré la tabla de procesos:

ProcesoRSS
python -P -m cadgen.daemon284 MB
worker ×3 (cadgen.daemon.worker)499 MB, 477 MB, 471 MB
Total residente1,65 GiB

Cuatro procesos, 1,65 GiB, todavía residentes minuto y medio después de que el script terminara. El propio log del demonio fija las condiciones: idle timeout 3600s. Leyendo el código aparecen las constantes de diseño:

  • DEFAULT_SPARES = 2: workers de reserva que «han terminado de importar build123d y no están vinculados a nada», de modo que la primera compilación de un modelo nuevo no paga el coste de importación.
  • WORKER_SEED_BYTES = 512 MiB: lo que se cobra por worker en la admisión de memoria hasta que el pool ha medido un worker ocioso real.
  • La admisión de memoria por defecto es el 70 % de la memoria física (CADGEN_MEMORY_MB lo sobrescribe, 0 la desactiva).
  • DEFAULT_IDLE_UNBIND_SECONDS = 600.0: un worker vinculado se libera tras diez minutos ociosos; DEFAULT_RECYCLE_AFTER = 1000 trabajos por worker como seguro contra fugas.

La regla declarada en el docstring del pool es «nothing waits on another build». Es una respuesta de ingeniería real a un problema real —un agente que hace diez ediciones seguidas no debería pagar diez importaciones del kernel— y también es una hora con ~1,7 GiB ocupados en un portátil por una pieza que tardó un segundo en construirse. En la máquina Linux de 8 GB donde medí, era el 21 % de la memoria física comprometida por una herramienta que invocaste una vez, en silencio, como efecto secundario de ejecutar un archivo Python. El almacén que mantiene en ~/.cache/cadgen tiene un límite de 20 GB y ocho categorías de índice (model, document, output, component, surface, bounds, mesh, drawing); la limpieza que lo mantiene por debajo del techo se ejecuta cuando el demonio está ocioso.

Las 19 leyes y la que no puede cubrir

Lo más insólito del proyecto está en su página de PyPI, no en el README. La descripción del paquete son 28.118 caracteres de especificación titulada «The design laws»: 19 leyes numeradas, con pruebas de presión, que el código debe cumplir. Tres merecen cita porque predicen cómo se comporta la herramienta cuando la aprietas:

8. No backwards compatibility. Solo cortes duros. Toda superficie retirada falla de forma ruidosa con un error didáctico que nombra su reemplazo: nunca un alias, nunca un shim.

10. Loud failure or correct output, nothing between. El pecado cardinal es una salida plausible-pero-incorrecta con código de salida 0. Sin retrocesos silenciosos, sin globs, sin adivinar; un render fallido no deja archivo en la ruta pedida.

19. Nothing waits on the network. Una vez instalado, cadgen funciona sin conexión: cada comando, compilación, snapshot, servidor y página.

La ley 10 es la que hay que aplicar contra los propios benchmarks del proyecto. En el hilo de Hacker News de 2026-05, un comentarista que firma voidUpdate hizo exactamente eso, y sus tres observaciones siguen sin respuesta en el README:

«In the benchmarks, there is a strange lack of measurements that I’d expect in a CAD process (EG in benchmark 1, the positions of the 4 holes are not specified at all). I’m assuming that’s why the gussets in benchmark 3 overlap the holes and make a part that cannot be used.»

Una pieza cuyos refuerzos se solapan con los agujeros es salida plausible-pero-incorrecta. Cumple la ley 10 en la capa que cadgen controla —el código es determinista, el STEP es válido, el snapshot se renderiza— y viola la intención de la ley 10 en la capa que no controla: convertir una frase incompleta en geometría. Ese es el límite honesto de este diseño. Los contratos exigibles se acaban donde empieza el lenguaje natural, y ningún determinismo de bytes cruza esa frontera.

El mismo hilo contiene la formulación más limpia de la otra mitad del problema, de randusername:

«I don’t feel like text-to-CAD is a viable workflow for me because of the ’language barrier’. I would need, like, a visual dictionary of terms.»

alnwlsn, argumentando desde la práctica del dibujo técnico, plantea la posición contraria: «What a nightmare to describe all this in text! when the language of drafting is able to describe it perfectly, wordlessly and unambiguous, in a single drawing sheet.» Y Eisenstein aporta la versión sensata desde el uso real: el truco con el CAD generado por LLM «is to be an expert in the field already».

Un hallazgo original: el contenedor SURF

El repositorio incluye un formato de geometría que no aparece en ningún documento que haya podido encontrar: 19 archivos con extensión .surf, sin descripción en el README. Decodifiqué uno. sun_gear.surf (332.302 bytes) empieza así:

SURF \x02\x00\x00\x00 \x8a\xc8\x04\x00 {"version":2,"shapes":[{"ord":1,"kind":"solid","volume":12491.658278013321}],
"faces":[{"ord":1,"shape":1,"reversed":true,"uv":[...],"surfaceType":"plane","area":48.371601946768976,
"center":[...],"bbox":[...],"surface":...}]}

Un mágico de cuatro bytes, versión, un índice JSON con longitud prefijada que lleva metadatos B-rep por cara (tipo de superficie, uv, área, centro, caja envolvente) y el volumen por sólido, seguido de una carga útil float64 en crudo. Es el contenedor compacto propio del visor para los assets del hero de la documentación y las piezas de prueba: los mismos datos que transporta STEP, sin el sobrecoste de texto. No documentado, con licencia MIT y una señal razonable de hasta dónde llega este proyecto por su ruta de render.

Dónde se ve de verdad la adopción

Las estrellas miden atención. Las descargas miden uso, así que revisé los dos canales de distribución:

CanalNúmero
Assets de release en GitHub (cad-openai-plugin-0.7.15.zip)45 descargas
El mismo asset, ocho releases anteriores56, 2, 4, 9, 16, 3, 2, 2
Descargas de cadgen en PyPI, últimos 30 días23.603 (pico de 3.184 el 2026-10-05)
Descargas de cadgen en PyPI, primeros 56 días42.213

La página de releases de GitHub es un error de redondeo —aquí nadie instala un plugin desde un asset— mientras que PyPI mueve unas 787 instalaciones diarias, por encima de las 876 del primer día. Los contadores incluyen CI y re-resoluciones de uvx (cada uvx --from cadgen==0.7.15 puede volver a resolver), así que tomemos 23.603/mes como cota superior de humanos. Aun así: 17.978 estrellas, 93 watchers y unas 800 descargas diarias del paquete dibujan un cuadro coherente de una herramienta que mucha gente probó y relativamente poca ejecuta de forma continua.

Consejos prácticos

Instálalo si: haces trabajo paramétrico y verificable por máquina (soportes, utillajes, adaptadores, carcasas) con dimensiones que realmente conoces; quieres STEP y DXF más comprobaciones DFM e imprimibilidad dentro de la misma sesión del agente; estás en macOS o Linux; y te sobran disco y RAM.

Sáltalo si: estás en un Windows 11 recién instalado con Smart App Control activo —el README es notablemente honesto: el módulo nativo OCP sin firmar queda bloqueado, todo comando cadgen falla con ImportError: DLL load failed while importing OCP, el Visor de eventos registra el Event ID 3077 y tus únicas salidas son desactivar Smart App Control (irreversible sin reinstalar Windows) o trabajar bajo WSL—. Sáltalo también si tus piezas son artísticas y no dimensionales, o si necesitas ejecutarlo en un contenedor sellado donde un demonio ocioso de 1,65 GiB y una dependencia de navegador son inaceptables.

Audita el almacén antes de fiarte de la caché: cadgen store why <model>.py imprime una puerta de cinco comprobaciones (registro, cierre de fuentes, hijos, hash del árbol, salidas) y explica una recompilación inesperada en un solo comando. Es mejor observabilidad que la de la mayoría de herramientas de compilación de esta generación.

Dos notas operativas sacadas del código y que no están en el README: el presupuesto de memoria del demonio es por defecto el 70 % de tu RAM física y hay dos workers de reserva preimportados, así que ambos números escalan con tu máquina y no con tu pieza; y la analítica está apagada hasta que se responde, envía un código unidireccional más el formato de cada archivo (nunca nombres, rutas ni prompts) y se detiene por completo con DO_NOT_TRACK=1. La comprobación diaria de versión consulta api.texttocad.dev/v1/versions como máximo una vez y se puede desactivar con CADGEN_UPDATE_CHECK=0.

Preguntas frecuentes

¿text-to-cad y cadgen son lo mismo? No. El repositorio es el plugin más doce skills; cadgen es el paquete de PyPI (rueda de 11 MB, 225 módulos Python en la 0.7.15) que hace el trabajo. Las skills fijan una versión exacta y el plugin y la CLI comparten una única instalación.

¿Genera archivos STEP directamente desde un LLM? No. El LLM escribe Python que usa build123d; cadgen lo ejecuta y exporta STEP, STL, 3MF, GLB o DXF a través del binding OCP de OpenCascade. Entre versiones, la CLI se genera a partir de las firmas de las funciones públicas, y la ley 6 hace que decorador, función y CLI sean una sola superficie con el mismo nombre.

¿Por qué una herramienta CAD depende de Playwright? Por los snapshots. La ruta de render maneja un navegador headless sobre render.html y snapshot-render.js, que es lo que le permite producir vistas solid, render, xray, hidden-line y wireframe, cortes y vídeo .mp4/.gif de clips de animación. En mi ejecución el primer snapshot costó 9,7 segundos y produjo un PNG de 120 KB; el modo render costó 27,7 segundos para 1,6 MB.

¿El demonio es opcional? No mediante una opción documentada. El timeout de inactividad es de una hora, los workers de reserva son dos por defecto y la regla declarada del pool está pensada para agentes interactivos, no para uso por lotes. Existen las variables CADGEN_DAEMON_SPARES, CADGEN_DAEMON_IDLE_TIMEOUT, CADGEN_DAEMON_IDLE_UNBIND y CADGEN_MEMORY_MB: son las palancas que hay que usar.

¿Qué está mal en los benchmarks? La crítica pública más concreta, de voidUpdate en Hacker News, es que los prompts destacados no fijan la posición de los agujeros, que la especificación del soporte en L pide refuerzos que se solapan con los agujeros y que el agujero pasante del benchmark 7 no atraviesa visiblemente la pieza. Si copias esas cifras a un documento de compras, añade tú las dimensiones que faltan.

¿Funciona sin conexión? Según la ley 19, sí una vez instalado: compilaciones, snapshots, el visor y el servidor MCP funcionan con todas las peticiones de red rechazadas. Solo existen dos peticiones —analítica anónima (opt-in) y la comprobación diaria de versión— y ninguna bloquea un comando.

Veredicto

La ingeniería es más seria de lo que el número de estrellas prepara a esperar: caché de compilación direccionada por contenido con una puerta explicable, determinismo de bytes como promesa con el kernel explícitamente excluido, vocabularios cerrados y errores didácticos en lugar de alias, 76 releases con notas en cuatro meses y una página de PyPI que publica su propia constitución. La factura también es real, y es invisible en una instrucción de instalación de una línea: 822 MB de entorno, un navegador, hasta 1,65 GiB de demonio ocioso y una superficie MCP de exactamente dos herramientas porque la capacidad nunca estuvo en el protocolo.

Lo que hay que interiorizar es la frontera. Esta herramienta construirá fielmente, de forma reproducible y verificable, la pieza que especificaste. No te dirá que olvidaste especificar la posición de los agujeros — y la ley 10, por muy alto que esté escrita, solo gobierna la mitad del proceso donde quien comprueba es una máquina.