Skip to main content
Glama

svg-annotate-mcp

SVG-Artikelabbildungen direkt im Browser annotieren und senden – auf ein Element zeigen? No, translate properly:

SVG-Abbildungen von Papers im Browser annotieren – Elemente anklicken (Hover hebt das semantische Element unter dem Cursor hervor, im Claude‑Design-Stil) oder Bereiche auswählen. Die Annotationen (einschließlich der getroffenen SVG-Element-Informationen) werden per MCP an Claude Code zurückgegeben, woraufhin Claude Code die Quelldatei (SVG oder Generierungsskript) verändert; die Seite überwacht Dateiänderungen und aktualisiert sich automatisch – so entsteht ein geschlossener Kreislauf „Annotation → Änderung → Refresh → erneute Annotation“.

  • Backend: Python + offizielles mcp SDK (MCPServer, stdio); im selben Prozess läuft zusätzlich ein stdlib-HTTP-Thread (temporärer Port auf 127.0.0.1).

  • Frontend: eine einzelne Datei src/svg_annotate_mcp/web/index.html. Das SVG wird per fetch + DOMParser in Shadow DOM injiziert (beidseitig isoliert von Seiten-CSS und id-Werten), und die Annotationen werden auf ein separates Overlay gezeichnet; die Element-Treffererkennung läuft beim Absenden im Browser.

Installation und Registrierung

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-mcp

Optional: Den Timeout des MCP-Clients erhöhen, damit ein einzelner wait_for_annotations-Aufruf länger warten kann (Ohne Einstellung funktioniert es weiterhin, der Loop läuft häufiger.)Oder: In der Shell-Profile oder den Claude Code Settings unter env die Variable MCP_TOOL_TIMEOUT=600000 setzen.

Werkzeuge (4)

Werkzeug

Funktion

open_svg(svg_path, source_script="", title="")

Öffnet das SVG im Browser. source_script bekommt den Pfad des Skripts, das die Abbildung erzeugt (z. B. fig_*.py von matplotlib); das Skript wird bei jedem Batch unverändert mitgeliefert. Wenn die Seite bereits offen ist, wird der Tab wiederverwendet (Session-Event wird gesendet, um das Bild zu wechseln).

wait_for_annotations(timeout_s=120)

Blockiert und wartet darauf, bis der Benutzer „An Claude Code senden“ klickt. Ein Timeout mit status:"timeout" ist kein Fehler – einfach weiter warten und sofort erneut aufrufen (Loop-Protokoll; steht auch in der tool description).

get_annotations()

Nicht blockierender Fallback: Holt den zuletzt übermittelten Batch (zur Wiederherstellung, wenn die Timeout-Kette abgebrochen ist).

set_status(message)

Schiebt eine Statusmeldung in die Kopfleiste der Seite (z. B. „ fig_xxx.py ändere ich gerade, –”). Nach dem Ändern der Datei nicht aufrufen nötig; die Seite aktualisiert sich selbst.

Elementgenaue Auswahl (v2)

Im Standardwerkzeug „Auswahl": Hover hebt das semantische Element unter dem Cursor live hervor (bei matplotlib z. B. die g[id]-Gruppen text_N/line2d_N/legend_N, es wird die kleinstflächige Treffer immer verwendet; der Chip zeigt id+Text). Ein Klick öffnet den Element-Inspektor (Seitenleiste mit id,, wörtlichen Text, Vorfahren-Brotkrumen – Klick auf die Krume wählt die übergeordnete Gruppe neu aus, z.B. text_54 → legend_1). Erst wenn im „Änderungshinweis“ ein Text eingegeben wird, wird eine Annotation erzeugt (reines Klicken = Inspektor, keinerlei Nullannotationen); Esc oder „Auswahl aufheben“ beendet. Element Annotationen werden auf dem Bild als gestrichelte Box undigerung der Nummer angezeigt.

Rückgabestruktur der Annotationen (für Claude Code geschaffen)

