@webmcp-auto-ui/agent 2.5.8 → 2.5.10

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.
Files changed (45) hide show
  1. package/package.json +1 -1
  2. package/src/autoui-server.ts +134 -134
  3. package/src/diagnostics.ts +3 -3
  4. package/src/loop.ts +6 -6
  5. package/src/providers/wasm.ts +5 -5
  6. package/src/recipes/_generated.ts +446 -446
  7. package/src/recipes/afficher-oeuvres-art-collection-musee.md +45 -45
  8. package/src/recipes/analyser-actualites-hacker-news.md +52 -52
  9. package/src/recipes/cartographier-observations-biodiversite.md +44 -44
  10. package/src/recipes/cross-server.md +48 -48
  11. package/src/recipes/dashboard-kpi.md +45 -45
  12. package/src/recipes/explorer-dossiers-legislatifs-parcours-texte.md +48 -48
  13. package/src/recipes/gallery-images.md +33 -33
  14. package/src/recipes/parlementaire-profile.md +58 -58
  15. package/src/recipes/rechercher-textes-juridiques-legifrance.md +38 -38
  16. package/src/recipes/weather-viz.md +35 -35
  17. package/src/recipes/widgets/actions.md +6 -6
  18. package/src/recipes/widgets/alert.md +6 -6
  19. package/src/recipes/widgets/cards.md +10 -10
  20. package/src/recipes/widgets/carousel.md +8 -8
  21. package/src/recipes/widgets/chart-rich.md +10 -10
  22. package/src/recipes/widgets/chart.md +9 -9
  23. package/src/recipes/widgets/code.md +6 -6
  24. package/src/recipes/widgets/d3.md +10 -10
  25. package/src/recipes/widgets/data-table.md +10 -10
  26. package/src/recipes/widgets/gallery.md +10 -10
  27. package/src/recipes/widgets/grid-data.md +11 -11
  28. package/src/recipes/widgets/hemicycle.md +9 -9
  29. package/src/recipes/widgets/js-sandbox.md +10 -10
  30. package/src/recipes/widgets/json-viewer.md +8 -8
  31. package/src/recipes/widgets/kv.md +9 -9
  32. package/src/recipes/widgets/list.md +7 -7
  33. package/src/recipes/widgets/log.md +6 -6
  34. package/src/recipes/widgets/map.md +10 -10
  35. package/src/recipes/widgets/profile.md +9 -9
  36. package/src/recipes/widgets/recipe-browser.md +33 -33
  37. package/src/recipes/widgets/sankey.md +10 -10
  38. package/src/recipes/widgets/stat-card.md +7 -7
  39. package/src/recipes/widgets/stat.md +10 -10
  40. package/src/recipes/widgets/tags.md +6 -6
  41. package/src/recipes/widgets/text.md +6 -6
  42. package/src/recipes/widgets/timeline.md +6 -6
  43. package/src/recipes/widgets/trombinoscope.md +8 -8
  44. package/src/summarize.ts +6 -6
  45. package/src/tool-layers.ts +26 -26
