larkhre-mcp 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LARKHRE_POUR_IA.md +233 -0
- package/LICENSE +21 -0
- package/README.md +45 -0
- package/package.json +25 -0
- package/server.js +146 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# LARKHRE_POUR_IA.md — Spécification du langage Larkhré pour agents IA
|
|
2
|
+
|
|
3
|
+
> **Instruction pour l'agent IA qui lit ce fichier :** ce document contient la
|
|
4
|
+
> spécification complète et exacte du langage de programmation Larkhré (v8.0),
|
|
5
|
+
> créé par Ladji Doucaré. Après lecture, tu sais écrire du code Larkhré correct.
|
|
6
|
+
> Respecte STRICTEMENT cette syntaxe — n'invente jamais de mots-clés ou de
|
|
7
|
+
> fonctions absents de ce document. En cas de doute, préfère les constructions
|
|
8
|
+
> montrées dans les exemples canoniques en fin de fichier.
|
|
9
|
+
>
|
|
10
|
+
> Exécution : playground https://larkhre.github.io/code/ (navigateur)
|
|
11
|
+
> ou `pip install larkhre` puis `larkhre fichier.laz` (Python 3.8+).
|
|
12
|
+
> Fichiers : extension `.laz`, encodage UTF-8.
|
|
13
|
+
|
|
14
|
+
## 1. Règles de syntaxe fondamentales
|
|
15
|
+
|
|
16
|
+
- Blocs délimités par accolades `{ }`. PAS de point-virgules (autorisés mais inutiles). Une instruction par ligne.
|
|
17
|
+
- Commentaires : `# ...` ou `// ...` jusqu'à fin de ligne.
|
|
18
|
+
- Chaînes : guillemets doubles uniquement `"texte"`. Échappements : `\n \t \" \\`.
|
|
19
|
+
- Interpolation dans les chaînes : `"Salut {nom}"` remplace `{nom}` par la variable `nom` si elle existe (identificateurs simples uniquement, pas d'expressions). Accolades littérales : `{{` et `}}`.
|
|
20
|
+
- Identificateurs : lettres/chiffres/underscore, pas d'espaces. Sensible à la casse.
|
|
21
|
+
- Nombres : entiers `42` et décimaux `3.14`. Division `/` renvoie un entier si le résultat est entier (10/2 → 5, 10/3 → 3.333...).
|
|
22
|
+
- Pas de `NULL`/`None`/`nil` : la valeur vide est `walu`.
|
|
23
|
+
- Booléens : `vrai` et `faux` (PAS true/false, sauf pack de langue anglais actif).
|
|
24
|
+
|
|
25
|
+
## 2. Les 22 mots-clés (et RIEN d'autre)
|
|
26
|
+
|
|
27
|
+
| Mot-clé | Rôle | Équivalent Python |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `laz` | déclarer une variable : `laz x = 5` | `x = 5` (première affectation) |
|
|
30
|
+
| `garde` | variable PERSISTANTE entre exécutions : `garde score = 0` | (unique à Larkhré) |
|
|
31
|
+
| `fonk` | définir une fonction : `fonk f(a, b) { ... }` | `def` |
|
|
32
|
+
| `rend` | retourner une valeur : `rend x * 2` | `return` |
|
|
33
|
+
| `kan` | condition : `kan x > 5 { ... }` | `if` |
|
|
34
|
+
| `sinon kan` | sinon si (enchaînable) | `elif` |
|
|
35
|
+
| `sinon` | sinon : `sinon { ... }` | `else` |
|
|
36
|
+
| `tanke` | boucle tant que : `tanke x > 0 { ... }` | `while` |
|
|
37
|
+
| `pou` + `dan` | boucle pour : `pou i dan 1..10 { ... }` | `for i in` |
|
|
38
|
+
| `kase` | sortir de la boucle | `break` |
|
|
39
|
+
| `swiv` | passer à l'itération suivante | `continue` |
|
|
40
|
+
| `vrai` / `faux` | booléens | `True` / `False` |
|
|
41
|
+
| `walu` | valeur vide | `None` |
|
|
42
|
+
| `et` / `ou` / `non` | logique (aussi `&&` `\|\|` `!`) | `and` / `or` / `not` |
|
|
43
|
+
| `klas` | classe : `klas Chien { ... }` | `class` |
|
|
44
|
+
| `herite` | héritage : `klas Chiot herite Chien { ... }` | `class A(B)` |
|
|
45
|
+
| `importe` | importer un fichier : `importe "outils.laz"` (Python seulement, pas playground) | `import` |
|
|
46
|
+
| `essaie` / `rattrape` | gestion d'erreurs : `essaie { ... } rattrape err { ... }` | `try` / `except` |
|
|
47
|
+
|
|
48
|
+
Réaffectation SANS `laz` : `x = x + 1`. Raccourcis : `+=` `-=` `*=` `/=` (sur variables, éléments de liste/dico, propriétés d'objet).
|
|
49
|
+
|
|
50
|
+
## 3. Types et structures
|
|
51
|
+
|
|
52
|
+
- **Texte** : `"abc"`. Concaténation `+` (coerce automatiquement : `"a" + 5` → `"a5"`). Indexation `t[0]`, négatif `t[-1]`.
|
|
53
|
+
- **Liste** : `[1, 2, 3]`. Indexation base 0, négatifs OK. Affectation `l[0] = x`.
|
|
54
|
+
- **Dictionnaire** : `{ "cle": valeur, "n": 2 }` (clés : textes ou nombres). Lecture `d["cle"]` (erreur si clé absente — vérifier avec `contient`), écriture/ajout `d["cle"] = v`. `pou k dan d { }` itère sur les CLÉS.
|
|
55
|
+
- **Intervalle** : `1..10` produit la liste [1..10] BORNES INCLUSES. `10..1` compte à rebours.
|
|
56
|
+
- Vérité : `faux`, `walu`, `0`, `""`, `[]`, `{}` sont faux ; tout le reste est vrai.
|
|
57
|
+
- Comparaisons : `==` `!=` `<` `>` `<=` `>=`. Égalité par valeur (listes/dicos comparés en profondeur).
|
|
58
|
+
- Modulo `%` : signe du diviseur (comme Python).
|
|
59
|
+
|
|
60
|
+
## 4. Classes
|
|
61
|
+
|
|
62
|
+
```larkhre
|
|
63
|
+
klas Animal {
|
|
64
|
+
fonk init(moi, nom) { # constructeur, appelé par Animal("Rex")
|
|
65
|
+
moi.nom = nom # propriétés via moi.xxx
|
|
66
|
+
}
|
|
67
|
+
fonk parler(moi) { # TOUTE fonction de klas a "moi" en 1er paramètre
|
|
68
|
+
vox(moi.nom, "fait du bruit")
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
klas Chien herite Animal { # hérite de toutes les fonctions du parent
|
|
72
|
+
fonk parler(moi) {
|
|
73
|
+
vox(moi.nom, "dit wouf")
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
laz rex = Chien("Rex") # instanciation SANS mot-clé new
|
|
77
|
+
rex.parler()
|
|
78
|
+
rex.nom = "Rexou" # accès/écriture direct des propriétés
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Pas de `super`, pas de méthodes statiques, pas d'attributs de classe.
|
|
82
|
+
|
|
83
|
+
## 5. Les 48 fonctions intégrées (signatures exactes)
|
|
84
|
+
|
|
85
|
+
E/S : `vox(a, b, ...)` affiche (sépare par espaces) · `demand(prompt)` → texte saisi (TOUJOURS convertir avec `nombre()` pour du calcul) · `vox_couleur(texte..., couleur)` dernier argument = couleur parmi rouge, vert, jaune, bleu, violet, cyan, blanc, or, gris, rose, noir · `stylise(texte, style)` → texte stylé (couleurs + "gras", "souligne") · `efface_ecran()` · `ralenti(sec)` exécution pas-à-pas visible (0 = normal, max 3 ; ignoré en mode traduit) · `echoue(message)` lève une erreur rattrapable.
|
|
86
|
+
|
|
87
|
+
Conversion/inspection : `nombre(x)` · `texte(x)` · `tip(x)` → "nombre"/"texte"/"buli"/"liste"/"dico"/"walu"/"fonk"/nom de klas · `taille(x)` textes, listes, dicos.
|
|
88
|
+
|
|
89
|
+
Listes : `ajoute(liste, x)` (mutation, fin) · `retire(liste, index)` ou `retire(dico, cle)` (mutation, renvoie l'élément) · `tri(liste)` → NOUVELLE liste triée (types homogènes) · `contient(conteneur, x)` texte/liste/dico → vrai/faux · `colle(liste, sep)` → texte joint.
|
|
90
|
+
|
|
91
|
+
Dictionnaires : `cles(d)` → liste · `valeurs(d)` → liste.
|
|
92
|
+
|
|
93
|
+
Textes : `majus(t)` · `minus(t)` · `koupe(t, sep)` → liste · `remplace(t, ancien, nouveau)`.
|
|
94
|
+
|
|
95
|
+
Nombres : `hasard(a, b)` entier aléatoire inclusif · `arondi(x, decimales?)`.
|
|
96
|
+
|
|
97
|
+
Fichiers (Python : disque réel ; playground : fichiers virtuels de session) : `lis_fichier(chemin)` · `ecris_fichier(chemin, texte)` · `ajoute_fichier(chemin, texte)` · `fichier_existe(chemin)`.
|
|
98
|
+
|
|
99
|
+
Dessin (playground : toile visible en direct ; Python : buffer puis `sauve_dessin` → fichier SVG) : `toile(largeur, hauteur)` OBLIGATOIRE avant tout dessin · `fond(couleur)` · `trace_ligne(x1, y1, x2, y2, couleur)` · `trace_rect(x, y, l, h, couleur)` · `rect_plein(...)` · `trace_cercle(x, y, rayon, couleur)` · `cercle_plein(...)` · `trace_texte(x, y, texte, couleur)` · `sauve_dessin(chemin)`. Origine (0,0) en haut à gauche, y vers le bas. Couleurs : noms français ci-dessus ou `"#rrggbb"`.
|
|
100
|
+
|
|
101
|
+
Mode JEU temps réel (v6 ; playground : toile animée + sons ; Python : vraie fenêtre tkinter) : `chaque_image(f)` enregistre la fonction `f` (SANS parenthèses : `chaque_image(image)`, pas `chaque_image(image())`) appelée ~30 fois/seconde APRÈS la fin du programme principal · `touche_pressee(nom)` → vrai/faux, la touche est-elle enfoncée maintenant (noms : `"a"`-`"z"`, `"0"`-`"9"`, `"haut"`, `"bas"`, `"gauche"`, `"droite"`, `"espace"`, `"entree"`, `"echap"`) · `arrete_jeu()` termine la boucle de jeu · `joue_son(nom)` parmi piece, saut, explosion, clic, moteur, victoire, defaite · `dis(texte...)` (v8) prononce le texte à voix haute en français (synthèse vocale ; silencieux si indisponible).
|
|
102
|
+
|
|
103
|
+
Mode INTERFACE (v7 ; playground : widgets au-dessus de la console ; Python : fenêtre tkinter) : `titre(texte)` grand titre · `etiquette(texte)` → id (garder dans une variable pour `change_texte`) · `bouton(texte, f)` bouton cliquable, `f` SANS parenthèses, appelée à chaque clic · `champ(indication)` → id, zone de saisie · `valeur_de(id)` → TEXTE saisi (convertir avec `nombre()` pour calculer) · `change_texte(id, texte)` met à jour n'importe quel widget · `efface_interface()`. L'application reste vivante après le programme principal (elle attend les clics) ; `arrete_jeu()` la ferme. Compatible avec `garde`, `joue_son`, la toile et `chaque_image`.
|
|
104
|
+
|
|
105
|
+
## 6. Pièges à éviter (erreurs fréquentes des IA)
|
|
106
|
+
|
|
107
|
+
- PAS de `print`, `def`, `if`, `while`, `for`, `return`, `elif`, `try`, `let`, `function`, `console.log` → utiliser les mots-clés Larkhré.
|
|
108
|
+
- PAS de `f"..."` ni de `.format()` ni de `${}` → interpolation native `"{variable}"`.
|
|
109
|
+
- PAS de `else if` → `sinon kan`.
|
|
110
|
+
- PAS de `:` après les conditions, PAS d'indentation significative → accolades obligatoires.
|
|
111
|
+
- PAS de méthodes sur objets natifs : `liste.append(x)` n'existe PAS → `ajoute(liste, x)` ; `t.upper()` n'existe pas → `majus(t)` ; `len(x)` → `taille(x)`.
|
|
112
|
+
- `demand()` renvoie du TEXTE : `laz n = nombre(demand("? "))` pour un nombre.
|
|
113
|
+
- Une seule instruction par ligne dans les blocs (chaque `vox(...)` sur sa ligne).
|
|
114
|
+
- `pou i dan 1..n` : bornes INCLUSES (1 à n, pas n-1).
|
|
115
|
+
- Fonction de klas : premier paramètre `moi`, appels externes SANS passer moi : `rex.parler()`.
|
|
116
|
+
- `rattrape` exige un nom de variable : `rattrape erreur { vox(erreur) }`.
|
|
117
|
+
- Mode interface : l'état vit dans des variables globales ; les fonctions de boutons les modifient puis appellent `change_texte` ; JAMAIS de `demand()` dans une fonction de bouton (utiliser `champ` + `valeur_de`).
|
|
118
|
+
- Mode jeu : la fonction d'image doit REDESSINER toute la scène (`toile` + `fond` + formes) à chaque appel ; JAMAIS de `demand()` ni `ralenti()` dedans ; l'état du jeu vit dans des variables globales déclarées AVANT `chaque_image(...)` ; `chaque_image` s'appelle UNE fois, en fin de programme.
|
|
119
|
+
|
|
120
|
+
## 7. Packs de langue (v5)
|
|
121
|
+
|
|
122
|
+
Un commentaire en tête de fichier change les mots-clés : `#langue: anglais` (let, func, give, when, else, while, for, in, true, false, null, and, or, not, stop, next, class, extends, load, try, catch, keep) · `#langue: francais` — français académique (soit, fonction, retourne, si, sinon, tantque, pour, dans, vrai, faux, rien, et, ou, non, casse, continue, classe, herite, importe, essaie, rattrape, garde). Brouillons `bambara` et `wolof` existent. Les fonctions intégrées (`vox`, etc.) restent identiques dans toutes les langues. Conversion de fichier : `larkhre --traduire-vers anglais f.laz`.
|
|
123
|
+
|
|
124
|
+
## 8. Ligne de commande
|
|
125
|
+
|
|
126
|
+
`larkhre f.laz` exécuter · `larkhre` REPL · `larkhre --traduire f.laz` → génère f.py Python natif (souvent ~10× plus rapide, nécessite larkhre installé) · `larkhre --traduire-vers <langue> f.laz` conversion de mots-clés.
|
|
127
|
+
|
|
128
|
+
## 9. Exemples canoniques (à imiter)
|
|
129
|
+
|
|
130
|
+
```larkhre
|
|
131
|
+
# Jeu complet : boucle + condition + état + saisie
|
|
132
|
+
laz secret = hasard(1, 100)
|
|
133
|
+
laz trouve = faux
|
|
134
|
+
laz essais = 0
|
|
135
|
+
tanke non trouve {
|
|
136
|
+
laz nb = nombre(demand("Ton essai : "))
|
|
137
|
+
essais += 1
|
|
138
|
+
kan nb == secret {
|
|
139
|
+
trouve = vrai
|
|
140
|
+
vox_couleur("BRAVO en {essais} essais !", "vert")
|
|
141
|
+
} sinon kan nb < secret {
|
|
142
|
+
vox("Plus grand !")
|
|
143
|
+
} sinon {
|
|
144
|
+
vox("Plus petit !")
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
```larkhre
|
|
150
|
+
# Données : liste de dicos + fonction + interpolation
|
|
151
|
+
fonk moyenne(notes) {
|
|
152
|
+
laz total = 0
|
|
153
|
+
pou n dan notes {
|
|
154
|
+
total += n
|
|
155
|
+
}
|
|
156
|
+
rend total / taille(notes)
|
|
157
|
+
}
|
|
158
|
+
laz eleves = [
|
|
159
|
+
{ "nom": "Awa", "notes": [12, 15, 18] },
|
|
160
|
+
{ "nom": "Issa", "notes": [9, 14, 11] }
|
|
161
|
+
]
|
|
162
|
+
pou e dan eleves {
|
|
163
|
+
vox(e["nom"], "→ moyenne :", arondi(moyenne(e["notes"]), 1))
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```larkhre
|
|
168
|
+
# Persistance + erreurs + fichier
|
|
169
|
+
garde visites = 0
|
|
170
|
+
visites += 1
|
|
171
|
+
essaie {
|
|
172
|
+
ecris_fichier("journal.txt", "Visite numéro {visites}")
|
|
173
|
+
vox(lis_fichier("journal.txt"))
|
|
174
|
+
} rattrape probleme {
|
|
175
|
+
vox_couleur("Souci : {probleme}", "rouge")
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```larkhre
|
|
180
|
+
# Jeu temps réel (v6) : la boucle d'images + clavier direct
|
|
181
|
+
laz x = 200
|
|
182
|
+
laz score = 0
|
|
183
|
+
fonk image() {
|
|
184
|
+
kan touche_pressee("gauche") ou touche_pressee("q") {
|
|
185
|
+
x -= 5
|
|
186
|
+
}
|
|
187
|
+
kan touche_pressee("droite") ou touche_pressee("d") {
|
|
188
|
+
x += 5
|
|
189
|
+
}
|
|
190
|
+
toile(400, 200)
|
|
191
|
+
fond("noir")
|
|
192
|
+
cercle_plein(x, 100, 14, "cyan")
|
|
193
|
+
trace_texte(10, 24, "Score : {score}", "blanc")
|
|
194
|
+
kan touche_pressee("echap") {
|
|
195
|
+
joue_son("victoire")
|
|
196
|
+
arrete_jeu()
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
chaque_image(image)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```larkhre
|
|
203
|
+
# Application (v7) : boutons + champ + mise à jour
|
|
204
|
+
titre("Compteur de points")
|
|
205
|
+
laz points = 0
|
|
206
|
+
laz aff = etiquette("Points : 0")
|
|
207
|
+
laz zone = champ("Combien de points ajouter ?")
|
|
208
|
+
fonk ajoute_points() {
|
|
209
|
+
laz n = nombre(valeur_de(zone))
|
|
210
|
+
points += n
|
|
211
|
+
change_texte(aff, "Points : {points}")
|
|
212
|
+
}
|
|
213
|
+
bouton("Ajouter", ajoute_points)
|
|
214
|
+
fonk fini() {
|
|
215
|
+
arrete_jeu()
|
|
216
|
+
}
|
|
217
|
+
bouton("Fermer", fini)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
```larkhre
|
|
221
|
+
# Dessin génératif
|
|
222
|
+
toile(400, 300)
|
|
223
|
+
fond("noir")
|
|
224
|
+
pou i dan 1..10 {
|
|
225
|
+
cercle_plein(i * 38, 150, i * 3, "cyan")
|
|
226
|
+
}
|
|
227
|
+
trace_texte(140, 40, "Dessiné en Larkhré", "or")
|
|
228
|
+
sauve_dessin("art.svg")
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
*Larkhré v8.0 — langage open source (MIT) créé par Ladji Doucaré.
|
|
233
|
+
Guide humain : GUIDE_LARKHRE.md · Source : github.com/larkhre/larkhre.github.io*
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ladji Doucaré
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# larkhre-mcp 🤖
|
|
2
|
+
|
|
3
|
+
**Le serveur MCP du langage [Larkhré](https://larkhre.github.io/code/)** — il donne à n'importe quel agent IA (Claude, et tout client compatible Model Context Protocol) le pouvoir d'**écrire ET d'exécuter** du code Larkhré pour de vrai.
|
|
4
|
+
|
|
5
|
+
## Ce que l'IA peut faire
|
|
6
|
+
|
|
7
|
+
- `spec_larkhre` — lire la spécification complète du langage (mots-clés, 48 fonctions, pièges, exemples)
|
|
8
|
+
- `executer_larkhre` — lancer un programme et recevoir : la sortie console, les **erreurs pédagogiques en français**, les **dessins SVG**, les fichiers écrits, les paroles `dis()`, les sons, et la mémoire persistante `garde`
|
|
9
|
+
|
|
10
|
+
Demandez à votre IA : *« Code-moi un jeu en Larkhré et teste-le »* — elle écrit le code, l'exécute, lit les erreurs, corrige, et vous livre un programme **vérifié**.
|
|
11
|
+
|
|
12
|
+
## Installation (Claude Desktop)
|
|
13
|
+
|
|
14
|
+
Dans votre fichier `claude_desktop_config.json` :
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"mcpServers": {
|
|
19
|
+
"larkhre": {
|
|
20
|
+
"command": "npx",
|
|
21
|
+
"args": ["-y", "larkhre-mcp"]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Redémarrez Claude Desktop — les outils `executer_larkhre` et `spec_larkhre` apparaissent.
|
|
28
|
+
|
|
29
|
+
## Tout client MCP
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx -y larkhre-mcp # serveur stdio
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Le bac à sable
|
|
36
|
+
|
|
37
|
+
- Exécution limitée à 8 secondes / 240 images de jeu — les boucles infinies s'arrêtent proprement
|
|
38
|
+
- Fichiers virtuels (rien n'est écrit sur le disque)
|
|
39
|
+
- `demand()` lit le paramètre `entrees` ; `garde` persiste le temps de la session
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
*Larkhré — un langage libre (MIT) créé par Ladji Doucaré ·
|
|
43
|
+
[Playground](https://larkhre.github.io/code/) ·
|
|
44
|
+
[GitHub](https://github.com/larkhre/larkhre.github.io) ·
|
|
45
|
+
`pip install larkhre` · `npm install larkhre`*
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "larkhre-mcp",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Serveur MCP du langage Larkhré : permet à n'importe quel agent IA (Claude, etc.) d'ÉCRIRE et d'EXÉCUTER du code Larkhré pour de vrai — sortie console, dessins SVG, mémoire persistante.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"larkhre-mcp": "server.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"server.js",
|
|
11
|
+
"LARKHRE_POUR_IA.md",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
17
|
+
"larkhre": "^11.1.0",
|
|
18
|
+
"zod": "^3.23.0"
|
|
19
|
+
},
|
|
20
|
+
"keywords": ["larkhre", "mcp", "model-context-protocol", "ai", "agent", "langage", "french", "education"],
|
|
21
|
+
"author": "Ladji Doucaré",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": { "type": "git", "url": "git+https://github.com/larkhre/larkhre.github.io.git" },
|
|
24
|
+
"homepage": "https://larkhre.github.io/code/"
|
|
25
|
+
}
|
package/server.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* ============================================================
|
|
3
|
+
larkhre-mcp — le serveur MCP du langage Larkhré
|
|
4
|
+
Créé par Ladji Doucaré · https://larkhre.github.io/code/
|
|
5
|
+
|
|
6
|
+
Donne à n'importe quel agent IA le pouvoir d'ÉCRIRE et
|
|
7
|
+
d'EXÉCUTER du code Larkhré pour de vrai :
|
|
8
|
+
- executer_larkhre : lance un programme, renvoie la sortie,
|
|
9
|
+
les erreurs pédagogiques, les dessins SVG, la mémoire garde
|
|
10
|
+
- spec_larkhre : la spécification complète du langage
|
|
11
|
+
============================================================ */
|
|
12
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
13
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
import { readFileSync } from 'fs';
|
|
16
|
+
import { fileURLToPath } from 'url';
|
|
17
|
+
import { dirname, join } from 'path';
|
|
18
|
+
import { createRequire } from 'module';
|
|
19
|
+
|
|
20
|
+
const require = createRequire(import.meta.url);
|
|
21
|
+
const Larkhre = require('larkhre');
|
|
22
|
+
const ICI = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
|
|
24
|
+
const LIMITE_MS = 8000; // temps maximum d'exécution
|
|
25
|
+
const LIMITE_IMAGES = 240; // ~8 secondes de jeu à 30 images/s
|
|
26
|
+
|
|
27
|
+
// la mémoire « garde » vit le temps de la session MCP
|
|
28
|
+
const memoireSession = {};
|
|
29
|
+
|
|
30
|
+
async function executer(code, entrees) {
|
|
31
|
+
const sortie = [];
|
|
32
|
+
const sons = [];
|
|
33
|
+
const paroles = [];
|
|
34
|
+
const fs = new Map();
|
|
35
|
+
const fileEntrees = [...(entrees || [])];
|
|
36
|
+
let images = 0;
|
|
37
|
+
const debut = Date.now();
|
|
38
|
+
let tempsDepasse = false;
|
|
39
|
+
|
|
40
|
+
const resultat = await Larkhre.run(code, {
|
|
41
|
+
onPrint: (t) => { if (sortie.length < 2000) sortie.push(t); },
|
|
42
|
+
onInput: async () => fileEntrees.length ? String(fileEntrees.shift()) : '',
|
|
43
|
+
shouldStop: () => {
|
|
44
|
+
if (Date.now() - debut > LIMITE_MS || images > LIMITE_IMAGES) {
|
|
45
|
+
tempsDepasse = true;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
},
|
|
50
|
+
onYield: () => new Promise((r) => setImmediate(r)),
|
|
51
|
+
onFrame: () => { images++; return new Promise((r) => setImmediate(r)); },
|
|
52
|
+
onDraw: () => { },
|
|
53
|
+
onSon: (n) => { if (sons.length < 50) sons.push(n); },
|
|
54
|
+
onDis: (t) => { if (paroles.length < 50) paroles.push(t); },
|
|
55
|
+
toucheEnfoncee: () => false,
|
|
56
|
+
prendClics: () => [],
|
|
57
|
+
getChampValeur: () => '',
|
|
58
|
+
onWidget: () => { },
|
|
59
|
+
fs,
|
|
60
|
+
memoire: memoireSession,
|
|
61
|
+
onSauveMemoire: (data) => Object.assign(memoireSession, data),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const fichiers = {};
|
|
65
|
+
const dessins = {};
|
|
66
|
+
for (const [nom, contenu] of fs.entries()) {
|
|
67
|
+
if (nom.endsWith('.svg')) dessins[nom] = contenu;
|
|
68
|
+
else fichiers[nom] = String(contenu).slice(0, 20000);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const rapport = [];
|
|
72
|
+
if (resultat.ok) rapport.push('✔ Programme exécuté avec succès.');
|
|
73
|
+
else if (resultat.stopped) {
|
|
74
|
+
rapport.push(tempsDepasse
|
|
75
|
+
? `⏱ Programme arrêté par le bac à sable (limite : ${LIMITE_MS / 1000}s / ${LIMITE_IMAGES} images de jeu). La sortie ci-dessous est celle produite avant l'arrêt.`
|
|
76
|
+
: '⏹ Programme arrêté.');
|
|
77
|
+
} else {
|
|
78
|
+
rapport.push(resultat.error);
|
|
79
|
+
if (resultat.histoire && resultat.histoire.length) {
|
|
80
|
+
rapport.push("— Le film juste avant l'erreur :");
|
|
81
|
+
for (const [l, n, v] of resultat.histoire) rapport.push(` ligne ${l} : ${n} = ${v}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
rapport.push('');
|
|
85
|
+
rapport.push('=== SORTIE CONSOLE ===');
|
|
86
|
+
rapport.push(sortie.length ? sortie.join('\n') : '(aucune sortie)');
|
|
87
|
+
if (paroles.length) {
|
|
88
|
+
rapport.push('');
|
|
89
|
+
rapport.push('=== PAROLES (dis) ===');
|
|
90
|
+
rapport.push(paroles.join('\n'));
|
|
91
|
+
}
|
|
92
|
+
if (sons.length) {
|
|
93
|
+
rapport.push('');
|
|
94
|
+
rapport.push('=== SONS JOUÉS ===');
|
|
95
|
+
rapport.push(sons.join(', '));
|
|
96
|
+
}
|
|
97
|
+
for (const [nom, svg] of Object.entries(dessins)) {
|
|
98
|
+
rapport.push('');
|
|
99
|
+
rapport.push(`=== DESSIN ${nom} (SVG) ===`);
|
|
100
|
+
rapport.push(svg.length > 30000 ? svg.slice(0, 30000) + '\n[...SVG tronqué...]' : svg);
|
|
101
|
+
}
|
|
102
|
+
for (const [nom, contenu] of Object.entries(fichiers)) {
|
|
103
|
+
rapport.push('');
|
|
104
|
+
rapport.push(`=== FICHIER ${nom} ===`);
|
|
105
|
+
rapport.push(contenu);
|
|
106
|
+
}
|
|
107
|
+
const gardes = Object.keys(memoireSession);
|
|
108
|
+
if (gardes.length) {
|
|
109
|
+
rapport.push('');
|
|
110
|
+
rapport.push('=== MÉMOIRE garde (persiste entre les exécutions de cette session) ===');
|
|
111
|
+
rapport.push(JSON.stringify(memoireSession));
|
|
112
|
+
}
|
|
113
|
+
return rapport.join('\n');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const server = new McpServer({ name: 'larkhre', version: '1.0.0' });
|
|
117
|
+
|
|
118
|
+
server.tool(
|
|
119
|
+
'executer_larkhre',
|
|
120
|
+
"Exécute un programme Larkhré (le langage de programmation en français créé par Ladji Doucaré) et renvoie la sortie console, les erreurs pédagogiques, les dessins SVG, les fichiers écrits et la mémoire persistante « garde ». Utilise l'outil spec_larkhre d'abord si tu ne connais pas la syntaxe. Les demand() lisent la liste `entrees` (une réponse par appel, '' si épuisée). Les jeux temps réel (chaque_image) tournent au maximum 240 images puis s'arrêtent proprement.",
|
|
121
|
+
{
|
|
122
|
+
code: z.string().describe('Le programme Larkhré complet à exécuter (extension .laz, UTF-8)'),
|
|
123
|
+
entrees: z.array(z.string()).optional().describe('Réponses aux demand(), dans l\'ordre'),
|
|
124
|
+
},
|
|
125
|
+
async ({ code, entrees }) => {
|
|
126
|
+
try {
|
|
127
|
+
const rapport = await executer(code, entrees);
|
|
128
|
+
return { content: [{ type: 'text', text: rapport }] };
|
|
129
|
+
} catch (e) {
|
|
130
|
+
return { content: [{ type: 'text', text: '✘ Erreur interne du bac à sable : ' + (e && e.message ? e.message : String(e)) }], isError: true };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
server.tool(
|
|
136
|
+
'spec_larkhre',
|
|
137
|
+
'Renvoie la spécification complète et exacte du langage Larkhré (mots-clés, fonctions intégrées, pièges à éviter, exemples canoniques). À lire AVANT d\'écrire du code Larkhré.',
|
|
138
|
+
{},
|
|
139
|
+
async () => {
|
|
140
|
+
const spec = readFileSync(join(ICI, 'LARKHRE_POUR_IA.md'), 'utf-8');
|
|
141
|
+
return { content: [{ type: 'text', text: spec }] };
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const transport = new StdioServerTransport();
|
|
146
|
+
await server.connect(transport);
|