File size: 26,633 Bytes
179bb0f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
# Spécification produit et technique

## Portail universel de recherche IIIF + lecture Mirador + connecteurs MCP

## 1. Résumé exécutif

Objectif : créer une application web déployable sur un Hugging Face Space permettant de rechercher des objets patrimoniaux dans plusieurs bibliothèques et musées numériques du monde, d’afficher une galerie de résultats unifiée, puis d’ouvrir directement les objets sélectionnés dans Mirador pour lecture, comparaison et navigation.

Le produit doit être pensé comme trois couches distinctes :

1. **Couche découverte** : moteur fédéré de recherche multi-sources.
2. **Couche lecture** : viewer IIIF basé sur Mirador.
3. **Couche interopérabilité** : API interne normalisée et outils MCP permettant à un agent IA ou à un autre client de lancer une recherche, récupérer un manifest et ouvrir une ressource dans Mirador.

Le projet ne doit pas supposer que Mirador sait faire la recherche. Mirador est uniquement la couche d’affichage.

---

## 2. Vision produit

### 2.1 Problème

Aujourd’hui, les ressources IIIF sont dispersées entre de nombreuses institutions. Même quand les objets sont accessibles, l’utilisateur doit souvent :

* connaître l’institution à l’avance ;
* chercher sur plusieurs interfaces différentes ;
* comprendre des modèles de données hétérogènes ;
* récupérer manuellement les manifests ;
* changer de viewer selon l’établissement.

### 2.2 Proposition de valeur

Le produit doit offrir une expérience simple :

**Chercher partout, lire tout de suite, comparer dans le même viewer.**

### 2.3 Utilisateurs cibles

* chercheurs en histoire, histoire de l’art, philologie, humanités numériques ;
* conservateurs, documentalistes, bibliothécaires ;
* étudiants ;
* amateurs avancés ;
* développeurs IA ou agents utilisant MCP.

### 2.4 Promesse utilisateur

Un utilisateur tape une requête unique, obtient une galerie d’objets provenant de plusieurs institutions, filtre les résultats, puis ouvre un ou plusieurs objets dans Mirador sans avoir à manipuler les manifests manuellement.

---

## 3. Périmètre du MVP

Le MVP doit rester réaliste, robuste et simple à déployer.

### 3.1 Ce que le MVP doit faire

* proposer une recherche fédérée sur un petit nombre de sources initiales ;
* normaliser les résultats dans un format unique ;
* afficher une galerie de résultats ;
* ouvrir un résultat dans Mirador via son manifest IIIF ;
* permettre l’ouverture de plusieurs objets en parallèle dans Mirador ;
* exposer les fonctions principales via une API interne ;
* exposer éventuellement ces fonctions via MCP.

### 3.2 Sources initiales recommandées pour le MVP

Commencer avec 3 à 5 connecteurs maximum.

Je recommande :

* Gallica / BnF
* Bodleian Digital
* Europeana
* un connecteur générique IIIF manifest-by-URL
* éventuellement une source de démonstration simple et stable

### 3.3 Ce que le MVP ne doit pas faire

* pas de moissonnage global du web patrimonial ;
* pas d’index mondial complet ;
* pas de promesse de couverture exhaustive ;
* pas de création de compte utilisateur ;
* pas d’annotation collaborative persistante dans la première version ;
* pas de recherche plein texte universelle sur tous les OCR du monde.

---

## 4. Principes de conception

### 4.1 Architecture modulaire

Chaque source doit être branchée via un connecteur indépendant. L’ajout d’une nouvelle institution ne doit pas nécessiter de modifier le cœur applicatif.

### 4.2 Dégradation élégante

Si une source ne fournit pas toutes les métadonnées attendues, le système doit renvoyer un résultat partiel plutôt qu’un échec global.

### 4.3 Transparence

Chaque résultat doit afficher clairement :

* l’institution source ;
* le type de source ;
* l’URL de notice ;
* la disponibilité ou non d’un manifest IIIF ;
* les droits si connus.