@@ -3,49 +3,49 @@
3
3
  export const RAW_RECIPES: Record<string, string> = {
4
4
  'afficher-oeuvres-art-collection-musee': `---
5
5
  id: afficher-oeuvres-art-collection-musee
6
- name: Afficher les oeuvres d'art d'une collection de musee en galerie visuelle
6
+ name: Display artworks from a museum collection in a visual gallery
7
7
  components_used: [gallery, cards, kv, stat-card]
8
- when: l'utilisateur demande des oeuvres d'art, des collections de musee, des tableaux, sculptures ou objets d'art du Metropolitan Museum of Art
8
+ when: the user asks for artworks, museum collections, paintings, sculptures, or art objects from the Metropolitan Museum of Art
9
9
  servers: [metmuseum]
10
10
  layout:
11
11
  type: grid
12
12
  columns: 2
13
- arrangement: stats en haut, galerie pleine largeur au centre, details en bas
13
+ arrangement: stats at top, full-width gallery in the center, details at the bottom
14
14
  ---
15
15
 
16
- ## Quand utiliser
16
+ ## When to use
17
17
 
18
- L'utilisateur s'interesse a des oeuvres d'art ou des collections de musee :
19
- - "Montre-moi des tableaux impressionnistes du Met Museum"
20
- - "Les sculptures grecques du Metropolitan"
21
- - "Quelles oeuvres de Van Gogh sont au Met ?"
22
- - "Les oeuvres d'art egyptien du musee"
23
- - "Cherche des estampes japonaises"
18
+ The user is interested in artworks or museum collections:
19
+ - "Show me Impressionist paintings from the Met Museum"
20
+ - "Greek sculptures at the Metropolitan"
21
+ - "What Van Gogh works are at the Met?"
22
+ - "Egyptian art objects from the museum"
23
+ - "Search for Japanese woodblock prints"
24
24
 
25
- Le serveur Met Museum donne acces a la collection du Metropolitan Museum of Art de New York (plus de 470 000 oeuvres, dont beaucoup avec images en domaine public).
25
+ The Met Museum server provides access to the Metropolitan Museum of Art collection in New York (more than 470,000 objects, many with public domain images).
26
26
 
27
- ## Comment
27
+ ## How to use
28
28
 
29
- 1. **Rechercher les oeuvres** par theme, artiste ou departement :
29
+ 1. **Search for artworks** by theme, artist, or department:
30
30
  \`\`\`
31
31
  search_objects({query: "impressionism sunflower", hasImages: true})
32
32
  \`\`\`
33
- Retourne une liste d'\`objectID\`s.
33
+ Returns a list of \`objectID\`s.
34
34
 
35
- 2. **Recuperer les details** de chaque oeuvre (limiter a 5-10 pour la performance) :
35
+ 2. **Fetch the details** of each artwork (limit to 5-10 for performance):
36
36
  \`\`\`
37
37
  get_object({objectID: 436524})
38
38
  \`\`\`
39
- Retourne : \`title\`, \`artistDisplayName\`, \`primaryImage\`, \`objectDate\`, \`medium\`, \`department\`, \`culture\`, etc.
39
+ Returns: \`title\`, \`artistDisplayName\`, \`primaryImage\`, \`objectDate\`, \`medium\`, \`department\`, \`culture\`, etc.
40
40
 
41
- 3. **Afficher les statistiques** de la recherche :
41
+ 3. **Display search statistics**:
42
42
  \`\`\`
43
- component("stat-card", {label: "Resultats", value: total, icon: "image"})
44
- component("stat-card", {label: "Avec image", value: withImage, icon: "camera"})
45
- component("stat-card", {label: "Domaine public", value: publicDomain, icon: "unlock"})
43
+ component("stat-card", {label: "Results", value: total, icon: "image"})
44
+ component("stat-card", {label: "With image", value: withImage, icon: "camera"})
45
+ component("stat-card", {label: "Public domain", value: publicDomain, icon: "unlock"})
46
46
  \`\`\`
47
47
 
48
- 4. **Galerie des oeuvres** avec images haute definition :
48
+ 4. **Artwork gallery** with high-resolution images:
49
49
  \`\`\`
50
50
  component("gallery", {
51
51
  images: objects
@@ -58,124 +58,124 @@ Le serveur Met Museum donne acces a la collection du Metropolitan Museum of Art
58
58
  })
59
59
  \`\`\`
60
60
 
61
- 5. **Fiches detaillees** en cards pour les oeuvres principales :
61
+ 5. **Detailed cards** for the main artworks:
62
62
  \`\`\`
63
63
  component("cards", {
64
64
  items: objects.map(o => ({
65
65
  title: o.title,
66
- subtitle: o.artistDisplayName || "Artiste inconnu",
66
+ subtitle: o.artistDisplayName || "Unknown artist",
67
67
  image: o.primaryImageSmall,
68
68
  body: [o.objectDate, o.medium, o.department].filter(Boolean).join(" — ")
69
69
  }))
70
70
  })
71
71
  \`\`\`
72
72
 
73
- 6. **Details d'une oeuvre specifique** en kv :
73
+ 6. **Details of a specific artwork** in kv:
74
74
  \`\`\`
75
75
  component("kv", {pairs: [
76
- ["Titre", obj.title],
77
- ["Artiste", obj.artistDisplayName],
76
+ ["Title", obj.title],
77
+ ["Artist", obj.artistDisplayName],
78
78
  ["Date", obj.objectDate],
79
79
  ["Medium", obj.medium],
80
80
  ["Dimensions", obj.dimensions],
81
- ["Departement", obj.department],
81
+ ["Department", obj.department],
82
82
  ["Culture", obj.culture],
83
83
  ["Credit", obj.creditLine],
84
- ["Domaine public", obj.isPublicDomain ? "Oui" : "Non"]
84
+ ["Public domain", obj.isPublicDomain ? "Yes" : "No"]
85
85
  ]})
86
86
  \`\`\`
87
87
 
88
- ## Exemples
88
+ ## Examples
89
89
 
90
- ### Tableaux de Van Gogh
90
+ ### Van Gogh paintings
91
91
  \`\`\`
92
- // 1. Recherche
92
+ // 1. Search
93
93
  search_objects({query: "van gogh", hasImages: true}) // → [436532, 436529, ...]
94
94
 
95
- // 2. Details (premiers 8 resultats)
95
+ // 2. Details (first 8 results)
96
96
  objectIDs.slice(0, 8).forEach(id => get_object({objectID: id}))
97
97
 
98
- // 3. Rendu
99
- component("stat-card", {label: "Oeuvres de Van Gogh", value: "8", icon: "palette"})
98
+ // 3. Render
99
+ component("stat-card", {label: "Van Gogh works", value: "8", icon: "palette"})
100
100
  component("gallery", {images: vanGoghWorks.map(w => ({src: w.primaryImageSmall, alt: w.title, caption: w.objectDate}))})
101
101
  component("cards", {items: vanGoghWorks.map(w => ({title: w.title, subtitle: w.objectDate, image: w.primaryImageSmall, body: w.medium}))})
102
102
  \`\`\`
103
103
 
104
- ### Art egyptien
104
+ ### Egyptian art
105
105
  \`\`\`
106
- // 1. Recherche par departement
106
+ // 1. Search by department
107
107
  search_objects({query: "egypt pharaoh", departmentId: 10, hasImages: true})
108
108
 
109
- // 2. Rendu avec metadonnees culturelles
109
+ // 2. Render with cultural metadata
110
110
  component("gallery", {images: egyptWorks.map(w => ({src: w.primaryImageSmall, alt: w.title}))})
111
- component("table", {columns: ["Titre", "Periode", "Culture", "Medium"], rows: egyptDetails})
112
- component("kv", {pairs: [["Departement", "Egyptian Art"], ["Source", "Met Museum — Open Access"]]})
111
+ component("table", {columns: ["Title", "Period", "Culture", "Medium"], rows: egyptDetails})
112
+ component("kv", {pairs: [["Department", "Egyptian Art"], ["Source", "Met Museum — Open Access"]]})
113
113
  \`\`\`
114
114
 
115
- ## Erreurs courantes
115
+ ## Common mistakes
116
116
 
117
- - **Trop d'appels \`get_object\`** : la recherche retourne parfois des centaines d'IDs — limiter a 5-10 appels detail pour la performance
118
- - **Oeuvres sans image** : beaucoup d'objets Met n'ont pas de \`primaryImage\` — toujours filtrer avec \`hasImages: true\` dans la recherche ou verifier le champ
119
- - **Images haute resolution cassees** : utiliser \`primaryImageSmall\` (web-large) pour la galerie et les cards — les URLs \`primaryImage\` (original) retournent souvent des 404
120
- - **Oublier la licence** : les oeuvres en domaine public (\`isPublicDomain: true\`) peuvent etre affichees librement, les autres ont un champ \`rights\` a respecter
121
- - **Artiste inconnu** : beaucoup d'oeuvres anciennes n'ont pas d'\`artistDisplayName\` — afficher "Artiste inconnu" ou la culture/periode a la place
117
+ - **Too many \`get_object\` calls**: a search sometimes returns hundreds of IDs — limit to 5-10 detail calls for performance
118
+ - **Artworks without images**: many Met objects have no \`primaryImage\` — always filter with \`hasImages: true\` in the search or check the field
119
+ - **Broken high-resolution images**: use \`primaryImageSmall\` (web-large) for the gallery and cards — \`primaryImage\` (original) URLs often return 404s
120
+ - **Forgetting the license**: works in the public domain (\`isPublicDomain: true\`) can be displayed freely; others have a \`rights\` field to respect
121
+ - **Unknown artist**: many ancient works have no \`artistDisplayName\` — display "Unknown artist" or the culture/period instead
122
122
  `,
123
123
  'analyser-actualites-hacker-news': `---
124
124
  id: analyser-actualites-hacker-news
125
- name: Analyser les actualites et tendances Hacker News en tableau et graphiques
125
+ name: Analyze Hacker News news and trends with tables and charts
126
126
  components_used: [table, chart, stat-card, cards]
127
- when: l'utilisateur demande les actualites tech, les tendances Hacker News, les top stories, ou une analyse des discussions et commentaires HN
127
+ when: the user asks for tech news, Hacker News trends, top stories, or an analysis of HN discussions and comments
128
128
  servers: [hackernews]
129
129
  layout:
130
130
  type: grid
131
131
  columns: 2
132
- arrangement: stats en ligne, table pleine largeur, chart en bas
132
+ arrangement: stats in a row, full-width table, chart at the bottom
133
133
  ---
134
134
 
135
- ## Quand utiliser
135
+ ## When to use
136
136
 
137
- L'utilisateur s'interesse aux actualites technologiques ou aux tendances de la communaute Hacker News :
138
- - "Quelles sont les top stories Hacker News ?"
139
- - "Montre-moi les posts les plus commentes aujourd'hui"
140
- - "Les tendances tech de la semaine sur HN"
141
- - "Analyse les Ask HN recents"
142
- - "Quels sujets dominent Hacker News en ce moment ?"
137
+ The user is interested in technology news or Hacker News community trends:
138
+ - "What are the top Hacker News stories?"
139
+ - "Show me the most commented posts today"
140
+ - "This week's tech trends on HN"
141
+ - "Analyze recent Ask HN posts"
142
+ - "What topics are dominating Hacker News right now?"
143
143
 
144
- Le serveur Hacker News donne acces aux stories, commentaires, classements et metadonnees de posts.
144
+ The Hacker News server provides access to stories, comments, rankings, and post metadata.
145
145
 
146
- ## Comment
146
+ ## How to use
147
147
 
148
- 1. **Recuperer les top stories** :
148
+ 1. **Fetch the top stories**:
149
149
  \`\`\`
150
150
  get_top_stories({limit: 30})
151
151
  \`\`\`
152
- Retourne les IDs des stories les plus populaires.
152
+ Returns the IDs of the most popular stories.
153
153
 
154
- 2. **Recuperer les details** de chaque story :
154
+ 2. **Fetch the details** of each story:
155
155
  \`\`\`
156
156
  get_item({id: storyId})
157
157
  \`\`\`
158
- Retourne : \`title\`, \`url\`, \`score\`, \`by\` (auteur), \`descendants\` (nb commentaires), \`time\`, \`type\`.
158
+ Returns: \`title\`, \`url\`, \`score\`, \`by\` (author), \`descendants\` (comment count), \`time\`, \`type\`.
159
159
 
160
- 3. **Afficher les KPIs** en stat-cards :
160
+ 3. **Display KPIs** in stat-cards:
161
161
  \`\`\`
162
162
  component("stat-card", {label: "Top Stories", value: "30", icon: "newspaper"})
163
- component("stat-card", {label: "Score moyen", value: Math.round(avgScore), icon: "trending-up"})
164
- component("stat-card", {label: "Commentaires moyen", value: Math.round(avgComments), icon: "message-circle"})
165
- component("stat-card", {label: "Score max", value: maxScore + " pts", icon: "award"})
163
+ component("stat-card", {label: "Average score", value: Math.round(avgScore), icon: "trending-up"})
164
+ component("stat-card", {label: "Average comments", value: Math.round(avgComments), icon: "message-circle"})
165
+ component("stat-card", {label: "Max score", value: maxScore + " pts", icon: "award"})
166
166
  \`\`\`
167
167
 
168
- 4. **Tableau des stories** trie par score :
168
+ 4. **Stories table** sorted by score:
169
169
  \`\`\`
170
170
  component("table", {
171
- columns: ["#", "Titre", "Score", "Commentaires", "Auteur"],
171
+ columns: ["#", "Title", "Score", "Comments", "Author"],
172
172
  rows: stories.sort((a, b) => b.score - a.score).map((s, i) => [
173
173
  i + 1, s.title, s.score, s.descendants, s.by
174
174
  ])
175
175
  })
176
176
  \`\`\`
177
177
 
178
- 5. **Graphique de distribution** des scores :
178
+ 5. **Score distribution chart**:
179
179
  \`\`\`
180
180
  component("chart", {
181
181
  type: "bar",
@@ -184,105 +184,105 @@ Le serveur Hacker News donne acces aux stories, commentaires, classements et met
184
184
  })
185
185
  \`\`\`
186
186
 
187
- 6. **Cards pour les stories vedettes** (top 5) :
187
+ 6. **Cards for featured stories** (top 5):
188
188
  \`\`\`
189
189
  component("cards", {
190
190
  items: top5.map(s => ({
191
191
  title: s.title,
192
192
  subtitle: s.by + " — " + s.score + " points",
193
- body: s.descendants + " commentaires | " + new Date(s.time * 1000).toLocaleDateString(),
193
+ body: s.descendants + " comments | " + new Date(s.time * 1000).toLocaleDateString(),
194
194
  url: s.url
195
195
  }))
196
196
  })
197
197
  \`\`\`
198
198
 
199
- ## Exemples
199
+ ## Examples
200
200
 
201
- ### Top 10 stories du moment
201
+ ### Top 10 stories right now
202
202
  \`\`\`
203
- // 1. Recuperer
203
+ // 1. Fetch
204
204
  get_top_stories({limit: 10})
205
- // Pour chaque ID: get_item({id})
205
+ // For each ID: get_item({id})
206
206
 
207
- // 2. Rendu
208
- component("stat-card", {label: "Score total", value: totalScore, icon: "zap"})
209
- component("stat-card", {label: "Commentaires total", value: totalComments, icon: "message-circle"})
207
+ // 2. Render
208
+ component("stat-card", {label: "Total score", value: totalScore, icon: "zap"})
209
+ component("stat-card", {label: "Total comments", value: totalComments, icon: "message-circle"})
210
210
  component("table", {
211
- columns: ["Rang", "Titre", "Score", "Commentaires", "Auteur", "Age"],
211
+ columns: ["Rank", "Title", "Score", "Comments", "Author", "Age"],
212
212
  rows: rankedStories
213
213
  })
214
214
  component("cards", {items: top3Stories})
215
215
  \`\`\`
216
216
 
217
- ### Analyse Ask HN
217
+ ### Ask HN analysis
218
218
  \`\`\`
219
- // 1. Rechercher les Ask HN recents
219
+ // 1. Fetch recent Ask HN posts
220
220
  get_ask_stories({limit: 20})
221
221
 
222
- // 2. Rendu
223
- component("stat-card", {label: "Ask HN recents", value: "20", icon: "help-circle"})
224
- component("stat-card", {label: "Reponses moyennes", value: avgReplies, icon: "message-circle"})
225
- component("table", {columns: ["Titre", "Reponses", "Score", "Auteur"], rows: askStories})
226
- component("chart", {type: "bar", labels: titles, datasets: [{label: "Reponses", data: replyCounts}]})
222
+ // 2. Render
223
+ component("stat-card", {label: "Recent Ask HN", value: "20", icon: "help-circle"})
224
+ component("stat-card", {label: "Average replies", value: avgReplies, icon: "message-circle"})
225
+ component("table", {columns: ["Title", "Replies", "Score", "Author"], rows: askStories})
226
+ component("chart", {type: "bar", labels: titles, datasets: [{label: "Replies", data: replyCounts}]})
227
227
  \`\`\`
228
228
 
229
- ### Tendances par domaine
229
+ ### Trends by domain
230
230
  \`\`\`
231
- // 1. Recuperer top stories et extraire les domaines des URLs
231
+ // 1. Fetch top stories and extract domains from URLs
232
232
  get_top_stories({limit: 50})
233
233
 
234
- // 2. Grouper par domaine
234
+ // 2. Group by domain
235
235
  const domains = groupBy(stories, s => new URL(s.url).hostname)
236
236
 
237
- // 3. Rendu
238
- component("stat-card", {label: "Domaines uniques", value: Object.keys(domains).length, icon: "globe"})
237
+ // 3. Render
238
+ component("stat-card", {label: "Unique domains", value: Object.keys(domains).length, icon: "globe"})
239
239
  component("chart", {type: "bar", labels: topDomains.map(d => d.name), datasets: [{label: "Stories", data: topDomains.map(d => d.count)}]})
240
- component("table", {columns: ["Domaine", "Stories", "Score total"], rows: domainStats})
240
+ component("table", {columns: ["Domain", "Stories", "Total score"], rows: domainStats})
241
241
  \`\`\`
242
242
 
243
- ## Erreurs courantes
243
+ ## Common mistakes
244
244
 
245
- - **Trop d'appels \`get_item\`** : chaque story necessite un appel individuellimiter a 20-30 pour eviter la lenteur
246
- - **Timestamps non convertis** : HN retourne des timestamps Unix convertir en dates lisibles
247
- - **Titres tronques dans les graphiques** : les titres HN sont longs tronquer a 30-40 caracteres pour les labels de graphiques
248
- - **Oublier les stories sans URL** : les "Ask HN", "Show HN" et "Tell HN" n'ont pas toujours d'URL externe gerer ce cas
249
- - **Ne pas distinguer les types** : HN a des stories, jobs, polls — filtrer par type si l'utilisateur demande un type specifique
245
+ - **Too many \`get_item\` calls**: each story requires an individual calllimit to 20-30 to avoid slowness
246
+ - **Unconverted timestamps**: HN returns Unix timestamps — convert them to human-readable dates
247
+ - **Truncated titles in charts**: HN titles are longtruncate to 30-40 characters for chart labels
248
+ - **Forgetting stories without a URL**: "Ask HN", "Show HN", and "Tell HN" posts don't always have an external URL — handle this case
249
+ - **Not distinguishing types**: HN has stories, jobs, and polls — filter by type if the user asks for a specific type
250
250
  `,
251
251
  'cartographier-observations-biodiversite': `---
252
- id: cartographier-observations-biodiversite
253
- name: Cartographier les observations de biodiversite sur une zone geographique
252
+ id: map-biodiversity-observations
253
+ name: Map biodiversity observations on a geographic area
254
254
  components_used: [map, gallery, table, stat-card]
255
- when: l'utilisateur demande une carte des observations naturalistes, la biodiversite d'une zone, les especes presentes dans un lieu, ou les observations iNaturalist d'une region
255
+ when: the user asks for a map of naturalist observations, the biodiversity of an area, the species present in a location, or iNaturalist observations in a region
256
256
  servers: [inaturalist]
257
257
  layout:
258
258
  type: grid
259
259
  columns: 2
260
- arrangement: carte pleine largeur en haut, galerie + stats en dessous
260
+ arrangement: full-width map at top, gallery + stats below
261
261
  ---
262
262
 
263
- ## Quand utiliser
263
+ ## When to use
264
264
 
265
- L'utilisateur pose une question sur la biodiversite d'un lieu ou demande une carte des observations :
266
- - "Quelles especes d'oiseaux observe-t-on a Paris ?"
267
- - "Montre-moi une carte des observations de papillons dans les Alpes"
268
- - "Quelle est la biodiversite autour du lac d'Annecy ?"
269
- - "Les especes menacees observees en Ile-de-France"
265
+ The user asks a question about the biodiversity of a location or requests a map of observations:
266
+ - "What bird species are observed in Paris?"
267
+ - "Show me a map of butterfly observations in the Alps"
268
+ - "What is the biodiversity around Lake Annecy?"
269
+ - "Endangered species observed in Ile-de-France"
270
270
 
271
- Le serveur iNaturalist fournit des observations georeferencees avec photos, taxons, dates et observateurs.
271
+ The iNaturalist server provides georeferenced observations with photos, taxa, dates, and observers.
272
272
 
273
- ## Comment
273
+ ## How to use
274
274
 
275
- 1. **Rechercher les observations** dans la zone cible :
275
+ 1. **Search for observations** in the target area:
276
276
  \`\`\`
277
277
  search_observations({lat: 48.85, lng: 2.35, radius: 10, taxon_name: "Aves", per_page: 50})
278
278
  \`\`\`
279
- Parametres utiles :
280
- - \`lat\`, \`lng\`, \`radius\` : centre et rayon de la zone en km
281
- - \`taxon_name\` : filtre taxonomique ("Aves", "Lepidoptera", "Mammalia", etc.)
282
- - \`quality_grade\` : "research" pour les observations verifiees
283
- - \`per_page\` : nombre de resultats (max 200)
279
+ Useful parameters:
280
+ - \`lat\`, \`lng\`, \`radius\`: center and radius of the area in km
281
+ - \`taxon_name\`: taxonomic filter ("Aves", "Lepidoptera", "Mammalia", etc.)
282
+ - \`quality_grade\`: "research" for verified observations
283
+ - \`per_page\`: number of results (max 200)
284
284
 
285
- 2. **Afficher la carte** avec les marqueurs d'observation :
285
+ 2. **Display the map** with observation markers:
286
286
  \`\`\`
287
287
  component("map", {
288
288
  center: [48.85, 2.35],
@@ -296,15 +296,15 @@ Le serveur iNaturalist fournit des observations georeferencees avec photos, taxo
296
296
  })
297
297
  \`\`\`
298
298
 
299
- 3. **Statistiques de la zone** en stat-cards :
299
+ 3. **Area statistics** in stat-cards:
300
300
  \`\`\`
301
301
  component("stat-card", {label: "Observations", value: total_results, icon: "eye"})
302
- component("stat-card", {label: "Especes uniques", value: uniqueSpecies.length, icon: "leaf"})
303
- component("stat-card", {label: "Observateurs", value: uniqueObservers.length, icon: "users"})
304
- component("stat-card", {label: "Grade recherche", value: researchGradeCount, icon: "check-circle"})
302
+ component("stat-card", {label: "Unique species", value: uniqueSpecies.length, icon: "leaf"})
303
+ component("stat-card", {label: "Observers", value: uniqueObservers.length, icon: "users"})
304
+ component("stat-card", {label: "Research grade", value: researchGradeCount, icon: "check-circle"})
305
305
  \`\`\`
306
306
 
307
- 4. **Galerie des especes avec photos** :
307
+ 4. **Species gallery with photos**:
308
308
  \`\`\`
309
309
  component("gallery", {
310
310
  images: observations
@@ -317,105 +317,105 @@ Le serveur iNaturalist fournit des observations georeferencees avec photos, taxo
317
317
  })
318
318
  \`\`\`
319
319
 
320
- 5. **Tableau recapitulatif** des especes :
320
+ 5. **Summary table** of species:
321
321
  \`\`\`
322
322
  component("table", {
323
- columns: ["Espece", "Nom scientifique", "Observations", "Derniere obs."],
323
+ columns: ["Species", "Scientific name", "Observations", "Last obs."],
324
324
  rows: speciesSummary
325
325
  })
326
326
  \`\`\`
327
327
 
328
- ## Exemples
328
+ ## Examples
329
329
 
330
- ### Oiseaux de Paris
330
+ ### Birds of Paris
331
331
  \`\`\`
332
- // 1. Recherche
332
+ // 1. Search
333
333
  search_observations({lat: 48.8566, lng: 2.3522, radius: 10, taxon_name: "Aves", quality_grade: "research", per_page: 100})
334
334
 
335
- // 2. Rendu
335
+ // 2. Render
336
336
  component("map", {center: [48.8566, 2.3522], zoom: 12, markers: birdMarkers})
337
- component("stat-card", {label: "Especes d'oiseaux", value: "47", icon: "bird"})
338
- component("stat-card", {label: "Observations verifiees", value: "312", icon: "check"})
337
+ component("stat-card", {label: "Bird species", value: "47", icon: "bird"})
338
+ component("stat-card", {label: "Verified observations", value: "312", icon: "check"})
339
339
  component("gallery", {images: birdPhotos})
340
- component("table", {columns: ["Espece", "Observations", "Derniere"], rows: birdSummary})
340
+ component("table", {columns: ["Species", "Observations", "Last"], rows: birdSummary})
341
341
  \`\`\`
342
342
 
343
- ### Papillons des Alpes
343
+ ### Butterflies in the Alps
344
344
  \`\`\`
345
- // 1. Zone large autour de Chamonix
345
+ // 1. Wide area around Chamonix
346
346
  search_observations({lat: 45.9237, lng: 6.8694, radius: 30, taxon_name: "Lepidoptera", per_page: 100})
347
347
 
348
- // 2. Rendu avec clustering sur la carte
348
+ // 2. Render with clustering on the map
349
349
  component("map", {center: [45.9237, 6.8694], zoom: 10, markers: butterflyMarkers, cluster: true})
350
- component("stat-card", {label: "Especes de papillons", value: uniqueSpecies.length})
350
+ component("stat-card", {label: "Butterfly species", value: uniqueSpecies.length})
351
351
  component("gallery", {images: butterflyPhotos})
352
- component("table", {columns: ["Espece", "Altitude", "Mois", "Observateur"], rows: enrichedData})
352
+ component("table", {columns: ["Species", "Altitude", "Month", "Observer"], rows: enrichedData})
353
353
  \`\`\`
354
354
 
355
- ## Erreurs courantes
355
+ ## Common mistakes
356
356
 
357
- - **Rayon trop large** : un rayon de 100 km retourne trop de resultats et noie l'information — preferer 5-20 km et augmenter si peu de resultats
358
- - **Thumbnails iNaturalist** : les URLs par defaut sont en format "square" (75x75) — remplacer "square" par "medium" (200px) ou "large" (500px)
359
- - **Pas de filtre taxonomique** : sans filtre, iNaturalist retourne plantes + animaux + champignons ensembletoujours filtrer par groupe si l'utilisateur en mentionne un
360
- - **Oublier le grade de qualite** : les observations "casual" peuvent etre mal identifieespreferer \`quality_grade: "research"\` pour des donnees fiables
361
- - **Carte sans zoom adapte** : ajuster le zoom en fonction du rayon de recherche (5 km → zoom 13, 20 km → zoom 11, 50 km → zoom 9)
357
+ - **Radius too large**: a 100 km radius returns too many results and buries the information — prefer 5-20 km and increase if few results are found
358
+ - **iNaturalist thumbnails**: default URLs are in "square" format (75x75) — replace "square" with "medium" (200px) or "large" (500px)
359
+ - **No taxonomic filter**: without a filter, iNaturalist returns plants + animals + fungi togetheralways filter by group if the user mentions one
360
+ - **Forgetting the quality grade**: "casual" observations may be misidentifiedprefer \`quality_grade: "research"\` for reliable data
361
+ - **Map with wrong zoom level**: adjust zoom based on the search radius (5 km → zoom 13, 20 km → zoom 11, 50 km → zoom 9)
362
362
  `,
363
363
  'cross-server': `---
364
- id: croiser-donnees-de-plusieurs-serveurs-mcp-connectes
365
- name: Croiser les donnees de plusieurs serveurs MCP connectes simultanement
364
+ id: cross-reference-data-from-multiple-connected-mcp-servers
365
+ name: Cross-reference data from multiple simultaneously connected MCP servers
366
366
  components_used: [map, gallery, table, kv, stat-card]
367
- when: la question de l'utilisateur necessite de combiner des donnees provenant de plusieurs serveurs MCP connectes, par exemple croiser geolocalisation et observations, ou enrichir des donnees parlementaires avec Wikipedia
367
+ when: the user's question requires combining data from multiple connected MCP servers, for example cross-referencing geolocation and observations, or enriching parliamentary data with Wikipedia
368
368
  servers: []
369
369
  layout:
370
370
  type: grid
371
371
  columns: 2
372
372
  ---
373
373
 
374
- ## Quand utiliser
374
+ ## When to use
375
375
 
376
- L'utilisateur pose une question qui ne peut etre satisfaite par un seul serveur MCP. Exemples :
377
- - "Quels oiseaux peut-on observer pres du Louvre ?" → geocoding + iNaturalist
378
- - "Montre-moi la meteo et les observations naturalistes a Marseille" → Open-Meteo + iNaturalist
379
- - "Compare les oeuvres du Met Museum sur le theme des fleurs avec les especes observees a New York" → Met Museum + iNaturalist
380
- - "Donne-moi le profil Wikipedia du depute qui a depose le plus d'amendements" → Tricoteuses + Wikipedia
376
+ The user asks a question that cannot be answered by a single MCP server. Examples:
377
+ - "What birds can be spotted near the Louvre?" → geocoding + iNaturalist
378
+ - "Show me the weather and nature observations in Marseille" → Open-Meteo + iNaturalist
379
+ - "Compare artworks from the Met Museum on the theme of flowers with species observed in New York" → Met Museum + iNaturalist
380
+ - "Give me the Wikipedia profile of the MP who filed the most amendments" → Tricoteuses + Wikipedia
381
381
 
382
- La recette s'applique des que 2+ serveurs MCP sont necessaires pour repondre.
382
+ This recipe applies whenever 2+ MCP servers are needed to answer the question.
383
383
 
384
- ## Comment
384
+ ## How to use
385
385
 
386
- 1. **Identifier quels serveurs MCP fournissent quelles donnees** :
387
- - Serveur A fournit les donnees de reference (coordonnees, IDs, noms)
388
- - Serveur B enrichit avec des donnees complementaires
389
- 2. **Appeler le premier serveur** pour obtenir les donnees de base :
386
+ 1. **Identify which MCP servers provide which data**:
387
+ - Server A provides reference data (coordinates, IDs, names)
388
+ - Server B enriches with complementary data
389
+ 2. **Call the first server** to obtain the base data:
390
390
  \`\`\`
391
- // Exemple : geocoder un lieu
391
+ // Example: geocode a location
392
392
  geocode({query: "Louvre, Paris"}) → {lat: 48.8606, lon: 2.3376}
393
393
  \`\`\`
394
- 3. **Utiliser les resultats comme input** pour le deuxieme serveur :
394
+ 3. **Use the results as input** for the second server:
395
395
  \`\`\`
396
- // Exemple : chercher les observations dans un rayon
396
+ // Example: search for observations within a radius
397
397
  search_observations({lat: 48.8606, lng: 2.3376, radius: 5, taxon: "Aves"})
398
398
  \`\`\`
399
- 4. **Combiner les resultats** dans une visualisation coherente :
400
- - \`component("map", ...)\` si des coordonnees sont impliquees (marqueurs des deux sources)
401
- - \`component("table", ...)\` pour les resultats combines avec colonnes des deux sources
402
- - \`component("gallery", ...)\` si les deux sources fournissent des images
403
- 5. **Toujours citer les sources** avec un composant \`kv\` final :
399
+ 4. **Combine the results** in a coherent visualization:
400
+ - \`component("map", ...)\` if coordinates are involved (markers from both sources)
401
+ - \`component("table", ...)\` for combined results with columns from both sources
402
+ - \`component("gallery", ...)\` if both sources provide images
403
+ 5. **Always cite the sources** with a final \`kv\` component:
404
404
  \`\`\`
405
- component("kv", {pairs: [["Source 1", "iNaturalist"], ["Source 2", "Open-Meteo"], ["Zone", "5 km autour du Louvre"]]})
405
+ component("kv", {pairs: [["Source 1", "iNaturalist"], ["Source 2", "Open-Meteo"], ["Area", "5 km around the Louvre"]]})
406
406
  \`\`\`
407
407
 
408
- ## Exemples
408
+ ## Examples
409
409
 
410
- ### Oiseaux pres d'un monument (geocoding + iNaturalist)
410
+ ### Birds near a landmark (geocoding + iNaturalist)
411
411
  \`\`\`
412
- // 1. Geocoder le lieu
412
+ // 1. Geocode the location
413
413
  geocode({query: "Tour Eiffel, Paris"}) → lat: 48.8584, lon: 2.2945
414
414
 
415
- // 2. Chercher les observations d'oiseaux
415
+ // 2. Search for bird observations
416
416
  search_observations({lat: 48.8584, lng: 2.2945, radius: 3, taxon_name: "Aves"})
417
417
 
418
- // 3. Rendu combine
418
+ // 3. Combined render
419
419
  component("map", {
420
420
  center: [48.8584, 2.2945],
421
421
  zoom: 14,
@@ -423,164 +423,164 @@ component("map", {
423
423
  .concat(observations.map(o => ({lat: o.lat, lon: o.lon, label: o.species_guess})))
424
424
  })
425
425
  component("gallery", {images: observations.flatMap(o => o.photos.map(p => ({src: p.url, alt: o.species_guess})))})
426
- component("table", {columns: ["Espece", "Date", "Distance", "Observateur"], rows: formattedObs})
427
- component("stat-card", {label: "Especes distinctes", value: "23", icon: "bird"})
426
+ component("table", {columns: ["Species", "Date", "Distance", "Observer"], rows: formattedObs})
427
+ component("stat-card", {label: "Distinct species", value: "23", icon: "bird"})
428
428
  \`\`\`
429
429
 
430
- ### Profil enrichi (Tricoteuses + Wikipedia)
430
+ ### Enriched profile (Tricoteuses + Wikipedia)
431
431
  \`\`\`
432
- // 1. Chercher le depute le plus actif
432
+ // 1. Find the most active MP
433
433
  query_sql({sql: "SELECT depute, COUNT(*) as nb FROM amendements GROUP BY depute ORDER BY nb DESC LIMIT 1"})
434
434
 
435
- // 2. Enrichir avec Wikipedia
435
+ // 2. Enrich with Wikipedia
436
436
  search_wikipedia({query: depute.nom})
437
437
 
438
- // 3. Rendu combine
438
+ // 3. Combined render
439
439
  component("profile", {name: depute.nom, subtitle: depute.groupe, details: wikipedia.extract})
440
- component("stat-card", {label: "Amendements deposes", value: depute.nb})
441
- component("kv", {pairs: [["Source parlementaire", "Tricoteuses"], ["Biographie", "Wikipedia"]]})
440
+ component("stat-card", {label: "Amendments filed", value: depute.nb})
441
+ component("kv", {pairs: [["Parliamentary source", "Tricoteuses"], ["Biography", "Wikipedia"]]})
442
442
  \`\`\`
443
443
 
444
- ### Meteo + Biodiversite dans une region
444
+ ### Weather + Biodiversity in a region
445
445
  \`\`\`
446
- // 1. Meteo pour Marseille
446
+ // 1. Weather for Marseille
447
447
  get_forecast({latitude: 43.2965, longitude: 5.3698, daily: "temperature_2m_max"})
448
448
 
449
- // 2. Observations naturalistes
449
+ // 2. Nature observations
450
450
  search_observations({lat: 43.2965, lng: 5.3698, radius: 20})
451
451
 
452
- // 3. Dashboard combine
453
- component("stat-card", {label: "Temperature max", value: "26°C", icon: "thermometer"})
454
- component("stat-card", {label: "Observations recentes", value: "412", icon: "eye"})
452
+ // 3. Combined dashboard
453
+ component("stat-card", {label: "Max temperature", value: "26°C", icon: "thermometer"})
454
+ component("stat-card", {label: "Recent observations", value: "412", icon: "eye"})
455
455
  component("map", {center: [43.2965, 5.3698], markers: observations})
456
456
  component("chart", {type: "line", labels: dates, datasets: [{label: "Temperature", data: temps}, {label: "Observations", data: obsCounts}]})
457
457
  \`\`\`
458
458
 
459
- ## Erreurs courantes
459
+ ## Common mistakes
460
460
 
461
- - **Faire plus de 3 appels DATA sans render intermediaire** : l'utilisateur attend des resultats visuels entre les etapes
462
- - **Ne pas expliquer quels serveurs sont utilises** : toujours afficher les sources avec un \`kv\`
463
- - **Melanger les donnees sans structure** : les resultats combines doivent etre dans un tableau ou une carte coherente, pas un dump brut
464
- - **Oublier de gerer les cas sans correspondance** : si le geocoding ne trouve rien, ou si iNaturalist n'a pas d'observations dans la zone, l'afficher clairement
461
+ - **Making more than 3 DATA calls without an intermediate render**: the user expects visual results between steps
462
+ - **Not explaining which servers are being used**: always display the sources with a \`kv\`
463
+ - **Mixing data without structure**: combined results must be in a coherent table or map, not a raw dump
464
+ - **Forgetting to handle no-match cases**: if geocoding finds nothing, or if iNaturalist has no observations in the area, display that clearly
465
465
  `,
466
466
  'dashboard-kpi': `---
467
- id: composer-tableau-de-bord-kpi-depuis-metriques-agregees
468
- name: Composer un tableau de bord KPI a partir de metriques agregees
467
+ id: compose-kpi-dashboard-from-aggregated-metrics
468
+ name: Compose a KPI dashboard from aggregated metrics
469
469
  components_used: [stat-card, chart, table, kv]
470
- when: les donnees MCP contiennent des metriques numeriques, compteurs, totaux, pourcentages ou statistiques agregees qui meritent un tableau de bord visuel
470
+ when: MCP data contains numeric metrics, counters, totals, percentages, or aggregated statistics that warrant a visual dashboard
471
471
  servers: []
472
472
  layout:
473
473
  type: grid
474
474
  columns: 3
475
- arrangement: stat-cards en ligne, chart + table en dessous
475
+ arrangement: stat-cards in a row, chart + table below
476
476
  ---
477
477
 
478
- ## Quand utiliser
478
+ ## When to use
479
479
 
480
- Les resultats MCP contiennent des metriques numeriques qu'il faut presenter de facon synthetique. Cette recette est transversale : elle s'applique quel que soit le serveur MCP, des lors que les donnees contiennent :
481
- - Des totaux, compteurs ou moyennes (revenus, nombre d'articles, participation, etc.)
482
- - Des pourcentages ou ratios (taux de churn, participation electorale, etc.)
483
- - Des series temporelles de metriques (evolution mois par mois, trimestre par trimestre)
484
- - Des ventilations par categorie (par groupe politique, par pays, par type d'objet)
480
+ MCP results contain numeric metrics that need to be presented concisely. This recipe is cross-cutting: it applies regardless of the MCP server, as long as the data contains:
481
+ - Totals, counters, or averages (revenue, article count, participation, etc.)
482
+ - Percentages or ratios (churn rate, voter turnout, etc.)
483
+ - Time series of metrics (month-over-month, quarter-over-quarter trends)
484
+ - Breakdowns by category (by political group, by country, by object type)
485
485
 
486
- ## Comment
486
+ ## How to use
487
487
 
488
- 1. **Identifier les 3-5 KPIs principaux** dans les donnees retournees par le serveur MCP
489
- 2. **Afficher chaque KPI en stat-card** avec formatage soigne :
488
+ 1. **Identify the 35 main KPIs** in the data returned by the MCP server
489
+ 2. **Display each KPI as a stat-card** with clean formatting:
490
490
  \`\`\`
491
- component("stat-card", {label: "Chiffre d'affaires", value: "45 230 EUR", trend: "+12.4%", trendDir: "up", icon: "trending-up"})
491
+ component("stat-card", {label: "Revenue", value: "45 230 EUR", trend: "+12.4%", trendDir: "up", icon: "trending-up"})
492
492
  \`\`\`
493
- - Toujours formater les nombres : separateurs de milliers, unites, symboles
494
- - Ajouter \`trend\` et \`trendDir\` si une comparaison est disponible (vs mois precedent, vs annee precedente)
495
- 3. **Si des series temporelles existent**, ajouter un graphique :
493
+ - Always format numbers: thousands separators, units, symbols
494
+ - Add \`trend\` and \`trendDir\` if a comparison is available (vs. previous month, vs. previous year)
495
+ 3. **If time series exist**, add a chart:
496
496
  \`\`\`
497
- component("chart", {type: "bar", labels: ["Q1", "Q2", "Q3", "Q4"], datasets: [{label: "CA", data: [98000, 112000, 128000, 142000]}]})
497
+ component("chart", {type: "bar", labels: ["Q1", "Q2", "Q3", "Q4"], datasets: [{label: "Revenue", data: [98000, 112000, 128000, 142000]}]})
498
498
  \`\`\`
499
- - "bar" pour des comparaisons entre categories/periodes
500
- - "line" pour des evolutions continues
501
- 4. **Si des details tabulaires existent**, ajouter une table :
499
+ - "bar" for comparisons between categories/periods
500
+ - "line" for continuous trends
501
+ 4. **If tabular details exist**, add a table:
502
502
  \`\`\`
503
- component("table", {columns: ["Categorie", "Valeur", "Evolution"], rows: [...]})
503
+ component("table", {columns: ["Category", "Value", "Change"], rows: [...]})
504
504
  \`\`\`
505
- 5. **Pour les metadonnees complementaires**, utiliser kv :
505
+ 5. **For supplementary metadata**, use kv:
506
506
  \`\`\`
507
- component("kv", {pairs: [["Source", "data.gouv.fr"], ["Derniere mise a jour", "2026-04-01"], ["Periode", "T1 2026"]]})
507
+ component("kv", {pairs: [["Source", "data.gouv.fr"], ["Last updated", "2026-04-01"], ["Period", "Q1 2026"]]})
508
508
  \`\`\`
509
509
 
510
- ## Exemples
510
+ ## Examples
511
511
 
512
- ### Dashboard parlementaire (Tricoteuses)
512
+ ### Parliamentary dashboard (Tricoteuses)
513
513
  \`\`\`
514
- // Apres query_sql sur les scrutins de la legislature
515
- component("stat-card", {label: "Scrutins publics", value: "1 247", icon: "vote"})
516
- component("stat-card", {label: "Amendements deposes", value: "42 831", icon: "file-text"})
517
- component("stat-card", {label: "Participation moyenne", value: "61.3%", trend: "-2.1%", trendDir: "down"})
518
- component("chart", {type: "bar", labels: mois, datasets: [{label: "Scrutins/mois", data: counts}]})
519
- component("table", {columns: ["Groupe", "Amendements", "Adoptes", "Taux"], rows: groupStats})
514
+ // After query_sql on votes for the legislature
515
+ component("stat-card", {label: "Public votes", value: "1 247", icon: "vote"})
516
+ component("stat-card", {label: "Amendments filed", value: "42 831", icon: "file-text"})
517
+ component("stat-card", {label: "Average participation", value: "61.3%", trend: "-2.1%", trendDir: "down"})
518
+ component("chart", {type: "bar", labels: months, datasets: [{label: "Votes/month", data: counts}]})
519
+ component("table", {columns: ["Group", "Amendments", "Adopted", "Rate"], rows: groupStats})
520
520
  \`\`\`
521
521
 
522
- ### Dashboard biodiversite (iNaturalist)
522
+ ### Biodiversity dashboard (iNaturalist)
523
523
  \`\`\`
524
524
  component("stat-card", {label: "Observations", value: "3 412", icon: "eye"})
525
- component("stat-card", {label: "Especes uniques", value: "287", icon: "leaf"})
526
- component("stat-card", {label: "Observateurs", value: "156", icon: "users"})
527
- component("chart", {type: "line", labels: dates, datasets: [{label: "Observations/jour", data: dailyCounts}]})
525
+ component("stat-card", {label: "Unique species", value: "287", icon: "leaf"})
526
+ component("stat-card", {label: "Observers", value: "156", icon: "users"})
527
+ component("chart", {type: "line", labels: dates, datasets: [{label: "Observations/day", data: dailyCounts}]})
528
528
  \`\`\`
529
529
 
530
- ### Dashboard actualites (Hacker News)
530
+ ### News dashboard (Hacker News)
531
531
  \`\`\`
532
532
  component("stat-card", {label: "Top stories", value: "500", icon: "newspaper"})
533
- component("stat-card", {label: "Score moyen", value: "142", icon: "trending-up"})
534
- component("stat-card", {label: "Commentaires moyen", value: "87", icon: "message-circle"})
535
- component("table", {columns: ["Rang", "Titre", "Score", "Commentaires"], rows: topStories})
533
+ component("stat-card", {label: "Average score", value: "142", icon: "trending-up"})
534
+ component("stat-card", {label: "Average comments", value: "87", icon: "message-circle"})
535
+ component("table", {columns: ["Rank", "Title", "Score", "Comments"], rows: topStories})
536
536
  \`\`\`
537
537
 
538
- ## Erreurs courantes
538
+ ## Common mistakes
539
539
 
540
- - **Trop de stat-cards** : au-dela de 5, basculer vers un \`kv\` ou un \`table\` pour les metriques secondaires
541
- - **Nombres non formates** : afficher "45230" au lieu de "45 230" nuit a la lisibilite
542
- - **Oublier les unites** : "45 230" ne signifie rien sans "EUR", "%", "observations", etc.
543
- - **Graphique sans contexte** : toujours accompagner un graphique de stat-cards qui donnent les chiffres cles instantanement
540
+ - **Too many stat-cards**: beyond 5, switch to a \`kv\` or \`table\` for secondary metrics
541
+ - **Unformatted numbers**: displaying "45230" instead of "45 230" hurts readability
542
+ - **Missing units**: "45 230" means nothing without "EUR", "%", "observations", etc.
543
+ - **Chart without context**: always accompany a chart with stat-cards that surface key figures instantly
544
544
  `,
545
545
  'explorer-dossiers-legislatifs-parcours-texte': `---
546
- id: explorer-dossiers-legislatifs-parcours-texte
547
- name: Explorer les dossiers legislatifs et le parcours d'un texte entre Assemblee et Senat
546
+ id: explore-legislative-files-text-journey
547
+ name: Explore legislative files and the journey of a text between the Assembly and the Senate
548
548
  components_used: [timeline, table, kv, stat-card]
549
- when: l'utilisateur demande le parcours d'un projet ou proposition de loi, la navette parlementaire, les lectures successives, ou le suivi d'un dossier legislatif
549
+ when: the user asks about the journey of a bill or legislative proposal, the parliamentary shuttle, successive readings, or the tracking of a legislative file
550
550
  servers: [tricoteuses]
551
551
  layout:
552
552
  type: grid
553
553
  columns: 2
554
- arrangement: timeline pleine largeur en haut, stats + table en dessous
554
+ arrangement: full-width timeline at top, stats + table below
555
555
  ---
556
556
 
557
- ## Quand utiliser
557
+ ## When to use
558
558
 
559
- L'utilisateur s'interesse au parcours d'un texte de loi a travers les institutions :
560
- - "Ou en est le projet de loi sur l'immigration ?"
561
- - "Montre-moi la navette parlementaire de la reforme des retraites"
562
- - "Combien de lectures a subi ce texte ?"
563
- - "Quels amendements ont ete adoptes en commission ?"
559
+ The user is interested in the journey of a bill through the institutions:
560
+ - "Where does the immigration bill currently stand?"
561
+ - "Show me the parliamentary shuttle for the pension reform"
562
+ - "How many readings did this text go through?"
563
+ - "Which amendments were adopted in committee?"
564
564
 
565
- Le serveur Tricoteuses contient les dossiers legislatifs avec leurs etapes : depot, renvoi en commission, discussion en seance, vote, navette, promulgation.
565
+ The Tricoteuses server contains legislative files with their stages: filing, referral to committee, floor debate, vote, shuttle, promulgation.
566
566
 
567
- ## Comment
567
+ ## How to use
568
568
 
569
- 1. **Rechercher le dossier legislatif** :
569
+ 1. **Search for the legislative file**:
570
570
  \`\`\`
571
571
  query_sql({sql: "SELECT * FROM assemblee.dossiers WHERE titre ILIKE '%immigration%' ORDER BY date_depot DESC LIMIT 5"})
572
572
  \`\`\`
573
- Ou via les recettes Tricoteuses :
573
+ Or via Tricoteuses recipes:
574
574
  \`\`\`
575
575
  search_recipes({query: "dossier legislatif parcours"})
576
576
  \`\`\`
577
577
 
578
- 2. **Recuperer les etapes du parcours** :
578
+ 2. **Retrieve the journey stages**:
579
579
  \`\`\`
580
580
  query_sql({sql: "SELECT etape, chambre, date, resultat FROM assemblee.dossier_etapes WHERE dossier_id = $id ORDER BY date"})
581
581
  \`\`\`
582
582
 
583
- 3. **Afficher la timeline du parcours** :
583
+ 3. **Display the journey timeline**:
584
584
  \`\`\`
585
585
  component("timeline", {
586
586
  events: etapes.map(e => ({
@@ -592,77 +592,77 @@ Le serveur Tricoteuses contient les dossiers legislatifs avec leurs etapes : dep
592
592
  })
593
593
  \`\`\`
594
594
 
595
- 4. **Statistiques du dossier** en stat-cards :
595
+ 4. **File statistics** in stat-cards:
596
596
  \`\`\`
597
- component("stat-card", {label: "Lectures", value: "3", icon: "book-open"})
598
- component("stat-card", {label: "Amendements deposes", value: "1 247", icon: "file-text"})
599
- component("stat-card", {label: "Amendements adoptes", value: "312", icon: "check"})
600
- component("stat-card", {label: "Duree totale", value: "14 mois", icon: "clock"})
597
+ component("stat-card", {label: "Readings", value: "3", icon: "book-open"})
598
+ component("stat-card", {label: "Amendments filed", value: "1 247", icon: "file-text"})
599
+ component("stat-card", {label: "Amendments adopted", value: "312", icon: "check"})
600
+ component("stat-card", {label: "Total duration", value: "14 months", icon: "clock"})
601
601
  \`\`\`
602
602
 
603
- 5. **Details des amendements par etape** en table :
603
+ 5. **Amendment details by stage** in a table:
604
604
  \`\`\`
605
605
  component("table", {
606
- columns: ["Etape", "Chambre", "Amendements deposes", "Adoptes", "Rejetes"],
606
+ columns: ["Stage", "Chamber", "Amendments filed", "Adopted", "Rejected"],
607
607
  rows: etapeStats
608
608
  })
609
609
  \`\`\`
610
610
 
611
- 6. **Metadonnees du dossier** en kv :
611
+ 6. **File metadata** in kv:
612
612
  \`\`\`
613
613
  component("kv", {pairs: [
614
- ["Titre", dossier.titre],
614
+ ["Title", dossier.titre],
615
615
  ["Nature", "Projet de loi"],
616
- ["Auteur", "Gouvernement"],
617
- ["Date de depot", dossier.date_depot],
618
- ["Etat actuel", dossier.etat],
616
+ ["Author", "Gouvernement"],
617
+ ["Filing date", dossier.date_depot],
618
+ ["Current status", dossier.etat],
619
619
  ["Source", "Tricoteuses"]
620
620
  ]})
621
621
  \`\`\`
622
622
 
623
- ## Exemples
623
+ ## Examples
624
624
 
625
- ### Parcours complet d'un texte
625
+ ### Complete text journey
626
626
  \`\`\`
627
- // 1. Recuperer le dossier
627
+ // 1. Retrieve the file
628
628
  query_sql({sql: "SELECT id, titre, date_depot, etat FROM assemblee.dossiers WHERE titre ILIKE '%retraites%2023%' LIMIT 1"})
629
629
 
630
- // 2. Etapes
630
+ // 2. Stages
631
631
  query_sql({sql: "SELECT * FROM assemblee.dossier_etapes WHERE dossier_id = $id ORDER BY date"})
632
632
 
633
- // 3. Amendements par etape
633
+ // 3. Amendments by stage
634
634
  query_sql({sql: "SELECT etape, COUNT(*) as total, COUNT(*) FILTER (WHERE sort='Adopte') as adoptes FROM assemblee.amendements WHERE dossier_id = $id GROUP BY etape"})
635
635
 
636
- // 4. Rendu
637
- component("kv", {pairs: [["Dossier", titre], ["Etat", etat]]})
636
+ // 4. Render
637
+ component("kv", {pairs: [["File", titre], ["Status", etat]]})
638
638
  component("timeline", {events: etapes})
639
- component("stat-card", {label: "Total amendements", value: totalAmendements})
640
- component("stat-card", {label: "Adoptes", value: totalAdoptes})
641
- component("table", {columns: ["Etape", "Deposes", "Adoptes", "Taux"], rows: etapeStats})
639
+ component("stat-card", {label: "Total amendments", value: totalAmendements})
640
+ component("stat-card", {label: "Adopted", value: totalAdoptes})
641
+ component("table", {columns: ["Stage", "Filed", "Adopted", "Rate"], rows: etapeStats})
642
642
  \`\`\`
643
643
 
644
- ### Comparaison entre chambres
644
+ ### Comparison between chambers
645
645
  \`\`\`
646
- // Amendements par chambre
646
+ // Amendments by chamber
647
647
  query_sql({sql: "SELECT chambre, COUNT(*) as deposes, COUNT(*) FILTER (WHERE sort='Adopte') as adoptes FROM assemblee.amendements WHERE dossier_id = $id GROUP BY chambre"})
648
648
 
649
- component("stat-card", {label: "AssembleeAdoptes", value: an_adoptes + "/" + an_deposes})
650
- component("stat-card", {label: "SenatAdoptes", value: senat_adoptes + "/" + senat_deposes})
651
- component("chart", {type: "bar", labels: ["Assemblee", "Senat"], datasets: [{label: "Deposes", data: [an_deposes, senat_deposes]}, {label: "Adoptes", data: [an_adoptes, senat_adoptes]}]})
649
+ component("stat-card", {label: "AssemblyAdopted", value: an_adoptes + "/" + an_deposes})
650
+ component("stat-card", {label: "SenateAdopted", value: senat_adoptes + "/" + senat_deposes})
651
+ component("chart", {type: "bar", labels: ["Assemblee", "Senat"], datasets: [{label: "Filed", data: [an_deposes, senat_deposes]}, {label: "Adopted", data: [an_adoptes, senat_adoptes]}]})
652
652
  \`\`\`
653
653
 
654
- ## Erreurs courantes
654
+ ## Common mistakes
655
655
 
656
- - **Confondre projet et proposition de loi** : un projet vient du gouvernement, une proposition vient d'un parlementaireverifier le champ \`nature\`
657
- - **Ne pas suivre la navette** : un texte peut faire plusieurs allers-retours entre Assemblee et Senatafficher TOUTES les etapes
658
- - **Oublier la CMP** : la Commission Mixte Paritaire est une etape cruciale entre les deux chambres, ne pas l'omettre dans la timeline
659
- - **Timeline non chronologique** : toujours trier les etapes par date croissante
656
+ - **Confusing bill types**: a "projet de loi" comes from the government, a "proposition de loi" comes from a parliamentary member check the \`nature\` field
657
+ - **Not following the shuttle**: a text can make several back-and-forth trips between the Assembly and the Senate display ALL stages
658
+ - **Forgetting the CMP**: the Commission Mixte Paritaire (Joint Committee) is a crucial stage between the two chambers do not omit it from the timeline
659
+ - **Non-chronological timeline**: always sort stages by ascending date
660
660
  `,
661
661
  'gallery-images': `---
662
- id: afficher-galerie-images-depuis-urls-mcp
663
- name: Afficher une galerie d'images a partir d'URLs retournees par un serveur MCP
662
+ id: display-image-gallery-from-mcp-urls
663
+ name: Display an image gallery from URLs returned by an MCP server
664
664
  components_used: [gallery, carousel, cards]
665
- when: les donnees MCP contiennent des champs URL pointant vers des images (jpg, png, webp, svg) comme hdurl, primaryImage, image_url, photos[].url, ou tout champ similaire
665
+ when: MCP data contains URL fields pointing to images (jpg, png, webp, svg) such as hdurl, primaryImage, image_url, photos[].url, or any similar field
666
666
  servers: [nasa, metmuseum, inaturalist]
667
667
  layout:
668
668
  type: grid
@@ -671,32 +671,32 @@ interactions:
671
671
  - source: gallery, target: lightbox, event: click, action: zoom
672
672
  ---
673
673
 
674
- ## Quand utiliser
674
+ ## When to use
675
675
 
676
- Les resultats d'un appel MCP contiennent des URLs d'images reelles. Cela inclut :
677
- - **NASA** : champ \`hdurl\` ou \`url\` dans les reponses APOD, Earth, Mars Rover Photos
678
- - **Met Museum** : champ \`primaryImage\` ou \`primaryImageSmall\` apres \`get_object\`
679
- - **iNaturalist** : champ \`photos[].url\` dans les observations
676
+ The results of an MCP call contain real image URLs. This includes:
677
+ - **NASA**: \`hdurl\` or \`url\` field in APOD, Earth, and Mars Rover Photos responses
678
+ - **Met Museum**: \`primaryImage\` or \`primaryImageSmall\` field after \`get_object\`
679
+ - **iNaturalist**: \`photos[].url\` field in observations
680
680
 
681
- La recette s'applique des qu'au moins 2 images sont disponibles dans les donnees. Pour une seule image, preferer un composant \`card\` avec l'image en en-tete.
681
+ This recipe applies whenever at least 2 images are available in the data. For a single image, prefer a \`card\` component with the image as a header.
682
682
 
683
- ## Comment
683
+ ## How to use
684
684
 
685
- 1. **Appeler l'outil DATA** du serveur MCP pour recuperer les donnees contenant les images
686
- 2. **Extraire les URLs reelles** depuis les resultatsne JAMAIS inventer d'URLs placeholder
687
- 3. **Verifier que les URLs sont valides** : elles doivent pointer vers des domaines connus (apod.nasa.gov, images.metmuseum.org, inaturalist-open-data.s3.amazonaws.com, etc.)
688
- 4. **Choisir le composant** :
689
- - 2-5 images : \`component("gallery", {images: [{src, alt, caption?}]})\`
690
- - 6+ images : \`component("carousel", {images: [{src, alt}]})\` pour eviter une page trop longue
691
- - Images avec metadonnees riches : \`component("cards", {items: [{title, image, subtitle, body}]})\`
692
- 5. **Toujours fournir un \`alt\` descriptif** pour chaque image (titre de l'oeuvre, nom de l'espece, titre APOD)
693
- 6. **Ajouter un titre contextuel** avant la galerie avec \`component("text", {content: "..."})\`
685
+ 1. **Call the DATA tool** from the MCP server to retrieve the data containing the images
686
+ 2. **Extract the real URLs** from the resultsNEVER invent placeholder URLs
687
+ 3. **Verify that the URLs are valid**: they must point to known domains (apod.nasa.gov, images.metmuseum.org, inaturalist-open-data.s3.amazonaws.com, etc.)
688
+ 4. **Choose the component**:
689
+ - 2-5 images: \`component("gallery", {images: [{src, alt, caption?}]})\`
690
+ - 6+ images: \`component("carousel", {images: [{src, alt}]})\` to avoid an overly long page
691
+ - Images with rich metadata: \`component("cards", {items: [{title, image, subtitle, body}]})\`
692
+ 5. **Always provide a descriptive \`alt\`** for each image (artwork title, species name, APOD title)
693
+ 6. **Add a contextual title** before the gallery with \`component("text", {content: "..."})\`
694
694
 
695
- ## Exemples
695
+ ## Examples
696
696
 
697
697
  ### NASA APOD (Astronomy Picture of the Day)
698
- Outil : \`nasa_apod\` ou \`nasa_apod_range\`
699
- Reponse type : \`{hdurl: "https://apod.nasa.gov/apod/image/2401/...", title: "Horsehead Nebula", explanation: "..."}\`
698
+ Tool: \`nasa_apod\` or \`nasa_apod_range\`
699
+ Typical response: \`{hdurl: "https://apod.nasa.gov/apod/image/2401/...", title: "Horsehead Nebula", explanation: "..."}\`
700
700
 
701
701
  \`\`\`
702
702
  component("gallery", {
@@ -708,9 +708,9 @@ component("gallery", {
708
708
  })
709
709
  \`\`\`
710
710
 
711
- ### Met Museum — Recherche d'oeuvres
712
- Etape 1 : \`search_objects({query: "impressionism sunflower"})\` → liste d'IDs
713
- Etape 2 : pour chaque ID, \`get_object({objectID: id})\` → \`{primaryImage, title, artistDisplayName}\`
711
+ ### Met Museum — Artwork search
712
+ Step 1: \`search_objects({query: "impressionism sunflower"})\` → list of IDs
713
+ Step 2: for each ID, \`get_object({objectID: id})\` → \`{primaryImage, title, artistDisplayName}\`
714
714
 
715
715
  \`\`\`
716
716
  component("gallery", {
@@ -722,9 +722,9 @@ component("gallery", {
722
722
  })
723
723
  \`\`\`
724
724
 
725
- ### iNaturalist — Observations avec photos
726
- Outil : \`search_observations({taxon_name: "Parus major", lat: 48.85, lng: 2.35, radius: 10})\`
727
- Reponse type : \`{photos: [{url: "..."}], species_guess: "Mesange charbonniere", place_guess: "Paris"}\`
725
+ ### iNaturalist — Observations with photos
726
+ Tool: \`search_observations({taxon_name: "Parus major", lat: 48.85, lng: 2.35, radius: 10})\`
727
+ Typical response: \`{photos: [{url: "..."}], species_guess: "Mesange charbonniere", place_guess: "Paris"}\`
728
728
 
729
729
  \`\`\`
730
730
  component("gallery", {
@@ -738,49 +738,49 @@ component("gallery", {
738
738
  })
739
739
  \`\`\`
740
740
 
741
- ## Erreurs courantes
741
+ ## Common mistakes
742
742
 
743
- - **Inventer des URLs placeholder** (\`https://example.com/image.jpg\`, \`via.placeholder.com\`, \`placehold.co\`, \`dummyimage.com\`, \`?text=...\`) — strictement INTERDIT. Si aucune image réelle n'est retournée par l'API, ne PAS afficher de galerie.
744
- - **Oublier de verifier** que le champ image existe dans les donnees retournees (certains objets Met Museum n'ont pas de \`primaryImage\`)
745
- - **Utiliser \`text\` pour afficher des URLs** au lieu de \`gallery\` — les images doivent etre rendues visuellement
746
- - **Ne pas adapter la taille** : iNaturalist retourne des thumbnails "square" par defaut, remplacer par "medium" ou "large" dans l'URL
743
+ - **Inventing placeholder URLs** (\`https://example.com/image.jpg\`, \`via.placeholder.com\`, \`placehold.co\`, \`dummyimage.com\`, \`?text=...\`) — strictly FORBIDDEN. If no real image is returned by the API, do NOT display a gallery.
744
+ - **Forgetting to check** that the image field exists in the returned data (some Met Museum objects have no \`primaryImage\`)
745
+ - **Using \`text\` to display URLs** instead of \`gallery\` — images must be rendered visually
746
+ - **Not adapting the size**: iNaturalist returns "square" thumbnails by default replace with "medium" or "large" in the URL
747
747
  `,
748
748
  'parlementaire-profile': `---
749
- id: afficher-profil-parlementaire-avec-hemicycle-et-votes
750
- name: Afficher le profil d'un parlementaire avec hemicycle, votes et timeline des mandats
749
+ id: display-parliamentary-profile-with-hemicycle-and-votes
750
+ name: Display a parliamentary profile with hemicycle, votes and mandate timeline
751
751
  components_used: [profile, hemicycle, timeline, table, stat-card, kv]
752
- when: les donnees MCP concernent un depute, senateur, groupe parlementaire, scrutin ou amendement provenant d'une base de donnees parlementaire
752
+ when: MCP data concerns a deputy, senator, parliamentary group, vote, or amendment from a parliamentary database
753
753
  servers: [tricoteuses]
754
754
  layout:
755
755
  type: grid
756
756
  columns: 2
757
- arrangement: profile + stats en haut, hemicycle + timeline en bas
757
+ arrangement: profile + stats at top, hemicycle + timeline below
758
758
  ---
759
759
 
760
- ## Quand utiliser
760
+ ## When to use
761
761
 
762
- Les resultats MCP proviennent du serveur Tricoteuses (base de donnees parlementaire francaise) et concernent :
763
- - Un **depute ou senateur** : fiche individuelle, mandats, activite
764
- - Un **groupe politique** : composition, votes, positionnement
765
- - Des **scrutins publics** : resultats de vote, repartition par groupe
766
- - Des **amendements** : texte, auteur, sort, article vise
767
- - Des **dossiers legislatifs** : parcours d'un texte, navette parlementaire
762
+ MCP results come from the Tricoteuses server (French parliamentary database) and concern:
763
+ - A **deputy or senator**: individual profile, mandates, activity
764
+ - A **political group**: composition, votes, positioning
765
+ - **Public votes**: vote results, breakdown by group
766
+ - **Amendments**: text, author, outcome, targeted article
767
+ - **Legislative files**: text journey, parliamentary shuttle
768
768
 
769
- La recette est specifique au serveur Tricoteuses et a ses outils : \`query_sql\`, \`search_recipes\`, \`list_tables\`, \`describe_table\`.
769
+ This recipe is specific to the Tricoteuses server and its tools: \`query_sql\`, \`search_recipes\`, \`list_tables\`, \`describe_table\`.
770
770
 
771
- ## Comment
771
+ ## How to use
772
772
 
773
- 1. **Recuperer les informations du parlementaire** via les outils Tricoteuses :
773
+ 1. **Retrieve the parliamentary member's information** via the Tricoteuses tools:
774
774
  \`\`\`
775
775
  query_sql({sql: "SELECT * FROM acteurs WHERE nom ILIKE '%dupont%' AND type = 'depute'"})
776
776
  \`\`\`
777
- Ou via les recipes Tricoteuses :
777
+ Or via Tricoteuses recipes:
778
778
  \`\`\`
779
779
  search_recipes({query: "profil depute"})
780
780
  get_recipe({name: "recipe-name"})
781
781
  \`\`\`
782
782
 
783
- 2. **Afficher la fiche profil** :
783
+ 2. **Display the profile card**:
784
784
  \`\`\`
785
785
  component("profile", {
786
786
  name: "Jean Dupont",
@@ -790,15 +790,15 @@ La recette est specifique au serveur Tricoteuses et a ses outils : \`query_sql\`
790
790
  })
791
791
  \`\`\`
792
792
 
793
- 3. **Afficher les statistiques d'activite** en stat-cards :
793
+ 3. **Display activity statistics** in stat-cards:
794
794
  \`\`\`
795
- component("stat-card", {label: "Participation aux scrutins", value: "87%", trend: "+3%", trendDir: "up"})
796
- component("stat-card", {label: "Amendements deposes", value: "42", icon: "file-text"})
797
- component("stat-card", {label: "Questions ecrites", value: "18", icon: "help-circle"})
798
- component("stat-card", {label: "Interventions hemicycle", value: "7", icon: "mic"})
795
+ component("stat-card", {label: "Vote participation", value: "87%", trend: "+3%", trendDir: "up"})
796
+ component("stat-card", {label: "Amendments filed", value: "42", icon: "file-text"})
797
+ component("stat-card", {label: "Written questions", value: "18", icon: "help-circle"})
798
+ component("stat-card", {label: "Hemicycle speeches", value: "7", icon: "mic"})
799
799
  \`\`\`
800
800
 
801
- 4. **Si des donnees de vote par groupe sont disponibles**, afficher l'hemicycle :
801
+ 4. **If vote data by group is available**, display the hemicycle:
802
802
  \`\`\`
803
803
  component("hemicycle", {
804
804
  groups: [
@@ -810,219 +810,219 @@ La recette est specifique au serveur Tricoteuses et a ses outils : \`query_sql\`
810
810
  })
811
811
  \`\`\`
812
812
 
813
- 5. **Pour l'historique des mandats ou votes cles**, utiliser la timeline :
813
+ 5. **For the mandate history or key votes**, use the timeline:
814
814
  \`\`\`
815
815
  component("timeline", {
816
816
  events: [
817
- {date: "2022-06-19", title: "Elu depute", description: "3e circ. du Rhone"},
818
- {date: "2023-03-16", title: "Vote 49.3 retraites", description: "Motion de censure rejetee"},
819
- {date: "2024-07-07", title: "Reelu depute", description: "2e tour"}
817
+ {date: "2022-06-19", title: "Elected deputy", description: "3e circ. du Rhone"},
818
+ {date: "2023-03-16", title: "Vote 49.3 pensions reform", description: "Motion de censure rejetee"},
819
+ {date: "2024-07-07", title: "Re-elected deputy", description: "2nd round"}
820
820
  ]
821
821
  })
822
822
  \`\`\`
823
823
 
824
- 6. **Pour les details des votes ou amendements**, utiliser une table :
824
+ 6. **For vote or amendment details**, use a table:
825
825
  \`\`\`
826
826
  component("table", {
827
- columns: ["Date", "Scrutin", "Vote", "Resultat"],
827
+ columns: ["Date", "Vote", "Position", "Result"],
828
828
  rows: votes.map(v => [v.date, v.intitule, v.position, v.resultat])
829
829
  })
830
830
  \`\`\`
831
831
 
832
- 7. **Completer avec les metadonnees** en kv :
832
+ 7. **Complete with metadata** in kv:
833
833
  \`\`\`
834
834
  component("kv", {pairs: [
835
835
  ["Legislature", "XVIe (2022-2027)"],
836
- ["Groupe", "Renaissance"],
837
- ["Commission permanente", "Lois"],
836
+ ["Group", "Renaissance"],
837
+ ["Standing committee", "Lois"],
838
838
  ["Source", "Tricoteuses — mcp.code4code.eu"]
839
839
  ]})
840
840
  \`\`\`
841
841
 
842
- ## Exemples
842
+ ## Examples
843
843
 
844
- ### Profil complet d'un depute
844
+ ### Complete deputy profile
845
845
  \`\`\`
846
- // 1. Chercher le depute
846
+ // 1. Look up the deputy
847
847
  query_sql({sql: "SELECT * FROM acteurs WHERE nom ILIKE '%dupont%' LIMIT 1"})
848
848
 
849
- // 2. Ses amendements
849
+ // 2. Their amendments
850
850
  query_sql({sql: "SELECT COUNT(*) as total, SUM(CASE WHEN sort='Adopte' THEN 1 ELSE 0 END) as adoptes FROM amendements WHERE auteur_id = $id"})
851
851
 
852
- // 3. Ses votes recents
852
+ // 3. Their recent votes
853
853
  query_sql({sql: "SELECT s.date, s.intitule, v.position FROM scrutins s JOIN votes v ON s.id = v.scrutin_id WHERE v.acteur_id = $id ORDER BY s.date DESC LIMIT 10"})
854
854
 
855
- // 4. Rendu complet
855
+ // 4. Full render
856
856
  component("profile", {name, subtitle, photo})
857
- component("stat-card", {label: "Amendements", value: total, icon: "file-text"})
858
- component("stat-card", {label: "Adoptes", value: adoptes, icon: "check"})
859
- component("stat-card", {label: "Taux d'adoption", value: Math.round(adoptes/total*100) + "%"})
860
- component("table", {columns: ["Date", "Scrutin", "Position"], rows: votes})
857
+ component("stat-card", {label: "Amendments", value: total, icon: "file-text"})
858
+ component("stat-card", {label: "Adopted", value: adoptes, icon: "check"})
859
+ component("stat-card", {label: "Adoption rate", value: Math.round(adoptes/total*100) + "%"})
860
+ component("table", {columns: ["Date", "Vote", "Position"], rows: votes})
861
861
  component("timeline", {events: mandats})
862
862
  \`\`\`
863
863
 
864
- ### Resultat d'un scrutin avec hemicycle
864
+ ### Vote result with hemicycle
865
865
  \`\`\`
866
- // 1. Recuperer le scrutin
866
+ // 1. Retrieve the vote
867
867
  query_sql({sql: "SELECT * FROM scrutins WHERE intitule ILIKE '%budget%' ORDER BY date DESC LIMIT 1"})
868
868
 
869
- // 2. Repartition par groupe
869
+ // 2. Breakdown by group
870
870
  query_sql({sql: "SELECT g.nom, g.couleur, COUNT(*) FILTER (WHERE v.position='pour') as pour, COUNT(*) FILTER (WHERE v.position='contre') as contre FROM votes v JOIN groupes g ON v.groupe_id = g.id WHERE v.scrutin_id = $id GROUP BY g.nom, g.couleur"})
871
871
 
872
- // 3. Rendu
873
- component("stat-card", {label: "Pour", value: "312", trendDir: "up"})
874
- component("stat-card", {label: "Contre", value: "245", trendDir: "down"})
872
+ // 3. Render
873
+ component("stat-card", {label: "For", value: "312", trendDir: "up"})
874
+ component("stat-card", {label: "Against", value: "245", trendDir: "down"})
875
875
  component("hemicycle", {groups: groupResults, result: {pour: 312, contre: 245, abstention: 18}})
876
- component("table", {columns: ["Groupe", "Pour", "Contre", "Abstention"], rows: groupDetails})
876
+ component("table", {columns: ["Group", "For", "Against", "Abstention"], rows: groupDetails})
877
877
  \`\`\`
878
878
 
879
- ## Composants specifiques au domaine parlementaire
879
+ ## Components specific to the parliamentary domain
880
880
 
881
- - **hemicycle** : arc de cercle montrant la repartition des votes par groupe politique, avec couleurs par parti
882
- - **profile** : fiche individuelle avec photo, nom, fonction, details structurees
883
- - **timeline** : chronologie des mandats, votes cles, evenements parlementaires
884
- - **trombinoscope** : grille de photos pour un groupe de parlementaires (utile pour les commissions)
881
+ - **hemicycle**: semicircular arc showing vote distribution by political group, with party colors
882
+ - **profile**: individual card with photo, name, role, structured details
883
+ - **timeline**: chronology of mandates, key votes, parliamentary events
884
+ - **trombinoscope**: photo grid for a group of parliamentary members (useful for committees)
885
885
 
886
- ## Erreurs courantes
886
+ ## Common mistakes
887
887
 
888
- - **Requetes SQL trop larges** : toujours utiliser LIMIT et des filtres precis pour eviter de surcharger le serveur
889
- - **Confondre acteurs et mandats** : un depute peut avoir plusieurs mandats dans la base, filtrer par legislature courante
890
- - **Oublier la source** : toujours crediter "Tricoteuses" comme source dans un kv final
891
- - **Hemicycle sans contexte** : toujours accompagner le composant hemicycle de stat-cards avec les totaux pour/contre/abstention
888
+ - **Overly broad SQL queries**: always use LIMIT and precise filters to avoid overloading the server
889
+ - **Confusing actors and mandates**: a deputy can have multiple mandates in the database filter by current legislature
890
+ - **Forgetting the source**: always credit "Tricoteuses" as the source in a final kv
891
+ - **Hemicycle without context**: always accompany the hemicycle component with stat-cards showing for/against/abstention totals
892
892
  `,
893
893
  'rechercher-textes-juridiques-legifrance': `---
894
- id: rechercher-textes-juridiques-legifrance
895
- name: Rechercher des textes juridiques, codes et lois via les donnees Legifrance
894
+ id: search-legal-texts-legifrance
895
+ name: Search legal texts, codes and laws via Legifrance data
896
896
  components_used: [table, text, kv, code]
897
- when: l'utilisateur demande des informations sur des lois, codes juridiques, articles de loi, decrets, ordonnances ou textes legislatifs francais disponibles dans la base Tricoteuses
897
+ when: the user asks for information about French laws, legal codes, articles of law, decrees, ordinances or legislative texts available in the Tricoteuses database
898
898
  servers: [tricoteuses]
899
899
  layout:
900
900
  type: grid
901
901
  columns: 1
902
902
  ---
903
903
 
904
- ## Quand utiliser
904
+ ## When to use
905
905
 
906
- L'utilisateur pose une question sur le droit francais, la legislation, ou un texte juridique specifique :
907
- - "Que dit l'article 49-3 de la Constitution ?"
908
- - "Quels decrets ont ete publies sur le droit du travail en 2025 ?"
909
- - "Montre-moi les articles du Code civil sur la filiation"
910
- - "Quelles lois ont ete promulguees ce mois-ci ?"
906
+ The user asks a question about French law, legislation, or a specific legal text:
907
+ - "What does article 49-3 of the Constitution say?"
908
+ - "Which decrees were published on labour law in 2025?"
909
+ - "Show me the articles of the Civil Code on filiation"
910
+ - "Which laws were enacted this month?"
911
911
 
912
- La base Tricoteuses contient des donnees issues de Legifrance (textes consolides, codes, lois, decrets). Utiliser \`list_tables\` pour decouvrir les tables disponibles dans le schema legifrance, puis \`describe_table\` et \`query_sql\` pour extraire les textes.
912
+ The Tricoteuses database contains data from Legifrance (consolidated texts, codes, laws, decrees). Use \`list_tables\` to discover the available tables in the legifrance schema, then \`describe_table\` and \`query_sql\` to extract the texts.
913
913
 
914
- ## Comment
914
+ ## How to use
915
915
 
916
- 1. **Decouvrir les tables disponibles** :
916
+ 1. **Discover available tables**:
917
917
  \`\`\`
918
918
  list_tables({schema: "legifrance"})
919
919
  \`\`\`
920
- Tables typiques : \`textes_versions\`, \`articles\`, \`codes\`, \`sections\`
920
+ Typical tables: \`textes_versions\`, \`articles\`, \`codes\`, \`sections\`
921
921
 
922
- 2. **Decrire la structure** d'une table pour comprendre les colonnes :
922
+ 2. **Describe the structure** of a table to understand the columns:
923
923
  \`\`\`
924
924
  describe_table({schema: "legifrance", table: "articles"})
925
925
  \`\`\`
926
926
 
927
- 3. **Rechercher les textes** avec des requetes SQL :
927
+ 3. **Search for texts** with SQL queries:
928
928
  \`\`\`
929
929
  query_sql({sql: "SELECT titre, date_publi, nature FROM legifrance.textes_versions WHERE titre ILIKE '%travail%' ORDER BY date_publi DESC LIMIT 20"})
930
930
  \`\`\`
931
931
 
932
- 4. **Afficher les resultats** :
933
- - **Table** pour les listes de textes :
932
+ 4. **Display results**:
933
+ - **Table** for lists of texts:
934
934
  \`\`\`
935
- component("table", {columns: ["Titre", "Nature", "Date", "Etat"], rows: textes})
935
+ component("table", {columns: ["Title", "Nature", "Date", "Status"], rows: textes})
936
936
  \`\`\`
937
- - **Text** pour le contenu d'un article :
937
+ - **Text** for the content of an article:
938
938
  \`\`\`
939
939
  component("text", {content: "### Article 49-3 de la Constitution\\n\\n" + article.contenu})
940
940
  \`\`\`
941
- - **Code** pour les extraits legislatifs avec mise en forme :
941
+ - **Code** for legislative excerpts with formatting:
942
942
  \`\`\`
943
943
  component("code", {language: "text", content: article.texte_integral})
944
944
  \`\`\`
945
- - **KV** pour les metadonnees du texte :
945
+ - **KV** for text metadata:
946
946
  \`\`\`
947
- component("kv", {pairs: [["Nature", "Loi organique"], ["Date", "2023-04-14"], ["NOR", "JUSX2300001L"], ["Etat", "En vigueur"]]})
947
+ component("kv", {pairs: [["Nature", "Loi organique"], ["Date", "2023-04-14"], ["NOR", "JUSX2300001L"], ["Status", "En vigueur"]]})
948
948
  \`\`\`
949
949
 
950
- ## Exemples
950
+ ## Examples
951
951
 
952
- ### Articles d'un code juridique
952
+ ### Articles from a legal code
953
953
  \`\`\`
954
- // 1. Trouver les articles du Code civil sur la filiation
954
+ // 1. Find articles of the Civil Code on filiation
955
955
  query_sql({sql: "SELECT num_article, contenu, etat FROM legifrance.articles WHERE code = 'Code civil' AND section ILIKE '%filiation%' ORDER BY num_article"})
956
956
 
957
- // 2. Rendu
958
- component("kv", {pairs: [["Code", "Code civil"], ["Section", "De la filiation"], ["Articles trouves", results.length]]})
959
- component("table", {columns: ["Article", "Contenu (extrait)", "Etat"], rows: results.map(r => [r.num_article, r.contenu.slice(0, 200) + "...", r.etat])})
957
+ // 2. Render
958
+ component("kv", {pairs: [["Code", "Code civil"], ["Section", "De la filiation"], ["Articles found", results.length]]})
959
+ component("table", {columns: ["Article", "Content (excerpt)", "Status"], rows: results.map(r => [r.num_article, r.contenu.slice(0, 200) + "...", r.etat])})
960
960
  \`\`\`
961
961
 
962
- ### Textes recents par nature
962
+ ### Recent texts by nature
963
963
  \`\`\`
964
- // 1. Lois promulguees en 2026
964
+ // 1. Laws enacted in 2026
965
965
  query_sql({sql: "SELECT titre, date_publi, nor FROM legifrance.textes_versions WHERE nature = 'LOI' AND date_publi >= '2026-01-01' ORDER BY date_publi DESC"})
966
966
 
967
- // 2. Rendu
968
- component("stat-card", {label: "Lois promulguees en 2026", value: results.length, icon: "scale"})
969
- component("table", {columns: ["Titre", "Date", "NOR"], rows: results})
967
+ // 2. Render
968
+ component("stat-card", {label: "Laws enacted in 2026", value: results.length, icon: "scale"})
969
+ component("table", {columns: ["Title", "Date", "NOR"], rows: results})
970
970
  \`\`\`
971
971
 
972
- ## Erreurs courantes
972
+ ## Common mistakes
973
973
 
974
- - **Confondre les schemas** : les donnees Legifrance sont dans le schema \`legifrance\`, pas dans \`assemblee\` — verifier avec \`list_tables\`
975
- - **Requetes trop larges** : toujours utiliser LIMIT et des filtres WHERE precis
976
- - **Afficher le texte integral brut** : utiliser le composant \`text\` avec formatage Markdown plutot que de dumper le JSON
977
- - **Oublier de preciser l'etat du texte** : un article peut etre abroge, modifie ou en vigueurtoujours l'indiquer
974
+ - **Confusing schemas**: Legifrance data is in the \`legifrance\` schema, not in \`assemblee\` — verify with \`list_tables\`
975
+ - **Overly broad queries**: always use LIMIT and precise WHERE filters
976
+ - **Displaying raw full text**: use the \`text\` component with Markdown formatting rather than dumping the JSON
977
+ - **Forgetting to specify the text status**: an article can be repealed, amended or in forcealways indicate it
978
978
  `,
979
979
  'weather-viz': `---
980
- id: visualiser-previsions-meteo-avec-graphiques-et-kpi
981
- name: Visualiser des previsions meteo avec graphiques temporels et indicateurs cles
980
+ id: visualize-weather-forecasts-with-charts-and-kpis
981
+ name: Visualize weather forecasts with time series charts and key indicators
982
982
  components_used: [stat-card, chart-rich, map, kv]
983
- when: les donnees MCP contiennent des mesures meteorologiques (temperature, humidite, vent, precipitations) ou des previsions horaires/journalieres
983
+ when: MCP data contains weather measurements (temperature, humidity, wind, precipitation) or hourly/daily forecasts
984
984
  servers: [openmeteo]
985
985
  layout:
986
986
  type: grid
987
987
  columns: 2
988
- arrangement: stat-cards en haut, chart en bas sur toute la largeur
988
+ arrangement: stat-cards at the top, chart spanning full width at the bottom
989
989
  ---
990
990
 
991
- ## Quand utiliser
991
+ ## When to use
992
992
 
993
- Les resultats MCP contiennent des donnees meteorologiques structurees. Typiquement :
994
- - **Open-Meteo** : previsions horaires (\`hourly.temperature_2m\`, \`hourly.precipitation\`), previsions journalieres (\`daily.temperature_2m_max/min\`), conditions actuelles (\`current.temperature_2m\`, \`current.wind_speed_10m\`)
995
- - Toute source retournant des series temporelles de mesures climatiques
993
+ MCP results contain structured weather data. Typically:
994
+ - **Open-Meteo**: hourly forecasts (\`hourly.temperature_2m\`, \`hourly.precipitation\`), daily forecasts (\`daily.temperature_2m_max/min\`), current conditions (\`current.temperature_2m\`, \`current.wind_speed_10m\`)
995
+ - Any source returning time series of climate measurements
996
996
 
997
- La recette s'applique meme pour une seule ville : les stat-cards montrent les conditions actuelles, le graphique montre l'evolution.
997
+ This recipe applies even for a single city: stat-cards show current conditions, the chart shows the trend over time.
998
998
 
999
- ## Comment
999
+ ## How to use
1000
1000
 
1001
- 1. **Appeler l'outil meteo** avec les coordonnees ou le nom de la ville :
1002
- - Open-Meteo : \`get_forecast({latitude: 48.85, longitude: 2.35, hourly: "temperature_2m,precipitation", daily: "temperature_2m_max,temperature_2m_min"})\`
1003
- 2. **Extraire les KPIs actuels** et les afficher en stat-cards :
1004
- - Temperature actuelle : \`component("stat-card", {label: "Temperature", value: "18.5°C", icon: "thermometer"})\`
1005
- - Vent : \`component("stat-card", {label: "Vent", value: "12.3 km/h", icon: "wind"})\`
1006
- - Humidite : \`component("stat-card", {label: "Humidite", value: "72%", icon: "droplets"})\`
1007
- - Precipitations : \`component("stat-card", {label: "Pluie (24h)", value: "2.1 mm", icon: "cloud-rain"})\`
1008
- 3. **Construire le graphique temporel** a partir des series horaires ou journalieres :
1001
+ 1. **Call the weather tool** with coordinates or city name:
1002
+ - Open-Meteo: \`get_forecast({latitude: 48.85, longitude: 2.35, hourly: "temperature_2m,precipitation", daily: "temperature_2m_max,temperature_2m_min"})\`
1003
+ 2. **Extract current KPIs** and display them as stat-cards:
1004
+ - Current temperature: \`component("stat-card", {label: "Temperature", value: "18.5°C", icon: "thermometer"})\`
1005
+ - Wind: \`component("stat-card", {label: "Wind", value: "12.3 km/h", icon: "wind"})\`
1006
+ - Humidity: \`component("stat-card", {label: "Humidity", value: "72%", icon: "droplets"})\`
1007
+ - Precipitation: \`component("stat-card", {label: "Rain (24h)", value: "2.1 mm", icon: "cloud-rain"})\`
1008
+ 3. **Build the time series chart** from hourly or daily series:
1009
1009
  - \`component("chart-rich", {type: "line", labels: hourly.time, datasets: [{label: "Temperature °C", data: hourly.temperature_2m}]})\`
1010
- - Pour les precipitations, preferer un type "bar" superpose
1011
- 4. **Ajouter la carte** si des coordonnees sont disponibles :
1010
+ - For precipitation, prefer an overlaid "bar" type
1011
+ 4. **Add the map** if coordinates are available:
1012
1012
  - \`component("map", {center: [lat, lon], zoom: 10, markers: [{lat, lon, label: "Paris"}]})\`
1013
- 5. **Completer avec les details** en kv :
1014
- - \`component("kv", {pairs: [["Lever du soleil", "06:42"], ["Coucher", "20:15"], ["Indice UV", "5 (modere)"]]})\`
1013
+ 5. **Complete with details** in kv:
1014
+ - \`component("kv", {pairs: [["Sunrise", "06:42"], ["Sunset", "20:15"], ["UV Index", "5 (moderate)"]]})\`
1015
1015
 
1016
- ## Exemples
1016
+ ## Examples
1017
1017
 
1018
- ### Previsions 7 jours pour Paris
1018
+ ### 7-day forecast for Paris
1019
1019
  \`\`\`
1020
- // Appel MCP
1020
+ // MCP call
1021
1021
  get_forecast({latitude: 48.8566, longitude: 2.3522, daily: "temperature_2m_max,temperature_2m_min,precipitation_sum"})
1022
1022
 
1023
- // Rendu
1024
- component("stat-card", {label: "Aujourd'hui", value: "22°C / 14°C", icon: "thermometer"})
1025
- component("stat-card", {label: "Pluie prevue", value: "0 mm", icon: "sun"})
1023
+ // Render
1024
+ component("stat-card", {label: "Today", value: "22°C / 14°C", icon: "thermometer"})
1025
+ component("stat-card", {label: "Expected rain", value: "0 mm", icon: "sun"})
1026
1026
  component("chart-rich", {
1027
1027
  type: "line",
1028
1028
  labels: daily.time, // ["2026-04-09", "2026-04-10", ...]
@@ -1033,23 +1033,23 @@ component("chart-rich", {
1033
1033
  })
1034
1034
  \`\`\`
1035
1035
 
1036
- ### Previsions horaires avec precipitations
1036
+ ### Hourly forecast with precipitation
1037
1037
  \`\`\`
1038
1038
  component("chart-rich", {
1039
1039
  type: "line",
1040
1040
  labels: hourly.time.slice(0, 24),
1041
1041
  datasets: [
1042
1042
  {label: "Temperature °C", data: hourly.temperature_2m.slice(0, 24), yAxisID: "y"},
1043
- {label: "Pluie mm", data: hourly.precipitation.slice(0, 24), yAxisID: "y1", type: "bar"}
1043
+ {label: "Rain mm", data: hourly.precipitation.slice(0, 24), yAxisID: "y1", type: "bar"}
1044
1044
  ]
1045
1045
  })
1046
1046
  \`\`\`
1047
1047
 
1048
- ## Erreurs courantes
1048
+ ## Common mistakes
1049
1049
 
1050
- - **Afficher les donnees brutes JSON** au lieu de les visualiser avec des composants
1051
- - **Ne pas convertir les unites** : Open-Meteo retourne en unites SI par defaut (m/s pour le vent convertir en km/h si l'utilisateur est francophone)
1052
- - **Oublier les stat-cards** pour les KPIs principaux : un graphique seul ne donne pas les chiffres cles immediatement
1053
- - **Trop de points sur le graphique** : pour des previsions horaires sur 7 jours (168 points), preferer un resample journalier ou limiter a 48h en mode horaire
1050
+ - **Displaying raw JSON data** instead of visualizing it with components
1051
+ - **Not converting units**: Open-Meteo returns SI units by default (m/s for windconvert to km/h if appropriate for the user)
1052
+ - **Forgetting stat-cards** for the main KPIs: a chart alone does not surface key figures immediately
1053
+ - **Too many points on the chart**: for hourly forecasts over 7 days (168 points), prefer daily resampling or limit to 48h in hourly mode
1054
1054
  `,
1055
1055
  };