Inkscape

Un modèle pilote Inkscape 1.4 par le Hub pour composer une carte de visite sur deux calques. Inkscape est la seule application ici qui n’a rien à démarrer : le Hub lance un processus Python pour chaque script. Cette page donne le texte, le script, les réponses du Hub et la capture.

La fenêtre d'Inkscape : un dessin sombre de deux orbites autour d'un cercle, cinq modules étiquetés sur les orbites, et l'éditeur XML ouvert sur les nœuds que le script a écrits.
Un dessin fait par un script et ouvert dans Inkscape : c'est un SVG ordinaire, dont les nœuds XML se modifient à la main.

Activer Inkscape

Il n’y a ni pont ni fenêtre. Le Hub cherche /usr/bin/python3, /usr/bin/inkscape, et les extensions d’Inkscape dans /usr/share/inkscape/extensions. Toute la préparation tient en une entrée dans ~/.config/libremodular-mcp/config.toml, après libremodular-mcp init :

[apps.inkscape]
enabled = true

Sur une Debian 13 vierge, import inkex demande aussi python3-tinycss2, que le paquet inkscape n’installe pas : sudo apt install inkscape python3-tinycss2.

get_state sur hub a listé Inkscape comme connecté en mode cli, avec la version qu’Inkscape déclarait :

Réponse du Hub
{
  "app": "hub",
  "app_version": "0.1.0",
  "bridge_version": null,
  "captured_at": "2026-10-21T12:47:13.608Z",
  "connected": true,
  "content_origin": "hub",
  "screenshot": null,
  "state": {
    "modules": [
      {
        "app_version": "1.4",
        "bridge_version": null,
        "connected": true,
        "mode": "cli",
        "name": "inkscape"
      }
    ]
  },
  "warnings": []
}

Texte donné au modèle

Texte donné au modèle
In Inkscape, lay out a name card, A6 landscape (148 by 105 mm). Dark blue background (#1c385c), a copper circle (#c25438) in the top-left corner, the name “Alice Martin” in white and large, a thin amber line (#f2b344) under it, then the role “Sound engineer” in light grey and smaller. Put the background shapes on a layer named “Background” and the text on a layer named “Text”.

Avant d’écrire, le modèle a appelé get_state sur inkscape. Le state était vide et un avertissement disait no document yet: the working document is created by the first script.

Script écrit

Un modèle, Claude Sonnet 5.5, a écrit ce script à partir du guide et de cet état. Ce n’est pas un script tiré des tests du projet. Deux lignes viennent du guide : le script retire d’abord les enfants de la racine, parce que le document est conservé d’un script au suivant, et il règle cx, cy et r du cercle un par un, parce que, comme le guide l’avertit, les deux arguments de inkex.Circle.new ne font pas un centre.

python · Script écrit
import json
import os

import inkex

document = inkex.load_svg(os.environ["LMM_DOCUMENT"])
svg = document.getroot()
for child in list(svg):
    svg.remove(child)
svg.set("width", "148mm")
svg.set("height", "105mm")
svg.set("viewBox", "0 0 148 105")

background = svg.add(inkex.Layer.new("Background"))
panel = inkex.Rectangle.new(0, 0, 148, 105)
panel.style = {"fill": "#1c385c"}
background.add(panel)
disc = inkex.Circle()
disc.set("cx", "20")
disc.set("cy", "20")
disc.set("r", "9")
disc.style = {"fill": "#c25438"}
background.add(disc)
rule = inkex.Rectangle.new(20, 62, 108, 0.8)
rule.style = {"fill": "#f2b344"}
background.add(rule)

texts = svg.add(inkex.Layer.new("Text"))
for content, y, size, colour, weight in [("Alice Martin", 56, 12, "#ffffff", "bold"), ("Sound engineer", 76, 6, "#cfd4da", "normal")]:
    line = inkex.TextElement()
    line.text = content
    line.set("x", "20")
    line.set("y", str(y))
    line.style = {"fill": colour, "font-size": f"{size}px", "font-family": "sans-serif", "font-weight": weight}
    texts.add(line)

document.write(os.environ["LMM_DOCUMENT"])
print("LMM_RESULT: " + json.dumps({"layers": [layer.label for layer in svg.descendants().filter(inkex.Layer)], "elements": len(list(svg.descendants()))}))

La réponse du Hub

Réponse du Hub
{
  "app": "inkscape",
  "duration_ms": 151,
  "error": null,
  "result": {
    "elements": 8,
    "layers": [
      "Background",
      "Text"
    ]
  },
  "status": "ok",
  "stderr": "",
  "stdout": "",
  "truncated": false
}

Le script a duré 151 ms : un processus Python a démarré et s’est terminé. Le script a imprimé son result sur la dernière ligne, après LMM_RESULT: , ce qui est la façon dont un script Inkscape renvoie une valeur. get_state a ensuite décrit le document de travail et renvoyé une capture :

Réponse du Hub
{
  "app": "inkscape",
  "app_version": "1.4",
  "bridge_version": null,
  "captured_at": "2026-10-21T12:47:14.185Z",
  "connected": true,
  "content_origin": "document",
  "screenshot": "$XDG_RUNTIME_DIR/libremodular-mcp/captures/inkscape-20261021T124713832Z.png",
  "state": {
    "element_count": 8,
    "height": "105mm",
    "layers": [
      {
        "id": null,
        "label": "Background"
      },
      {
        "id": null,
        "label": "Text"
      }
    ],
    "width": "148mm"
  },
  "warnings": []
}

L’image produite

La capture est le document de travail exporté par inkscape lui-même, de 559 par 397 pixels pour une carte de 148 par 105 mm. Le texte est dessiné dans la police sans-serif de cette machine ; une autre machine le dessine dans la sienne. Le pixel en haut à gauche de la capture vaut #1C385C et celui qui est dans le cercle vaut #C25438.

Une carte bleu foncé avec un cercle cuivre en haut à gauche, le nom Alice Martin en blanc, un trait ambre et la fonction Sound engineer en gris.
La capture du document de travail après le script, telle que le Hub l'a exportée.

Guide de référence dans le dépôt →

SuiteLibreOffice →