### 4.4 IIIF-first mais pas IIIF-only

Le système privilégie les ressources IIIF, mais il doit accepter que certains catalogues aient une recherche propre et un accès au manifest indirect.

### 4.5 Viewer séparé du moteur

Mirador ne contient aucune logique de recherche métier. Il reçoit des manifests déjà identifiés par la couche découverte.

---

## 5. Cas d’usage

### 5.1 Recherche simple

L’utilisateur tape « book of hours » et voit des résultats venant de plusieurs institutions.

### 5.2 Filtrage

L’utilisateur filtre par institution, type d’objet, langue, période ou disponibilité IIIF.

### 5.3 Lecture

L’utilisateur clique sur un résultat et l’objet s’ouvre dans Mirador.

### 5.4 Comparaison

L’utilisateur sélectionne plusieurs résultats et les ouvre côte à côte dans Mirador.

### 5.5 Import manuel

L’utilisateur colle l’URL d’un manifest IIIF ou d’une notice ; le système tente de détecter le manifest et l’ouvre.

### 5.6 Usage agentique

Un agent appelle le serveur MCP pour rechercher « Dante manuscript », filtre les résultats par institution, récupère les manifests et renvoie au client une URL d’ouverture dans Mirador.

---

## 6. Fonctionnalités détaillées

## 6.1 Onglet Recherche

Doit contenir :

* champ de recherche plein texte ;
* bouton rechercher ;
* filtres latéraux ;
* sélecteur de sources ;
* mode recherche simple / avancée ;
* affichage galerie ;
* pagination ou scroll infini.

### 6.1.1 Recherche simple

Un seul champ libre interroge plusieurs sources.

### 6.1.2 Recherche avancée

Champs optionnels :

* mots-clés ;
* institution ;
* type de document ;
* langue ;
* date min ;
* date max ;
* disponibilité IIIF ;
* présence d’image ;
* présence d’OCR.

### 6.1.3 Galerie de résultats

Chaque carte doit afficher :

* miniature ;
* titre ;
* institution ;
* type d’objet ;
* date ;
* auteur / créateur si disponible ;
* indicateur IIIF ;
* indicateur OCR ;
* lien notice ;
* bouton « Ouvrir dans Mirador » ;
* case à cocher « comparer ».

## 6.2 Onglet Lecture

Cet onglet embarque Mirador.

Fonctions attendues :

* ouverture d’un manifest ;
* ouverture de plusieurs manifests ;
* disposition multi-fenêtres ;
* zoom, rotation, navigation canvas/page ;
* panneau métadonnées ;
* bouton partage d’URL ;
* bouton retour résultats.

## 6.3 Barre d’actions rapides

Visible depuis les cartes de résultats :

* ouvrir ;
* ajouter à la comparaison ;
* copier manifest ;
* copier notice ;
* ouvrir dans un nouvel onglet.

## 6.4 Import manuel

Une interface doit permettre :

* de coller une URL de manifest ;
* de coller une URL de notice ;
* de saisir un identifiant connu selon certaines sources.

Le backend tente alors :

1. de reconnaître la source ;
2. de récupérer le manifest ;
3. de charger Mirador.

## 6.5 Journal de transparence

Pour chaque résultat, un panneau optionnel « Voir la provenance » doit pouvoir afficher :

* connecteur utilisé ;
* requête envoyée ;
* temps de réponse ;
* niveau de confiance de normalisation ;
* champs absents.

---

## 7. Architecture globale

## 7.1 Vue d’ensemble

Le système comporte :

### Frontend

* application React ou Next.js ;
* interface utilisateur ;
* intégration Mirador ;
* gestion de l’état ;
* appel à l’API backend.

### Backend

* FastAPI ;
* orchestrateur de recherche ;
* normalisation des métadonnées ;
* cache ;
* résolution de manifests ;
* endpoints REST ;
* couche MCP optionnelle.

### Déploiement

* Hugging Face Space Docker ;
* variables d’environnement ;
* stockage léger pour cache local ;
* logs standardisés.

