ArianeWebMCP est un serveur MCP (Model Context Protocol) en TypeScript. Il expose 4 outils à un client IA (Claude, Cursor...) via un transport stdio JSON-RPC.
Client IA (Claude Desktop, Cursor...)
│ JSON-RPC sur stdin/stdout
▼
┌─────────────────────────────────────────┐
│ src/index.ts │ Point d'entrée, transport stdio
│ src/server.ts │ Serveur MCP, routing des outils
├─────────────────┬───────────────────────┤
│ src/tools/ │ │ Logique métier des 4 outils
│ search.ts │ decision.ts │
│ conclusions.ts│ analysis.ts │
├─────────────────┴───────────────────────┤
│ src/clients/ │ Couche HTTP (gelée)
│ arianeweb.ts │ opendata.ts │
└─────────────────────────────────────────┘
│ │
▼ ▼
conseil-etat.fr opendata.justice-
(ArianeWeb / administrative.fr
Sinequa)
src/
├── index.ts # Point d'entrée : crée le serveur et démarre le transport stdio
├── server.ts # Définition des outils MCP et dispatch des requêtes
├── clients/
│ ├── arianeweb.ts # Client HTTP ArianeWeb (Sinequa / conseil-etat.fr)
│ └── opendata.ts # Client HTTP OpenData (justice-administrative.fr)
├── tools/
│ ├── search.ts # handleSearchDecisions — logique de recherche et formatage
│ ├── decision.ts # handleGetDecision — récupération texte intégral
│ ├── conclusions.ts # handleGetConclusions — extraction PDF conclusions
│ └── analysis.ts # handleGetDecisionAnalysis — analyse jurisprudentielle
└── types/
├── decision.ts # Types partagés : DecisionMetadata, DecisionComplete, etc.
└── search.ts # Types : SearchParams, SearchResult
tests/
├── clients/
│ ├── arianeweb.test.ts # Tests d'intégration ArianeWebClient (réseau réel)
│ └── opendata.test.ts # Tests d'intégration OpenDataClient (réseau réel)
└── tools/
├── search.test.ts # Tests unitaires handleSearchDecisions (mock client)
├── decision.test.ts # Tests unitaires handleGetDecision (mock client)
├── conclusions.test.ts# Tests unitaires handleGetConclusions (mock client)
└── analysis.test.ts # Tests unitaires handleGetDecisionAnalysis (mock client)
Exemple : le client IA appelle search_decisions avec { query: "droit au logement" }.
1. index.ts Démarre le serveur MCP avec StdioServerTransport
2. server.ts Reçoit la requête JSON-RPC CallToolRequest
→ switch(name) → handleSearchDecisions(args, client, openDataClient)
3. tools/search.ts Valide les paramètres
jurisdiction = "CE+TC" (défaut) → routing vers ArianeWebClient
→ client.search({ query, jurisdiction, classification, ... })
4. clients/ Deux requêtes GET en parallèle (CE + TC)
arianeweb.ts → fusion et tri des résultats par date
→ filtre classification A/B (défaut)
→ si aucun résultat A/B → fallback CAA (signalé dans la réponse)
5. tools/search.ts Formate le résultat en texte structuré (Markdown)
6. server.ts Retourne { content: [{ type: "text", text }] }
7. index.ts Sérialise en JSON-RPC sur stdout → le client IA reçoit la réponse
Point d'entrée minimal. Crée le serveur via createServer() et le connecte à un StdioServerTransport. Le serveur tourne tant que stdin est ouvert.
- Définit les 4 outils MCP avec leurs schémas JSON (descriptions optimisées pour les LLM)
- Instancie
ArianeWebClientetOpenDataClient - Dispatche les requêtes
call_toolvers les handlers correspondants - Gère les erreurs domaine : retournées au LLM via
isError: truesans crash du transport
Client pour l'API Sinequa d'ArianeWeb. Couche gelée — ne pas modifier après validation.
search(params)— recherche CE+TC (deux requêtes fusionnées), CE, CAA ou TCgetDecision(id)— trouve le document Sinequa par numéro d'affaire, télécharge le HTMLgetConclusions(id)— télécharge le PDF de conclusions, extrait le texte avecpdf-parsegetAnalyse(id)— télécharge le HTML d'analyse, extrait le texte aveccheerio
Points techniques :
- Rate limiting : 500 ms minimum entre requêtes (respect des CGU)
- Encodage : les décisions et analyses HTML sont en
iso-8859-1(décodé aveclatin1) - Conclusions : format PDF (pas HTML — l'interface web affiche le PDF en iframe)
- CE+TC : l'API Sinequa ne supporte pas
SourceStr4=AW_DCE OR AW_DTC— deux requêtes enPromise.all - Recherche par numéro : retourne aussi les docs AW_CRP/AW_AJCE → filtre sur
SourceStr4
Client pour l'API Elasticsearch d'opendata.justice-administrative.fr.
search(params)— recherche dans tous les TA ou CAA via chaîne OR de codes juridictiongetDecision(id)— récupère le texte intégral via l'endpointtestView(highlight Elasticsearch)isOpenDataId(id)— détecte si un identifiant est au format OpenData (TA76:...)parseId(id)— décompose l'id en{ codeJuridiction, numeroDossier, identification }
Format d'identifiant : {Code_Juridiction}:{Numero_Dossier}:{Identification_sans_ext}
Handler handleSearchDecisions. Responsabilités :
- Validation et normalisation des paramètres
- Routing :
jurisdiction = "TA"→OpenDataClient, sinon →ArianeWebClient - Logique de fallback CE+TC → CAA si aucun résultat A/B
- Tri des résultats : A > B > C
- Formatage en texte lisible par le LLM
Handler handleGetDecision. Détecte le format de l'id pour router vers le bon client :
OpenDataClient.isOpenDataId(id)→openDataClient.getDecision(id)- Sinon →
arianeWebClient.getDecision(id)
Handler handleGetConclusions. Délègue à arianeWebClient.getConclusions(id). Formate le texte extrait du PDF avec nom du rapporteur public si disponible.
Handler handleGetDecisionAnalysis. Délègue à arianeWebClient.getAnalyse(id). Formate le texte HTML extrait.
Interfaces TypeScript partagées entre clients et outils :
DecisionMetadata— données de base d'une décision (id, numero, date, juridiction...)DecisionComplete— étendDecisionMetadataavec le texte intégral et les champs détaillésConclusions— texte des conclusions + nom du rapporteur publicAnalyse— texte de l'analyse jurisprudentielleSearchParams/SearchResult— paramètres et résultats de recherche
ArianeWeb (Sinequa) et OpenData (Elasticsearch) ont des APIs, des formats de réponse et des formats d'identifiants radicalement différents. La séparation évite tout couplage et permet de tester chaque client indépendamment.
Les tests d'intégration valident les clients contre les APIs réelles. Une fois verts, les clients constituent un contrat stable. Les outils (tools/) ne dépendent que de l'interface publique des clients, jamais des détails HTTP.
Le protocole MCP recommande stdio pour les serveurs locaux — pas de port à ouvrir, pas de problème de réseau ou d'authentification. Docker avec --rm -i est la façon standard de packager un serveur MCP stdio.
Les erreurs domaine (décision introuvable, conclusions non disponibles) sont retournées au LLM via isError: true dans la réponse MCP — le LLM peut alors expliquer le problème à l'utilisateur. Les erreurs réseau propagent une exception qui est attrapée dans server.ts.
| Outil | Version | Usage |
|---|---|---|
| TypeScript | 5.x | Langage principal (ESM, NodeNext) |
| Node.js | 20+ | Runtime (fetch natif) |
@modelcontextprotocol/sdk |
1.x | Serveur MCP, transport stdio |
cheerio |
1.x | Parsing HTML des décisions et analyses |
pdf-parse |
2.x | Extraction texte des PDF de conclusions |
vitest |
4.x | Tests unitaires et d'intégration |
| Docker | — | Packaging et déploiement |