Jede Annotation enthält:

  • kind (element/rect/arrow/freehand/text), note (die Änderungsbeschreibung des Nutzers), number (die Nummer auf dem Canvas);

  • Bei kind:"element" zusätzlich der target: zugewiesen... :

  • bei kind:"element" gibt es target: tag/id/text (wörtlich)/ancestors/d_prefix/bbox_svg des Elementebes der angeklickten Elements – das ist die stärkste Verankerung; bevorzugt verwenden (id oder text direkt per grep auf das Generation Skript/ SVG suchen);

  • geometry_norm (0–1) und geometry_svg (viewBox-Koordinaten, serverseitig bereits konvertiert);

  • hits[]: die vom Auswahlbereich getroffenen SVG-Elemente; jedes mit tag / id (semantische Gruppen wie matplotlib text_N/line2d_N) / text (Element um Text unnötig? Elementtext, wörtlich) / ancestors (Vorfahren-id-Kette, z. B. ["figure_1","legend_1","text_54"]) / bbox_svg / coverage / d_prefix (die ersten 30 Zeichen des d-Attributs des Pfades, ein grep-Anker beim direkten Ändern des SVG). Schon geRauschen: Hinter–Container entfernt, semantische Gruppen prioritär (Blätter in der Gruppe nicht doppelt), je Annotation max. 10 Einträge;

  • texts_in_region: alle Texte im Auswahlbereich (im Kursübrigen, deduplikt) – der erste Hinweis für eine grep- Suche im Generierungsskript.

Wo geändert wird, entscheidet Claude Code: Wenn die Annotation source_script enthält, sollte zuerst das Skript und die Abbildung neu generiert werden (die SVG-Datei als Prodikat würde beim nächsten Lauf überschrieben); wenn kein Skript existiert, direkt die SVG-Datei bearbeiten. Die Seite macht keine Annahmen über die Änderungsmethode, sondern überwacht nur die Datei-mtime (500ms-Pollings, stabil über zweifache Zeitreihe, </svg> am Ende als Schreibschutz).

Lifecycle

Nicht mehr übernommen – Wording:

Nicht übermittelte (unkommittete) Entwürfe bleiben auch nach dem Refresh erhalten. Nach dem Klick auf „An Claude Code senden“ wird dieser (diesen) Stapel zu 35 % halbtransparent auf dem Bild behon (Nummer bleibt, damit man zuordnen kann); Die nächste Aktualisierung, die ausgelöst wird, weil Claude Code Dateien geändert hat, entfernt die halbtransparenten Annotationen.

Typischer Kreislauf

用户: 帮我改 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()        # 等下一轮

Tests

uv run python tests/smoke_test.py     # 端到端:握手/HTTP/阻塞等待/坐标换算/SSE reload/复用 tab
uv run python tests/manual_driver.py <svg> [script]   # 起 server 供手动/浏览器自动化测试,批次落盘 tests/out/batches.jsonl

Debug-Parameter für die Seite: ?nosse=1 überspringt SSE (für Headless-Screenshots); ?autotest=x,y,w,h zeichnet und drückt nach dem Laden automatisch ein VB-Rechteck (0–1 normalisierte Koordinaten); ?autotest_click=x,y wählt das Element in diesem Punkt und drückt es automatisch ab (mit &autotest_stage=pick nur klick it den Hover, zum Screenshoten des Inspektors).

Umgebungsvariablen: SVG_ANNOTATE_NO_OPEN=1 sorgt dafür, dass open_svg den Browser nicht automatisch aufruft (für Tests).

Bekannte Grenken (der ersten Version)

Einzelsitzung (ein Bild zur Zeit; erneutes open wechselt das Bild); keine Konkurrenz (mehrere Claude-Concurrent-Sitzungen behindern sich nicht, da getrennte Server); keine Export von annotations.json (Annotationen werden nur im Speicher übergeben, gehen beim Server-Neustart verloren, Rückhalt komplett über die Claude-Kontext); externen Bilder/Ressourcen in SVG werden nicht proxied (open_svg gibt eine Warnung aus; matplotlib-Outputs sind alle eingebettet, lösen das nicht aus); nur macOS open öffnet Browser; localhost ist ohne Authentifizierung.

-
license - not tested
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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.

View all MCP Connectors

Latest Blog Posts

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