---

## 8. Choix techniques recommandés

## 8.1 Frontend

* React + TypeScript
* Vite ou Next.js
* TanStack Query pour les appels réseau
* Zustand ou Redux Toolkit pour l’état global
* composant Mirador intégré dans une vue dédiée
* Tailwind CSS pour l’UI

## 8.2 Backend

* Python 3.11+
* FastAPI
* httpx pour appels asynchrones
* pydantic pour modèles
* cachetools ou redis optionnel si disponible
* structlog ou logging JSON

## 8.3 MCP

* couche séparée, facultative au démarrage
* outils exposés par-dessus les mêmes services métiers que le REST
* surtout aucune logique dupliquée entre REST et MCP

## 8.4 Déploiement

* conteneur unique au MVP
* frontend servi soit statiquement, soit via le backend
* config simple via variables d’environnement

---

## 9. Architecture logique détaillée

## 9.1 Modules backend

### a. Search Orchestrator

Responsabilités :

* recevoir une requête utilisateur ;
* sélectionner les connecteurs à appeler ;
* lancer les appels en parallèle ;
* agréger les réponses ;
* normaliser les résultats ;
* classer ;
* renvoyer la réponse unifiée.

### b. Connector Registry

Registre de tous les connecteurs disponibles.

Fonctions :

* lister les connecteurs ;
* indiquer leurs capacités ;
* savoir lesquels sont activés ;
* fournir l’instance du connecteur demandé.

### c. Source Connectors

Un connecteur par source.

Interface commune obligatoire :

* `search(query, filters, page, page_size)`
* `get_item(source_id)`
* `resolve_manifest(item_or_url)`
* `healthcheck()`
* `capabilities()`

### d. Normalization Layer

Transforme des réponses hétérogènes en schéma commun.

### e. Manifest Resolver

Essaye de trouver un manifest à partir de :

* métadonnées de résultat ;
* URL de notice ;
* endpoint spécifique de la source ;
* heuristiques configurées.

### f. Cache Layer

Cache des requêtes de recherche et des résolutions de manifest.

### g. MCP Adapter

Expose certains services backend sous forme d’outils MCP.

---

## 10. Contrat des connecteurs

Chaque connecteur doit implémenter une classe abstraite commune.

### 10.1 Interface conceptuelle

```python
class BaseConnector(ABC):
    name: str
    source_type: str

    @abstractmethod
    async def search(self, query: str, filters: dict, page: int, page_size: int) -> SearchResultPage:
        ...

    @abstractmethod
    async def get_item(self, source_id: str) -> NormalizedItem | None:
        ...

    @abstractmethod
    async def resolve_manifest(self, item: NormalizedItem | None = None, url: str | None = None) -> str | None:
        ...

    @abstractmethod
    async def capabilities(self) -> dict:
        ...

    @abstractmethod
    async def healthcheck(self) -> dict:
        ...
```

### 10.2 Capacités déclaratives

Un connecteur doit déclarer explicitement :

* recherche libre oui/non ;
* recherche structurée oui/non ;
* pagination oui/non ;
* facettes oui/non ;
* résolution manifest directe oui/non ;
* OCR signalé oui/non ;
* miniature disponible oui/non.

---

## 11. Schéma de données normalisé

## 11.1 Objet principal : NormalizedItem

```json
{
  "id": "global-unique-id",
  "source": "gallica",
  "source_label": "Gallica / BnF",
  "source_item_id": "ark-or-internal-id",
  "title": "string",
  "subtitle": "string|null",
  "creators": ["string"],
  "date_display": "string|null",
  "date_sort": "string|null",
  "languages": ["string"],
  "object_type": "manuscript|book|image|map|newspaper|other",
  "description": "string|null",
  "thumbnail_url": "string|null",
  "preview_image_url": "string|null",
  "institution": "string|null",
  "collection": "string|null",
  "rights": "string|null",
  "license": "string|null",
  "record_url": "string|null",
  "manifest_url": "string|null",
  "iiif_image_service_url": "string|null",
  "has_iiif_manifest": true,
  "has_images": true,
  "has_ocr": false,
  "availability": "public|restricted|unknown",
  "relevance_score": 0.0,
  "raw_source_payload": {},
  "normalization_warnings": ["string"]
}
```

