Repository navigation
mcp_server FR
Permet à un assistant IA (Claude, ou tout autre client MCP) de lire et de vérifier des projets QElectroTech directement, plutôt que de raisonner à partir d'une capture d'écran : ce que contient un projet, ce qu'une modification a réellement changé, et ce que contient tout un corpus de projets.
Il se trouve dans misc/qet-mcp/qet_mcp.py dans l'arborescence des sources —
un petit serveur Model Context Protocol
en stdio, Python 3.9+ et la bibliothèque standard uniquement, sans
dépendance au SDK MCP. Ajouté dans la
PR #969 ;
sa fondation d'API de script a été ajoutée en même temps dans la
PR #970.
Les deux ont été fusionnées le 2026-09-21.
Vérifier une modification par capture d'écran n'est pas fiable, et cet outil existe parce que cette non-fiabilité a produit deux conclusions erronées dans une seule session de relecture :
- Un glisser-déposer d'une sélection multi-éléments semblait avoir laissé
les symboles en place et détaché leurs étiquettes. La comparaison du
fichier enregistré a montré que les quatre éléments avaient tous bougé
d'un même delta
(0, -80)et qu'aucune étiquette n'avait bougé du tout. Un rapport de bogue est passé à un cheveu d'être déposé. - Un bouton « Appliquer » semblait ne rien faire. Il était désactivé, car un champ obligatoire était vide.
Les deux fois, les pixels ont induit en erreur et le modèle a dit la vérité. Les outils présentés ici lisent donc le modèle — le XML du projet et, quand elle existe, la base de données du projet — plutôt que la scène rendue.
La plupart des outils analysent directement le fichier .qet/.elmt :
rapide, sans affichage nécessaire, insensible à une boîte de dialogue
égarée. Deux d'entre eux (qet_export, qet_edit) lancent QElectroTech
lui-même, dans un bac à sable isolé, parce qu'exporter et modifier via
l'application réelle est le seul moyen d'obtenir le comportement réel —
voir Scripts JavaScript pour le moteur que qet_edit et
qet_query pilotent en dessous.
# depuis l'arborescence des sources de QElectroTech
python3 misc/qet-mcp/qet_mcp.py --list # liste les outils puis quitte
python3 misc/qet-mcp/qet_mcp.py # parle MCP sur stdin/stdoutEnregistrez-le auprès d'un client MCP — pour Claude Code ou Claude Desktop,
un bloc mcpServers :
{
"mcpServers": {
"qet": {
"command": "python3",
"args": ["/path/to/qelectrotech/misc/qet-mcp/qet_mcp.py"],
"env": {
"QET_MCP_WORKSPACE": "/home/you/drawings",
"QET_ENABLE_SCRIPTING": "1"
}
}
}
}Rien à compiler, rien à installer via pip — les deux variables
d'environnement ci-dessus sont la seule configuration qui compte, et les
deux sont détaillées plus bas.
| Variable | Effet |
|---|---|
QET_MCP_WORKSPACE |
Répertoires que les appels d'outils peuvent lire et écrire, séparés par : (; sous Windows). Non définie : le répertoire depuis lequel le serveur a été démarré. |
QET_MCP_ALLOW_ANY_PATH=1 |
Désactive entièrement la vérification de l'espace de travail — équivalent à donner au client un accès au système de fichiers local avec les privilèges de ce processus. |
QET_ENABLE_SCRIPTING=1 |
Requis par cinq outils (voir plus bas) ; QElectroTech refuse --run sans cette variable, désactivée par défaut depuis la PR #984. |
Chaque chemin passé dans un appel d'outil est choisi par le modèle. Sans
politique de contrôle, cela ferait du serveur une primitive de
lecture/écriture pour tout ce que le système d'exploitation permet au
processus d'atteindre : lire n'importe quel projet sur le disque, exporter
ailleurs, écraser un fichier sans rapport, intégrer une image ou un PDF
local arbitraire. C'est pourquoi les chemins de données sont confinés à
QET_MCP_WORKSPACE, vérifiés au point où les arguments entrent dans le
serveur. Un chemin en dehors de celui-ci est refusé avec une erreur nommant
ce qui était autorisé ; les liens symboliques sont résolus au préalable, si
bien qu'un lien planté à l'intérieur de l'espace de travail est jugé
d'après ce vers quoi il pointe.
Deux arguments ne sont volontairement pas confinés : binary
(l'exécutable qelectrotech) et elements_dir (la collection
d'éléments). Ce sont des réglages de configuration, choisis une fois par
la personne qui exécute le serveur, et tous deux vivent normalement dans
/usr ou dans une arborescence de compilation — en dehors de tout espace
de travail raisonnable. Les confiner rejetterait le cas ordinaire sans rien
empêcher.
Rien n'est écrasé sans autorisation. qet_export, qet_edit,
qet_project_new et qet_element_build refusent une output déjà
existante à moins que l'appel ne passe "overwrite": true — la seule étape
que ce serveur ne peut pas annuler est la seule qu'il ne franchira pas de
lui-même.
Nécessite QET_ENABLE_SCRIPTING=1
|
qet_query, qet_continuity, qet_check, qet_project_new, qet_edit
|
| Non concernés | Tous les autres — ils lisent le .qet/.elmt directement, ou, pour qet_export, utilisent un simple indicateur de ligne de commande |
La variable se règle dans l'environnement où le serveur est démarré (le
bloc env ci-dessus), et le serveur la transmet telle quelle à
QElectroTech — il ne la définit pas lui-même. Un interrupteur qu'un
programme active pour lui-même n'est pas un interrupteur : c'est la
personne qui a configuré le serveur et l'a pointé vers un binaire
QElectroTech qui a fait ce choix, et son propre QElectroTech interactif
conserve ce que dit son propre réglage. Sans la variable, les cinq outils
ci-dessus renvoient "ok": false avec un hint nommant la variable. Les
versions antérieures à l'existence de ce réglage n'ont besoin de rien.
| Outil | À quoi il répond | Lance QET ? |
|---|---|---|
qet_project_info |
Titre, version du format, folios, décompte d'éléments/conducteurs par folio | Non |
qet_elements |
Éléments placés : uuid, type, position, étiquette, sac d'informations ; filtre par folio ou par nom | Non |
qet_conductors |
Conducteurs et leurs champs documentaires (num, formula, cable, bus, function, colour, section) ; filtre par attribut |
Non |
qet_diff |
Ce qu'une modification a réellement changé — déplacements/ajouts/suppressions/réétiquetages d'éléments, changements des champs de conducteur, champs/textes/formes/images/champs de texte de symboles/borniers de folio | Non |
qet_scan |
Parcourt un répertoire de projets, comptant les nœuds portant un attribut, avec les valeurs distinctes trouvées | Non |
qet_element_info |
Introspection d'un .elmt : noms traduits, bornes, champs de texte dynamique, décompte des parties |
Non |
qet_export |
Export sans interface : pdf, png, svg, bom, cables, wires, wiring, nets, links, info | Oui |
qet_edit |
Modifier un projet — placer, déplacer, faire pivoter, étiqueter, câbler, numéroter, faire des renvois, ajouter texte/formes/images, restyler les champs de texte d'un symbole, supprimer ; renvoie un qet_diff du résultat |
Oui |
qet_query |
Requête SQL en lecture seule (SELECT/WITH) sur la base SQLite du projet ; omettre sql liste les vues/tables interrogeables |
Oui* |
qet_continuity |
Vérifications de type ERC sur le graphe Borne/Conducteur en direct : bornes non connectées, incohérences de potentiel, incohérences de renvoi de folio | Oui* |
qet_project_new |
Démarrer de zéro : un projet vide avec un titre et des folios, écrit et relu par QElectroTech lui-même | Oui* |
qet_element_search |
Trouver un symbole dans une collection par nom (toute langue), type de liaison, genre ou nombre de bornes ; les résultats portent le chemin common:// et l'ordre d'index des bornes dont qet_edit a besoin |
Non |
qet_check |
Vérifications de règles de conception : étiquettes en double, bornes non connectées, conducteurs sans numéro, folios vides, maîtres sans référence fabricant | Oui* |
qet_element_build |
Créer un nouveau .elmt : dessiné à partir de lignes/rectangles/ellipses/cercles/arcs/polygones/texte, avec des bornes pour le câbler ; calcule et vérifie l'en-tête de dimensions |
Non |
* Nécessite QET_ENABLE_SCRIPTING=1.
{"name": "qet_diff", "arguments": {"before": "a.qet", "after": "b.qet"}}"elements": { "moved_count": 4,
"distinct_move_deltas": [[0.0, -80.0]],
"relabelled": [], "info_changed": [] }Quatre éléments ont bougé d'un même delta ; rien n'a été réétiqueté. C'est la réponse qu'une capture d'écran donnait à tort.
{"name": "qet_edit", "arguments": {
"binary": "/path/to/qelectrotech",
"project": "in.qet", "output": "out.qet",
"elements_dir": "/path/to/qelectrotech/elements",
"operations": [
{"op": "add_folio", "id": "f"},
{"op": "set_folio_title", "folio": "$f", "title": "Starter"},
{"op": "add_element", "id": "k1", "folio": "$f", "path": "common://.../coil.elmt", "x": 100, "y": 100},
{"op": "add_element", "id": "k2", "folio": "$f", "path": "common://.../coil.elmt", "x": 320, "y": 100},
{"op": "add_conductor", "folio": "$f", "from": "$k1", "from_terminal": 0, "to": "$k2", "to_terminal": 0},
{"op": "set_conductor", "folio": "$f", "element": "$k1", "terminal": 0, "property": "num", "value": "W7"},
{"op": "set_label", "folio": "$f", "element": "$k1", "label": "KM1"}
]}}Une opération qui crée quelque chose porte un "id" ; les opérations
suivantes le désignent comme "$id". Les bornes sont adressées par
index — de haut en bas puis de gauche à droite, pas l'ordre dans lequel
le .elmt les liste ; qet_element_info et qet_element_search
rapportent tous deux cet ordre d'index. Le résultat porte à la fois un
résultat par opération et un qet_diff, car "addConductor → true" dit
que l'appel a été accepté, pas que le fichier obtenu est correct :
"diff": {"elements": {"before": 11, "after": 13, "added": ["{0aa3…}", "{6f63…}"]},
"conductors": {"before": 47, "after": 48, "added": ["4:{0aa3…}/{2904…}--{6f63…}/{2904…}"],
"removed": []}}{"name": "qet_element_build", "arguments": {
"output": "/path/to/collection/99_custom/my_resistor.elmt",
"names": {"en": "Test resistor", "fr": "Résistance de test"},
"parts": [
{"type": "rect", "x": -10, "y": -20, "width": 20, "height": 40},
{"type": "line", "x1": 0, "y1": -30, "x2": 0, "y2": -20},
{"type": "line", "x1": 0, "y1": 20, "x2": 0, "y2": 30},
{"type": "text", "x": 14, "y": -4, "text": "R"}
],
"terminals": [{"x": 0, "y": -30, "orientation": "n", "name": "1"},
{"x": 0, "y": 30, "orientation": "s", "name": "2"}]}}Placez-le ensuite avec qet_edit comme n'importe quel élément de
catalogue. Contrairement à un projet, un .elmt n'est pas réécrit par
QElectroTech lors d'un aller-retour, donc en générer un ici est sûr d'une
façon qui ne le serait pas pour un .qet — il n'y a pas de toXml() en
embuscade pour perdre ce que cet écrivain ne savait pas produire.
{"name": "qet_query", "arguments": {
"binary": "/path/to/qelectrotech", "project": "industrial.qet",
"sql": "SELECT label, COUNT(*) AS n FROM element_nomenclature_view WHERE label <> '' GROUP BY label HAVING n > 1 ORDER BY n DESC"}}"rows": [{"label": "V6", "n": 7}, {"label": "V5", "n": 6}, {"label": "V4", "n": 6}]Des étiquettes d'éléments en double dans un exemple fourni — une question
de règle de conception, à laquelle la base de données savait déjà
répondre. Voir La base de données du projet pour ce
que couvrent element_nomenclature_view, project_summary_view et
wiring_list_view.
{"name": "qet_scan",
"arguments": {"directory": "examples", "tag": "conductor", "attribute": "cable"}}{ "files": 24, "total": 3190, "non_empty": 0, "distinct_values": [] }Sur les exemples fournis : 3190 conducteurs, aucun n'a de valeur de câble.
-
qet_exportisole son lancement. SingleApplication indexe son socket surapplicationFilePath(), donc un second lancement du même chemin binaire redirige sa requête vers une instance déjà en cours d'exécution et renvoie sa réponse, sans erreur. L'outil copie le binaire vers un chemin temporaire unique, lui donne unHOMEprivé, et le lance sur la plateforme hors écran. Un lien symbolique ne fonctionnerait pas —applicationFilePath()le résout jusqu'au chemin réel. -
La ligne de commande exige la forme exacte des indicateurs.
--export-bom out.csvest la forme prise en charge ;--export-bom=out.csvn'est pas reconnu comme un export du tout, donc l'application démarre son interface à la place et un lancement sans interface reste bloqué. L'outil utilise la forme positionnelle. -
L'identité des conducteurs est la partie difficile de
qet_diff. Les conducteurs sont identifiés par l'uuid de l'élément propriétaire plus la borne, ce qui est stable d'un enregistrement à l'autre — les identifiants entiers propres au folio du fichier sont renumérotés à chaque enregistrement et feraient lire chaque conducteur d'un folio non modifié comme supprimé puis rajouté. Quand un élément est antérieur aux uuid persistés, l'extrémité ne peut pas être résolue et conserve une clé instable marquée#; le diff rapporte alorsunstable_keysplutôt que de prétendre être comparable. - Les textes, formes et images n'ont pas d'uuid, donc un texte modifié est lu comme l'ancien supprimé et un nouveau ajouté, les deux étant affichés. Les formes et images sont identifiées par position, donc un changement de style ou d'échelle est rapporté comme un changement de cet élément, mais un déplacement se lit comme une suppression plus un ajout.
-
qet_editnécessite une version dont l'API de script porte les verbes de dessin. Face à une version plus ancienne, il indique précisément quelles méthodes manquent et ne change rien. -
elements_dirn'est pas optionnel pour les cheminscommon://. Le lancement en bac à sable a son propreHOMEvide, donc QElectroTech se rabat sur le chemin de collection compilé en dur, qui n'existe pas sur une machine oùmake installn'a jamais été exécuté. Le seul symptôme est queadd_elementrapporte qu'un fichier pourtant bien présent « ne correspond à aucun élément ». Un chemin.elmtabsolu fonctionne sans cela. -
set_conductorchange tout le potentiel, pas un seul segment — c'est ce que fait l'application, puisqu'un numéro de fil décrit un potentiel. Nommez une borne portant exactement un conducteur ; une borne où plusieurs conducteurs se rejoignent n'en désigne aucun et est refusée. -
link_elementsprend un folio pour chaque extrémité, car un maître et son esclave sont normalement sur des folios différents. La possibilité de lier une paire est décidée par leisLinkable()propre à QElectroTech, donc un script ne peut pas créer un lien que l'interface refuserait. -
Un élément doit se trouver dans une collection pour être plaçable.
Un chemin
.elmtabsolu fonctionne, mais seulement si le fichier se trouve sous un répertoire que QElectroTech connaît comme collection — écrivez-le sous l'arborescence passée commeelements_dir. -
qet_element_buildvérifie son en-tête de dimensions par rapport à une contrainte de contenance, pas une formule : la boîte déclarée va de(-hotspot_x, -hotspot_y)à(width - hotspot_x, height - hotspot_y)et le dessin doit tenir à l'intérieur. Un dessin qui a débordé de sa boîte est la façon classique dont un élément écrit à la main s'affiche tronqué dans le panneau de collection tout en semblant correct dans le XML. -
QElectroTech interrompt un script au bout de 30 s de sa propre
initiative, indépendamment du
timeoutpropre à l'outil. Une très longue liste d'opérations atteint cette limite en premier. -
qet_editn'écrit jamais le fichier d'entrée. Il enregistre dans un fichier séparé et compare les deux, donc l'original reste toujours ce contre quoi le diff est fait.
python3 misc/qet-mcp/test_qet_mcp.py # unitaires + protocole, sans QElectroTech
QET_BINARY=/path/to/qelectrotech \
QET_ELEMENTS=/path/to/qelectrotech/elements \
QET_EXAMPLES=/path/to/qelectrotech/examples \
QET_ENABLE_SCRIPTING=1 \
python3 misc/qet-mcp/test_qet_mcp.py # tout, y compris l'intégrationQET_ENABLE_SCRIPTING=1 compte aussi ici : sans cette variable, les tests
d'intégration qui pilotent QElectroTech via un script échouent tous, et ils
échouent en disant « la modification n'a rien fait » plutôt que « les
scripts sont désactivés » — ce qui ressemble à une régression de la chose
testée, pas à un interrupteur manquant.
-
Scripts JavaScript — le moteur
qet.*queqet_editetqet_querypilotent en dessous -
La base de données du projet — ce que
qet_querylit -
Référence de la ligne de commande — les indicateurs
d'export que
qet_exportenveloppe - Automatiser QElectroTech — le format de fichier et l'export sans interface sur lesquels ce serveur s'appuie
- PR #969 — le serveur lui-même
- PR #970 — les verbes de l'API de script dont il dépend
Getting Started
🌐 Languages — English · Français · Deutsch
Windows without admin rights — the portable archive, no installer
Guides
Conductors — wire properties, what feeds which export, cables, and hops where wires cross
Wires per terminal — limit the wires on a terminal, chain wiring instead of stars
Printing and exporting — paper, PDF, images, and what each path does differently
Linking elements — master, slave, terminal
PLC modules — I/O tables and linking a wire to a specific point
Using the element editor — drawing tools, saving, checks
Grid size and element size — why symbols aren't all the same scale, and scaling one without leaving the grid
Preferences reference — what each settings page does
Saving and loading settings — your whole setup in one file, to copy or keep
Keyboard-only control — mouseless QET, and what still needs a mouse
Mouse modifiers — what Shift, Ctrl and Alt change while you drag
3D mouse — SpaceMouse pan, zoom and buttons
Aligning items — snap symbols back to the grid, or line them up
Pictures on a sheet — labels, crop, transparency, what they cost in the file
Arcs and curved wires — the Arc tool, pulling an arc in or out, rounding a corner with a fillet, dashed arcs for lighting layouts
Grouping items — select, move and copy several items as one
Finding your place on a sheet — go to a cell like B13 or 4-B7, keep the headers in sight, show the cell limits, zoom and pan
Showing and hiding kinds of items — hide texts, wire numbers, shapes, pictures, tables or cross-references on every sheet
Drawing faster — place without dragging, the S shortcut bar, command search, gestures
Customising QElectroTech — keys, toolbar size and contents, the gesture ring (partly pending)
Managing collections — folders, writability, building your own shortlist
Templates — reusable multi-element blocks, placed by double-click or drag
Search & Replace — bulk property changes
Building a nomenclature query — the BOM/summary table builder
Linking wires across pages — sheet reports
Variables & formulas — %f, %{label}, sequences
Auto-numbering — schemes, sequences, freezing
Terminal strips — strips, levels, bridges
Title block templates — the .titleblock format
Importing EPLAN parts (.edz) — EPLAN Data Portal
DXF import & export — two unrelated features, one format; command-line export and layers
The project database — the in-memory SQLite cache
Development
Automating QET — CLI, XML formats, external tools
CLI Reference — command line usage
JavaScript Scripting — --run, geometry editing, undo
MCP server — let an AI assistant read, verify and edit projects
Connecting an AI assistant — setup for Claude, Copilot, Gemini, Codex, Cursor, LM Studio
Script buttons — stored scripts with an icon, by hand or by an assistant
Live mode — an assistant working in the open project while you watch
Macro recorder — record a task by hand, for an assistant to script
Vision — proposal, under discussion
