@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.
- package/package.json +1 -1
- package/src/autoui-server.ts +134 -134
- package/src/diagnostics.ts +3 -3
- package/src/loop.ts +6 -6
- package/src/providers/wasm.ts +5 -5
- package/src/recipes/_generated.ts +446 -446
- package/src/recipes/afficher-oeuvres-art-collection-musee.md +45 -45
- package/src/recipes/analyser-actualites-hacker-news.md +52 -52
- package/src/recipes/cartographier-observations-biodiversite.md +44 -44
- package/src/recipes/cross-server.md +48 -48
- package/src/recipes/dashboard-kpi.md +45 -45
- package/src/recipes/explorer-dossiers-legislatifs-parcours-texte.md +48 -48
- package/src/recipes/gallery-images.md +33 -33
- package/src/recipes/parlementaire-profile.md +58 -58
- package/src/recipes/rechercher-textes-juridiques-legifrance.md +38 -38
- package/src/recipes/weather-viz.md +35 -35
- package/src/recipes/widgets/actions.md +6 -6
- package/src/recipes/widgets/alert.md +6 -6
- package/src/recipes/widgets/cards.md +10 -10
- package/src/recipes/widgets/carousel.md +8 -8
- package/src/recipes/widgets/chart-rich.md +10 -10
- package/src/recipes/widgets/chart.md +9 -9
- package/src/recipes/widgets/code.md +6 -6
- package/src/recipes/widgets/d3.md +10 -10
- package/src/recipes/widgets/data-table.md +10 -10
- package/src/recipes/widgets/gallery.md +10 -10
- package/src/recipes/widgets/grid-data.md +11 -11
- package/src/recipes/widgets/hemicycle.md +9 -9
- package/src/recipes/widgets/js-sandbox.md +10 -10
- package/src/recipes/widgets/json-viewer.md +8 -8
- package/src/recipes/widgets/kv.md +9 -9
- package/src/recipes/widgets/list.md +7 -7
- package/src/recipes/widgets/log.md +6 -6
- package/src/recipes/widgets/map.md +10 -10
- package/src/recipes/widgets/profile.md +9 -9
- package/src/recipes/widgets/recipe-browser.md +33 -33
- package/src/recipes/widgets/sankey.md +10 -10
- package/src/recipes/widgets/stat-card.md +7 -7
- package/src/recipes/widgets/stat.md +10 -10
- package/src/recipes/widgets/tags.md +6 -6
- package/src/recipes/widgets/text.md +6 -6
- package/src/recipes/widgets/timeline.md +6 -6
- package/src/recipes/widgets/trombinoscope.md +8 -8
- package/src/summarize.ts +6 -6
- package/src/tool-layers.ts +26 -26
|
@@ -1,59 +1,59 @@
|
|
|
1
1
|
---
|
|
2
|
-
id:
|
|
3
|
-
name:
|
|
2
|
+
id: cross-reference-data-from-multiple-connected-mcp-servers
|
|
3
|
+
name: Cross-reference data from multiple simultaneously connected MCP servers
|
|
4
4
|
components_used: [map, gallery, table, kv, stat-card]
|
|
5
|
-
when:
|
|
5
|
+
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
|
|
6
6
|
servers: []
|
|
7
7
|
layout:
|
|
8
8
|
type: grid
|
|
9
9
|
columns: 2
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## When to use
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
- "
|
|
16
|
-
- "
|
|
17
|
-
- "Compare
|
|
18
|
-
- "
|
|
14
|
+
The user asks a question that cannot be answered by a single MCP server. Examples:
|
|
15
|
+
- "What birds can be spotted near the Louvre?" → geocoding + iNaturalist
|
|
16
|
+
- "Show me the weather and nature observations in Marseille" → Open-Meteo + iNaturalist
|
|
17
|
+
- "Compare artworks from the Met Museum on the theme of flowers with species observed in New York" → Met Museum + iNaturalist
|
|
18
|
+
- "Give me the Wikipedia profile of the MP who filed the most amendments" → Tricoteuses + Wikipedia
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
This recipe applies whenever 2+ MCP servers are needed to answer the question.
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## How to use
|
|
23
23
|
|
|
24
|
-
1. **
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
2. **
|
|
24
|
+
1. **Identify which MCP servers provide which data**:
|
|
25
|
+
- Server A provides reference data (coordinates, IDs, names)
|
|
26
|
+
- Server B enriches with complementary data
|
|
27
|
+
2. **Call the first server** to obtain the base data:
|
|
28
28
|
```
|
|
29
|
-
//
|
|
29
|
+
// Example: geocode a location
|
|
30
30
|
geocode({query: "Louvre, Paris"}) → {lat: 48.8606, lon: 2.3376}
|
|
31
31
|
```
|
|
32
|
-
3. **
|
|
32
|
+
3. **Use the results as input** for the second server:
|
|
33
33
|
```
|
|
34
|
-
//
|
|
34
|
+
// Example: search for observations within a radius
|
|
35
35
|
search_observations({lat: 48.8606, lng: 2.3376, radius: 5, taxon: "Aves"})
|
|
36
36
|
```
|
|
37
|
-
4. **
|
|
38
|
-
- `component("map", ...)`
|
|
39
|
-
- `component("table", ...)`
|
|
40
|
-
- `component("gallery", ...)`
|
|
41
|
-
5. **
|
|
37
|
+
4. **Combine the results** in a coherent visualization:
|
|
38
|
+
- `component("map", ...)` if coordinates are involved (markers from both sources)
|
|
39
|
+
- `component("table", ...)` for combined results with columns from both sources
|
|
40
|
+
- `component("gallery", ...)` if both sources provide images
|
|
41
|
+
5. **Always cite the sources** with a final `kv` component:
|
|
42
42
|
```
|
|
43
|
-
component("kv", {pairs: [["Source 1", "iNaturalist"], ["Source 2", "Open-Meteo"], ["
|
|
43
|
+
component("kv", {pairs: [["Source 1", "iNaturalist"], ["Source 2", "Open-Meteo"], ["Area", "5 km around the Louvre"]]})
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
##
|
|
46
|
+
## Examples
|
|
47
47
|
|
|
48
|
-
###
|
|
48
|
+
### Birds near a landmark (geocoding + iNaturalist)
|
|
49
49
|
```
|
|
50
|
-
// 1.
|
|
50
|
+
// 1. Geocode the location
|
|
51
51
|
geocode({query: "Tour Eiffel, Paris"}) → lat: 48.8584, lon: 2.2945
|
|
52
52
|
|
|
53
|
-
// 2.
|
|
53
|
+
// 2. Search for bird observations
|
|
54
54
|
search_observations({lat: 48.8584, lng: 2.2945, radius: 3, taxon_name: "Aves"})
|
|
55
55
|
|
|
56
|
-
// 3.
|
|
56
|
+
// 3. Combined render
|
|
57
57
|
component("map", {
|
|
58
58
|
center: [48.8584, 2.2945],
|
|
59
59
|
zoom: 14,
|
|
@@ -61,42 +61,42 @@ component("map", {
|
|
|
61
61
|
.concat(observations.map(o => ({lat: o.lat, lon: o.lon, label: o.species_guess})))
|
|
62
62
|
})
|
|
63
63
|
component("gallery", {images: observations.flatMap(o => o.photos.map(p => ({src: p.url, alt: o.species_guess})))})
|
|
64
|
-
component("table", {columns: ["
|
|
65
|
-
component("stat-card", {label: "
|
|
64
|
+
component("table", {columns: ["Species", "Date", "Distance", "Observer"], rows: formattedObs})
|
|
65
|
+
component("stat-card", {label: "Distinct species", value: "23", icon: "bird"})
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
###
|
|
68
|
+
### Enriched profile (Tricoteuses + Wikipedia)
|
|
69
69
|
```
|
|
70
|
-
// 1.
|
|
70
|
+
// 1. Find the most active MP
|
|
71
71
|
query_sql({sql: "SELECT depute, COUNT(*) as nb FROM amendements GROUP BY depute ORDER BY nb DESC LIMIT 1"})
|
|
72
72
|
|
|
73
|
-
// 2.
|
|
73
|
+
// 2. Enrich with Wikipedia
|
|
74
74
|
search_wikipedia({query: depute.nom})
|
|
75
75
|
|
|
76
|
-
// 3.
|
|
76
|
+
// 3. Combined render
|
|
77
77
|
component("profile", {name: depute.nom, subtitle: depute.groupe, details: wikipedia.extract})
|
|
78
|
-
component("stat-card", {label: "
|
|
79
|
-
component("kv", {pairs: [["
|
|
78
|
+
component("stat-card", {label: "Amendments filed", value: depute.nb})
|
|
79
|
+
component("kv", {pairs: [["Parliamentary source", "Tricoteuses"], ["Biography", "Wikipedia"]]})
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
###
|
|
82
|
+
### Weather + Biodiversity in a region
|
|
83
83
|
```
|
|
84
|
-
// 1.
|
|
84
|
+
// 1. Weather for Marseille
|
|
85
85
|
get_forecast({latitude: 43.2965, longitude: 5.3698, daily: "temperature_2m_max"})
|
|
86
86
|
|
|
87
|
-
// 2.
|
|
87
|
+
// 2. Nature observations
|
|
88
88
|
search_observations({lat: 43.2965, lng: 5.3698, radius: 20})
|
|
89
89
|
|
|
90
|
-
// 3.
|
|
91
|
-
component("stat-card", {label: "
|
|
92
|
-
component("stat-card", {label: "
|
|
90
|
+
// 3. Combined dashboard
|
|
91
|
+
component("stat-card", {label: "Max temperature", value: "26°C", icon: "thermometer"})
|
|
92
|
+
component("stat-card", {label: "Recent observations", value: "412", icon: "eye"})
|
|
93
93
|
component("map", {center: [43.2965, 5.3698], markers: observations})
|
|
94
94
|
component("chart", {type: "line", labels: dates, datasets: [{label: "Temperature", data: temps}, {label: "Observations", data: obsCounts}]})
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
##
|
|
97
|
+
## Common mistakes
|
|
98
98
|
|
|
99
|
-
- **
|
|
100
|
-
- **
|
|
101
|
-
- **
|
|
102
|
-
- **
|
|
99
|
+
- **Making more than 3 DATA calls without an intermediate render**: the user expects visual results between steps
|
|
100
|
+
- **Not explaining which servers are being used**: always display the sources with a `kv`
|
|
101
|
+
- **Mixing data without structure**: combined results must be in a coherent table or map, not a raw dump
|
|
102
|
+
- **Forgetting to handle no-match cases**: if geocoding finds nothing, or if iNaturalist has no observations in the area, display that clearly
|
|
@@ -1,78 +1,78 @@
|
|
|
1
1
|
---
|
|
2
|
-
id:
|
|
3
|
-
name:
|
|
2
|
+
id: compose-kpi-dashboard-from-aggregated-metrics
|
|
3
|
+
name: Compose a KPI dashboard from aggregated metrics
|
|
4
4
|
components_used: [stat-card, chart, table, kv]
|
|
5
|
-
when:
|
|
5
|
+
when: MCP data contains numeric metrics, counters, totals, percentages, or aggregated statistics that warrant a visual dashboard
|
|
6
6
|
servers: []
|
|
7
7
|
layout:
|
|
8
8
|
type: grid
|
|
9
9
|
columns: 3
|
|
10
|
-
arrangement: stat-cards
|
|
10
|
+
arrangement: stat-cards in a row, chart + table below
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## When to use
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
15
|
+
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:
|
|
16
|
+
- Totals, counters, or averages (revenue, article count, participation, etc.)
|
|
17
|
+
- Percentages or ratios (churn rate, voter turnout, etc.)
|
|
18
|
+
- Time series of metrics (month-over-month, quarter-over-quarter trends)
|
|
19
|
+
- Breakdowns by category (by political group, by country, by object type)
|
|
20
20
|
|
|
21
|
-
##
|
|
21
|
+
## How to use
|
|
22
22
|
|
|
23
|
-
1. **
|
|
24
|
-
2. **
|
|
23
|
+
1. **Identify the 3–5 main KPIs** in the data returned by the MCP server
|
|
24
|
+
2. **Display each KPI as a stat-card** with clean formatting:
|
|
25
25
|
```
|
|
26
|
-
component("stat-card", {label: "
|
|
26
|
+
component("stat-card", {label: "Revenue", value: "45 230 EUR", trend: "+12.4%", trendDir: "up", icon: "trending-up"})
|
|
27
27
|
```
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
3. **
|
|
28
|
+
- Always format numbers: thousands separators, units, symbols
|
|
29
|
+
- Add `trend` and `trendDir` if a comparison is available (vs. previous month, vs. previous year)
|
|
30
|
+
3. **If time series exist**, add a chart:
|
|
31
31
|
```
|
|
32
|
-
component("chart", {type: "bar", labels: ["Q1", "Q2", "Q3", "Q4"], datasets: [{label: "
|
|
32
|
+
component("chart", {type: "bar", labels: ["Q1", "Q2", "Q3", "Q4"], datasets: [{label: "Revenue", data: [98000, 112000, 128000, 142000]}]})
|
|
33
33
|
```
|
|
34
|
-
- "bar"
|
|
35
|
-
- "line"
|
|
36
|
-
4. **
|
|
34
|
+
- "bar" for comparisons between categories/periods
|
|
35
|
+
- "line" for continuous trends
|
|
36
|
+
4. **If tabular details exist**, add a table:
|
|
37
37
|
```
|
|
38
|
-
component("table", {columns: ["
|
|
38
|
+
component("table", {columns: ["Category", "Value", "Change"], rows: [...]})
|
|
39
39
|
```
|
|
40
|
-
5. **
|
|
40
|
+
5. **For supplementary metadata**, use kv:
|
|
41
41
|
```
|
|
42
|
-
component("kv", {pairs: [["Source", "data.gouv.fr"], ["
|
|
42
|
+
component("kv", {pairs: [["Source", "data.gouv.fr"], ["Last updated", "2026-04-01"], ["Period", "Q1 2026"]]})
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
##
|
|
45
|
+
## Examples
|
|
46
46
|
|
|
47
|
-
###
|
|
47
|
+
### Parliamentary dashboard (Tricoteuses)
|
|
48
48
|
```
|
|
49
|
-
//
|
|
50
|
-
component("stat-card", {label: "
|
|
51
|
-
component("stat-card", {label: "
|
|
52
|
-
component("stat-card", {label: "
|
|
53
|
-
component("chart", {type: "bar", labels:
|
|
54
|
-
component("table", {columns: ["
|
|
49
|
+
// After query_sql on votes for the legislature
|
|
50
|
+
component("stat-card", {label: "Public votes", value: "1 247", icon: "vote"})
|
|
51
|
+
component("stat-card", {label: "Amendments filed", value: "42 831", icon: "file-text"})
|
|
52
|
+
component("stat-card", {label: "Average participation", value: "61.3%", trend: "-2.1%", trendDir: "down"})
|
|
53
|
+
component("chart", {type: "bar", labels: months, datasets: [{label: "Votes/month", data: counts}]})
|
|
54
|
+
component("table", {columns: ["Group", "Amendments", "Adopted", "Rate"], rows: groupStats})
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
###
|
|
57
|
+
### Biodiversity dashboard (iNaturalist)
|
|
58
58
|
```
|
|
59
59
|
component("stat-card", {label: "Observations", value: "3 412", icon: "eye"})
|
|
60
|
-
component("stat-card", {label: "
|
|
61
|
-
component("stat-card", {label: "
|
|
62
|
-
component("chart", {type: "line", labels: dates, datasets: [{label: "Observations/
|
|
60
|
+
component("stat-card", {label: "Unique species", value: "287", icon: "leaf"})
|
|
61
|
+
component("stat-card", {label: "Observers", value: "156", icon: "users"})
|
|
62
|
+
component("chart", {type: "line", labels: dates, datasets: [{label: "Observations/day", data: dailyCounts}]})
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
###
|
|
65
|
+
### News dashboard (Hacker News)
|
|
66
66
|
```
|
|
67
67
|
component("stat-card", {label: "Top stories", value: "500", icon: "newspaper"})
|
|
68
|
-
component("stat-card", {label: "
|
|
69
|
-
component("stat-card", {label: "
|
|
70
|
-
component("table", {columns: ["
|
|
68
|
+
component("stat-card", {label: "Average score", value: "142", icon: "trending-up"})
|
|
69
|
+
component("stat-card", {label: "Average comments", value: "87", icon: "message-circle"})
|
|
70
|
+
component("table", {columns: ["Rank", "Title", "Score", "Comments"], rows: topStories})
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
##
|
|
73
|
+
## Common mistakes
|
|
74
74
|
|
|
75
|
-
- **
|
|
76
|
-
- **
|
|
77
|
-
- **
|
|
78
|
-
- **
|
|
75
|
+
- **Too many stat-cards**: beyond 5, switch to a `kv` or `table` for secondary metrics
|
|
76
|
+
- **Unformatted numbers**: displaying "45230" instead of "45 230" hurts readability
|
|
77
|
+
- **Missing units**: "45 230" means nothing without "EUR", "%", "observations", etc.
|
|
78
|
+
- **Chart without context**: always accompany a chart with stat-cards that surface key figures instantly
|
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
---
|
|
2
|
-
id:
|
|
3
|
-
name:
|
|
2
|
+
id: explore-legislative-files-text-journey
|
|
3
|
+
name: Explore legislative files and the journey of a text between the Assembly and the Senate
|
|
4
4
|
components_used: [timeline, table, kv, stat-card]
|
|
5
|
-
when:
|
|
5
|
+
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
|
|
6
6
|
servers: [tricoteuses]
|
|
7
7
|
layout:
|
|
8
8
|
type: grid
|
|
9
9
|
columns: 2
|
|
10
|
-
arrangement: timeline
|
|
10
|
+
arrangement: full-width timeline at top, stats + table below
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## When to use
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
- "
|
|
17
|
-
- "
|
|
18
|
-
- "
|
|
19
|
-
- "
|
|
15
|
+
The user is interested in the journey of a bill through the institutions:
|
|
16
|
+
- "Where does the immigration bill currently stand?"
|
|
17
|
+
- "Show me the parliamentary shuttle for the pension reform"
|
|
18
|
+
- "How many readings did this text go through?"
|
|
19
|
+
- "Which amendments were adopted in committee?"
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
The Tricoteuses server contains legislative files with their stages: filing, referral to committee, floor debate, vote, shuttle, promulgation.
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## How to use
|
|
24
24
|
|
|
25
|
-
1. **
|
|
25
|
+
1. **Search for the legislative file**:
|
|
26
26
|
```
|
|
27
27
|
query_sql({sql: "SELECT * FROM assemblee.dossiers WHERE titre ILIKE '%immigration%' ORDER BY date_depot DESC LIMIT 5"})
|
|
28
28
|
```
|
|
29
|
-
|
|
29
|
+
Or via Tricoteuses recipes:
|
|
30
30
|
```
|
|
31
31
|
search_recipes({query: "dossier legislatif parcours"})
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
2. **
|
|
34
|
+
2. **Retrieve the journey stages**:
|
|
35
35
|
```
|
|
36
36
|
query_sql({sql: "SELECT etape, chambre, date, resultat FROM assemblee.dossier_etapes WHERE dossier_id = $id ORDER BY date"})
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
3. **
|
|
39
|
+
3. **Display the journey timeline**:
|
|
40
40
|
```
|
|
41
41
|
component("timeline", {
|
|
42
42
|
events: etapes.map(e => ({
|
|
@@ -48,68 +48,68 @@ Le serveur Tricoteuses contient les dossiers legislatifs avec leurs etapes : dep
|
|
|
48
48
|
})
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
4. **
|
|
51
|
+
4. **File statistics** in stat-cards:
|
|
52
52
|
```
|
|
53
|
-
component("stat-card", {label: "
|
|
54
|
-
component("stat-card", {label: "
|
|
55
|
-
component("stat-card", {label: "
|
|
56
|
-
component("stat-card", {label: "
|
|
53
|
+
component("stat-card", {label: "Readings", value: "3", icon: "book-open"})
|
|
54
|
+
component("stat-card", {label: "Amendments filed", value: "1 247", icon: "file-text"})
|
|
55
|
+
component("stat-card", {label: "Amendments adopted", value: "312", icon: "check"})
|
|
56
|
+
component("stat-card", {label: "Total duration", value: "14 months", icon: "clock"})
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
5. **
|
|
59
|
+
5. **Amendment details by stage** in a table:
|
|
60
60
|
```
|
|
61
61
|
component("table", {
|
|
62
|
-
columns: ["
|
|
62
|
+
columns: ["Stage", "Chamber", "Amendments filed", "Adopted", "Rejected"],
|
|
63
63
|
rows: etapeStats
|
|
64
64
|
})
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
6. **
|
|
67
|
+
6. **File metadata** in kv:
|
|
68
68
|
```
|
|
69
69
|
component("kv", {pairs: [
|
|
70
|
-
["
|
|
70
|
+
["Title", dossier.titre],
|
|
71
71
|
["Nature", "Projet de loi"],
|
|
72
|
-
["
|
|
73
|
-
["
|
|
74
|
-
["
|
|
72
|
+
["Author", "Gouvernement"],
|
|
73
|
+
["Filing date", dossier.date_depot],
|
|
74
|
+
["Current status", dossier.etat],
|
|
75
75
|
["Source", "Tricoteuses"]
|
|
76
76
|
]})
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
##
|
|
79
|
+
## Examples
|
|
80
80
|
|
|
81
|
-
###
|
|
81
|
+
### Complete text journey
|
|
82
82
|
```
|
|
83
|
-
// 1.
|
|
83
|
+
// 1. Retrieve the file
|
|
84
84
|
query_sql({sql: "SELECT id, titre, date_depot, etat FROM assemblee.dossiers WHERE titre ILIKE '%retraites%2023%' LIMIT 1"})
|
|
85
85
|
|
|
86
|
-
// 2.
|
|
86
|
+
// 2. Stages
|
|
87
87
|
query_sql({sql: "SELECT * FROM assemblee.dossier_etapes WHERE dossier_id = $id ORDER BY date"})
|
|
88
88
|
|
|
89
|
-
// 3.
|
|
89
|
+
// 3. Amendments by stage
|
|
90
90
|
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"})
|
|
91
91
|
|
|
92
|
-
// 4.
|
|
93
|
-
component("kv", {pairs: [["
|
|
92
|
+
// 4. Render
|
|
93
|
+
component("kv", {pairs: [["File", titre], ["Status", etat]]})
|
|
94
94
|
component("timeline", {events: etapes})
|
|
95
|
-
component("stat-card", {label: "Total
|
|
96
|
-
component("stat-card", {label: "
|
|
97
|
-
component("table", {columns: ["
|
|
95
|
+
component("stat-card", {label: "Total amendments", value: totalAmendements})
|
|
96
|
+
component("stat-card", {label: "Adopted", value: totalAdoptes})
|
|
97
|
+
component("table", {columns: ["Stage", "Filed", "Adopted", "Rate"], rows: etapeStats})
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
###
|
|
100
|
+
### Comparison between chambers
|
|
101
101
|
```
|
|
102
|
-
//
|
|
102
|
+
// Amendments by chamber
|
|
103
103
|
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"})
|
|
104
104
|
|
|
105
|
-
component("stat-card", {label: "
|
|
106
|
-
component("stat-card", {label: "
|
|
107
|
-
component("chart", {type: "bar", labels: ["Assemblee", "Senat"], datasets: [{label: "
|
|
105
|
+
component("stat-card", {label: "Assembly — Adopted", value: an_adoptes + "/" + an_deposes})
|
|
106
|
+
component("stat-card", {label: "Senate — Adopted", value: senat_adoptes + "/" + senat_deposes})
|
|
107
|
+
component("chart", {type: "bar", labels: ["Assemblee", "Senat"], datasets: [{label: "Filed", data: [an_deposes, senat_deposes]}, {label: "Adopted", data: [an_adoptes, senat_adoptes]}]})
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
##
|
|
110
|
+
## Common mistakes
|
|
111
111
|
|
|
112
|
-
- **
|
|
113
|
-
- **
|
|
114
|
-
- **
|
|
115
|
-
- **
|
|
112
|
+
- **Confusing bill types**: a "projet de loi" comes from the government, a "proposition de loi" comes from a parliamentary member — check the `nature` field
|
|
113
|
+
- **Not following the shuttle**: a text can make several back-and-forth trips between the Assembly and the Senate — display ALL stages
|
|
114
|
+
- **Forgetting the CMP**: the Commission Mixte Paritaire (Joint Committee) is a crucial stage between the two chambers — do not omit it from the timeline
|
|
115
|
+
- **Non-chronological timeline**: always sort stages by ascending date
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
id:
|
|
3
|
-
name:
|
|
2
|
+
id: display-image-gallery-from-mcp-urls
|
|
3
|
+
name: Display an image gallery from URLs returned by an MCP server
|
|
4
4
|
components_used: [gallery, carousel, cards]
|
|
5
|
-
when:
|
|
5
|
+
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
|
|
6
6
|
servers: [nasa, metmuseum, inaturalist]
|
|
7
7
|
layout:
|
|
8
8
|
type: grid
|
|
@@ -11,32 +11,32 @@ interactions:
|
|
|
11
11
|
- source: gallery, target: lightbox, event: click, action: zoom
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## When to use
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
- **NASA
|
|
18
|
-
- **Met Museum
|
|
19
|
-
- **iNaturalist
|
|
16
|
+
The results of an MCP call contain real image URLs. This includes:
|
|
17
|
+
- **NASA**: `hdurl` or `url` field in APOD, Earth, and Mars Rover Photos responses
|
|
18
|
+
- **Met Museum**: `primaryImage` or `primaryImageSmall` field after `get_object`
|
|
19
|
+
- **iNaturalist**: `photos[].url` field in observations
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
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.
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## How to use
|
|
24
24
|
|
|
25
|
-
1. **
|
|
26
|
-
2. **
|
|
27
|
-
3. **
|
|
28
|
-
4. **
|
|
29
|
-
- 2-5 images
|
|
30
|
-
- 6+ images
|
|
31
|
-
- Images
|
|
32
|
-
5. **
|
|
33
|
-
6. **
|
|
25
|
+
1. **Call the DATA tool** from the MCP server to retrieve the data containing the images
|
|
26
|
+
2. **Extract the real URLs** from the results — NEVER invent placeholder URLs
|
|
27
|
+
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.)
|
|
28
|
+
4. **Choose the component**:
|
|
29
|
+
- 2-5 images: `component("gallery", {images: [{src, alt, caption?}]})`
|
|
30
|
+
- 6+ images: `component("carousel", {images: [{src, alt}]})` to avoid an overly long page
|
|
31
|
+
- Images with rich metadata: `component("cards", {items: [{title, image, subtitle, body}]})`
|
|
32
|
+
5. **Always provide a descriptive `alt`** for each image (artwork title, species name, APOD title)
|
|
33
|
+
6. **Add a contextual title** before the gallery with `component("text", {content: "..."})`
|
|
34
34
|
|
|
35
|
-
##
|
|
35
|
+
## Examples
|
|
36
36
|
|
|
37
37
|
### NASA APOD (Astronomy Picture of the Day)
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
Tool: `nasa_apod` or `nasa_apod_range`
|
|
39
|
+
Typical response: `{hdurl: "https://apod.nasa.gov/apod/image/2401/...", title: "Horsehead Nebula", explanation: "..."}`
|
|
40
40
|
|
|
41
41
|
```
|
|
42
42
|
component("gallery", {
|
|
@@ -48,9 +48,9 @@ component("gallery", {
|
|
|
48
48
|
})
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
### Met Museum —
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
### Met Museum — Artwork search
|
|
52
|
+
Step 1: `search_objects({query: "impressionism sunflower"})` → list of IDs
|
|
53
|
+
Step 2: for each ID, `get_object({objectID: id})` → `{primaryImage, title, artistDisplayName}`
|
|
54
54
|
|
|
55
55
|
```
|
|
56
56
|
component("gallery", {
|
|
@@ -62,9 +62,9 @@ component("gallery", {
|
|
|
62
62
|
})
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
### iNaturalist — Observations
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
### iNaturalist — Observations with photos
|
|
66
|
+
Tool: `search_observations({taxon_name: "Parus major", lat: 48.85, lng: 2.35, radius: 10})`
|
|
67
|
+
Typical response: `{photos: [{url: "..."}], species_guess: "Mesange charbonniere", place_guess: "Paris"}`
|
|
68
68
|
|
|
69
69
|
```
|
|
70
70
|
component("gallery", {
|
|
@@ -78,9 +78,9 @@ component("gallery", {
|
|
|
78
78
|
})
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
##
|
|
81
|
+
## Common mistakes
|
|
82
82
|
|
|
83
|
-
- **
|
|
84
|
-
- **
|
|
85
|
-
- **
|
|
86
|
-
- **
|
|
83
|
+
- **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.
|
|
84
|
+
- **Forgetting to check** that the image field exists in the returned data (some Met Museum objects have no `primaryImage`)
|
|
85
|
+
- **Using `text` to display URLs** instead of `gallery` — images must be rendered visually
|
|
86
|
+
- **Not adapting the size**: iNaturalist returns "square" thumbnails by default — replace with "medium" or "large" in the URL
|