## 11.2 Réponse de recherche

```json
{
  "query": "book of hours",
  "page": 1,
  "page_size": 24,
  "total_estimated": 214,
  "results": [],
  "sources_used": ["gallica", "bodleian", "europeana"],
  "partial_failures": [
    {
      "source": "bodleian",
      "error": null,
      "status": "ok"
    }
  ],
  "duration_ms": 942,
  "facets": {
    "source": [],
    "object_type": [],
    "language": []
  }
}
```

---

## 12. API REST interne

## 12.1 Endpoints principaux

### `GET /api/health`

Retourne l’état global de l’application.

### `GET /api/sources`

Retourne la liste des connecteurs actifs et leurs capacités.

### `POST /api/search`

Entrée :

```json
{
  "query": "book of hours",
  "sources": ["gallica", "bodleian"],
  "filters": {
    "object_type": ["manuscript"],
    "language": ["lat"],
    "has_iiif_manifest": true
  },
  "page": 1,
  "page_size": 24,
  "sort": "relevance"
}
```

Sortie : SearchResponse normalisée.

### `GET /api/item/{global_id}`

Retourne le détail d’un item normalisé.

### `POST /api/resolve-manifest`

Entrée :

```json
{
  "source": "gallica",
  "source_item_id": "identifier",
  "record_url": "optional"
}
```

Sortie :

```json
{
  "manifest_url": "https://.../manifest",
  "status": "resolved",
  "method": "metadata|heuristic|source-api"
}
```

### `POST /api/open`

Entrée :

```json
{
  "manifest_urls": ["https://example.org/manifest/1"],
  "workspace": "default"
}
```

Sortie :

```json
{
  "mirador_state": {}
}
```

### `POST /api/import`

Permet d’importer une URL libre.

Entrée :

```json
{
  "url": "https://example.org/item/or/manifest"
}
```

Sortie :

```json
{
  "detected_source": "bodleian",
  "record_url": "...",
  "manifest_url": "...",
  "item": {}
}
```

---

## 13. Outils MCP à exposer

Le serveur MCP doit réutiliser les mêmes services métiers que l’API REST.

### Outils minimum

#### `search_items`

Paramètres :

* query
* sources
* filters
* page
* page_size

Retour : liste normalisée de résultats.

#### `get_item`

Paramètres :

* global_id

Retour : détail d’un item.

#### `resolve_manifest`

Paramètres :

* global_id ou URL

Retour : manifest URL.

#### `open_in_mirador`

Paramètres :

* une ou plusieurs manifest URLs

Retour : URL d’ouverture ou état Mirador sérialisé.

#### `list_sources`

Retour : sources actives et capacités.

### Ressources MCP éventuelles

* catalogue des sources
* documentation des schémas
* exemples de requêtes

---

## 14. Intégration Mirador

## 14.1 Attentes fonctionnelles

Mirador doit être embarqué comme composant de lecture.

L’application doit pouvoir :

* initialiser Mirador avec zéro, une ou plusieurs fenêtres ;
* injecter dynamiquement les manifests sélectionnés ;
* mémoriser l’état courant de la session ;
* permettre un partage d’URL si possible.

## 14.2 Mode d’intégration

Créer un composant `MiradorWorkspace` qui reçoit une liste de manifests et une configuration.

Exemple de props :

```ts
interface MiradorWorkspaceProps {
  manifestUrls: string[]
  initialView?: "single" | "compare"
  showMetadata?: boolean
  onStateChange?: (state: unknown) => void
}
```

## 14.3 État applicatif

Le frontend doit stocker séparément :

* résultats de recherche ;
* sélection pour comparaison ;
* manifests ouverts ;
* configuration du workspace.

---

## 15. UI et UX détaillées

## 15.1 Navigation principale

