Skip to main content
Glama
droit_francais_MCP.py21.2 kB
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 🏛️ Serveur MCP de requête aux API publiques Légifrance et Judilibre Copyright (c) 2025 Jean-Michel Tanguy Licensed under the MIT License (see LICENSE file) Remarques : Certaines parties de ce code ont été développées avec l’aide de Vibe Coding et d’outils d’intelligence artificielle. """ import logging import sys from typing import Any, Dict, List, Optional, Union from fastmcp import FastMCP from __version__ import __author__, __description__, __version__ from api_judilibre import JudilibreAPI from api_legifrance import LegifranceAPI # ============================================================================ # CONFIGURATION ET INITIALISATION # ============================================================================ # Configuration du logging pour debugging logging.basicConfig( level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", handlers=[ logging.StreamHandler(sys.stderr), # Envoi vers stderr pour MCP ], ) logger = logging.getLogger(__name__) # Initialisation de FastMCP try: mcp = FastMCP(f"FR Légifrance et Judilibre MCP Server v{__version__} - Droit Français Officiel") except Exception as e: logger.error(f"ERREUR ÉTAPE 1: Échec de l'initialisation du serveur MCP: {e}") raise # Initialisation de l'API LegiFrance try: legifranceapi = LegifranceAPI(sandbox=False) except Exception as e: logger.error(f"Erreur lors de l'initialisation de l'API LegiFrance: {e}") legifranceapi = None # Initialisation de l'API Judilibre try: judilibreapi = JudilibreAPI(sandbox=False) except Exception as e: logger.error(f"Erreur lors de l'initialisation de l'API Judilibre: {e}") judilibreapi = None # ============================================================================ # RESOURCES - DOCUMENTATION DÉTAILLÉE # ============================================================================ @mcp.resource("legifrance://documentation/fonds") def documentation_fonds_legifrance() -> str: """Fonds de recherche Légifrance disponibles.""" return """ # FONDS LÉGIFRANCE **ALL** (défaut): Tous les fonds | **CODE_ETAT/CODE_DATE**: Codes consolidés | **LODA_ETAT/LODA_DATE**: Lois, Ordonnances, Décrets, Arrêtés | **JORF**: Journal Officiel | **JURI**: Jurisprudence judiciaire | **CETAT**: Conseil d'État | **JUFI**: Cour des comptes | **CONSTIT**: Conseil Constitutionnel | **KALI**: Conventions collectives | **ACCO**: Accords d'entreprise | **CIRC**: Circulaires | **CNIL**: CNIL """ @mcp.resource("legifrance://documentation/champs") def documentation_champs_legifrance() -> str: """Types de champs de recherche Légifrance.""" return """ # CHAMPS DE RECHERCHE **ALL** (défaut): Tous les champs | **TITLE**: Titre | **ARTICLE**: Contenu articles | **TEXTE**: Texte complet | **NUM_ARTICLE**: N° article | **NOR**: N° ordre réglementaire | **NUM**: N° texte | **RESUMES**: Résumés jurisprudence | **MINISTERE**: Ministère | **IDCC**: Convention collective | **MOTS_CLES**: Mots-clés """ @mcp.resource("legifrance://documentation/types-recherche") def documentation_types_recherche_legifrance() -> str: """Types de recherche Légifrance.""" return """ # TYPES DE RECHERCHE **EXACTE** (défaut, recommandé): Expression exacte | **TOUS_LES_MOTS_DANS_UN_CHAMP**: Tous les mots présents (ET) | **UN_DES_MOTS**: Au moins un mot (OU) | **AUCUN_DES_MOTS**: Exclusion de mots | **AUCUNE_CORRESPONDANCE_A_CETTE_EXPRESSION**: Exclusion expression exacte """ @mcp.resource("legifrance://documentation/options-tri") def documentation_options_tri_legifrance() -> str: """Options de tri Légifrance.""" return """ # TRI DES RÉSULTATS **PERTINENCE** (recommandé): Score de pertinence | **SIGNATURE_DATE_DESC**: Date signature récente→ancienne | **SIGNATURE_DATE_ASC**: Date signature ancienne→récente | **DATE_PUBLI_DESC**: Date publication récente→ancienne | **DATE_PUBLI_ASC**: Date publication ancienne→récente """ @mcp.resource("legifrance://documentation/filtres-dates") def documentation_filtres_dates_legifrance() -> str: """Filtres de dates Légifrance.""" return """ # FILTRES DE DATES Format: YYYY-MM-DD (date_debut obligatoire, date_fin optionnel) ⚠️ **FONDS COMPATIBLES**: JORF, LODA_DATE, LODA_ETAT (DATE_PUBLICATION) | JURI, CETAT, JUFI, CONSTIT (DATE_DECISION) | KALI, CIRC, ACCO (DATE_SIGNATURE) ❌ **INCOMPATIBLES**: ALL, CODE_DATE, CODE_ETAT, CNIL (filtres ignorés avec avertissement) Note: DATE_PUBLICATION ≠ DATE_DECISION ≠ DATE_SIGNATURE. Pour jurisprudence: date de décision, pas de mise en ligne. """ @mcp.resource("judilibre://documentation/juridictions") def documentation_juridictions_judilibre() -> str: """Juridictions Judilibre.""" return """ # JURIDICTIONS **cc**: Cour de cassation | **ca**: Cours d'appel | **tj**: Tribunaux judiciaires | **tcom**: Tribunaux de commerce """ @mcp.resource("judilibre://documentation/chambres") def documentation_chambres_judilibre() -> str: """Chambres Cour de cassation.""" return """ # CHAMBRES (Cour de cassation) **pl**: Assemblée plénière | **mi**: Chambre mixte | **civ1**: 1ère civ. | **civ2**: 2e civ. | **civ3**: 3e civ. | **comm**: Commerciale | **soc**: Sociale | **cr**: Criminelle | **creun**: Chambres réunies | **ordo**: Ordonnance | **allciv**: Toutes civ. | **other**: Autre Note: Claude convertit automatiquement les noms complets en codes. """ @mcp.resource("judilibre://documentation/solutions") def documentation_solutions_judilibre() -> str: """Solutions des décisions.""" return """ # SOLUTIONS **cassation**: Cassation | **cassation_partielle**: Cassation partielle | **rejet**: Rejet | **annulation**: Annulation | **irrecevabilite**: Irrecevabilité | **desistement**: Désistement | **non-lieu**: Non-lieu | **qpc**: QPC """ @mcp.resource("judilibre://documentation/localisations") def documentation_localisations_judilibre() -> str: """Localisations (sièges de juridictions).""" return """ # LOCALISATIONS **Cours d'appel**: ca_<ville> (ex: ca_paris, ca_lyon) | **Tribunaux judiciaires**: tj<INSEE> (ex: tj75101=Paris, tj69123=Lyon) | **Tribunaux commerce**: tcom<INSEE> Utiliser `obtenir_taxonomie_judilibre(taxonomy_id="location", context_value="ca|tj|tcom")` pour liste complète. Claude convertit automatiquement les noms de villes. """ @mcp.resource("judilibre://documentation/types-decision") def documentation_types_decision_judilibre() -> str: """Types de décision.""" return """ # TYPES DE DÉCISION **arret**: Juridictions collégiales (cc, ca, ce, caa, crc) | **ordonnance**: Juge unique, référés, toutes juridictions | **qpc**: QPC (cc, ce uniquement) | **saisie**: Saisies (tj uniquement) """ @mcp.resource("judilibre://documentation/themes") def documentation_themes_judilibre() -> str: """Thèmes juridiques.""" return """ # THÈMES (Matières juridiques) Liste longue (centaines). Catégories: **Civil** (responsabilité, contrats, famille, successions) | **Commercial** (sociétés, concurrence, PI, proc. collectives) | **Travail** (licenciement, salaires, sécurité sociale) | **Pénal** (infractions, procédure, peines) | **Admin** (fonction publique, urbanisme) | **Fiscal** (IR, TVA, IS) Utiliser `obtenir_taxonomie_judilibre(taxonomy_id="theme")` pour codes exacts. Explorer sans filtre puis affiner. """ @mcp.resource("judilibre://documentation/options-tri") def documentation_options_tri_judilibre() -> str: """Options de tri Judilibre.""" return """ # TRI **scorepub** (défaut, recommandé): Pertinence + publication (Bulletin>Rapport>Lettre>Communiqué>Non publié) | **score**: Pertinence seule | **date**: Date décision **Ordre**: desc (défaut, récent→ancien) | asc (ancien→récent) """ # ============================================================================ # OUTILS LEGIFRANCE - RECHERCHE DES TEXTES DE DROIT FRANÇAIS # ============================================================================ @mcp.tool def rechercher_legifrance( recherche: str, fond: str = "ALL", type_champ: str = "ALL", type_recherche: str = "TOUS_LES_MOTS_DANS_UN_CHAMP", code: Optional[str] = None, date_debut: Optional[str] = None, date_fin: Optional[str] = None, page: int = 0, page_taille: int = 20, tri: Optional[str] = "PERTINENCE", operateur: str = "ET", ) -> Any: """ Recherche avancée dans la base juridique Légifrance (codes, lois, jurisprudence, conventions). Args: recherche: Terme(s) de recherche. Ex: "mariage", "responsabilité civile" fond: Fonds (ALL, CODE_ETAT, LODA_ETAT, JORF, JURI, KALI, etc.). Défaut: "ALL" type_champ: Champ de recherche (ALL, TITLE, ARTICLE, etc.). Défaut: "ALL" type_recherche: Type de recherche : EXACTE, TOUS_LES_MOTS_DANS_UN_CHAMP, UN_DES_MOTS, AUCUN_DES_MOTS. Défaut: "TOUS_LES_MOTS_DANS_UN_CHAMP" code: Nom du code (ex: "Code civil") pour fonds CODE_ETAT/CODE_DATE. Défaut : None date_debut: Date de début pour filtres dates (YYYY-MM-DD) avec les fonds: JORF, LODA_DATE, LODA_ETAT, JURI, CETAT, JUFI, CONSTIT, KALI, CIRC, ACCO. Défaut : None date_fin: Date de fin pour filtres dates (YYYY-MM-DD) avec les fonds: JORF, LODA_DATE, LODA_ETAT, JURI, CETAT, JUFI, CONSTIT, KALI, CIRC, ACCO : Défaut : None page: Numéro de page. Défaut: 0 page_taille: Résultats par page (max 50). Défaut: 20 tri: Ordre de tri avec PERTINENCE, SIGNATURE_DATE_DESC, SIGNATURE_DATE_ASC, DATE_PUBLI_DESC, DATE_PUBLI_ASC Défaut: PERTINENCE operateur: Opérateur entre champs (ET, OU). Défaut: "ET" Returns: Liste de résultats avec métadonnées. Utiliser l'outil consult_legifrance(id) pour le contenu complet. Ressources utiles: - legifrance://documentation/fonds - Liste des fonds disponibles - legifrance://documentation/champs - Types de champs de recherche - legifrance://documentation/types-recherche - Valeurs pour type_recherche - legifrance://documentation/filtres-dates - Guide sur les filtres de dates - legifrance://documentation/options-tri - Valeurs pour sort """ try: # Validation des paramètres if not recherche or not recherche.strip(): logger.error("Requête de recherche vide") return [] # Vérification de l'initialisation de l'API if legifranceapi is None: logger.error("API Légifrance non initialisée") return [] # Validation des filtres de dates selon le fond FONDS_WITH_DATE_FILTERS = ["JORF", "LODA_DATE", "LODA_ETAT", "JURI", "CETAT", "JUFI", "CONSTIT", "KALI", "CIRC", "ACCO"] if (date_debut or date_fin) and fond not in FONDS_WITH_DATE_FILTERS: warning = [ f"⚠️ ATTENTION: Les filtres de dates (date_debut/date_fin) sont ignorés pour le fond '{fond}'. " f"Les filtres de dates ne fonctionnent que pour les fonds: {', '.join(FONDS_WITH_DATE_FILTERS)}" ] # Effacer les filtres de dates pour éviter toute confusion date_debut = None date_fin = None else: warning = None search_results = legifranceapi.search( query=recherche, fond=fond, field_type=type_champ, search_type=type_recherche, code=code, date_start=date_debut, date_end=date_fin, page_number=page, page_size=page_taille, sort=tri, operator=operateur, ) search_results = search_results or [] if warning: search_results = {"warning": warning, "results": search_results} return search_results except Exception as e: logger.error(f"Erreur lors de la recherche '{recherche}': {e}") return "Erreur lors de la recherche" @mcp.tool def consulter_legifrance(id: str) -> Any: """ Récupère le texte intégral d'un article juridique depuis Légifrance. Cette fonction est la DEUXIÈME ÉTAPE après toute recherche pour obtenir le contenu complet. Args: id: ID de l'article (LEGIARTI..., LEGITEXT..., JURITEXT..., etc.) Obtenu depuis les résultats de recherche (metadata 'id') Returns: Le contenu juridique """ try: # Validation des paramètres if not id or not id.strip(): logger.error("ID article vide") return {"erreur": "L'ID de l'article ne peut pas être vide"} # Vérification de l'initialisation de l'API if legifranceapi is None: logger.error("API Légifrance non initialisée") return {"erreur": "L'API Légifrance n'est pas initialisée"} article = legifranceapi.consult(id) return article except Exception as e: logger.error(f"Erreur lors de la récupération de l'article '{id}': {e}") return {"erreur": f"Erreur de récupération d'article: {str(e)}"} # ============================================================================ # OUTILS JUDILIBRE - RECHERCHE DE JURISPRUDENCE # ============================================================================ @mcp.tool def obtenir_taxonomie_judilibre( taxonomy_id: Optional[str] = None, key: Optional[str] = None, value: Optional[str] = None, context_value: Optional[str] = None, ) -> Any: """ Récupère les valeurs valides pour les filtres de recherche Judilibre (juridictions, chambres, solutions, etc.). Utiliser les ressources en priorité pour connaître les valeurs possibles avant d'utiliser cette fonction. Args: taxonomy_id: Type de taxonomie (jurisdiction, chamber, solution, theme, location, etc.) key: Clé pour obtenir l'intitulé complet value: Intitulé pour obtenir la clé context_value: Contexte pour certaines taxonomies (cc, ca, tj) Returns: Données de taxonomie (liste ou dict selon les paramètres) Exemples d'usage: - obtenir_taxonomie_judilibre() → toutes les taxonomies - obtenir_taxonomie_judilibre(taxonomy_id="chamber", context_value="cc") → chambres Cour de cassation - obtenir_taxonomie_judilibre(taxonomy_id="location", context_value="ca") → cours d'appel Ressources utiles: - judilibre://documentation/juridictions - Juridictions disponibles - judilibre://documentation/chambres - Chambres de la Cour de cassation - judilibre://documentation/solutions - Types de solutions """ logger.debug( f"APPEL: obtenir_taxonomie_judilibre(taxonomy_id='{taxonomy_id}', key='{key}', value='{value}', context_value='{context_value}')" ) try: if judilibreapi is None: logger.error("API Judilibre non initialisée") return {"erreur": "L'API Judilibre n'est pas initialisée"} result = judilibreapi.taxonomy( taxonomy_id=taxonomy_id, key=key, value=value, context_value=context_value ) return result except Exception as e: logger.error(f"Erreur lors de la récupération de la taxonomie: {e}") return {"erreur": f"Erreur taxonomie: {str(e)}"} @mcp.tool def rechercher_jurisprudence_judilibre( recherche: Optional[str] = None, juridiction: Optional[str] = None, localisation: Optional[str] = None, chambre: Optional[str] = None, type_decision: Optional[str] = None, theme: Optional[str] = None, solution: Optional[str] = None, date_debut: Optional[str] = None, date_fin: Optional[str] = None, tri: str = "scorepub", ordre: str = "desc", nombre_resultats: int = 20, page: int = 0, ) -> Any: """ Recherche de jurisprudence dans la base Judilibre (décisions de toutes les juridictions françaises). À utiliser en priorité pour la recherche jurisprudentielle. Args: recherche: Texte de recherche juridiction: Code juridiction (cc, ca, tj, tcom). Defaut : None (recherche dans toutes les juridictions) localisation: Code siège. Format varie selon juridiction (très nombreux, donc à utiliser que si nécessaire). Défaut : None Exemples: "ca_paris", "tj06088" (Nice), "ca_lyon" Utiliser obtenir_taxonomie_judilibre(taxonomy_id="location", context_value="ca" ou "tj") chambre: Code chambre - CLÉS: "pl", "mi", "civ1", "civ2", "civ3", "comm", "soc", "cr" Défaut : None ⚠️ Utiliser les CLÉS, pas les noms complets type_decision: Type de décision. Valeurs: arret, ordonnance, qpc, saisie. Défaut : None theme: Code matière juridique (très nombreux, donc à utiliser que si nécessaire). Défaut : None Utiliser obtenir_taxonomie_judilibre(taxonomy_id="theme") pour la liste solution: Solution (cassation, rejet, annulation, etc.). Défaut : None date_debut: Date début ISO (ex: 2023-01-15). Défaut : None date_fin: Date fin ISO (ex: 2023-12-15). Défaut : None tri: Ordre de tri. Valeurs: scorepub, score, date. Défaut: "scorepub" ordre: Sens du tri (desc, asc). Défaut: "desc" nombre_resultats: Résultats par page (max 50). Défaut: 20 page: Numéro de page (commence à 0). Défaut : 0 Returns: Liste de décisions incluant les id. Utiliser obtenir_decision_judilibre(id) pour le texte complet. Ressources utiles: - judilibre://documentation/juridictions - Codes de juridictions - judilibre://documentation/chambres - Codes de chambres - judilibre://documentation/localisations - Codes de localisations (sièges) - judilibre://documentation/types-decision - Types de décision - judilibre://documentation/themes - Thèmes (matières juridiques) - judilibre://documentation/solutions - Types de solutions - judilibre://documentation/options-tri - Options de tri (tri + ordre) """ try: if judilibreapi is None: logger.error("API Judilibre non initialisée") return [{"erreur": "L'API Judilibre n'est pas initialisée"}] # Conversion des paramètres en listes si fournis jurisdiction_list = [juridiction] if juridiction else ["cc", "ca", "tj", "tcom"] # Par défaut toutes les juridictions location_list = [localisation] if localisation else None chamber_list = [chambre] if chambre else None type_list = [type_decision] if type_decision else None theme_list = [theme] if theme else None solution_list = [solution] if solution else None results = judilibreapi.search( query=recherche, jurisdiction=jurisdiction_list, location=location_list, chamber=chamber_list, type=type_list, theme=theme_list, solution=solution_list, date_start=date_debut, date_end=date_fin, sort=tri, order=ordre, page_size=nombre_resultats, page=page, resolve_references=True, # Obtenir les intitulés complets ) return results except Exception as e: logger.error(f"Erreur lors de la recherche Judilibre: {e}") return "Erreur lors de la recherche Judilibre" @mcp.tool def consulter_decision_judilibre(decision_id: str) -> Any: """ Récupère le contenu d'une décision de justice depuis Judilibre. Cette fonction est la DEUXIÈME ÉTAPE après rechercher_jurisprudence_judilibre() pour obtenir le texte complet. Args: decision_id: ID unique de la décision (champ 'id' des résultats de recherche) Returns: La décision complète. """ logger.debug(f"APPEL: obtenir_decision_judilibre(decision_id='{decision_id}')") try: if not decision_id or not decision_id.strip(): logger.error("ID décision vide") return {"erreur": "L'ID de la décision ne peut pas être vide"} if judilibreapi is None: logger.error("API Judilibre non initialisée") return {"erreur": "L'API Judilibre n'est pas initialisée"} decision = judilibreapi.consult(decision_id=decision_id) return decision except Exception as e: logger.error(f"Erreur lors de la récupération de la décision '{decision_id}': {e}") return {"erreur": f"Erreur récupération décision: {str(e)}"} if __name__ == "__main__": mcp.run()

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jmtanguy/droit-francais-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server