svg-annotate-mcp
svg-annotate-mcp
En el navegador puedes anotar figuras de artículos científicos en SVG: haz clic en un elemento (al pasar el cursor se resalta el elemento semántico, estilo Claude Design) o marca un área de selección. La anotación —con la información de los elementos SVG alcanzados— se devuelve a Claude a través de MCP; Claude modifica el archivo de origen (el SVG o el script generador), y la página vigila los cambios del archivo para refrescarse automáticamente. Así se cierra el ciclo «anotar → modificar → refrescar → volver a anotar».
Backend: Python + el SDK oficial
mcp(MCPServer, stdio); se levanta dentro del proceso un hilo HTTP de la librería estándar (127.0.0.1, puerto temporal).Frontend: un único archivo
src/svg_annotate_mcp/web/index.html. El SVG se inyecta mediantefetch+DOMParseren un Shadow DOM (con aislamiento en ambos sentidos de estilos e ids respecto a la página); las anotaciones se dibujan en un overlay independiente. Al enviar, la detección de elementos alcanzados se hace en el lado del navegador.
Instalación y registro
cd ~/Projects/svg-annotate-mcp && uv sync
claude mcp add --scope user svg-annotate -- \
uv run --directory /Users/boryant/Projects/svg-annotate-mcp svg-annotate-mcpOpcional: puedes ampliar el tiempo de espera del cliente MCP para que una sola llamada a wait_for_annotations pueda esperar más (si no lo haces también funciona, pero el bucle será más frecuente). Define MCP_TOOL_TIMEOUT=600000 en el perfil del shell o en la sección env de la configuración de Claude Code.
Herramientas (4)
Herramienta | Función |
| Abre el SVG en el navegador. En |
| Espera en bloqueo a que el usuario pulse «Enviar a Claude». Si devuelve |
| Recuperación no bloqueante: obtiene el lote más reciente de anotaciones enviadas (para restablecer el estado cuando la cadena de timeouts se ha roto). |
| Envía un estado a la barra superior de la página (por ejemplo, «modificando fig_xxx.py y reejecutando…»). Después de cambiar el archivo no hace falta llamarla; la página se actualiza sola. |
Selección por clic en elementos (v2)
Con la herramienta «Selección» por defecto, al pasar el cursor se resalta en tiempo real el elemento semántico que está debajo (los grupos g[id] de matplotlib, como text_N / line2d_N / legend_N; se toma el de menor área y la etiqueta muestra id + texto). Al hacer clic se abre el inspector de elementos: la barra lateral muestra id, el texto literal y las migas de pan de los ancestros; al pulsar una miga puedes cambiar la selección al grupo padre (por ejemplo, de text_54 a legend_1). Solo se genera una anotación si escribes contenido en el campo «Instrucciones de modificación» (un clic simple = inspector, y no crea anotaciones basura). Con Esc o «Cancelar selección» se sale del modo. Las anotaciones de tipo elemento aparecen en el dibujo como un recuadro discontinuo + número.
Estructura devuelta con las anotaciones (pensada para que Claude localice los cambios)
Cada anotación contiene:
kind(element/rect/arrow/freehand/text),note(la instrucción de modificación del usuario) ynumber(el número en el lienzo);cuando
kind:"element", además se incluyetarget: eltag/id/text(literal)/ancestors/d_prefix/bbox_svgdel elemento seleccionado —es el ancla más fuerte para localizarlo; úsala en primer lugar (grep delido deltextdirectamente en el script generador o en el SVG);geometry_norm(0–1) ygeometry_svg(coordenadas delviewBox, ya convertidas por el servidor);hits[]: elementos SVG alcanzados en la selección; cada uno incluyetag/id(grupos semánticos de matplotlib, comotext_N/line2d_N) /text(texto literal del elemento) /ancestors(cadena de ids ancestros, p. ej.["figure_1","legend_1","text_54"]) /bbox_svg/coverage/d_prefix(primeros 30 caracteres delddepath, ancla para hacer grep cuando se edita el SVG directamente). Ya está eliminado el ruido: se descartan los contenedores de fondo, los grupos semánticos tienen prioridad y no se repiten las hojas, y hay un máximo de 10 por lote;texts_in_region: todos los textos del área (orden de lectura, sin duplicados): primera pista para localizar con grep en el script generador.
Dónde modificar lo decide Claude: si la anotación trae source_script, conviene modificar el script y reejecutarlo (si tocas el SVG de salida, la próxima ejecución lo sobrescribe). Si no hay script, se edita directamente el archivo SVG. La página no asume nada sobre la forma de modificar; solo observa el mtime del archivo (sondeo de 500 ms, doble comprobación de estabilidad y validación de cierre </svg> para no leer un archivo a medias).
Ciclo de vida de las anotaciones
Los borradores no enviados se conservan cuando la página se refresca. Al pulsar «Enviar a Claude», esa anotación queda en el lienzo con un 35 % de opacidad (manteniendo su número para el comparado). El siguiente refresco que provoque el cambio de archivo por parte de Claude elimina esas anotaciones semitransparentes.
Bucle típico
用户: 帮我改 figure11,我来圈
Claude: open_svg("/path/figures/figure11.svg", source_script="/path/fig_tri_complement.py")
wait_for_annotations() # 挂起
用户: (浏览器里圈图例写「图例移到右上」,点提交)
Claude: 收到批注 → set_status("正在改 fig_tri_complement.py…")
→ 改脚本 → 重跑出图 → 页面自动刷新
→ wait_for_annotations() # 等下一轮Pruebas
uv run python tests/smoke_test.py # 端到端:握手/HTTP/阻塞等待/坐标换算/SSE reload/复用 tab
uv run python tests/manual_driver.py <svg> [script] # 起 server 供手动/浏览器自动化测试,批次落盘 tests/out/batches.jsonlParámetros de depuración en la página: ?nosse=1 omite el SSE (para capturas de pantalla headless); ?autotest=x,y,w,h dibuja automáticamente un rectángulo de anotación y lo envía (coordenadas 0–1 normalizadas); ?autotest_click=x,y hace clic en el elemento de ese punto y lo convierte en anotación de envío (con &autotest_stage=pick solo selecciona y resalta, para las capturas del inspector).
Variable de entorno: SVG_ANNOTATE_NO_OPEN=1 hace que open_svg no abra el navegador automáticamente (para pruebas).
Limitaciones conocidas (primera versión)
Sesión única (una sola figura a la vez; volver a abrir elimina la anterior; varias sesiones de Claude = varias instancias de servidor independientes que no interfieren). No se exporta annotations.json (las anotaciones se devuelven solo en memoria; se pierdebog al reiniciar el server, así que el contexto de Claude guarda la última copia). Los enlaces SVG externos no se proxyan (open_svg dará un aviso; los SVG de matplotlib van incrustados, no lo activan). La apertura del navegador solo funciona con open en macOS. localhost no tiene autenticación.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Real-time collaborative whiteboard — AI agents and humans edit the same board live over MCP.
Live browser debugging for AI assistants — DOM, console, network via MCP.
Cross-agent artifact workspace with provenance across Claude Code, Codex, Cursor, LangGraph.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/sunjianbo-123/svg-annotate-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server