* onglet Recherche
* onglet Lecture
* onglet Import
* onglet Sources
* onglet À propos

## 15.2 Page Recherche

### Colonne gauche

* sources
* type d’objet
* période
* langue
* disponibilité IIIF
* présence OCR

### Zone centrale

* barre de recherche
* tri
* nombre de résultats
* cartes résultats

### Colonne droite optionnelle

* panier de comparaison
* aperçu rapide du résultat sélectionné

## 15.3 Cartes résultats

Doivent être lisibles, homogènes et sobres.

Actions visibles :

* ouvrir
* comparer
* détails

## 15.4 Page Lecture

* bandeau avec titres des objets ouverts
* zone Mirador principale
* panneau latéral avec métadonnées et provenance

## 15.5 Page Sources

Affiche pour chaque source :

* nom
* description
* statut
* capacités
* notes spécifiques
* exemple de requête test

---

## 16. Classement et fusion des résultats

Le système doit agréger des résultats hétérogènes. Il faut donc définir une stratégie de ranking simple au MVP.

### 16.1 Score de base

Combiner :

* score natif de la source si disponible ;
* qualité de correspondance textuelle locale ;
* disponibilité d’un manifest ;
* présence d’une miniature ;
* complétude des métadonnées.

### 16.2 Déduplication légère

Tenter de détecter des doublons faibles via :

* titre normalisé ;
* identifiants communs ;
* URLs canoniques ;
* manifest identique.

En cas de doute, ne pas fusionner automatiquement au MVP.

---

## 17. Performance et robustesse

## 17.1 Appels concurrents

Le backend doit appeler les connecteurs en parallèle avec timeout par source.

## 17.2 Timeouts

Chaque source doit avoir :

* timeout connexion ;
* timeout lecture ;
* timeout global par requête.

## 17.3 Partial success

Si une source échoue, les autres résultats doivent quand même apparaître.

## 17.4 Cache

Mettre en cache :

* résultats de recherche récents ;
* résolutions de manifest ;
* capacités des sources.

## 17.5 Limitation

Ajouter un plafond raisonnable de sources interrogées simultanément au MVP.

---

## 18. Sécurité et conformité

## 18.1 Sécurité minimale

* validation stricte des URLs importées ;
* blocage des schémas non autorisés ;
* prévention SSRF basique ;
* limitation des domaines si nécessaire ;
* logs sans données sensibles.

## 18.2 Respect des sources

* afficher la source d’origine ;
* ne pas masquer les droits ;
* ne pas revendiquer la propriété des données ;
* lien systématique vers la notice d’origine.

## 18.3 CORS et proxy

Prévoir un backend pouvant agir comme intermédiaire pour certains appels si le frontend rencontre des restrictions cross-origin.

---

## 19. Observabilité

Prévoir :

* logs structurés ;
* mesure des temps par source ;
* taux de résolution des manifests ;
* taux d’échec par connecteur ;
* nombre moyen de résultats par source.

---

## 20. Arborescence de projet recommandée

```text
app/
  frontend/
    src/
      components/
      pages/
      hooks/
      store/
      lib/
      types/
  backend/
    app/
      api/
      services/
      connectors/
      models/
      mcp/
      utils/
      config/
  tests/
    unit/
    integration/
  docs/
  Dockerfile
  docker-compose.yml
  README.md
```

---

## 21. Plan de développement par étapes

## Phase 1 — socle

* boot frontend ;
* boot backend ;
* endpoint health ;
* intégration Mirador minimale ;
* schéma `NormalizedItem`.

## Phase 2 — connecteurs MVP

* connecteur Gallica ;
* connecteur Bodleian ;
* connecteur Europeana ;
* registre de connecteurs ;
* orchestration de recherche.

## Phase 3 — UX recherche

* galerie ;
* filtres ;
* pagination ;
* détail item ;
* ouverture Mirador.

## Phase 4 — import et résolution

* import URL ;
* résolution manifest ;
* comparaison multi-fenêtres.

## Phase 5 — MCP

