@seed-ship/mcp-ui-solid 6.5.0 → 6.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +130 -0
- package/README.md +37 -0
- package/dist/adapters/connector.cjs +112 -0
- package/dist/adapters/connector.cjs.map +1 -0
- package/dist/adapters/connector.d.ts +71 -0
- package/dist/adapters/connector.d.ts.map +1 -0
- package/dist/adapters/connector.js +112 -0
- package/dist/adapters/connector.js.map +1 -0
- package/dist/adapters/index.d.ts +18 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters.cjs +6 -0
- package/dist/adapters.cjs.map +1 -0
- package/dist/adapters.d.cts +18 -0
- package/dist/adapters.d.ts +18 -0
- package/dist/adapters.js +6 -0
- package/dist/adapters.js.map +1 -0
- package/dist/components/ExpandableWrapper.cjs +24 -6
- package/dist/components/ExpandableWrapper.cjs.map +1 -1
- package/dist/components/ExpandableWrapper.d.ts.map +1 -1
- package/dist/components/ExpandableWrapper.js +24 -6
- package/dist/components/ExpandableWrapper.js.map +1 -1
- package/dist/components/FeedbackInline.cjs +6 -2
- package/dist/components/FeedbackInline.cjs.map +1 -1
- package/dist/components/FeedbackInline.d.ts +2 -2
- package/dist/components/FeedbackInline.d.ts.map +1 -1
- package/dist/components/FeedbackInline.js +7 -3
- package/dist/components/FeedbackInline.js.map +1 -1
- package/dist/components/PresentationFeedback.cjs +207 -0
- package/dist/components/PresentationFeedback.cjs.map +1 -0
- package/dist/components/PresentationFeedback.d.ts +113 -0
- package/dist/components/PresentationFeedback.d.ts.map +1 -0
- package/dist/components/PresentationFeedback.js +207 -0
- package/dist/components/PresentationFeedback.js.map +1 -0
- package/dist/components/StreamingUIRenderer.cjs +82 -195
- package/dist/components/StreamingUIRenderer.cjs.map +1 -1
- package/dist/components/StreamingUIRenderer.d.ts +25 -5
- package/dist/components/StreamingUIRenderer.d.ts.map +1 -1
- package/dist/components/StreamingUIRenderer.js +84 -197
- package/dist/components/StreamingUIRenderer.js.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components.cjs +3 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +2 -0
- package/dist/components.d.ts +2 -0
- package/dist/components.js +3 -0
- package/dist/components.js.map +1 -1
- package/dist/context/MCPUIStringsContext.cjs +38 -0
- package/dist/context/MCPUIStringsContext.cjs.map +1 -0
- package/dist/context/MCPUIStringsContext.d.ts +95 -0
- package/dist/context/MCPUIStringsContext.d.ts.map +1 -0
- package/dist/context/MCPUIStringsContext.js +38 -0
- package/dist/context/MCPUIStringsContext.js.map +1 -0
- package/dist/index.cjs +8 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-ui-spec/dist/schemas.cjs +103 -0
- package/dist/mcp-ui-spec/dist/schemas.cjs.map +1 -1
- package/dist/mcp-ui-spec/dist/schemas.js +103 -0
- package/dist/mcp-ui-spec/dist/schemas.js.map +1 -1
- package/docs/briefs/ROADMAP-opendata-macro-mcpui.md +912 -0
- package/package.json +17 -5
- package/src/adapters/connector.test.ts +165 -0
- package/src/adapters/connector.ts +234 -0
- package/src/adapters/index.ts +24 -0
- package/src/components/ExpandableWrapper.test.tsx +5 -2
- package/src/components/ExpandableWrapper.tsx +8 -6
- package/src/components/FeedbackInline.test.tsx +6 -3
- package/src/components/FeedbackInline.tsx +8 -6
- package/src/components/PresentationFeedback.test.tsx +163 -0
- package/src/components/PresentationFeedback.tsx +326 -0
- package/src/components/StreamingUIRenderer.parity.test.tsx +158 -0
- package/src/components/StreamingUIRenderer.tsx +42 -166
- package/src/components/index.ts +10 -0
- package/src/context/MCPUIStringsContext.test.tsx +116 -0
- package/src/context/MCPUIStringsContext.tsx +128 -0
- package/src/index.ts +27 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vite.config.ts +1 -0
|
@@ -0,0 +1,912 @@
|
|
|
1
|
+
# Roadmap MCPUI - OpenData, macros et rendu dynamique
|
|
2
|
+
|
|
3
|
+
Date: 2026-05-21
|
|
4
|
+
Statut: brief de sprint / base pour `/goal`
|
|
5
|
+
Repos concernes: `mcp-ui`, puis integration hot dans Deposium Solid + MCPs
|
|
6
|
+
|
|
7
|
+
## Final Decision Summary (etat verrouille - 2026-05-21)
|
|
8
|
+
|
|
9
|
+
> **Etat final : D1-D10 + R1-R4 sont contraignants.** Les notes anterieures
|
|
10
|
+
> "A clarifier", "DECISION REQUISE", "Points a trancher" sont **historiques**
|
|
11
|
+
> — conservees pour tracabilite — et ne s'appliquent QUE si elles ne sont
|
|
12
|
+
> pas recouvertes par D1-D10 ou R1-R4. En cas de divergence : R1-R4 prime
|
|
13
|
+
> sur D1-D10, qui prime sur tout le reste.
|
|
14
|
+
|
|
15
|
+
Aucun blocage ne subsiste pour le `/goal`. Ordre d'execution arrete :
|
|
16
|
+
|
|
17
|
+
1. `StreamingUIRenderer` parity (D3 / Gap 1 / Phase 3)
|
|
18
|
+
2. `MCPUIStringsProvider` + audit des strings en dur (D2 / R4)
|
|
19
|
+
3. `mcp-ui-spec` : contrat `ConnectorDynamicResultV1` + fixtures (R1)
|
|
20
|
+
4. `PresentationFeedback` (R3 / Phase 4)
|
|
21
|
+
5. Adapters opt-in `mcp-ui-solid/adapters` (D5 / D6 / R1)
|
|
22
|
+
|
|
23
|
+
Etapes 1-4 sont 100 % internes au repo `mcp-ui` ; l'integration MCPs +
|
|
24
|
+
Solid suit l'etape 5.
|
|
25
|
+
|
|
26
|
+
## Objectif
|
|
27
|
+
|
|
28
|
+
Faire de MCPUI la couche de rendu generique pour les resultats de connecteurs et de macros semi-automatiques, sans specialiser le coeur sur `data.gouv.fr`.
|
|
29
|
+
|
|
30
|
+
Le cas pilote reste OpenData / data.gouv.fr: une question comme `immobilier toulouse`, `dpe montpellier`, `pollution lyon`, `emploi nantes`, `ecoles marseille` doit pouvoir produire:
|
|
31
|
+
|
|
32
|
+
- un resultat principal lisible: table, chart, metric, map, source cards;
|
|
33
|
+
- des datasets et ressources connexes;
|
|
34
|
+
- des actions de poursuite: comparer, filtrer, charger un dataset, lancer une macro;
|
|
35
|
+
- des questions HITL seulement quand elles ajoutent de la valeur;
|
|
36
|
+
- du feedback de presentation, separe du feedback de qualite de reponse.
|
|
37
|
+
|
|
38
|
+
## Constats apres lecture du composant
|
|
39
|
+
|
|
40
|
+
MCPUI a deja la plupart des briques necessaires. Il faut eviter de recreer des composants qui existent deja.
|
|
41
|
+
|
|
42
|
+
| Besoin produit | Primitive MCPUI existante | Decision |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| Tables immobilier / DPE / resultats API | `UIResourceRenderer` avec `type: "table"`, `DataPreviewSection` dans `ScratchpadPanel` | Reutiliser. Ajouter des presets/adapters, pas un nouveau renderer. |
|
|
45
|
+
| Graphiques comparatifs entre villes | `type: "chart"` avec bar/line/doughnut/scatter | Reutiliser. Ajouter conventions de dataset Chart.js par domaine. |
|
|
46
|
+
| Cartographie / geojson / zones | `type: "map"`, section scratchpad `map` | Reutiliser pour pollution, DPE geo, equipements publics. |
|
|
47
|
+
| Datasets trouves / sources / documents | `link`, `artifact`, `carousel`, `grid` sont des types `UIResourceRenderer`. ATTENTION: `source_card` existe seulement comme section `ScratchpadPanel`, PAS comme type `UIResourceRenderer`. | Reutiliser pour le scratchpad. Pour un `UILayout` de chat: soit composer un `grid`/`carousel` de `link`/`artifact`, soit ajouter un renderer `source_card` a `UIResourceRenderer` (vrai item de scope, pas "reutiliser"). |
|
|
48
|
+
| Actions utilisateur | `action-group`, `MCPActionProvider`, `useAction` | Reutiliser pour `load_dataset`, `compare_city`, `run_macro`, `ask_followup`. |
|
|
49
|
+
| Choix bloquant dans le chat | `ChatPrompt` (`choice`, `confirm`, `form`) | Reutiliser. Pas de nouveau widget HITL bloquant. |
|
|
50
|
+
| Elicitation MCP standard | `ElicitationForm` + `elicitationToPromptConfig()` | Reutiliser pour les tools compatibles MCP `elicitation/create`. |
|
|
51
|
+
| Suivi d'un run macro/agent | `ScratchpadPanel`, sections `agent_card`, `stepper`, `split_stepper`, `action`, `data_preview` | Reutiliser comme workspace de macro. Ajouter un adapter macro -> scratchpad. |
|
|
52
|
+
| Feedback non bloquant | `FeedbackInline`, section scratchpad `feedback`, telemetry | Reutiliser, mais enrichir le schema de feedback de presentation. |
|
|
53
|
+
| Streaming de composants | `StreamingUIRenderer` | A ameliorer: rendu actuel trop simplifie, il ne delegue pas au vrai `UIResourceRenderer`. |
|
|
54
|
+
|
|
55
|
+
## Principe d'architecture
|
|
56
|
+
|
|
57
|
+
MCPUI doit rester une bibliotheque de rendu et d'interaction, pas un orchestrateur metier.
|
|
58
|
+
|
|
59
|
+
Responsabilites:
|
|
60
|
+
|
|
61
|
+
- MCPs/connecteurs: cherchent les donnees, produisent des payloads structures, exposent les actions possibles.
|
|
62
|
+
- Macros YAML / runtime agent: orchestrent plusieurs tools, gerent le plan, l'etat et les questions.
|
|
63
|
+
- Deposium Solid: decide quand afficher chat, scratchpad, prompt, feedback, et persiste les logs.
|
|
64
|
+
- MCPUI: rend les payloads, expose des primitives d'action/HITL/telemetry, reste agnostique de data.gouv.fr.
|
|
65
|
+
|
|
66
|
+
## Cible fonctionnelle
|
|
67
|
+
|
|
68
|
+
### 1. Rendu dynamique par intention
|
|
69
|
+
|
|
70
|
+
Un resultat connecteur devrait pouvoir emettre un layout riche:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
type ConnectorDynamicResultV1 = {
|
|
74
|
+
schemaVersion: 'connector-dynamic-result/v1'
|
|
75
|
+
connectorId: string
|
|
76
|
+
toolName: string
|
|
77
|
+
query: string
|
|
78
|
+
/** Stable key for persisted presentation feedback; required when feedback is stored. */
|
|
79
|
+
queryHash?: string
|
|
80
|
+
/** Normalized user intent, e.g. "real_estate_city" or "dpe_city". */
|
|
81
|
+
intent?: string
|
|
82
|
+
primary: UIComponent | UILayout
|
|
83
|
+
supplemental?: UIComponent[]
|
|
84
|
+
actions?: ConnectorAction[]
|
|
85
|
+
followups?: ConnectorFollowup[]
|
|
86
|
+
renderHints?: {
|
|
87
|
+
preferredLayout?: 'table' | 'cards' | 'bar' | 'line' | 'doughnut' | 'map' | 'metric'
|
|
88
|
+
/** Data domain, not intent: drives layout presets and examples. */
|
|
89
|
+
domain?: 'real_estate' | 'dpe' | 'pollution' | 'employment' | 'education' | 'health' | 'generic'
|
|
90
|
+
confidence?: number
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Le coeur MCPUI n'a pas besoin de connaitre ce type dans un premier temps. Le sprint doit fournir des adapters d'exemple qui transforment ce contrat en `UILayout`, `ScratchpadState` et `ChatPromptConfig`.
|
|
96
|
+
|
|
97
|
+
Contrat finalise pour le sprint:
|
|
98
|
+
|
|
99
|
+
- le type canonique vit dans `mcp-ui-spec` (voir R1), pas dans `mcp-ui-solid` core;
|
|
100
|
+
- `schemaVersion` est obligatoire et string namespacee;
|
|
101
|
+
- `queryHash` est optionnel dans le payload, mais requis des qu'un feedback de presentation est persiste;
|
|
102
|
+
- calcul recommande: `hash(connectorId + toolName + normalizedIntent)`, pas la query brute;
|
|
103
|
+
- `intent` = intention utilisateur normalisee; `renderHints.domain` = domaine de donnees pour les presets de rendu.
|
|
104
|
+
|
|
105
|
+
### 2. Cas data.gouv.fr a couvrir
|
|
106
|
+
|
|
107
|
+
Le but n'est pas seulement de choisir entre documents locaux et connecteur. Une fois le connecteur choisi, il faut enrichir le rendu selon la thematique.
|
|
108
|
+
|
|
109
|
+
| Scenario | Rendu principal | Rendu complementaire | Actions/HITL possibles |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| `immobilier toulouse` | table prix/m2 ou mutations si API disponible | datasets data.gouv connexes, source cards DVF, metrics medianes | comparer avec une ville, filtrer ancien/neuf, afficher evolution, charger dataset connexe |
|
|
112
|
+
| `immobilier montpellier paris` | bar chart + table comparative | cartes datasets par ville, liens ressources | demander periode, ajouter 3e ville, basculer table/chart |
|
|
113
|
+
| `dpe montpellier` | table DPE + metrics classes A-G | doughnut par classe, map si geo disponible, datasets ADEME/data.gouv | choisir logement/periode, comparer quartier/ville |
|
|
114
|
+
| `pollution lyon` | line/bar chart selon serie temporelle | map stations, table mesures, source cards | choisir polluant, periode, seuil reglementaire |
|
|
115
|
+
| `emploi nantes` | metrics taux/volume + table | chart evolution, datasets INSEE/DARES | choisir secteur, age, periode |
|
|
116
|
+
| `education marseille` | table etablissements/indicateurs | map, metrics, datasets ministere | filtrer public/prive, niveau, comparer commune |
|
|
117
|
+
| `clinicaltrials cancer pancreas` | table essais | cards essais, metrics phases/statuts | filtrer phase, statut, pays, ouvrir essai |
|
|
118
|
+
| connecteur vide / partiel / en erreur | etat vide ou carte d'erreur explicite (jamais de disparition silencieuse du rendu) | suggestion de reformulation, datasets connexes si dispo | reessayer, elargir la recherche, basculer sur documents locaux |
|
|
119
|
+
|
|
120
|
+
Ces scenarios doivent etre des fixtures/examples MCPUI: ils servent a tester les renderers et a guider les prompts/connecteurs, sans hardcoder data.gouv.fr dans la librairie.
|
|
121
|
+
|
|
122
|
+
### 3. Macros semi-automatiques
|
|
123
|
+
|
|
124
|
+
Les macros YAML restent une excellente source de verite pour decrire des scenarios reproductibles:
|
|
125
|
+
|
|
126
|
+
- sequence de tools MCP;
|
|
127
|
+
- variables d'entree;
|
|
128
|
+
- conditions de branchement;
|
|
129
|
+
- questions HITL;
|
|
130
|
+
- rendu attendu;
|
|
131
|
+
- checks anti-regression.
|
|
132
|
+
|
|
133
|
+
Mais dans le chat reel, le runtime doit pouvoir gerer SSE, interruptions, erreurs, reprise, scratchpad et action callbacks. La bonne cible n'est donc pas "macros OU agent runtime", mais:
|
|
134
|
+
|
|
135
|
+
1. YAML macro = plan declaratif testable.
|
|
136
|
+
2. Runtime agent/chat = execution conversationnelle.
|
|
137
|
+
3. MCPUI = rendu de l'etat macro et des demandes HITL.
|
|
138
|
+
|
|
139
|
+
Le declenchement `/macros macro-test-1` dans le chat reste pertinent: il doit lancer le runtime avec un `macroId`, pas bypasser la couche chat/SSE.
|
|
140
|
+
|
|
141
|
+
## Adapters a construire
|
|
142
|
+
|
|
143
|
+
### A. `macroRunToScratchpadState()`
|
|
144
|
+
|
|
145
|
+
Convertit un run macro en `ScratchpadState`.
|
|
146
|
+
|
|
147
|
+
Mapping recommande:
|
|
148
|
+
|
|
149
|
+
| Etat macro | Section Scratchpad |
|
|
150
|
+
|---|---|
|
|
151
|
+
| macro metadata / agent actif | `agent_card` |
|
|
152
|
+
| etapes sequentielles | `stepper` ou `steps` |
|
|
153
|
+
| etapes paralleles | `split_stepper` |
|
|
154
|
+
| question utilisateur | `prompt` ou `form` |
|
|
155
|
+
| resultats de tool | `data_preview`, `chart`, `map`, `source_card` |
|
|
156
|
+
| erreur retryable | `error` + `action` retry |
|
|
157
|
+
| decision finale | `verified_text` ou `briefing_diff` selon le cas |
|
|
158
|
+
|
|
159
|
+
Pas besoin d'un `MacroRunCard` au depart: `agent_card` + `stepper` couvrent deja le besoin.
|
|
160
|
+
|
|
161
|
+
### B. `macroInterrogationToChatPromptConfig()`
|
|
162
|
+
|
|
163
|
+
Convertit une question macro/HITL en `ChatPromptConfig` ou `ElicitationEvent`.
|
|
164
|
+
|
|
165
|
+
Mapping recommande:
|
|
166
|
+
|
|
167
|
+
| Type macro souhaite | MCPUI actuel |
|
|
168
|
+
|---|---|
|
|
169
|
+
| choix simple | `ChatPrompt` type `choice` |
|
|
170
|
+
| confirmation | `ChatPrompt` type `confirm` |
|
|
171
|
+
| texte/date/nombre/select/multiselect | `ChatPrompt` type `form` |
|
|
172
|
+
| schema MCP officiel | `ElicitationForm` |
|
|
173
|
+
| choix avec confiance/source | `choice` + `metadata` + `optionRenderer` |
|
|
174
|
+
|
|
175
|
+
Point a clarifier dans le sprint: le host gere l'annulation, les timeouts et la re-entrance. MCPUI sait rendre le prompt, mais ne doit pas devenir gestionnaire global de lifecycle.
|
|
176
|
+
|
|
177
|
+
### C. `connectorResultToUILayout()`
|
|
178
|
+
|
|
179
|
+
Adapter generique qui convertit un resultat connecteur en `UILayout`.
|
|
180
|
+
|
|
181
|
+
Regles de base:
|
|
182
|
+
|
|
183
|
+
- donnees tabulaires -> `table`;
|
|
184
|
+
- comparaison categorical -> `chart` type `bar`;
|
|
185
|
+
- serie temporelle -> `chart` type `line`;
|
|
186
|
+
- distribution -> `chart` type `doughnut`;
|
|
187
|
+
- geographie -> `map`;
|
|
188
|
+
- liens/datasets -> `link`, `artifact`, `carousel` ou `grid` (`source_card` n'est PAS un type `UIResourceRenderer`, cf. Constats — ne pas l'emettre dans un `UILayout`);
|
|
189
|
+
- actions de suite -> `action-group`.
|
|
190
|
+
|
|
191
|
+
Ce composant doit accepter des `renderHints`, mais ne doit jamais sacrifier les donnees brutes: si un chart est affiche, garder une table exportable en complement.
|
|
192
|
+
|
|
193
|
+
### D. `connectorActionsToActionGroup()`
|
|
194
|
+
|
|
195
|
+
Convertit les actions proposes par un connecteur en `action-group`.
|
|
196
|
+
|
|
197
|
+
Exemples:
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"type": "action-group",
|
|
202
|
+
"params": {
|
|
203
|
+
"title": "Continuer l'analyse",
|
|
204
|
+
"actions": [
|
|
205
|
+
{
|
|
206
|
+
"label": "Comparer avec Paris",
|
|
207
|
+
"action": "tool-call",
|
|
208
|
+
"toolName": "datagouv.search",
|
|
209
|
+
"params": { "query": "immobilier paris" }
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"label": "Lancer la macro DVF",
|
|
213
|
+
"action": "tool-call",
|
|
214
|
+
"toolName": "macros.run",
|
|
215
|
+
"params": { "macroId": "opendata.dvf_city_compare", "city": "Toulouse" }
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Le host fournit le `MCPActionProvider.executor`. MCPUI ne doit pas appeler directement le backend.
|
|
223
|
+
|
|
224
|
+
## Gaps MCPUI reels
|
|
225
|
+
|
|
226
|
+
### Gap 1 - Parite du streaming
|
|
227
|
+
|
|
228
|
+
`StreamingUIRenderer` valide les composants streamés mais utilise aujourd'hui un renderer inline simplifie: type, titre, metric basique. Il ne rend pas les tables/charts/maps avec la meme fidelite que `UIResourceRenderer`.
|
|
229
|
+
|
|
230
|
+
Verifie dans le code: le flux SSE livre des composants COMPLETS un par un (`UIComponent[]`), pas de rows/datasets partiels. La delegation `StreamingComponentRenderer -> UIResourceRenderer` est donc directe — pas besoin de bufferiser un composant incomplet. Seul reste a soigner: le swap skeleton -> composant reel ne doit pas changer la hauteur du conteneur (sinon cascade de reflow / scroll-reset, cf. bug "sidebar scroll reset" Deposium).
|
|
231
|
+
|
|
232
|
+
Action roadmap:
|
|
233
|
+
|
|
234
|
+
- remplacer `StreamingComponentRenderer` par une delegation a `UIResourceRenderer` pour chaque composant recu;
|
|
235
|
+
- conserver progress/skeleton/animation dans `StreamingUIRenderer`;
|
|
236
|
+
- partager `errorMode`, telemetry et validation avec le rendu statique;
|
|
237
|
+
- ajouter un test de parite: le meme `UIComponent` table/chart/map doit avoir le meme rendu fonctionnel en statique et en streaming.
|
|
238
|
+
|
|
239
|
+
### Gap 2 - Feedback de presentation plus riche
|
|
240
|
+
|
|
241
|
+
`FeedbackInline` est volontairement simple: thumbs up/down, non bloquant, persistence cote consumer. Pour les connecteurs, on a besoin de qualifier le probleme de presentation:
|
|
242
|
+
|
|
243
|
+
- `too_raw`
|
|
244
|
+
- `wrong_columns`
|
|
245
|
+
- `wrong_chart`
|
|
246
|
+
- `missing_dataset_context`
|
|
247
|
+
- `bad_grouping`
|
|
248
|
+
- `wrong_unit`
|
|
249
|
+
- `prefer_table`
|
|
250
|
+
- `prefer_chart`
|
|
251
|
+
- `prefer_map`
|
|
252
|
+
|
|
253
|
+
Action roadmap:
|
|
254
|
+
|
|
255
|
+
- ne pas remplacer `FeedbackInline`;
|
|
256
|
+
- ajouter un wrapper/example `PresentationFeedback` compose de `FeedbackInline` + `ChatPrompt`/`form` optionnel;
|
|
257
|
+
- standardiser le payload emis au host:
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
type ConnectorRenderFeedback = {
|
|
261
|
+
connectorId: string
|
|
262
|
+
toolName: string
|
|
263
|
+
renderKind?: string
|
|
264
|
+
layoutType?: string
|
|
265
|
+
queryHash?: string
|
|
266
|
+
feedbackValue: 'readable' | 'too_raw' | 'wrong_columns' | 'wrong_chart' | 'missing_context'
|
|
267
|
+
preferredLayout?: 'table' | 'cards' | 'bar' | 'line' | 'doughnut' | 'map' | 'metric'
|
|
268
|
+
missingFields?: string[]
|
|
269
|
+
selectedColumns?: string[]
|
|
270
|
+
wrongUnit?: string
|
|
271
|
+
wrongGrouping?: string
|
|
272
|
+
comment?: string
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
MCPUI expose l'evenement ou callback. Le host persiste dans sa table dediee.
|
|
277
|
+
|
|
278
|
+
### Gap 3 - Debug renderer selection
|
|
279
|
+
|
|
280
|
+
Le `AGENT-RENDERING-GUIDE.md` est tres utile, mais il manque un mode debug oriente integration connecteur.
|
|
281
|
+
|
|
282
|
+
Action roadmap:
|
|
283
|
+
|
|
284
|
+
- ajouter un exemple ou un helper affichant: `component.type`, renderer choisi, validation errors, fallback utilise, action callbacks attaches;
|
|
285
|
+
- exposer ces infos via telemetry ou debug overlay existant quand possible;
|
|
286
|
+
- documenter comment diagnostiquer: composant absent, params invalides, action non branchee, prompt jamais resolu.
|
|
287
|
+
|
|
288
|
+
### Gap 4 - Presets de domaine sans couplage metier
|
|
289
|
+
|
|
290
|
+
On veut aider data.gouv.fr, mais sans polluer MCPUI.
|
|
291
|
+
|
|
292
|
+
Action roadmap:
|
|
293
|
+
|
|
294
|
+
- creer des examples/fixtures `opendata-real-estate`, `opendata-dpe`, `opendata-pollution`, `opendata-employment`, `clinicaltrials`;
|
|
295
|
+
- fournir des `renderHints` et adapters demo;
|
|
296
|
+
- garder les vrais appels API hors MCPUI.
|
|
297
|
+
|
|
298
|
+
## Non-regressions attendues
|
|
299
|
+
|
|
300
|
+
1. Une table existante continue a trier/exporter/paginer apres passage par `connectorResultToUILayout()`.
|
|
301
|
+
2. Un chart comparatif entre deux villes conserve la table source en complement (repliee par defaut — ne pas doubler la hauteur du rendu dans une bulle de chat).
|
|
302
|
+
3. Une reponse streaming avec table/chart/map rend les vrais composants, pas une carte generique.
|
|
303
|
+
4. Un `action-group` declenche `MCPActionProvider.executor` avec `toolName`, `params`, `macroId` et `spaceIds` conserves.
|
|
304
|
+
5. Une question HITL schema MCP passe par `ElicitationForm` et renvoie un payload `accept` conforme.
|
|
305
|
+
6. Une question choix simple passe par `ChatPrompt` type `choice`, avec metadata conservee.
|
|
306
|
+
7. Le feedback de presentation est non bloquant: echec de persistence ne casse pas le rendu.
|
|
307
|
+
8. Les examples data.gouv.fr restent des fixtures: aucune dependance runtime dure a data.gouv.fr dans MCPUI.
|
|
308
|
+
9. Les composants `ScratchpadPanel`, `ChatPrompt`, `UIResourceRenderer` restent utilisables independamment.
|
|
309
|
+
10. Les tests couvrent au moins immobilier, DPE, pollution et clinicaltrials.
|
|
310
|
+
11. Un resultat connecteur vide, partiel ou en erreur rend un etat explicite (etat vide / carte d'erreur), jamais une disparition silencieuse du rendu.
|
|
311
|
+
|
|
312
|
+
## Plan de sprint propose
|
|
313
|
+
|
|
314
|
+
### Phase 0 - Documentation et fixtures
|
|
315
|
+
|
|
316
|
+
- Ajouter ce brief et des examples JSON de layouts OpenData.
|
|
317
|
+
- Ajouter une page courte: "Connector rendering cookbook".
|
|
318
|
+
- Documenter le mapping macro -> scratchpad -> prompt.
|
|
319
|
+
|
|
320
|
+
Livrable: examples sans changement fonctionnel majeur.
|
|
321
|
+
|
|
322
|
+
### Phase 1 - Adapters generiques
|
|
323
|
+
|
|
324
|
+
- Ajouter des helpers generiques si acceptables dans MCPUI:
|
|
325
|
+
- `connectorResultToUILayout()`
|
|
326
|
+
- `connectorActionsToActionGroup()`
|
|
327
|
+
- `macroRunToScratchpadState()`
|
|
328
|
+
- `macroInterrogationToChatPromptConfig()`
|
|
329
|
+
- Sinon les placer dans un package/example host pour ne pas charger le coeur.
|
|
330
|
+
|
|
331
|
+
Livrable: un connecteur peut emettre un resultat structure et obtenir table/chart/source/action sans logique UI custom.
|
|
332
|
+
|
|
333
|
+
### Phase 2 - Chat/HITL/macros
|
|
334
|
+
|
|
335
|
+
- Brancher `/macros <id>` sur le runtime chat avec `macroId`.
|
|
336
|
+
- Rendre l'etat dans `ScratchpadPanel`.
|
|
337
|
+
- Rendre les questions via `ChatPrompt` ou `ElicitationForm`.
|
|
338
|
+
- Declencher les outils via `MCPActionProvider`.
|
|
339
|
+
|
|
340
|
+
Livrable: une macro OpenData peut chercher, demander une precision, afficher une table/chart, puis proposer une action suivante.
|
|
341
|
+
|
|
342
|
+
### Phase 3 - Streaming parity
|
|
343
|
+
|
|
344
|
+
- Refactor `StreamingUIRenderer` pour deleguer le rendu de chaque composant a `UIResourceRenderer`.
|
|
345
|
+
- Garder progress/skeleton/metadata.
|
|
346
|
+
- Ajouter tests de parite.
|
|
347
|
+
|
|
348
|
+
Livrable: plus de regression visuelle entre reponse statique et SSE.
|
|
349
|
+
|
|
350
|
+
### Phase 4 - Feedback et observabilite
|
|
351
|
+
|
|
352
|
+
- Ajouter un wrapper de feedback de presentation.
|
|
353
|
+
- Standardiser le payload feedback.
|
|
354
|
+
- Relier callbacks/telemetry au host.
|
|
355
|
+
- Ajouter un debug mode pour comprendre pourquoi tel renderer a ete choisi.
|
|
356
|
+
|
|
357
|
+
Livrable: le HITL peut aussi corriger la mise en forme, pas seulement la qualite de reponse.
|
|
358
|
+
|
|
359
|
+
## Points a trancher pendant le `/goal`
|
|
360
|
+
|
|
361
|
+
1. Les adapters doivent-ils vivre dans MCPUI core, dans `examples`, ou dans Deposium Solid?
|
|
362
|
+
2. Est-ce que `StreamingUIRenderer` peut importer `UIResourceRenderer` sans cycle ni bundle cost excessif?
|
|
363
|
+
3. Faut-il creer un type `macro_run` dans `ScratchpadSection`, ou rester sur `agent_card` + `stepper`?
|
|
364
|
+
4. Le feedback de presentation doit-il etre un composant exporte ou un cookbook de composition?
|
|
365
|
+
5. Les macros YAML doivent-elles emettre directement `UILayout`, ou seulement un contrat metier transforme par le host?
|
|
366
|
+
6. Le feedback de presentation FERME-t-il la boucle (re-render immediat dans le layout choisi) ou se contente-t-il de logger pour ameliorer les prompts futurs? Le livrable Phase 4 dit "corriger la mise en forme" — si c'est seulement logger, reformuler le livrable (logger != corriger).
|
|
367
|
+
7. Precedence de `preferredLayout` (present a 3 endroits): figer la chaine heuristique `connectorResultToUILayout()` (plancher) < `renderHints.preferredLayout` (connecteur) < feedback utilisateur persiste.
|
|
368
|
+
8. Schema de feedback: l'enum prose de Gap 2 (9 valeurs) et le type `ConnectorRenderFeedback.feedbackValue` (5 valeurs differentes) divergent, et melent verdict (`readable`/`too_raw`) et probleme precis (`wrong_columns`). Choisir un enum canonique unique, ou separer `verdict` + `problem[]`. Et: `FeedbackInlineContext` est deja extensible (`[key: string]: unknown`, porte deja `intent`/`confidenceBand`/`tags`) — rouler dessus plutot que creer un type parallele?
|
|
369
|
+
9. i18n: aucune plomberie locale dans les renderers — les labels sont des strings brutes dans les payloads. Fork a trancher: les labels d'action sont fournis (localises) par le connecteur, qui doit alors recevoir la locale utilisateur? Ou MCPUI fournit des cles? Le cahier des charges co-membres impose EN/FR sur tout nouveau libelle.
|
|
370
|
+
10. Macro scratchpad vs prompt HITL: dans Deposium les panneaux ephemeres au-dessus du chat input (HITL `chat_prompt`, suggestion chips, scratchpad) sont mutuellement exclusifs via `chat-motion.ts`. Ou vit le scratchpad d'une macro pendant qu'elle pose une question HITL? Reste-t-il visible? A specifier sinon collision avec la regle d'exclusion existante.
|
|
371
|
+
11. `/macros <id>`: le parsing de la slash-command de chat input appartient a Deposium Solid (host, "decide quand afficher le chat"), pas a MCPUI. Confirmer et assigner le repo en Phase 2.
|
|
372
|
+
|
|
373
|
+
## Recommandation
|
|
374
|
+
|
|
375
|
+
Pour le sprint, commencer par un chemin minimal et robuste:
|
|
376
|
+
|
|
377
|
+
1. ne pas ajouter de nouveaux renderers metier;
|
|
378
|
+
2. creer des fixtures OpenData et macros;
|
|
379
|
+
3. ajouter les adapters generiques;
|
|
380
|
+
4. corriger la parite `StreamingUIRenderer` -> `UIResourceRenderer`;
|
|
381
|
+
5. brancher feedback de presentation via callbacks host.
|
|
382
|
+
|
|
383
|
+
Cela maximise la reutilisation de MCPUI existant et evite que data.gouv.fr devienne une dependance implicite du package.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Reponses agent MCPUI - clarifications architecture (2026-05-21)
|
|
388
|
+
|
|
389
|
+
> Ajoute par l'agent MCPUI a la demande de l'equipe. Repond aux 3 points
|
|
390
|
+
> qui touchent l'architecture du repo `mcp-ui` lui-meme (pas seulement le
|
|
391
|
+
> brief produit). Verifications faites sur le code reel a la date ci-dessus
|
|
392
|
+
> (`mcp-ui-solid@6.5.0`). Les arbitrages encore ouverts sont marques
|
|
393
|
+
> **DECISION REQUISE** avec une valeur par defaut si pas de reponse.
|
|
394
|
+
|
|
395
|
+
### Q1 (point 6) - Boucle de feedback : re-render ou log seul ?
|
|
396
|
+
|
|
397
|
+
**Verifie dans le code** (`src/components/FeedbackInline.tsx`) :
|
|
398
|
+
`FeedbackInline` est un callback fire-and-forget pur. Il capture la note,
|
|
399
|
+
appelle `onSubmit(rating, context)`, bascule en etat "submitted" optimiste.
|
|
400
|
+
Aucune capacite de re-render, il ne touche jamais au layout.
|
|
401
|
+
|
|
402
|
+
**Reponse** :
|
|
403
|
+
|
|
404
|
+
- Le **mecanisme** de re-render existe deja : les renderers MCPUI sont des
|
|
405
|
+
fonctions pures de leurs props. Si `content` (ou un prop derive de
|
|
406
|
+
`renderHints`) change, Solid re-rend reactivement. Aucune plomberie
|
|
407
|
+
nouvelle necessaire pour "re-render".
|
|
408
|
+
- Ce qui manque n'est pas le rendu, c'est le **proprietaire de l'etat**
|
|
409
|
+
"l'utilisateur a choisi `prefer_map` pour ce layout". Cet etat n'existe
|
|
410
|
+
pas dans MCPUI aujourd'hui et n'y a pas sa place.
|
|
411
|
+
- **Recommandation** : MCPUI fournit la capacite (reactivite, deja la) mais
|
|
412
|
+
ne possede pas l'etat de feedback. Boucle cible :
|
|
413
|
+
`feedback control -> evenement -> HOST mappe vers nouveau preferredLayout
|
|
414
|
+
-> host re-execute connectorResultToUILayout() -> nouveau content descend
|
|
415
|
+
-> UIResourceRenderer re-rend`. MCPUI reste stateless, coherent avec
|
|
416
|
+
"librairie de rendu, pas orchestrateur".
|
|
417
|
+
- **Consequence Phase 4** : le livrable MCPUI se limite a
|
|
418
|
+
(a) payload/evenement de feedback enrichi ;
|
|
419
|
+
(b) garantir que `connectorResultToUILayout()` est PUR (re-execution
|
|
420
|
+
cheap, sans effet de bord) ;
|
|
421
|
+
(c) optionnel : un exemple `<PresentationFeedback>` montrant le cablage.
|
|
422
|
+
La correction de mise en forme se produit **cote host** (re-execution de
|
|
423
|
+
l'adapter), pas par mutation interne MCPUI. **Reformuler le livrable
|
|
424
|
+
Phase 4** : "permettre au host de corriger la mise en forme", pas
|
|
425
|
+
"MCPUI corrige".
|
|
426
|
+
|
|
427
|
+
**DECISION REQUISE** : voulez-vous que MCPUI livre EN PLUS un composant
|
|
428
|
+
controle stateful (ex. `<ConnectorResultRenderer>`) qui detient le signal
|
|
429
|
+
d'override en interne — DX plus simple cote host, mais met de l'etat
|
|
430
|
+
connecteur dans MCPUI ? Ou on reste sur adapter pur + etat cote host ?
|
|
431
|
+
_Defaut si pas de reponse : adapter pur + etat host (recommande)._
|
|
432
|
+
|
|
433
|
+
### Q2 (point 9) - i18n
|
|
434
|
+
|
|
435
|
+
**Verifie dans le code** : zero plomberie de locale dans les renderers.
|
|
436
|
+
Aucun prop `locale`, aucun catalogue de messages. Les labels de contenu
|
|
437
|
+
(titres, colonnes, labels d'action, questions de prompt) sont des strings
|
|
438
|
+
brutes passees dans les payloads — MCPUI les rend tels quels. MAIS MCPUI a
|
|
439
|
+
une poignee de strings "chrome" en dur, aujourd'hui un melange FR/EN
|
|
440
|
+
incoherent : `FeedbackInline` ("Merci !", "Note, on s'ameliore" — FR) ;
|
|
441
|
+
cartes d'erreur de validation + fallback `RenderContext` (EN) ; labels
|
|
442
|
+
toolbar `ExpandableWrapper` (Expand / Copy data / Download...).
|
|
443
|
+
|
|
444
|
+
**Reponse — la question se scinde en deux** :
|
|
445
|
+
|
|
446
|
+
1. **Labels de CONTENU (payload-driven)** : responsabilite du connecteur.
|
|
447
|
+
Le connecteur recoit la locale utilisateur et emet des labels deja
|
|
448
|
+
localises. => changement de signature cote **connecteur**, AUCUN
|
|
449
|
+
changement cote MCPUI. MCPUI n'a besoin d'aucun prop `locale` pour le
|
|
450
|
+
contenu payload-driven.
|
|
451
|
+
2. **Strings "chrome" internes a MCPUI** : seul i18n que MCPUI doit porter.
|
|
452
|
+
Un layer i18n complet (catalogue + prop `locale` filtre dans chaque
|
|
453
|
+
renderer) serait un changement transverse lourd touchant chaque
|
|
454
|
+
signature de renderer — **a eviter**. Solution bornee : un
|
|
455
|
+
`MCPUIStringsProvider` (context leger, sur le modele du telemetry
|
|
456
|
+
context deja en place) portant une map de strings plate, defaut EN, que
|
|
457
|
+
le host remplit en FR. Thread une seule fois, **aucun changement de
|
|
458
|
+
signature de renderer**.
|
|
459
|
+
|
|
460
|
+
=> Le mandat EN/FR co-membres est respecte sans i18n framework : contenu =
|
|
461
|
+
connecteur localise ; chrome = `MCPUIStringsProvider`.
|
|
462
|
+
|
|
463
|
+
**DECISION REQUISE** :
|
|
464
|
+
(a) confirmez le split contenu/chrome ci-dessus ;
|
|
465
|
+
(b) le `MCPUIStringsProvider` est-il dans le scope de CE sprint, ou differe ?
|
|
466
|
+
Il est additif et non bloquant — peut venir apres. _Sans lui, les strings
|
|
467
|
+
chrome restent le melange FR/EN actuel._
|
|
468
|
+
|
|
469
|
+
### Q3 (Gap 1) - StreamingUIRenderer delegue a UIResourceRenderer
|
|
470
|
+
|
|
471
|
+
**Verifie dans le code** :
|
|
472
|
+
|
|
473
|
+
- **Pas de cycle.** `UIResourceRenderer.tsx` n'importe PAS
|
|
474
|
+
`StreamingUIRenderer` (grep = 0 match). L'arete est strictement
|
|
475
|
+
unidirectionnelle.
|
|
476
|
+
- `StreamingUIRenderer` importe deja `type { ValidationErrorMode }` depuis
|
|
477
|
+
`./UIResourceRenderer` — import type-only, efface a la compilation.
|
|
478
|
+
- Confirme : le flux SSE livre des `UIComponent` COMPLETS un par un
|
|
479
|
+
(`StreamingComponentRenderer` lignes 165-198 est bien un renderer
|
|
480
|
+
simplifie "type + titre + metric basique"). La delegation est triviale
|
|
481
|
+
au niveau logique.
|
|
482
|
+
- Le repo a DEJA le pattern exact pour ce besoin : `RenderContext` /
|
|
483
|
+
`RenderProvider` / `useRenderContext` (`src/components/RenderContext.tsx`),
|
|
484
|
+
construits pour casser le cycle `UIResourceRenderer <-> Carousel/Grid`.
|
|
485
|
+
|
|
486
|
+
=> Le cycle d'import **n'est pas un risque**. Le vrai arbitrage est le
|
|
487
|
+
**bundle cost** :
|
|
488
|
+
|
|
489
|
+
- **Approche A — import valeur direct** `import { UIResourceRenderer }` :
|
|
490
|
+
simple. Mais `UIResourceRenderer.tsx` (~1850 lignes) importe
|
|
491
|
+
statiquement TOUS les renderers (Table, Chart wrapper, Map, Graph, Code,
|
|
492
|
+
Carousel, Gallery, Video, Form, Modal, ActionGroup, Footer, Artifact,
|
|
493
|
+
Grid). Les peer-deps lourdes (chart.js, @antv/g6, leaflet) sont en
|
|
494
|
+
`import()` dynamique => elles ne comptent PAS. Mais la logique de
|
|
495
|
+
dispatch + DOMPurify + marked + les renderers legers sont statiques. Le
|
|
496
|
+
`size-limit` a un budget "Streaming renderer" de **30 KB** qui sautera
|
|
497
|
+
presque surement. Approche A => relever ce budget (estime ~80-120 KB).
|
|
498
|
+
- **Approche B — injection via `RenderContext`** : `StreamingUIRenderer`
|
|
499
|
+
consomme `useRenderContext()`, le host l'enveloppe dans un
|
|
500
|
+
`RenderProvider`. Bundle streaming reste mince (zero import statique du
|
|
501
|
+
set de renderers). Inconvenient : `StreamingUIRenderer` ne rend les
|
|
502
|
+
vrais composants QUE monte dans un `RenderProvider` ; en standalone il
|
|
503
|
+
tombe sur le placeholder "cannot be rendered outside UIResourceRenderer".
|
|
504
|
+
- **Approche C — hybride** : prop optionnel `renderComponent`, defaut
|
|
505
|
+
`RenderContext`, sinon `import()` paresseux de `UIResourceRenderer`.
|
|
506
|
+
Plus flexible, plus de code.
|
|
507
|
+
|
|
508
|
+
**Recommandation** : Approche A + montee honnete du budget `size-limit`.
|
|
509
|
+
En pratique deposium rend le streaming DANS un chat qui utilise deja
|
|
510
|
+
`UIResourceRenderer` — le set de renderers est deja dans le bundle, le cout
|
|
511
|
+
marginal reel est quasi nul. Le budget 30 KB est une mesure d'isolation
|
|
512
|
+
synthetique, pas le cout reel chez le consommateur.
|
|
513
|
+
|
|
514
|
+
**DECISION REQUISE** : approuvez-vous Approche A (+ budget `size-limit`
|
|
515
|
+
releve, ex. 120 KB, documente au CHANGELOG) ? Si "streaming importable seul
|
|
516
|
+
en < 30 KB" est un vrai requirement produit, alors Approche B — mais il
|
|
517
|
+
faut accepter que `StreamingUIRenderer` standalone hors `RenderProvider`
|
|
518
|
+
ne rende plus rien d'utile. _Defaut si pas de reponse : Approche A._
|
|
519
|
+
|
|
520
|
+
### Questions transverses supplementaires (agent MCPUI)
|
|
521
|
+
|
|
522
|
+
Trois points qui touchent aussi l'architecture du repo et meritent une
|
|
523
|
+
reponse avant le `/goal` :
|
|
524
|
+
|
|
525
|
+
- **Q4 — `schemaVersion` obligatoire.** Le brief (§1) propose d'ajouter
|
|
526
|
+
`schemaVersion` au contrat `ConnectorDynamicResult`. Recommandation :
|
|
527
|
+
le rendre **obligatoire des le premier payload**, pas optionnel. Un champ
|
|
528
|
+
de versionnement optionnel n'est jamais rempli par les vieux emetteurs —
|
|
529
|
+
exactement le cas qu'il doit couvrir. Format propose : `schemaVersion: 1`
|
|
530
|
+
(entier, incremente sur breaking change).
|
|
531
|
+
- **Q5 — purete de `connectorResultToUILayout()`.** Pour que la boucle Q1
|
|
532
|
+
fonctionne, cet adapter DOIT etre une fonction pure deterministe (memes
|
|
533
|
+
entrees -> meme `UILayout`, zero acces reseau / horloge / random).
|
|
534
|
+
Confirmez que c'est acceptable comme contrainte de design (sinon la
|
|
535
|
+
re-execution sur feedback n'est pas sure).
|
|
536
|
+
- **Q6 — emplacement des adapters** (point 1 des "Points a trancher").
|
|
537
|
+
Recommandation agent MCPUI : sous-chemin dedie
|
|
538
|
+
(`@seed-ship/mcp-ui-solid/adapters`) ou package `examples/`, **PAS le
|
|
539
|
+
core**. Raison : les adapters encodent des conventions de mapping
|
|
540
|
+
(table/bar/line par domaine) qui evolueront plus vite que le core et ne
|
|
541
|
+
doivent pas peser sur le bundle des consommateurs qui n'en veulent pas.
|
|
542
|
+
Le core exporte les primitives ; les adapters sont une couche opt-in.
|
|
543
|
+
|
|
544
|
+
## Decisions apres clarification MCPUI
|
|
545
|
+
|
|
546
|
+
Cette section verrouille les arbitrages avant lancement du `/goal`. Les points ci-dessous priment sur les questions ouvertes precedentes quand ils les recouvrent.
|
|
547
|
+
|
|
548
|
+
### D1 - Boucle feedback de presentation
|
|
549
|
+
|
|
550
|
+
Decision: **adapter pur + etat host**.
|
|
551
|
+
|
|
552
|
+
MCPUI ne possede pas l'etat utilisateur du type "prefer_map", "prefer_table" ou "wrong_columns". MCPUI fournit la capacite d'interaction et le callback structure. Le host conserve l'etat, persiste le feedback, puis re-execute l'adapter avec les preferences applicables.
|
|
553
|
+
|
|
554
|
+
Implications:
|
|
555
|
+
|
|
556
|
+
- `FeedbackInline` reste fire-and-forget.
|
|
557
|
+
- Le re-render est declenche par changement de `content` cote host, pas par mutation interne MCPUI.
|
|
558
|
+
- `connectorResultToUILayout()` reste pur: memes entrees, meme sortie.
|
|
559
|
+
- Le livrable Phase 4 doit etre formule comme: "permettre au host de corriger la presentation", pas "MCPUI corrige la presentation".
|
|
560
|
+
|
|
561
|
+
Chaine de priorite des layouts:
|
|
562
|
+
|
|
563
|
+
1. default adapter;
|
|
564
|
+
2. `renderHints.preferredLayout` fourni par le connecteur;
|
|
565
|
+
3. feedback utilisateur persiste;
|
|
566
|
+
4. demande explicite dans le prompt courant.
|
|
567
|
+
|
|
568
|
+
### D2 - i18n
|
|
569
|
+
|
|
570
|
+
Decision: **split contenu metier / chrome MCPUI**.
|
|
571
|
+
|
|
572
|
+
- Les labels metier viennent du connecteur ou de l'adapter, dans la locale utilisateur transmise par le host.
|
|
573
|
+
- Les strings de chrome MCPUI passent par un provider leger, sans framework i18n lourd.
|
|
574
|
+
|
|
575
|
+
Livrable recommande:
|
|
576
|
+
|
|
577
|
+
```ts
|
|
578
|
+
type MCPUIStrings = {
|
|
579
|
+
retry: string
|
|
580
|
+
cancel: string
|
|
581
|
+
confirm: string
|
|
582
|
+
submit: string
|
|
583
|
+
loading: string
|
|
584
|
+
noData: string
|
|
585
|
+
export: string
|
|
586
|
+
validationError: string
|
|
587
|
+
feedbackUseful: string
|
|
588
|
+
feedbackNotUseful: string
|
|
589
|
+
}
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
MCPUI expose un `MCPUIStringsProvider` avec defaults. Les renderers lisent ce contexte pour leurs propres libelles uniquement. Les `title`, `columns`, `actions.label`, `source.name` restent fournis par les payloads.
|
|
593
|
+
|
|
594
|
+
### D3 - StreamingUIRenderer
|
|
595
|
+
|
|
596
|
+
Decision: **import direct de `UIResourceRenderer` + budget bundle releve**.
|
|
597
|
+
|
|
598
|
+
Le cycle d'import n'est pas le risque: `UIResourceRenderer` n'importe pas `StreamingUIRenderer`. Le vrai arbitrage est le budget. Pour Deposium, garder deux chemins de rendu est plus risque que relever le budget.
|
|
599
|
+
|
|
600
|
+
Implications:
|
|
601
|
+
|
|
602
|
+
- `StreamingComponentRenderer` delegue a `UIResourceRenderer`.
|
|
603
|
+
- Le budget size-limit du streaming doit etre ajuste apres mesure, avec une cible initiale realiste autour de `120 KB` au lieu de `30 KB`.
|
|
604
|
+
- Ajouter des tests de parite static/streaming sur `table`, `chart`, `map`, `action-group`.
|
|
605
|
+
- Conserver progress, skeleton, animation, metadata dans `StreamingUIRenderer`.
|
|
606
|
+
- Surveiller les reflows: le swap skeleton -> composant reel doit limiter les changements de hauteur.
|
|
607
|
+
|
|
608
|
+
### D4 - Versionnement du contrat
|
|
609
|
+
|
|
610
|
+
Decision: **`schemaVersion` obligatoire des le premier contrat partage**.
|
|
611
|
+
|
|
612
|
+
Les contrats traversent plusieurs repos deployes independamment. Sans version explicite, un vieux MCPs peut casser un nouveau MCPUI/Solid sans signal clair.
|
|
613
|
+
|
|
614
|
+
Recommandation:
|
|
615
|
+
|
|
616
|
+
```ts
|
|
617
|
+
type ConnectorDynamicResult = {
|
|
618
|
+
schemaVersion: 'connector-dynamic-result/v1'
|
|
619
|
+
connectorId: string
|
|
620
|
+
toolName: string
|
|
621
|
+
query: string
|
|
622
|
+
// ...
|
|
623
|
+
}
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
Un adapter doit refuser ou fallback proprement sur une version inconnue.
|
|
627
|
+
|
|
628
|
+
### D5 - Purete des adapters
|
|
629
|
+
|
|
630
|
+
Decision: **adapters purs**.
|
|
631
|
+
|
|
632
|
+
Les adapters ne font pas de fetch, ne lisent pas le localStorage, ne persistent rien, et n'accedent pas a l'etat global. Ils transforment un contrat d'entree en `UILayout`, `ScratchpadState`, `ChatPromptConfig` ou `ActionGroup`.
|
|
633
|
+
|
|
634
|
+
Cela permet:
|
|
635
|
+
|
|
636
|
+
- tests unitaires simples;
|
|
637
|
+
- re-render deterministe apres feedback;
|
|
638
|
+
- replay de fixtures;
|
|
639
|
+
- debugging des regressions de presentation.
|
|
640
|
+
|
|
641
|
+
### D6 - Emplacement des adapters
|
|
642
|
+
|
|
643
|
+
Decision: **sous-chemin opt-in, pas core renderer path**.
|
|
644
|
+
|
|
645
|
+
Option recommandee:
|
|
646
|
+
|
|
647
|
+
- **fixtures** (JSON, payloads d'exemple) : pres des docs / dans `examples`.
|
|
648
|
+
- **adapters** (code + tests, cf. D5) : dans `mcp-ui-solid/adapters` ou
|
|
649
|
+
`examples`, **jamais `docs/briefs`** (du code teste ne vit pas dans un
|
|
650
|
+
dossier de docs — corrige A3).
|
|
651
|
+
- Export opt-in type `@seed-ship/mcp-ui-solid/adapters` une fois stabilises.
|
|
652
|
+
- Ne pas importer les adapters depuis le chemin principal de `UIResourceRenderer`.
|
|
653
|
+
|
|
654
|
+
### D7 - Source de verite du contrat connecteur
|
|
655
|
+
|
|
656
|
+
Decision par defaut: **le contrat metier vit hors MCPUI core**.
|
|
657
|
+
|
|
658
|
+
MCPUI peut fournir des types d'aide et des adapters, mais la source de verite de `ConnectorDynamicResult` doit rester cote host/MCPs ou package partage. MCPUI ne doit pas devenir proprietaire des semantics data.gouv.fr, clinicaltrials, DVF, DPE, etc.
|
|
659
|
+
|
|
660
|
+
### D8 - Mode degrade obligatoire
|
|
661
|
+
|
|
662
|
+
Decision: **jamais de prose seule quand des donnees structurees existent**.
|
|
663
|
+
|
|
664
|
+
Fallback minimal d'un connecteur:
|
|
665
|
+
|
|
666
|
+
1. table exportable si lignes structurees;
|
|
667
|
+
2. cards/liens sources si datasets uniquement;
|
|
668
|
+
3. etat vide explicite si aucun resultat;
|
|
669
|
+
4. `action-group` avec au moins reessayer / elargir / reformuler quand applicable.
|
|
670
|
+
|
|
671
|
+
### D9 - Schema canonique de feedback
|
|
672
|
+
|
|
673
|
+
Decision: separer verdict, problemes et preference.
|
|
674
|
+
|
|
675
|
+
```ts
|
|
676
|
+
type ConnectorRenderFeedback = {
|
|
677
|
+
connectorId: string
|
|
678
|
+
toolName: string
|
|
679
|
+
queryHash?: string
|
|
680
|
+
renderKind?: string
|
|
681
|
+
layoutType?: string
|
|
682
|
+
verdict: 'readable' | 'not_readable'
|
|
683
|
+
problems?: Array<
|
|
684
|
+
| 'too_raw'
|
|
685
|
+
| 'wrong_columns'
|
|
686
|
+
| 'wrong_chart'
|
|
687
|
+
| 'missing_context'
|
|
688
|
+
| 'wrong_unit'
|
|
689
|
+
| 'bad_grouping'
|
|
690
|
+
| 'missing_dataset_context'
|
|
691
|
+
>
|
|
692
|
+
preferredLayout?: 'table' | 'cards' | 'bar' | 'line' | 'doughnut' | 'map' | 'metric'
|
|
693
|
+
missingFields?: string[]
|
|
694
|
+
selectedColumns?: string[]
|
|
695
|
+
wrongUnit?: string
|
|
696
|
+
wrongGrouping?: string
|
|
697
|
+
comment?: string
|
|
698
|
+
}
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
`FeedbackInlineContext` etant deja extensible, MCPUI peut passer ce payload via `context` au lieu d'imposer un composant stateful.
|
|
702
|
+
|
|
703
|
+
### D10 - Slash commands et runtime macro
|
|
704
|
+
|
|
705
|
+
Decision: **`/macros <id>` appartient au host chat, pas a MCPUI**.
|
|
706
|
+
|
|
707
|
+
MCPUI rend l'etat macro et les questions, mais le parsing de la slash-command, le lancement du runtime, le SSE, la persistence et les retries appartiennent a Deposium Solid / runtime macro.
|
|
708
|
+
|
|
709
|
+
MCPUI recoit ensuite:
|
|
710
|
+
|
|
711
|
+
- un `ScratchpadState` ou `ScratchpadEvent`;
|
|
712
|
+
- des `UIComponent` / `UILayout`;
|
|
713
|
+
- des `ChatPromptConfig` ou `ElicitationEvent`;
|
|
714
|
+
- des `action-group` branchees sur `MCPActionProvider`.
|
|
715
|
+
|
|
716
|
+
## Ajustements post-D10
|
|
717
|
+
|
|
718
|
+
Revue de D1-D10 par l'equipe Solid. D1-D10 est coherent ; les points
|
|
719
|
+
ci-dessous corrigent un trou, deux incoherences et trois precisions a
|
|
720
|
+
trancher avant le `/goal`.
|
|
721
|
+
|
|
722
|
+
### A1 - `queryHash` non defini mais devenu porteur (trou bloquant)
|
|
723
|
+
|
|
724
|
+
D1 (chaine de priorite, etape 3 "feedback utilisateur persiste") et D9
|
|
725
|
+
(`queryHash?` optionnel) supposent tous deux une cle stable pour stocker
|
|
726
|
+
et retrouver le feedback de presentation. La definition de `queryHash`
|
|
727
|
+
n'a jamais ete tranchee. Sans cle definie, **D1 etape 3 n'est pas
|
|
728
|
+
implementable**.
|
|
729
|
+
|
|
730
|
+
Decision a prendre: `queryHash = hash(connectorId + toolName + intent
|
|
731
|
+
normalise)` — surtout PAS la query brute (deux formulations proches
|
|
732
|
+
doivent partager la cle). Le rendre **requis** des qu'un feedback est
|
|
733
|
+
persiste (optionnel sinon).
|
|
734
|
+
|
|
735
|
+
### A2 - D9 recouple les deux axes de feedback (incoherence avec §Objectif)
|
|
736
|
+
|
|
737
|
+
L'objectif (point "feedback de presentation, separe du feedback de
|
|
738
|
+
qualite de reponse") pose deux axes distincts. D9 dit "MCPUI peut passer
|
|
739
|
+
ce payload via `context`" — or `FeedbackInlineContext` est le contexte du
|
|
740
|
+
widget de **qualite**. Faire transiter la presentation dans ce contexte
|
|
741
|
+
recouple les deux axes.
|
|
742
|
+
|
|
743
|
+
A clarifier: un seul widget a deux sections, ou deux widgets distincts ?
|
|
744
|
+
Si le transport reste partage, ecrire explicitement que la presentation
|
|
745
|
+
est une section/un controle distinct — "separe" ne doit pas se reduire a
|
|
746
|
+
"des champs differents dans le meme payload".
|
|
747
|
+
|
|
748
|
+
### A3 - D6 place du code dans un dossier de docs (incoherence avec D5)
|
|
749
|
+
|
|
750
|
+
D6 propose "fixtures et adapters dans `examples` ou `docs/briefs`". Mais
|
|
751
|
+
D5 impose des adapters **purs et testes unitairement**: du code avec
|
|
752
|
+
tests ne vit pas dans `docs/briefs`. Separer:
|
|
753
|
+
|
|
754
|
+
- **fixtures** (JSON, payloads d'exemple): peuvent vivre pres des docs;
|
|
755
|
+
- **adapters** (code + tests): `examples/` ou `src/adapters/`, jamais
|
|
756
|
+
`docs/briefs`.
|
|
757
|
+
|
|
758
|
+
### A4 - D7 encore "decision par defaut" alors qu'il verrouille D4/D5/D9
|
|
759
|
+
|
|
760
|
+
D4 (schemaVersion), D5 (purete) et D9 (schema feedback) referencent tous
|
|
761
|
+
la forme et le versioning du contrat ; le proprietaire du contrat possede
|
|
762
|
+
les bumps de version. D7 doit etre **durci avant le `/goal`**.
|
|
763
|
+
|
|
764
|
+
Recommandation: un **package partage** (pas "MCPs-owned"), pour que
|
|
765
|
+
l'emetteur MCPs et les adapters MCPUI importent le meme type
|
|
766
|
+
`schemaVersion`'d sans dependance repo-a-repo.
|
|
767
|
+
|
|
768
|
+
### A5 - D4 a change la reco Q4 sans le signaler
|
|
769
|
+
|
|
770
|
+
Q4 proposait `schemaVersion: 1` (entier) ; D4 a pose
|
|
771
|
+
`'connector-dynamic-result/v1'` (string namespacee). Le choix string est
|
|
772
|
+
meilleur (auto-descriptif) — mais a signaler explicitement pour que
|
|
773
|
+
l'agent MCPs (emetteur) code la forme string. Preciser aussi le
|
|
774
|
+
comportement sur version inconnue: D4 dit "refuser OU fallback" — trancher.
|
|
775
|
+
|
|
776
|
+
### A6 - Point 10 (collision scratchpad macro / prompt HITL) disparu
|
|
777
|
+
|
|
778
|
+
La collision scratchpad de macro contre prompt HITL — panneaux ephemeres
|
|
779
|
+
mutuellement exclusifs au-dessus du chat input via `chat-motion.ts` cote
|
|
780
|
+
Deposium — n'est traitee par aucun D. C'est une affaire host legitime,
|
|
781
|
+
mais a **acter comme explicitement deferee au sprint Deposium**, pas a
|
|
782
|
+
laisser s'evaporer.
|
|
783
|
+
|
|
784
|
+
### Mineurs
|
|
785
|
+
|
|
786
|
+
- `intent` et `renderHints.domain` sont toujours en doublon dans le
|
|
787
|
+
contrat §1 — desambiguiser ou fusionner.
|
|
788
|
+
- Le `MCPUIStrings` de D2 omet `expand` / `copyData` / `download`, pourtant
|
|
789
|
+
listes dans l'audit Q2. Marquer le type "non exhaustif, a completer par
|
|
790
|
+
un audit des strings en dur".
|
|
791
|
+
- La plomberie de locale host -> MCPs -> connecteur (D2, labels de
|
|
792
|
+
contenu) n'est portee par aucun repo identifie — flaguer comme action
|
|
793
|
+
cross-repo.
|
|
794
|
+
|
|
795
|
+
## Resolution A2 / A4 / A5 + i18n (2026-05-21)
|
|
796
|
+
|
|
797
|
+
Cette section verrouille les 3 points encore ouverts apres A1-A6, sur
|
|
798
|
+
arbitrage commun equipes MCPUI + MCPs + Solid. Elle prime sur D4, D7 et D9
|
|
799
|
+
quand elle les recouvre. Apres cette section, **aucun blocage ne subsiste
|
|
800
|
+
pour le `/goal`** : Phase 3 ET Phase 4 sont entierement specifiees.
|
|
801
|
+
|
|
802
|
+
### R1 - Home du contrat (resout A4 / durcit D7)
|
|
803
|
+
|
|
804
|
+
Le contrat `ConnectorDynamicResult` vit dans **`mcp-ui-spec`**, pas dans
|
|
805
|
+
`mcp-ui-solid` core. C'est un contrat JSON inter-repos, pas un type de
|
|
806
|
+
renderer Solid.
|
|
807
|
+
|
|
808
|
+
Repartition:
|
|
809
|
+
|
|
810
|
+
- **`mcp-ui-spec`** : type + schema canonique `ConnectorDynamicResultV1`
|
|
811
|
+
(porte `schemaVersion`, `queryHash` et les enums de feedback, cf. R2/R3).
|
|
812
|
+
- **`mcp-ui-solid/adapters`** : importe ce type, fournit
|
|
813
|
+
`connectorResultToUILayout()` et les autres adapters (purs, cf. D5).
|
|
814
|
+
- **MCPs** : emet du JSON conforme au schema.
|
|
815
|
+
- **Solid (host)** : orchestre et persiste, ne redefinit jamais le contrat.
|
|
816
|
+
|
|
817
|
+
Type canonique consolide (integre `schemaVersion` de R2 + `queryHash` de A1) :
|
|
818
|
+
|
|
819
|
+
```ts
|
|
820
|
+
type ConnectorDynamicResultV1 = {
|
|
821
|
+
schemaVersion: 'connector-dynamic-result/v1'
|
|
822
|
+
connectorId: string
|
|
823
|
+
toolName: string
|
|
824
|
+
query: string
|
|
825
|
+
// Cle stable pour stocker / retrouver le feedback de presentation
|
|
826
|
+
// (cf. D1 chaine de priorite, D9 ConnectorRenderFeedback). Requis
|
|
827
|
+
// UNIQUEMENT des qu'un feedback est persiste, optionnel sinon.
|
|
828
|
+
// Calcul recommande : hash(connectorId + toolName + normalizedIntent)
|
|
829
|
+
// — surtout PAS la query brute, pour que deux formulations proches
|
|
830
|
+
// partagent la meme cle (cf. A1).
|
|
831
|
+
queryHash?: string
|
|
832
|
+
intent?: string
|
|
833
|
+
primary: UIComponent | UILayout
|
|
834
|
+
supplemental?: UIComponent[]
|
|
835
|
+
actions?: ConnectorAction[]
|
|
836
|
+
followups?: ConnectorFollowup[]
|
|
837
|
+
renderHints?: {
|
|
838
|
+
preferredLayout?: 'table' | 'cards' | 'bar' | 'line' | 'doughnut' | 'map' | 'metric'
|
|
839
|
+
domain?: 'real_estate' | 'dpe' | 'pollution' | 'employment' | 'education' | 'health' | 'generic'
|
|
840
|
+
confidence?: number
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
Ce bloc est la **forme finale** du contrat — il prime sur le type
|
|
846
|
+
`ConnectorDynamicResult` esquisse en §1 (historique).
|
|
847
|
+
|
|
848
|
+
Demarrage: si `mcp-ui-spec` n'est pas encore consommable par MCPs, MCPUI
|
|
849
|
+
peut coder immediatement en posant le contrat dans `mcp-ui-spec` + une
|
|
850
|
+
fixture JSON ; l'integration MCPs suit. La creation/verification de
|
|
851
|
+
`mcp-ui-spec` est un prerequis explicite — le contrat n'a pas de domicile
|
|
852
|
+
sinon.
|
|
853
|
+
|
|
854
|
+
### R2 - `schemaVersion` inconnu (resout A5 / corrige D4)
|
|
855
|
+
|
|
856
|
+
La forme retenue est la string namespacee `'connector-dynamic-result/v1'` (et non l'entier `1`). Une `schemaVersion` inconnue **ne doit jamais throw dans le chemin de
|
|
857
|
+
rendu runtime**. Comportement requis:
|
|
858
|
+
|
|
859
|
+
- version connue -> adapter normal ;
|
|
860
|
+
- version inconnue mais donnees exploitables -> etat degrade explicite
|
|
861
|
+
(table / source cards si possible) + warning discret ;
|
|
862
|
+
- version inconnue inexploitable -> empty/error state explicite (cf. D8) ;
|
|
863
|
+
- en dev/test -> warning/telemetry fort ;
|
|
864
|
+
- jamais de disparition silencieuse.
|
|
865
|
+
|
|
866
|
+
Le `throw` reste acceptable uniquement dans des helpers stricts de test,
|
|
867
|
+
pas sur le chemin runtime. D4 est amende en ce sens.
|
|
868
|
+
|
|
869
|
+
### R3 - Feedback de presentation : deux widgets distincts (resout A2)
|
|
870
|
+
|
|
871
|
+
Decision: **deux composants distincts**, pas une extension de
|
|
872
|
+
`FeedbackInline`.
|
|
873
|
+
|
|
874
|
+
- `FeedbackInline` = qualite de la reponse / du message. Inchange.
|
|
875
|
+
- `PresentationFeedback` = qualite du rendu / layout. **Nouveau composant
|
|
876
|
+
opt-in**, export distinct.
|
|
877
|
+
|
|
878
|
+
`PresentationFeedback` peut reutiliser des primitives MCPUI
|
|
879
|
+
(`FeedbackInline`, `ChatPrompt`/`form`, telemetry) en interne, mais reste
|
|
880
|
+
un composant/export separe — sinon les deux axes se recollent dans l'UX et
|
|
881
|
+
dans les logs. Specs:
|
|
882
|
+
|
|
883
|
+
- callback payload = `ConnectorRenderFeedback` (schema fige en D9, heberge dans `mcp-ui-spec`) ;
|
|
884
|
+
- persistence cote host ;
|
|
885
|
+
- re-render cote host (cf. D1 : adapter pur + etat host);
|
|
886
|
+
- n'utilise pas `FeedbackInlineContext` comme canal implicite de feedback presentation, sauf eventuellement en implementation interne du composant.
|
|
887
|
+
|
|
888
|
+
=> `PresentationFeedback` est des lors **entierement specifie** (nom +
|
|
889
|
+
payload) : Phase 4 est codable en totalite, pas a moitie.
|
|
890
|
+
|
|
891
|
+
### R4 - i18n : defauts EN (confirme le micro-point D2)
|
|
892
|
+
|
|
893
|
+
`MCPUIStringsProvider` porte des **defauts EN**. Retro-compat:
|
|
894
|
+
|
|
895
|
+
- les props existantes (`positiveAck`, `negativeAck`, labels fournis)
|
|
896
|
+
**priment** toujours ;
|
|
897
|
+
- a defaut de provider, les strings chrome basculent FR -> EN (les defauts
|
|
898
|
+
FR en dur actuels de `FeedbackInline` partent dans la map FR du
|
|
899
|
+
provider).
|
|
900
|
+
|
|
901
|
+
Une lib publiee evite les defauts FR en dur. Deposium passe un
|
|
902
|
+
`MCPUIStringsProvider` FR pour son UI.
|
|
903
|
+
|
|
904
|
+
### Feu vert d'execution
|
|
905
|
+
|
|
906
|
+
Debloque immediatement, 100 % interne MCPUI:
|
|
907
|
+
|
|
908
|
+
- **D3** — parite `StreamingUIRenderer` -> `UIResourceRenderer` (Phase 3) ;
|
|
909
|
+
- **D2** — `MCPUIStringsProvider` + audit des strings en dur ;
|
|
910
|
+
- extension de `mcp-ui-spec` existant + fixtures `ConnectorDynamicResultV1` ;
|
|
911
|
+
- **Phase 4 complete** — `PresentationFeedback` (nom + payload figes ici).
|
|
912
|
+
|