* outils minimum ;
* documentation ;
* exemples clients.

## Phase 6 — polissage

* cache ;
* logs ;
* robustesse ;
* amélioration ranking.

---

## 22. Critères d’acceptation du MVP

Le MVP est considéré comme acceptable si :

1. une requête simple renvoie des résultats depuis au moins trois sources ;
2. les résultats sont affichés dans une galerie homogène ;
3. au moins 80 % des résultats disposant réellement d’un manifest peuvent être ouverts dans Mirador ;
4. l’ouverture de plusieurs objets dans Mirador fonctionne ;
5. l’échec d’une source n’empêche pas la réponse globale ;
6. l’API `/api/sources` décrit clairement les capacités ;
7. l’import d’une URL de manifest fonctionne ;
8. les fonctions principales sont exposées aussi via MCP si la couche MCP est activée.

---

## 23. Non-objectifs explicites

Le document doit clairement indiquer ce que le produit n’est pas :

* ce n’est pas un moteur de recherche web généraliste ;
* ce n’est pas un catalogue mondial exhaustif ;
* ce n’est pas une base propriétaire remplaçant les institutions ;
* ce n’est pas un viewer maison destiné à remplacer Mirador ;
* ce n’est pas une solution d’annotation scientifique complète au MVP.

---

## 24. Spécification pour génération de code par LLM

Le LLM chargé de coder le projet doit respecter les consignes suivantes.

### 24.1 Contraintes générales

* tout le code doit être typé ;
* tout le code doit être modulaire ;
* aucune logique métier dupliquée entre REST et MCP ;
* chaque connecteur doit être isolé ;
* chaque fonction publique doit avoir docstring ou commentaire clair ;
* les erreurs doivent être gérées proprement ;
* les appels réseau doivent être asynchrones côté backend.

### 24.2 Contraintes frontend

* TypeScript strict ;
* composants réutilisables ;
* éviter les dépendances inutiles ;
* UI simple et claire ;
* aucun style inline massif ;
* préférer Tailwind.

### 24.3 Contraintes backend

* FastAPI ;
* modèles Pydantic ;
* services séparés des routes ;
* tests unitaires des connecteurs ;
* tests d’intégration des endpoints.

### 24.4 Livrables attendus du LLM

* code complet frontend ;
* code complet backend ;
* Dockerfile ;
* README d’installation ;
* exemples `.env.example` ;
* tests minimum ;
* documentation d’API.

---

## 25. Prompt maître pour un LLM codeur

```text
Tu dois générer un projet complet déployable sur Hugging Face Spaces sous forme d’application web Docker.

Objectif du produit : une interface de recherche fédérée sur plusieurs sources patrimoniales, avec affichage d’une galerie de résultats normalisés, puis ouverture directe des objets dans Mirador à partir de leurs manifests IIIF.

Contraintes :
- frontend React + TypeScript ;
- backend FastAPI Python ;
- architecture modulaire ;
- connecteurs par source ;
- schéma de données normalisé ;
- Mirador intégré comme couche de lecture ;
- API REST interne ;
- couche MCP optionnelle mais prévue ;
- code propre, typé, documenté, testable.

Fonctionnalités MVP :
- champ de recherche ;
- sélection de sources ;
- filtres simples ;
- galerie de résultats ;
- bouton ouvrir dans Mirador ;
- comparaison multi-objets ;
- import manuel d’URL de manifest ;
- endpoint listant les sources et capacités.

Architecture attendue :
- dossier frontend ;
- dossier backend ;
- dossier tests ;
- Dockerfile ;
- README.

Backend :
- créer une interface abstraite BaseConnector ;
- implémenter un registry de connecteurs ;
- implémenter un orchestrateur de recherche ;
- définir les modèles Pydantic NormalizedItem, SearchResponse, SourceCapability ;
- créer les endpoints /api/health, /api/sources, /api/search, /api/item/{id}, /api/resolve-manifest, /api/import.

Frontend :
- créer une page Recherche ;
- créer une page Lecture avec Mirador ;
- créer un store pour la sélection des résultats ;
- créer des cartes de résultats ;
- permettre d’ouvrir un ou plusieurs manifests dans Mirador.

Important :
- ne pas coder de logique de recherche dans Mirador ;
- séparer nettement découverte et lecture ;
- gérer les erreurs partielles des sources ;
- prévoir des mocks si certaines API réelles sont instables.

Commence par générer l’arborescence complète du projet, puis le backend, puis le frontend, puis les tests, puis le Dockerfile et le README.
```

---

## 26. Prompts spécialisés par lot

## Prompt lot 1 — backend socle

```text
Génère uniquement le backend FastAPI du projet.

Exigences :
- Python 3.11+
- FastAPI
- Pydantic
- httpx async
- architecture modulaire
- BaseConnector abstraite
- ConnectorRegistry
- SearchOrchestrator
- modèles NormalizedItem, SearchResponse, SourceInfo
- endpoints : /api/health, /api/sources, /api/search, /api/item/{id}, /api/resolve-manifest, /api/import
- gestion d’erreurs propre
- docstrings
- tests unitaires de base

Ne génère pas encore le frontend.
```

## Prompt lot 2 — frontend socle

```text
Génère uniquement le frontend React + TypeScript du projet.

Exigences :
- page Recherche
- filtres latéraux
- galerie de résultats
- composant ResultCard
- store global pour manifests sélectionnés
- page Lecture intégrant Mirador
- navigation simple entre Recherche et Lecture
- Tailwind CSS
- code propre et typé

Le frontend doit appeler les endpoints du backend déjà définis.
```

## Prompt lot 3 — connecteurs

```text
Ajoute au backend un système de connecteurs source.

Exigences :
- implémenter BaseConnector
- créer au moins 3 connecteurs d’exemple
- chaque connecteur doit renvoyer des résultats normalisés
- prévoir un mode mock si la source distante n’est pas disponible
- ne pas dupliquer la logique de normalisation
```

## Prompt lot 4 — MCP

```text
Ajoute une couche MCP au backend existant.

Exigences :
- exposer les outils search_items, get_item, resolve_manifest, open_in_mirador, list_sources
- réutiliser les mêmes services métiers que les routes REST
- ne créer aucune duplication de logique
- fournir un exemple minimal d’utilisation par un client MCP
```

---

## 27. Backlog post-MVP

Après le MVP, prévoir :

* sauvegarde de sessions ;
* favoris ;
* export CSV / JSON des résultats ;
* partage de workspace Mirador ;
* annotation persistante ;
* index local optionnel ;
* détection de doublons plus avancée ;
* recherche intra-document via Content Search IIIF quand disponible ;
* enrichissement sémantique ;
* facettes avancées ;
* recommandation d’objets similaires.

---

## 28. Décisions produit à ne pas perdre

1. Mirador est la couche de lecture, jamais la couche de recherche.
2. Les connecteurs sont les unités fondamentales d’extension.
3. Le schéma normalisé est le cœur du projet.
4. Le MVP privilégie la simplicité et la robustesse sur l’exhaustivité.
5. Le MCP est utile comme surcouche d’interopérabilité, pas comme justification unique du produit.

---

## 29. Version courte à remettre à un développeur ou à un LLM

Créer une application web déployable sur Hugging Face Spaces qui permet de rechercher des objets patrimoniaux dans plusieurs institutions, d’afficher les résultats sous forme de galerie homogène, puis d’ouvrir directement les objets dans Mirador à partir de leurs manifests IIIF.

Le produit comprend :

* un frontend React/TypeScript ;
* un backend FastAPI ;
* des connecteurs source modulaires ;
* un schéma de données normalisé ;
* Mirador intégré ;
* une API REST ;
* une couche MCP optionnelle.

Le MVP doit inclure recherche fédérée, filtres simples, galerie, ouverture Mirador, comparaison multi-objets et import manuel d’URL de manifest.

Le système doit être robuste aux échecs partiels, simple à étendre et proprement documenté.