diff --git a/README.md b/README.md index 90c6c44..a8598b2 100644 --- a/README.md +++ b/README.md @@ -38,21 +38,21 @@ ```text 💬 Je vais d'abord rechercher les informations nĂ©cessaires : - les coordonnĂ©es de Vincennes et le type WFS pour les bĂątiments. + les coordonnĂ©es de Vincennes et le type GPF pour les bĂątiments. 🔧 Appel outil: geocode({'text': 'Vincennes'}) -🔧 Appel outil: gpf_wfs_search_types({'query': 'bĂątiment hauteur'}) +🔧 Appel outil: gpf_search_types({'query': 'bĂątiment hauteur'}) -💬 Parfait ! J'ai trouvĂ© le type WFS BDTOPO_V3:batiment. +💬 Parfait ! J'ai trouvĂ© le type GPF BDTOPO_V3:batiment. Je vais maintenant rĂ©cupĂ©rer le schĂ©ma de ce type pour connaĂźtre les propriĂ©tĂ©s exactes, notamment la hauteur et le code INSEE de Vincennes. -🔧 Appel outil: gpf_wfs_describe_type({'typename': 'BDTOPO_V3:batiment'}) +🔧 Appel outil: gpf_describe_type({'typename': 'BDTOPO_V3:batiment'}) 🔧 Appel outil: adminexpress({'lon': 2.437785,'lat': 48.847279}) 💬 J'ai toutes les informations nĂ©cessaires. La commune de Vincennes a le feature_id commune.33667. Je vais maintenant compter les bĂątiments de plus de 20 mĂštres de hauteur sur cette commune. -🔧 Appel outil: gpf_wfs_get_features({ +🔧 Appel outil: gpf_get_features({ 'typename': 'BDTOPO_V3:batiment', 'result_type': 'hits', 'where': [ @@ -190,10 +190,10 @@ Les fonctionnalitĂ©s correspondent aux outils MCP documentĂ©s dans [`docs/mcp-to | RĂ©cupĂ©rer le cadastre | `cadastre` | [WFS](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/diffusion/wfs/) + [PARCELLAIRE-EXPRESS](https://cartes.gouv.fr/rechercher-une-donnee/dataset/IGNF_PARCELLAIRE-EXPRESS-PCI) | Parcelle cadastrale | | RĂ©cupĂ©rer les documents d'urbanisme | `urbanisme` | [WFS](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/diffusion/wfs/) + [donnĂ©es GPU](https://www.geoportail-urbanisme.gouv.fr/) | PLU, POS, CC | | RĂ©cupĂ©rer les servitudes | `assiette_sup` | [WFS](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/diffusion/wfs/) + [donnĂ©es GPU](https://www.geoportail-urbanisme.gouv.fr/) | SUP autour d'un lieu | -| Trouver une couche WFS | `gpf_wfs_search_types` | [gpf-schema-store](https://github.com/ignfab/gpf-schema-store) | Trouver la table des bĂątiments | -| DĂ©crire une couche WFS | `gpf_wfs_describe_type` | [gpf-schema-store](https://github.com/ignfab/gpf-schema-store) | Lister les champs disponibles | -| Interroger une couche WFS | `gpf_wfs_get_features` | [WFS](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/diffusion/wfs/) | Extraire ou compter des objets | -| RĂ©cupĂ©rer un objet par identifiant | `gpf_wfs_get_feature_by_id` | [WFS](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/diffusion/wfs/) | Charger une commune prĂ©cise | +| Trouver une couche GPF | `gpf_search_types` | [gpf-schema-store](https://github.com/ignfab/gpf-schema-store) | Trouver la table des bĂątiments | +| DĂ©crire une couche GPF | `gpf_describe_type` | [gpf-schema-store](https://github.com/ignfab/gpf-schema-store) | Lister les champs disponibles | +| Interroger une couche GPF | `gpf_get_features` | [WFS](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/diffusion/wfs/) | Extraire ou compter des objets | +| RĂ©cupĂ©rer un objet par identifiant | `gpf_get_feature_by_id` | [WFS](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/diffusion/wfs/) | Charger une commune prĂ©cise | ## Architecture en bref @@ -264,7 +264,7 @@ Merci de **fournir la question type** pour laquelle vous souhaiteriez que le MCP - [mcp-framework](https://mcp-framework.com) : **cadre de dĂ©veloppement du MCP** - [@ignfab/gpf-schema-store](https://www.npmjs.com/package/@ignfab/gpf-schema-store) : **couche sĂ©mantique** / **catalogue de schĂ©mas embarquĂ©** (en attendant [OGC API - Features - schema](https://docs.ogc.org/is/23-058r2/23-058r2.html)) - [@camptocamp/ogc-client](https://camptocamp.github.io/ogc-client/#/) : **exploration WFS** (ex. : parsing [GetCapabilities](https://data.geopf.fr/wfs?request=GetCapabilities&version=2.0.0&service=WFS)) - - [MiniSearch](https://github.com/lucaong/minisearch) : **recherche par mot-clĂ©** (`gpf_wfs_search_types`) + - [MiniSearch](https://github.com/lucaong/minisearch) : **recherche par mot-clĂ©** (`gpf_search_types`) - [jsts](https://bjornharrtell.github.io/jsts/) : **traitements gĂ©omĂ©triques** (ex. : tri des rĂ©ponses par distance au point recherchĂ©). - [turfjs/distance](https://turfjs.org/docs/api/distance) : **calculs de distance** avec la [formule de Haversine](https://en.wikipedia.org/wiki/Haversine_formula). diff --git a/docs/config.md b/docs/config.md index 935dd4c..f23003c 100644 --- a/docs/config.md +++ b/docs/config.md @@ -19,7 +19,7 @@ | `GPF_GEOCODE_RATE_LIMIT` | Nombre maximum de requĂȘtes par seconde sur le service d'autocomplĂ©tion de la GĂ©oplateforme. | 50 | | `GPF_ALTI_RATE_LIMIT` | Nombre maximum de requĂȘtes par seconde sur le service d'altimĂ©trie de la GĂ©oplateforme. | 50 | | `GPF_NAVIGATION_RATE_LIMIT` | Nombre maximum de requĂȘtes par seconde sur le service d'isochrone/navigation de la GĂ©oplateforme. | 5 | -| `GPF_WFS_MINISEARCH_OPTIONS` | ChaĂźne JSON optionnelle permettant de configurer `gpf_wfs_search_types`. | options par dĂ©faut de `@ignfab/gpf-schema-store` | +| `GPF_WFS_MINISEARCH_OPTIONS` | ChaĂźne JSON optionnelle permettant de configurer `gpf_search_types`. | options par dĂ©faut de `@ignfab/gpf-schema-store` | | `LOG_FORMAT` | Le format d'Ă©criture des logs : "json" ou "simple". | "simple" | | `LOG_LEVEL` | Le niveau d'Ă©criture des logs : ["error", "info", ou "debug"](https://github.com/winstonjs/winston#logging-levels) | "debug" | diff --git a/docs/config/minisearch.md b/docs/config/minisearch.md index d5d20fc..c5fdd05 100644 --- a/docs/config/minisearch.md +++ b/docs/config/minisearch.md @@ -2,7 +2,7 @@ ## Contexte -La recherche des tables disponibles ( `gpf_wfs_search_types` ) s'appuie sur le moteur de recherche MiniSearch qui est intĂ©grĂ© au dĂ©pĂŽt [ignfab/gpf-schema-store](https://github.com/ignfab/gpf-schema-store#readme). +La recherche des tables disponibles ( `gpf_search_types` ) s'appuie sur le moteur de recherche MiniSearch qui est intĂ©grĂ© au dĂ©pĂŽt [ignfab/gpf-schema-store](https://github.com/ignfab/gpf-schema-store#readme). **Cette documentation est Ă  destination des dĂ©veloppeurs** souhaitant tester des modifications sur les poids. diff --git a/docs/mcp-tools.md b/docs/mcp-tools.md index 28e5a59..f12ed74 100644 --- a/docs/mcp-tools.md +++ b/docs/mcp-tools.md @@ -57,10 +57,10 @@ Tous les tools exposent les mĂȘmes annotations MCP dans leur dĂ©finition `tools/ - [`cadastre`](#cadastre) - [`urbanisme`](#urbanisme) - [`assiette_sup`](#assiette_sup) -- [`gpf_wfs_search_types`](#gpf_wfs_search_types) -- [`gpf_wfs_describe_type`](#gpf_wfs_describe_type) -- [`gpf_wfs_get_feature_by_id`](#gpf_wfs_get_feature_by_id) -- [`gpf_wfs_get_features`](#gpf_wfs_get_features) +- [`gpf_search_types`](#gpf_search_types) +- [`gpf_describe_type`](#gpf_describe_type) +- [`gpf_get_feature_by_id`](#gpf_get_feature_by_id) +- [`gpf_get_features`](#gpf_get_features) ## `geocode` @@ -292,9 +292,9 @@ UnitĂ©s administratives ``` Renvoie, pour un point donnĂ© par sa `longitude` et sa `latitude`, la liste des unitĂ©s administratives (arrondissement, arrondissement_municipal, canton, collectivite_territoriale, commune, commune_associee_ou_deleguee, departement, epci, region) qui le couvrent, sous forme d'objets typĂ©s contenant leurs propriĂ©tĂ©s administratives. -Les rĂ©sultats incluent un `feature_ref` WFS rĂ©utilisable. Les propriĂ©tĂ©s incluent notamment le code INSEE. -Le `feature_ref` de chaque unitĂ© administrative est directement rĂ©utilisable dans `gpf_wfs_get_features` avec `intersects_feature_filter` pour interroger d'autres donnĂ©es sur cette emprise. -Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_wfs_get_feature_by_id`. +Les rĂ©sultats incluent un `feature_ref` GPF rĂ©utilisable. Les propriĂ©tĂ©s incluent notamment le code INSEE. +Le `feature_ref` de chaque unitĂ© administrative est directement rĂ©utilisable dans `gpf_get_features` avec `intersects_feature_filter` pour interroger d'autres donnĂ©es sur cette emprise. +Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_get_feature_by_id`. (source : GĂ©oplateforme (WFS, ADMINEXPRESS-COG.LATEST)). ``` @@ -370,15 +370,15 @@ Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gp }, "feature_ref": { "type": "object", - "description": "RĂ©fĂ©rence WFS rĂ©utilisable, notamment avec `gpf_wfs_get_features` et `intersects_feature_filter`.", + "description": "RĂ©fĂ©rence GPF rĂ©utilisable, notamment avec `gpf_get_features` et `intersects_feature_filter`.", "properties": { "typename": { "type": "string", - "description": "Le `typename` WFS rĂ©utilisable pour une requĂȘte ultĂ©rieure." + "description": "Le `typename` GPF rĂ©utilisable pour une requĂȘte ultĂ©rieure." }, "feature_id": { "type": "string", - "description": "L'identifiant WFS rĂ©utilisable du feature." + "description": "L'identifiant GPF rĂ©utilisable du feature." } }, "required": [ @@ -422,10 +422,10 @@ Informations cadastrales ``` Renvoie, pour un point donnĂ© par sa `longitude` et sa `latitude`, la liste des objets cadastraux (arrondissement, commune, feuille, parcelle, subdivision_fiscale, localisant) les plus proches, avec leurs informations associĂ©es. -Les rĂ©sultats sont retournĂ©s au plus une fois par type lorsqu'ils sont disponibles et incluent un `feature_ref` WFS rĂ©utilisable. -Le `feature_ref` est directement rĂ©utilisable dans `gpf_wfs_get_features` avec `intersects_feature_filter`. +Les rĂ©sultats sont retournĂ©s au plus une fois par type lorsqu'ils sont disponibles et incluent un `feature_ref` GPF rĂ©utilisable. +Le `feature_ref` est directement rĂ©utilisable dans `gpf_get_features` avec `intersects_feature_filter`. La distance de recherche est fixĂ©e Ă  10 mĂštres. Si aucun objet n'est trouvĂ© dans les 10 mĂštres, le rĂ©sultat est vide. -Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_wfs_get_feature_by_id`. +Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_get_feature_by_id`. (source : GĂ©oplateforme (WFS, CADASTRALPARCELS.PARCELLAIRE_EXPRESS)). ``` @@ -501,15 +501,15 @@ Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gp }, "feature_ref": { "type": "object", - "description": "RĂ©fĂ©rence WFS rĂ©utilisable, notamment avec `gpf_wfs_get_features` et `intersects_feature_filter`.", + "description": "RĂ©fĂ©rence GPF rĂ©utilisable, notamment avec `gpf_get_features` et `intersects_feature_filter`.", "properties": { "typename": { "type": "string", - "description": "Le `typename` WFS rĂ©utilisable pour une requĂȘte ultĂ©rieure." + "description": "Le `typename` GPF rĂ©utilisable pour une requĂȘte ultĂ©rieure." }, "feature_id": { "type": "string", - "description": "L'identifiant WFS rĂ©utilisable du feature." + "description": "L'identifiant GPF rĂ©utilisable du feature." } }, "required": [ @@ -564,9 +564,9 @@ Informations d’urbanisme ``` Renvoie, pour un point donnĂ© par sa `longitude` et sa `latitude`, la liste des objets d'urbanisme pertinents du GĂ©oportail de l'Urbanisme (document, zones, prescriptions, informations, etc.), avec leurs propriĂ©tĂ©s associĂ©es. (source : GĂ©oplateforme - (WFS GĂ©oportail de l'Urbanisme)). Les rĂ©sultats peuvent notamment inclure le document d'urbanisme applicable ainsi que des Ă©lĂ©ments rĂ©glementaires associĂ©s Ă  proximitĂ© du point. -Quand un objet correspond Ă  une couche WFS rĂ©utilisable, il expose aussi un `feature_ref` compatible avec `gpf_wfs_get_features` et `intersects_feature_filter`. +Quand un objet correspond Ă  une couche GPF rĂ©utilisable, il expose aussi un `feature_ref` compatible avec `gpf_get_features` et `intersects_feature_filter`. Le zonage PLU (zone U, AU, A, N...) est inclus dans les zones retournĂ©es et constitue souvent l'information principale recherchĂ©e. -Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_wfs_get_feature_by_id`. +Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_get_feature_by_id`. ModĂšles d'URL GĂ©oportail de l'Urbanisme : - fiche document: https://www.geoportail-urbanisme.gouv.fr/document/by-id/{gpu_doc_id} - carte: https://www.geoportail-urbanisme.gouv.fr/map/?documentId={gpu_doc_id} @@ -645,15 +645,15 @@ ModĂšles d'URL GĂ©oportail de l'Urbanisme : }, "feature_ref": { "type": "object", - "description": "RĂ©fĂ©rence WFS rĂ©utilisable, notamment avec `gpf_wfs_get_features` et `intersects_feature_filter`.", + "description": "RĂ©fĂ©rence GPF rĂ©utilisable, notamment avec `gpf_get_features` et `intersects_feature_filter`.", "properties": { "typename": { "type": "string", - "description": "Le `typename` WFS rĂ©utilisable pour une requĂȘte ultĂ©rieure." + "description": "Le `typename` GPF rĂ©utilisable pour une requĂȘte ultĂ©rieure." }, "feature_id": { "type": "string", - "description": "L'identifiant WFS rĂ©utilisable du feature." + "description": "L'identifiant GPF rĂ©utilisable du feature." } }, "required": [ @@ -702,8 +702,8 @@ Servitudes d’utilitĂ© publique ``` Renvoie, pour un point donnĂ© par sa longitude et sa latitude, la liste des assiettes de servitudes d'utilitĂ© publique (SUP) pertinentes Ă  proximitĂ©, avec leurs propriĂ©tĂ©s associĂ©es. Une SUP est une contrainte lĂ©gale sur l'usage du sol liĂ©e Ă  un Ă©quipement ou une infrastructure publique (ex : AC pour patrimoine, EL pour voirie, PT pour tĂ©lĂ©coms, I pour installations classĂ©es...). -Les rĂ©sultats peuvent inclure des assiettes ponctuelles, linĂ©aires ou surfaciques et exposent un `feature_ref` WFS rĂ©utilisable quand il est disponible. -Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_wfs_get_feature_by_id`. +Les rĂ©sultats peuvent inclure des assiettes ponctuelles, linĂ©aires ou surfaciques et exposent un `feature_ref` GPF rĂ©utilisable quand il est disponible. +Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_get_feature_by_id`. (source : GĂ©oplateforme - (WFS GĂ©oportail de l'Urbanisme)). ``` @@ -779,15 +779,15 @@ Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gp }, "feature_ref": { "type": "object", - "description": "RĂ©fĂ©rence WFS rĂ©utilisable, notamment avec `gpf_wfs_get_features` et `intersects_feature_filter`.", + "description": "RĂ©fĂ©rence GPF rĂ©utilisable, notamment avec `gpf_get_features` et `intersects_feature_filter`.", "properties": { "typename": { "type": "string", - "description": "Le `typename` WFS rĂ©utilisable pour une requĂȘte ultĂ©rieure." + "description": "Le `typename` GPF rĂ©utilisable pour une requĂȘte ultĂ©rieure." }, "feature_id": { "type": "string", - "description": "L'identifiant WFS rĂ©utilisable du feature." + "description": "L'identifiant GPF rĂ©utilisable du feature." } }, "required": [ @@ -823,21 +823,21 @@ Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gp | SuccĂšs | oui | oui | `content[0].text` est `JSON.stringify(structuredContent)`. | | Erreur | oui | oui | `content[0].text` contient `structuredContent.detail`, pas le JSON d'erreur complet de `structuredContent`. | -## `gpf_wfs_search_types` +## `gpf_search_types` -Code Source : [src/tools/GpfWfsSearchTypesTool.ts](../src/tools/GpfWfsSearchTypesTool.ts) +Code Source : [src/tools/GpfSearchTypesTool.ts](../src/tools/GpfSearchTypesTool.ts) ### Titre -Recherche de types WFS +Recherche de types GPF ### Description du tool ``` -Recherche des types WFS de la GĂ©oplateforme (GPF) Ă  partir de mots-clĂ©s afin de trouver un identifiant de type (`typename`) valide. +Recherche des types de la GĂ©oplateforme (GPF) Ă  partir de mots-clĂ©s afin de trouver un identifiant de type (`typename`) valide. La recherche est textuelle (mini-search) et retourne une liste ordonnĂ©e de candidats avec leur identifiant, leur titre, leur description et un score de pertinence Ă©ventuel. Le paramĂštre `max_results` permet d'Ă©largir le nombre de candidats retournĂ©s (10 par dĂ©faut). -**Important** : Utiliser ce tool avant `gpf_wfs_describe_type` ou `gpf_wfs_get_features` lorsque le nom exact du type n'est pas connu. +**Important** : Utiliser ce tool avant `gpf_describe_type` ou `gpf_get_features` lorsque le nom exact du type n'est pas connu. **Important** : PrivilĂ©gier des termes mĂ©tier en français pour la recherche. ``` @@ -879,7 +879,7 @@ Le paramĂštre `max_results` permet d'Ă©largir le nombre de candidats retournĂ©s | Champ | Type | Requis | Description | | --- | --- | --- | --- | -| `results` | array | oui | La liste ordonnĂ©e des types WFS trouvĂ©s. | +| `results` | array | oui | La liste ordonnĂ©e des types GPF trouvĂ©s. |
SchĂ©ma de sortie brut @@ -890,21 +890,21 @@ Le paramĂštre `max_results` permet d'Ă©largir le nombre de candidats retournĂ©s "properties": { "results": { "type": "array", - "description": "La liste ordonnĂ©e des types WFS trouvĂ©s.", + "description": "La liste ordonnĂ©e des types GPF trouvĂ©s.", "items": { "type": "object", "properties": { "id": { "type": "string", - "description": "L'identifiant complet du type WFS." + "description": "L'identifiant complet du type GPF." }, "title": { "type": "string", - "description": "Le titre lisible du type WFS." + "description": "Le titre lisible du type GPF." }, "description": { "type": "string", - "description": "La description du type WFS." + "description": "La description du type GPF." }, "score": { "type": "number", @@ -934,19 +934,19 @@ Le paramĂštre `max_results` permet d'Ă©largir le nombre de candidats retournĂ©s | SuccĂšs | oui | oui | `content[0].text` est `JSON.stringify(structuredContent)`. | | Erreur | oui | oui | `content[0].text` contient `structuredContent.detail`, pas le JSON d'erreur complet de `structuredContent`. | -## `gpf_wfs_describe_type` +## `gpf_describe_type` -Code Source : [src/tools/GpfWfsDescribeTypeTool.ts](../src/tools/GpfWfsDescribeTypeTool.ts) +Code Source : [src/tools/GpfDescribeTypeTool.ts](../src/tools/GpfDescribeTypeTool.ts) ### Titre -Description d’un type WFS +Description d’un type GPF ### Description du tool ``` -Renvoie le schĂ©ma dĂ©taillĂ© d'un type WFS Ă  partir de son identifiant (`typename`) : identifiants, description et liste des propriĂ©tĂ©s. -Utiliser ce tool aprĂšs `gpf_wfs_search_types` pour inspecter les propriĂ©tĂ©s disponibles avant d'appeler `gpf_wfs_get_features`. +Renvoie le schĂ©ma dĂ©taillĂ© d'un type GPF Ă  partir de son identifiant (`typename`) : identifiants, description et liste des propriĂ©tĂ©s. +Utiliser ce tool aprĂšs `gpf_search_types` pour inspecter les propriĂ©tĂ©s disponibles avant d'appeler `gpf_get_features`. La sortie inclut notamment le type des propriĂ©tĂ©s, leur description, leurs valeurs possibles (`enum`) lorsqu'elles existent **IMPORTANT : Appel fortement recommandĂ© si les noms exacts des propriĂ©tĂ©s ne sont pas connus : un nom de propriĂ©tĂ© incorrect provoque une erreur**. ``` @@ -982,12 +982,12 @@ La sortie inclut notamment le type des propriĂ©tĂ©s, leur description, leurs val | Champ | Type | Requis | Description | | --- | --- | --- | --- | -| `description` | string | oui | La description du type WFS. | -| `id` | string | oui | L'identifiant complet du type WFS. | -| `name` | string | oui | Le nom court du type WFS. | -| `namespace` | string | oui | L'espace de nommage du type WFS. | -| `properties` | array | oui | La liste des propriĂ©tĂ©s du type WFS. | -| `title` | string | oui | Le titre lisible du type WFS. | +| `description` | string | oui | La description du type GPF. | +| `id` | string | oui | L'identifiant complet du type GPF. | +| `name` | string | oui | Le nom court du type GPF. | +| `namespace` | string | oui | L'espace de nommage du type GPF. | +| `properties` | array | oui | La liste des propriĂ©tĂ©s du type GPF. | +| `title` | string | oui | Le titre lisible du type GPF. |
SchĂ©ma de sortie brut @@ -998,27 +998,27 @@ La sortie inclut notamment le type des propriĂ©tĂ©s, leur description, leurs val "properties": { "id": { "type": "string", - "description": "L'identifiant complet du type WFS." + "description": "L'identifiant complet du type GPF." }, "namespace": { "type": "string", - "description": "L'espace de nommage du type WFS." + "description": "L'espace de nommage du type GPF." }, "name": { "type": "string", - "description": "Le nom court du type WFS." + "description": "Le nom court du type GPF." }, "title": { "type": "string", - "description": "Le titre lisible du type WFS." + "description": "Le titre lisible du type GPF." }, "description": { "type": "string", - "description": "La description du type WFS." + "description": "La description du type GPF." }, "properties": { "type": "array", - "description": "La liste des propriĂ©tĂ©s du type WFS.", + "description": "La liste des propriĂ©tĂ©s du type GPF.", "items": { "type": "object", "properties": { @@ -1077,31 +1077,31 @@ La sortie inclut notamment le type des propriĂ©tĂ©s, leur description, leurs val | SuccĂšs | oui | oui | `content[0].text` est `JSON.stringify(structuredContent)`. | | Erreur | oui | oui | `content[0].text` contient `structuredContent.detail`, pas le JSON d'erreur complet de `structuredContent`. | -## `gpf_wfs_get_feature_by_id` +## `gpf_get_feature_by_id` -Code Source : [src/tools/GpfWfsGetFeatureByIdTool.ts](../src/tools/GpfWfsGetFeatureByIdTool.ts) +Code Source : [src/tools/GpfGetFeatureByIdTool.ts](../src/tools/GpfGetFeatureByIdTool.ts) ### Titre -Lecture d’un objet WFS par identifiant +Lecture d’un objet GPF par identifiant ### Description du tool ``` -RĂ©cupĂšre exactement un objet WFS Ă  partir de `typename` et `feature_id`, sans filtre attributaire ni spatial. -Ce tool est le chemin robuste quand vous disposez dĂ©jĂ  d'une `feature_ref { typename, feature_id }` issue d'un autre tool (`adminexpress`, `cadastre`, `urbanisme`, `assiette_sup`, `gpf_wfs_get_features`). +RĂ©cupĂšre exactement un objet GPF Ă  partir de `typename` et `feature_id`, sans filtre attributaire ni spatial. +Ce tool est le chemin robuste quand vous disposez dĂ©jĂ  d'une `feature_ref { typename, feature_id }` issue d'un autre tool (`adminexpress`, `cadastre`, `urbanisme`, `assiette_sup`, `gpf_get_features`). Le contrat garantit une cardinalitĂ© stricte : 0 rĂ©sultat ou plusieurs rĂ©sultats provoquent une erreur explicite. -Utiliser `result_type="http_post_request"` pour rĂ©cupĂ©rer une requĂȘte WFS POST robuste, ou `result_type="http_get_url"` pour rĂ©cupĂ©rer l'URL GET WFS Ă©quivalente et l'utiliser ou la visualiser dans un outil la supportant. +Utiliser `result_type="http_post_request"` pour rĂ©cupĂ©rer une requĂȘte POST robuste, ou `result_type="http_get_url"` pour rĂ©cupĂ©rer l'URL GET Ă©quivalente et l'utiliser ou la visualiser dans un outil la supportant. ``` ### SchĂ©ma d’entrĂ©e | Champ | Type | Requis | Description | | --- | --- | --- | --- | -| `feature_id` | string | oui | Identifiant WFS exact de l'objet Ă  rĂ©cupĂ©rer, par exemple `commune.8952`. | -| `result_type` | string (enum) | non | `results` renvoie une FeatureCollection normalisĂ©e avec exactement un objet. `http_post_request` renvoie une requĂȘte POST WFS robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET WFS Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant. Valeurs : results, http_post_request, http_get_url. Valeur par dĂ©faut : results. | +| `feature_id` | string | oui | Identifiant GPF exact de l'objet Ă  rĂ©cupĂ©rer, par exemple `commune.8952`. | +| `result_type` | string (enum) | non | `results` renvoie une FeatureCollection normalisĂ©e avec exactement un objet. `http_post_request` renvoie une requĂȘte POST robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant. Valeurs : results, http_post_request, http_get_url. Valeur par dĂ©faut : results. | | `select` | array | non | Liste des propriĂ©tĂ©s non gĂ©omĂ©triques Ă  renvoyer. Quand `result_type="http_post_request"` ou `result_type="http_get_url"`, la gĂ©omĂ©trie est automatiquement ajoutĂ©e. | -| `typename` | string | oui | Nom exact du type WFS Ă  interroger, par exemple `ADMINEXPRESS-COG.LATEST:commune`. | +| `typename` | string | oui | Nom exact du type GPF Ă  interroger, par exemple `ADMINEXPRESS-COG.LATEST:commune`. |
SchĂ©ma d’entrĂ©e brut @@ -1113,12 +1113,12 @@ Utiliser `result_type="http_post_request"` pour rĂ©cupĂ©rer une requĂȘte WFS POS "typename": { "type": "string", "minLength": 1, - "description": "Nom exact du type WFS Ă  interroger, par exemple `ADMINEXPRESS-COG.LATEST:commune`." + "description": "Nom exact du type GPF Ă  interroger, par exemple `ADMINEXPRESS-COG.LATEST:commune`." }, "feature_id": { "type": "string", "minLength": 1, - "description": "Identifiant WFS exact de l'objet Ă  rĂ©cupĂ©rer, par exemple `commune.8952`." + "description": "Identifiant GPF exact de l'objet Ă  rĂ©cupĂ©rer, par exemple `commune.8952`." }, "result_type": { "type": "string", @@ -1128,7 +1128,7 @@ Utiliser `result_type="http_post_request"` pour rĂ©cupĂ©rer une requĂȘte WFS POS "http_get_url" ], "default": "results", - "description": "`results` renvoie une FeatureCollection normalisĂ©e avec exactement un objet. `http_post_request` renvoie une requĂȘte POST WFS robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET WFS Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant." + "description": "`results` renvoie une FeatureCollection normalisĂ©e avec exactement un objet. `http_post_request` renvoie une requĂȘte POST robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant." }, "select": { "type": "array", @@ -1164,18 +1164,18 @@ Aucun `outputSchema` unique n'est exposĂ©. La sortie dĂ©pend de `result_type` (` | SuccĂšs `result_type="http_get_url"` | oui | oui | `content[0].text` est `JSON.stringify(structuredContent)`. | | Erreur | oui | oui | `content[0].text` contient `structuredContent.detail`, pas le JSON d'erreur complet de `structuredContent`. | -## `gpf_wfs_get_features` +## `gpf_get_features` -Code Source : [src/tools/GpfWfsGetFeaturesTool.ts](../src/tools/GpfWfsGetFeaturesTool.ts) +Code Source : [src/tools/GpfGetFeaturesTool.ts](../src/tools/GpfGetFeaturesTool.ts) ### Titre -Lecture d’objets WFS +Lecture d’objets GPF ### Description du tool ``` -Interroge un type WFS et renvoie des rĂ©sultats structurĂ©s sans demander au modĂšle d'Ă©crire du CQL ou du WFS. +Interroge un type GPF et renvoie des rĂ©sultats structurĂ©s. Utiliser `select` pour choisir les propriĂ©tĂ©s, `where` pour filtrer, `order_by` pour trier et un filtre spatial dĂ©diĂ© (`bbox_filter`, `intersects_point_filter`, `dwithin_point_filter`, `intersects_feature_filter` ou `travel_time_filter`) pour le spatial. Avec `result_type="http_post_request"` ou `result_type="http_get_url"`, la gĂ©omĂ©trie est automatiquement ajoutĂ©e aux propriĂ©tĂ©s sĂ©lectionnĂ©es pour garantir une requĂȘte cartographiable. Exemple attributaire : `where=[{ property: "code_insee", operator: "eq", value: "75056" }]`. Exemple bbox : `bbox_filter={ west: 2.1, south: 48.7, east: 2.5, north: 48.9 }`. @@ -1183,9 +1183,9 @@ Exemple point dans gĂ©omĂ©trie : `intersects_point_filter={ lon: 2.35, lat: 48.8 Exemple distance : `dwithin_point_filter={ lon: 2.35, lat: 48.85, distance_m: 500 }`. Exemple rĂ©utilisation : `intersects_feature_filter={ typename, feature_id }` avec `typename` et `feature_id` issus d'une `feature_ref`. Exemple temps de trajet : `travel_time_filter={ lon: 2.35, lat: 48.85, minutes: 15, profile: "pedestrian" }` pour les objets atteignables en 15 minutes Ă  pied depuis ce point. -⚠ Quand `typename` et `intersects_feature_filter.typename` sont identiques, utiliser `gpf_wfs_get_feature_by_id` pour rĂ©cupĂ©rer exactement l'objet ciblĂ©. -**OBLIGATOIRE : toujours appeler `gpf_wfs_describe_type` avant ce tool, sauf si `gpf_wfs_describe_type` a dĂ©jĂ  Ă©tĂ© appelĂ© pour ce mĂȘme typename dans la conversation en cours.** -Les noms de propriĂ©tĂ©s **ne peuvent pas ĂȘtre devinĂ©s** : ils sont spĂ©cifiques Ă  chaque typename et diffĂšrent systĂ©matiquement des conventions habituelles (ex : pas de nom_officiel, navigabilite sans accent, etc.). Toute tentative sans appel prĂ©alable Ă  `gpf_wfs_describe_type` **provoquera une erreur.** +⚠ Quand `typename` et `intersects_feature_filter.typename` sont identiques, utiliser `gpf_get_feature_by_id` pour rĂ©cupĂ©rer exactement l'objet ciblĂ©. +**OBLIGATOIRE : toujours appeler `gpf_describe_type` avant ce tool, sauf si `gpf_describe_type` a dĂ©jĂ  Ă©tĂ© appelĂ© pour ce mĂȘme typename dans la conversation en cours.** +Les noms de propriĂ©tĂ©s **ne peuvent pas ĂȘtre devinĂ©s** : ils sont spĂ©cifiques Ă  chaque typename et diffĂšrent systĂ©matiquement des conventions habituelles (ex : pas de nom_officiel, navigabilite sans accent, etc.). Toute tentative sans appel prĂ©alable Ă  `gpf_describe_type` **provoquera une erreur.** ``` ### SchĂ©ma d’entrĂ©e @@ -1194,14 +1194,14 @@ Les noms de propriĂ©tĂ©s **ne peuvent pas ĂȘtre devinĂ©s** : ils sont spĂ©cifiqu | --- | --- | --- | --- | | `bbox_filter` | object | non | Filtre spatial par boĂźte englobante. Exclusif avec les autres filtres spatiaux. | | `dwithin_point_filter` | object | non | Filtre spatial par distance Ă  un point. Exclusif avec les autres filtres spatiaux. | -| `intersects_feature_filter` | object | non | Filtre spatial par intersection avec un feature WFS de rĂ©fĂ©rence. Exclusif avec les autres filtres spatiaux. | +| `intersects_feature_filter` | object | non | Filtre spatial par intersection avec un feature GPF de rĂ©fĂ©rence. Exclusif avec les autres filtres spatiaux. | | `intersects_point_filter` | object | non | Filtre spatial par intersection avec un point. Exclusif avec les autres filtres spatiaux. | | `limit` | integer | non | Nombre maximum d'objets Ă  renvoyer. Valeur par dĂ©faut : 100. Maximum : 5000. Valeur par dĂ©faut : 100. | | `order_by` | array | non | Liste ordonnĂ©e des critĂšres de tri. | -| `result_type` | string (enum) | non | `results` renvoie une FeatureCollection avec les propriĂ©tĂ©s attributaires uniquement — **les gĂ©omĂ©tries ne sont pas incluses**, ce mode ne peut donc pas ĂȘtre utilisĂ© directement pour cartographier. `hits` renvoie uniquement le nombre total d'objets correspondant Ă  la requĂȘte. `http_post_request` renvoie une requĂȘte POST WFS robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET WFS Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant. Avec `http_post_request` ou `http_get_url`, la gĂ©omĂ©trie est automatiquement ajoutĂ©e aux propriĂ©tĂ©s du `select` pour garantir l'affichage cartographique. Valeurs : results, hits, http_post_request, http_get_url. Valeur par dĂ©faut : results. | -| `select` | array | non | Liste des propriĂ©tĂ©s non gĂ©omĂ©triques Ă  renvoyer pour chaque objet. Utiliser `gpf_wfs_describe_type` pour connaĂźtre les noms exacts disponibles. Exemple : `["code_insee", "nom_officiel"]`. | +| `result_type` | string (enum) | non | `results` renvoie une FeatureCollection avec les propriĂ©tĂ©s attributaires uniquement — **les gĂ©omĂ©tries ne sont pas incluses**, ce mode ne peut donc pas ĂȘtre utilisĂ© directement pour cartographier. `hits` renvoie uniquement le nombre total d'objets correspondant Ă  la requĂȘte. `http_post_request` renvoie une requĂȘte POST robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant. Avec `http_post_request` ou `http_get_url`, la gĂ©omĂ©trie est automatiquement ajoutĂ©e aux propriĂ©tĂ©s du `select` pour garantir l'affichage cartographique. Valeurs : results, hits, http_post_request, http_get_url. Valeur par dĂ©faut : results. | +| `select` | array | non | Liste des propriĂ©tĂ©s non gĂ©omĂ©triques Ă  renvoyer pour chaque objet. Utiliser `gpf_describe_type` pour connaĂźtre les noms exacts disponibles. Exemple : `["code_insee", "nom_officiel"]`. | | `travel_time_filter` | object | non | Filtre spatial par temps de trajet depuis un point (`profile` voiture ou piĂ©ton). Exclusif avec les autres filtres spatiaux. | -| `typename` | string | oui | Nom exact du type WFS Ă  interroger, par exemple `BDTOPO_V3:batiment`. Utiliser `gpf_wfs_search_types` pour trouver un `typename` valide. | +| `typename` | string | oui | Nom exact du type GPF Ă  interroger, par exemple `BDTOPO_V3:batiment`. Utiliser `gpf_search_types` pour trouver un `typename` valide. | | `where` | array | non | Clauses de filtre attributaire, combinĂ©es avec `AND`. |
@@ -1214,7 +1214,7 @@ Les noms de propriĂ©tĂ©s **ne peuvent pas ĂȘtre devinĂ©s** : ils sont spĂ©cifiqu "typename": { "type": "string", "minLength": 1, - "description": "Nom exact du type WFS Ă  interroger, par exemple `BDTOPO_V3:batiment`. Utiliser `gpf_wfs_search_types` pour trouver un `typename` valide." + "description": "Nom exact du type GPF Ă  interroger, par exemple `BDTOPO_V3:batiment`. Utiliser `gpf_search_types` pour trouver un `typename` valide." }, "limit": { "type": "integer", @@ -1232,7 +1232,7 @@ Les noms de propriĂ©tĂ©s **ne peuvent pas ĂȘtre devinĂ©s** : ils sont spĂ©cifiqu "http_get_url" ], "default": "results", - "description": "`results` renvoie une FeatureCollection avec les propriĂ©tĂ©s attributaires uniquement — **les gĂ©omĂ©tries ne sont pas incluses**, ce mode ne peut donc pas ĂȘtre utilisĂ© directement pour cartographier. `hits` renvoie uniquement le nombre total d'objets correspondant Ă  la requĂȘte. `http_post_request` renvoie une requĂȘte POST WFS robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET WFS Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant. Avec `http_post_request` ou `http_get_url`, la gĂ©omĂ©trie est automatiquement ajoutĂ©e aux propriĂ©tĂ©s du `select` pour garantir l'affichage cartographique." + "description": "`results` renvoie une FeatureCollection avec les propriĂ©tĂ©s attributaires uniquement — **les gĂ©omĂ©tries ne sont pas incluses**, ce mode ne peut donc pas ĂȘtre utilisĂ© directement pour cartographier. `hits` renvoie uniquement le nombre total d'objets correspondant Ă  la requĂȘte. `http_post_request` renvoie une requĂȘte POST robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant. Avec `http_post_request` ou `http_get_url`, la gĂ©omĂ©trie est automatiquement ajoutĂ©e aux propriĂ©tĂ©s du `select` pour garantir l'affichage cartographique." }, "select": { "type": "array", @@ -1241,7 +1241,7 @@ Les noms de propriĂ©tĂ©s **ne peuvent pas ĂȘtre devinĂ©s** : ils sont spĂ©cifiqu "minLength": 1 }, "minItems": 1, - "description": "Liste des propriĂ©tĂ©s non gĂ©omĂ©triques Ă  renvoyer pour chaque objet. Utiliser `gpf_wfs_describe_type` pour connaĂźtre les noms exacts disponibles. Exemple : `[\"code_insee\", \"nom_officiel\"]`." + "description": "Liste des propriĂ©tĂ©s non gĂ©omĂ©triques Ă  renvoyer pour chaque objet. Utiliser `gpf_describe_type` pour connaĂźtre les noms exacts disponibles. Exemple : `[\"code_insee\", \"nom_officiel\"]`." }, "order_by": { "type": "array", @@ -1251,7 +1251,7 @@ Les noms de propriĂ©tĂ©s **ne peuvent pas ĂȘtre devinĂ©s** : ils sont spĂ©cifiqu "property": { "type": "string", "minLength": 1, - "description": "Nom exact d'une propriĂ©tĂ© non gĂ©omĂ©trique Ă  utiliser pour le tri. Utiliser `gpf_wfs_describe_type` pour connaĂźtre les noms exacts disponibles." + "description": "Nom exact d'une propriĂ©tĂ© non gĂ©omĂ©trique Ă  utiliser pour le tri. Utiliser `gpf_describe_type` pour connaĂźtre les noms exacts disponibles." }, "direction": { "type": "string", @@ -1280,7 +1280,7 @@ Les noms de propriĂ©tĂ©s **ne peuvent pas ĂȘtre devinĂ©s** : ils sont spĂ©cifiqu "property": { "type": "string", "minLength": 1, - "description": "Nom exact d'une propriĂ©tĂ© non gĂ©omĂ©trique du type WFS. Utiliser `gpf_wfs_describe_type` pour connaĂźtre les noms exacts disponibles." + "description": "Nom exact d'une propriĂ©tĂ© non gĂ©omĂ©trique du type GPF. Utiliser `gpf_describe_type` pour connaĂźtre les noms exacts disponibles." }, "operator": { "type": "string", @@ -1414,7 +1414,7 @@ Les noms de propriĂ©tĂ©s **ne peuvent pas ĂȘtre devinĂ©s** : ils sont spĂ©cifiqu "typename": { "type": "string", "minLength": 1, - "description": "Type WFS du feature de rĂ©fĂ©rence." + "description": "Type GPF du feature de rĂ©fĂ©rence." }, "feature_id": { "type": "string", @@ -1427,7 +1427,7 @@ Les noms de propriĂ©tĂ©s **ne peuvent pas ĂȘtre devinĂ©s** : ils sont spĂ©cifiqu "feature_id" ], "additionalProperties": false, - "description": "Filtre spatial par intersection avec un feature WFS de rĂ©fĂ©rence. Exclusif avec les autres filtres spatiaux." + "description": "Filtre spatial par intersection avec un feature GPF de rĂ©fĂ©rence. Exclusif avec les autres filtres spatiaux." }, "travel_time_filter": { "type": "object", diff --git a/scripts/generate-mcp-docs.mjs b/scripts/generate-mcp-docs.mjs index 6ee34d0..49572ad 100644 --- a/scripts/generate-mcp-docs.mjs +++ b/scripts/generate-mcp-docs.mjs @@ -16,10 +16,10 @@ const toolDisplayOrder = [ "cadastre", "urbanisme", "assiette_sup", - "gpf_wfs_search_types", - "gpf_wfs_describe_type", - "gpf_wfs_get_feature_by_id", - "gpf_wfs_get_features", + "gpf_search_types", + "gpf_describe_type", + "gpf_get_feature_by_id", + "gpf_get_features", ]; /** @@ -192,7 +192,7 @@ export function renderResponseContractSection(definition) { relation: "`content[0].text` contient `structuredContent.detail`, pas le JSON d'erreur complet de `structuredContent`.", }; - if (definition.name === "gpf_wfs_get_features") { + if (definition.name === "gpf_get_features") { return [ "### RĂ©ponse MCP", "", @@ -226,7 +226,7 @@ export function renderResponseContractSection(definition) { ].join("\n"); } - if (definition.name === "gpf_wfs_get_feature_by_id") { + if (definition.name === "gpf_get_feature_by_id") { return [ "### RĂ©ponse MCP", "", diff --git a/src/config/env.ts b/src/config/env.ts index 3f495e9..fd0bcf6 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -87,7 +87,7 @@ const envSchema = z.object({ GPF_GEOCODE_RATE_LIMIT: z.preprocess(emptyToUndefined, positiveIntegerSchema.default(50)), GPF_ALTI_RATE_LIMIT: z.preprocess(emptyToUndefined, positiveIntegerSchema.default(50)), GPF_NAVIGATION_RATE_LIMIT: z.preprocess(emptyToUndefined, positiveIntegerSchema.default(5)), - // GPF WFS + // GPF GPF_WFS_MINISEARCH_OPTIONS: z .string() .trim() diff --git a/src/helpers/schemas.ts b/src/helpers/schemas.ts index e3d800f..5181459 100644 --- a/src/helpers/schemas.ts +++ b/src/helpers/schemas.ts @@ -15,6 +15,6 @@ export const latSchema = z .describe("La latitude du point."); export const featureRefSchema = z.object({ - typename: z.string().describe("Le `typename` WFS rĂ©utilisable pour une requĂȘte ultĂ©rieure."), - feature_id: z.string().describe("L'identifiant WFS rĂ©utilisable du feature."), + typename: z.string().describe("Le `typename` GPF rĂ©utilisable pour une requĂȘte ultĂ©rieure."), + feature_id: z.string().describe("L'identifiant GPF rĂ©utilisable du feature."), }); diff --git a/src/tools/AdminexpressTool.ts b/src/tools/AdminexpressTool.ts index 53f174f..d682b1e 100644 --- a/src/tools/AdminexpressTool.ts +++ b/src/tools/AdminexpressTool.ts @@ -26,7 +26,7 @@ const adminexpressResultSchema = z type: z.string().describe(`Le type d'unitĂ© administrative (${ADMINEXPRESS_TYPES.join(", ")}).`), id: z.string().describe("L'identifiant de l'unitĂ© administrative."), bbox: z.array(z.number()).describe("La boĂźte englobante de l'unitĂ© administrative.").optional(), - feature_ref: featureRefSchema.describe("RĂ©fĂ©rence WFS rĂ©utilisable, notamment avec `gpf_wfs_get_features` et `intersects_feature_filter`."), + feature_ref: featureRefSchema.describe("RĂ©fĂ©rence GPF rĂ©utilisable, notamment avec `gpf_get_features` et `intersects_feature_filter`."), }) .catchall(z.unknown()); @@ -42,9 +42,9 @@ class AdminexpressTool extends BaseTool { annotations = READ_ONLY_OPEN_WORLD_TOOL_ANNOTATIONS; description = [ `Renvoie, pour un point donnĂ© par sa \`longitude\` et sa \`latitude\`, la liste des unitĂ©s administratives (${ADMINEXPRESS_TYPES.join(", ")}) qui le couvrent, sous forme d'objets typĂ©s contenant leurs propriĂ©tĂ©s administratives.`, - "Les rĂ©sultats incluent un `feature_ref` WFS rĂ©utilisable. Les propriĂ©tĂ©s incluent notamment le code INSEE.", - "Le `feature_ref` de chaque unitĂ© administrative est directement rĂ©utilisable dans `gpf_wfs_get_features` avec `intersects_feature_filter` pour interroger d'autres donnĂ©es sur cette emprise.", - "Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_wfs_get_feature_by_id`.", + "Les rĂ©sultats incluent un `feature_ref` GPF rĂ©utilisable. Les propriĂ©tĂ©s incluent notamment le code INSEE.", + "Le `feature_ref` de chaque unitĂ© administrative est directement rĂ©utilisable dans `gpf_get_features` avec `intersects_feature_filter` pour interroger d'autres donnĂ©es sur cette emprise.", + "Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_get_feature_by_id`.", `(source : ${ADMINEXPRESS_SOURCE}).` ].join("\n"); protected outputSchemaShape = adminexpressOutputSchema; diff --git a/src/tools/AssietteSupTool.ts b/src/tools/AssietteSupTool.ts index 75083d1..05971da 100644 --- a/src/tools/AssietteSupTool.ts +++ b/src/tools/AssietteSupTool.ts @@ -26,7 +26,7 @@ const assietteSupResultSchema = z type: z.string().describe("Le type d'assiette de servitude d'utilitĂ© publique renvoyĂ©."), id: z.string().describe("L'identifiant de l'assiette."), bbox: z.array(z.number()).describe("La boĂźte englobante de l'assiette.").optional(), - feature_ref: featureRefSchema.describe("RĂ©fĂ©rence WFS rĂ©utilisable, notamment avec `gpf_wfs_get_features` et `intersects_feature_filter`.").optional(), + feature_ref: featureRefSchema.describe("RĂ©fĂ©rence GPF rĂ©utilisable, notamment avec `gpf_get_features` et `intersects_feature_filter`.").optional(), distance: z.number().describe("La distance en mĂštres entre le point demandĂ© et l'assiette retenue."), }) .catchall(z.unknown()); @@ -44,8 +44,8 @@ class AssietteSupTool extends BaseTool { description = [ "Renvoie, pour un point donnĂ© par sa longitude et sa latitude, la liste des assiettes de servitudes d'utilitĂ© publique (SUP) pertinentes Ă  proximitĂ©, avec leurs propriĂ©tĂ©s associĂ©es.", "Une SUP est une contrainte lĂ©gale sur l'usage du sol liĂ©e Ă  un Ă©quipement ou une infrastructure publique (ex : AC pour patrimoine, EL pour voirie, PT pour tĂ©lĂ©coms, I pour installations classĂ©es...).", - "Les rĂ©sultats peuvent inclure des assiettes ponctuelles, linĂ©aires ou surfaciques et exposent un `feature_ref` WFS rĂ©utilisable quand il est disponible.", - "Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_wfs_get_feature_by_id`.", + "Les rĂ©sultats peuvent inclure des assiettes ponctuelles, linĂ©aires ou surfaciques et exposent un `feature_ref` GPF rĂ©utilisable quand il est disponible.", + "Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_get_feature_by_id`.", `(source : ${URBANISME_SOURCE}).` ].join("\n"); protected outputSchemaShape = assietteSupOutputSchema; diff --git a/src/tools/CadastreTool.ts b/src/tools/CadastreTool.ts index fc7dab6..9b79d84 100644 --- a/src/tools/CadastreTool.ts +++ b/src/tools/CadastreTool.ts @@ -26,7 +26,7 @@ const cadastreResultSchema = z type: z.string().describe(`Le type d'objet cadastral (${PARCELLAIRE_EXPRESS_TYPES.join(", ")}).`), id: z.string().describe("L'identifiant de l'objet cadastral."), bbox: z.array(z.number()).describe("La boĂźte englobante de l'objet cadastral.").optional(), - feature_ref: featureRefSchema.describe("RĂ©fĂ©rence WFS rĂ©utilisable, notamment avec `gpf_wfs_get_features` et `intersects_feature_filter`."), + feature_ref: featureRefSchema.describe("RĂ©fĂ©rence GPF rĂ©utilisable, notamment avec `gpf_get_features` et `intersects_feature_filter`."), distance: z.number().describe("La distance en mĂštres entre le point demandĂ© et l'objet cadastral retenu."), source: z.string().describe("La source des donnĂ©es cadastrales."), }) @@ -44,10 +44,10 @@ class CadastreTool extends BaseTool { annotations = READ_ONLY_OPEN_WORLD_TOOL_ANNOTATIONS; description = [ `Renvoie, pour un point donnĂ© par sa \`longitude\` et sa \`latitude\`, la liste des objets cadastraux (${PARCELLAIRE_EXPRESS_TYPES.join(", ")}) les plus proches, avec leurs informations associĂ©es.`, - "Les rĂ©sultats sont retournĂ©s au plus une fois par type lorsqu'ils sont disponibles et incluent un `feature_ref` WFS rĂ©utilisable.", - "Le `feature_ref` est directement rĂ©utilisable dans `gpf_wfs_get_features` avec `intersects_feature_filter`.", + "Les rĂ©sultats sont retournĂ©s au plus une fois par type lorsqu'ils sont disponibles et incluent un `feature_ref` GPF rĂ©utilisable.", + "Le `feature_ref` est directement rĂ©utilisable dans `gpf_get_features` avec `intersects_feature_filter`.", "La distance de recherche est fixĂ©e Ă  10 mĂštres. Si aucun objet n'est trouvĂ© dans les 10 mĂštres, le rĂ©sultat est vide.", - "Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_wfs_get_feature_by_id`.", + "Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_get_feature_by_id`.", `(source : ${PARCELLAIRE_EXPRESS_SOURCE}).` ].join("\n"); protected outputSchemaShape = cadastreOutputSchema; diff --git a/src/tools/GpfWfsDescribeTypeTool.ts b/src/tools/GpfDescribeTypeTool.ts similarity index 62% rename from src/tools/GpfWfsDescribeTypeTool.ts rename to src/tools/GpfDescribeTypeTool.ts index e1059f6..330573b 100644 --- a/src/tools/GpfWfsDescribeTypeTool.ts +++ b/src/tools/GpfDescribeTypeTool.ts @@ -12,7 +12,7 @@ import logger from "../logger.js"; // --- Schema --- -const gpfWfsDescribeTypeInputSchema = z.object({ +const GpfDescribeTypeInputSchema = z.object({ typename: z .string() .trim() @@ -22,9 +22,9 @@ const gpfWfsDescribeTypeInputSchema = z.object({ // --- Types --- -type GpfWfsDescribeTypeInput = z.infer; +type GpfDescribeTypeInput = z.infer; -const gpfWfsPropertySchema = z.object({ +const GpfPropertySchema = z.object({ name: z.string().describe("Le nom de la propriĂ©tĂ©."), type: z.string().describe("Le type de la propriĂ©tĂ©."), title: z.string().describe("Le titre lisible de la propriĂ©tĂ©.").optional(), @@ -33,30 +33,30 @@ const gpfWfsPropertySchema = z.object({ defaultCrs: z.string().describe("Le systĂšme de coordonnĂ©es par dĂ©faut si la propriĂ©tĂ© est gĂ©omĂ©trique.").optional(), }); -const gpfWfsDescribeTypeOutputSchema = z.object({ - id: z.string().describe("L'identifiant complet du type WFS."), - namespace: z.string().describe("L'espace de nommage du type WFS."), - name: z.string().describe("Le nom court du type WFS."), - title: z.string().describe("Le titre lisible du type WFS."), - description: z.string().describe("La description du type WFS."), - properties: z.array(gpfWfsPropertySchema).describe("La liste des propriĂ©tĂ©s du type WFS."), +const GpfDescribeTypeOutputSchema = z.object({ + id: z.string().describe("L'identifiant complet du type GPF."), + namespace: z.string().describe("L'espace de nommage du type GPF."), + name: z.string().describe("Le nom court du type GPF."), + title: z.string().describe("Le titre lisible du type GPF."), + description: z.string().describe("La description du type GPF."), + properties: z.array(GpfPropertySchema).describe("La liste des propriĂ©tĂ©s du type GPF."), }); // --- Tool --- -class GpfWfsDescribeTypeTool extends BaseTool { - name = "gpf_wfs_describe_type"; - title = "Description d’un type WFS"; +class GpfDescribeTypeTool extends BaseTool { + name = "gpf_describe_type"; + title = "Description d’un type GPF"; annotations = READ_ONLY_OPEN_WORLD_TOOL_ANNOTATIONS; description = [ - "Renvoie le schĂ©ma dĂ©taillĂ© d'un type WFS Ă  partir de son identifiant (`typename`) : identifiants, description et liste des propriĂ©tĂ©s.", - "Utiliser ce tool aprĂšs `gpf_wfs_search_types` pour inspecter les propriĂ©tĂ©s disponibles avant d'appeler `gpf_wfs_get_features`.", + "Renvoie le schĂ©ma dĂ©taillĂ© d'un type GPF Ă  partir de son identifiant (`typename`) : identifiants, description et liste des propriĂ©tĂ©s.", + "Utiliser ce tool aprĂšs `gpf_search_types` pour inspecter les propriĂ©tĂ©s disponibles avant d'appeler `gpf_get_features`.", "La sortie inclut notamment le type des propriĂ©tĂ©s, leur description, leurs valeurs possibles (`enum`) lorsqu'elles existent", "**IMPORTANT : Appel fortement recommandĂ© si les noms exacts des propriĂ©tĂ©s ne sont pas connus : un nom de propriĂ©tĂ© incorrect provoque une erreur**." ].join("\n"); - protected outputSchemaShape = gpfWfsDescribeTypeOutputSchema; + protected outputSchemaShape = GpfDescribeTypeOutputSchema; - schema = gpfWfsDescribeTypeInputSchema; + schema = GpfDescribeTypeInputSchema; /** * Loads the detailed schema description for one WFS typename. @@ -64,7 +64,7 @@ class GpfWfsDescribeTypeTool extends BaseTool { * @param input Normalized tool input. * @returns The detailed feature type description from the embedded catalog. */ - async execute(input: GpfWfsDescribeTypeInput) { + async execute(input: GpfDescribeTypeInput) { logger.info(`[tool] execute ${this.name} ...`, { input: input }); @@ -74,9 +74,9 @@ class GpfWfsDescribeTypeTool extends BaseTool { return featureType; } catch (e: unknown) { const message = e instanceof Error ? e.message : String(e); - throw new Error(`${message}. Utiliser gpf_wfs_search_types pour trouver un type valide.`); + throw new Error(`${message}. Utiliser gpf_search_types pour trouver un type valide.`); } } } -export default GpfWfsDescribeTypeTool; +export default GpfDescribeTypeTool; diff --git a/src/tools/GpfWfsGetFeatureByIdTool.ts b/src/tools/GpfGetFeatureByIdTool.ts similarity index 79% rename from src/tools/GpfWfsGetFeatureByIdTool.ts rename to src/tools/GpfGetFeatureByIdTool.ts index 21edb84..ce2ec21 100644 --- a/src/tools/GpfWfsGetFeatureByIdTool.ts +++ b/src/tools/GpfGetFeatureByIdTool.ts @@ -17,30 +17,30 @@ import { toWfsHttpPostRequestPayload, } from "../wfs/request.js"; import { - gpfWfsGetFeatureByIdHttpGetUrlOutputSchema, - gpfWfsGetFeatureByIdHttpPostRequestOutputSchema, - gpfWfsGetFeatureByIdInputSchema, - type GpfWfsGetFeatureByIdInput, - gpfWfsGetFeatureByIdPublishedInputSchema, + gpfGetFeatureByIdHttpGetUrlOutputSchema, + gpfGetFeatureByIdHttpPostRequestOutputSchema, + gpfGetFeatureByIdInputSchema, + type GpfGetFeatureByIdInput, + gpfGetFeatureByIdPublishedInputSchema, } from "../wfs/schema.js"; import logger from "../logger.js"; // --- Tool --- -class GpfWfsGetFeatureByIdTool extends BaseTool { - name = "gpf_wfs_get_feature_by_id"; - title = "Lecture d’un objet WFS par identifiant"; +class GpfGetFeatureByIdTool extends BaseTool { + name = "gpf_get_feature_by_id"; + title = "Lecture d’un objet GPF par identifiant"; annotations = READ_ONLY_OPEN_WORLD_TOOL_ANNOTATIONS; description = [ - "RĂ©cupĂšre exactement un objet WFS Ă  partir de `typename` et `feature_id`, sans filtre attributaire ni spatial.", - "Ce tool est le chemin robuste quand vous disposez dĂ©jĂ  d'une `feature_ref { typename, feature_id }` issue d'un autre tool (`adminexpress`, `cadastre`, `urbanisme`, `assiette_sup`, `gpf_wfs_get_features`).", + "RĂ©cupĂšre exactement un objet GPF Ă  partir de `typename` et `feature_id`, sans filtre attributaire ni spatial.", + "Ce tool est le chemin robuste quand vous disposez dĂ©jĂ  d'une `feature_ref { typename, feature_id }` issue d'un autre tool (`adminexpress`, `cadastre`, `urbanisme`, `assiette_sup`, `gpf_get_features`).", "Le contrat garantit une cardinalitĂ© stricte : 0 rĂ©sultat ou plusieurs rĂ©sultats provoquent une erreur explicite.", - "Utiliser `result_type=\"http_post_request\"` pour rĂ©cupĂ©rer une requĂȘte WFS POST robuste, ou `result_type=\"http_get_url\"` pour rĂ©cupĂ©rer l'URL GET WFS Ă©quivalente et l'utiliser ou la visualiser dans un outil la supportant." + "Utiliser `result_type=\"http_post_request\"` pour rĂ©cupĂ©rer une requĂȘte POST robuste, ou `result_type=\"http_get_url\"` pour rĂ©cupĂ©rer l'URL GET Ă©quivalente et l'utiliser ou la visualiser dans un outil la supportant." ].join("\n"); // `schema` remains the runtime validation source, while `inputSchema` // publishes the MCP-facing variant expected by clients. - schema = gpfWfsGetFeatureByIdInputSchema; + schema = gpfGetFeatureByIdInputSchema; /** * Exposes an input schema variant that stays compatible with most MCP integrations. @@ -48,7 +48,7 @@ class GpfWfsGetFeatureByIdTool extends BaseTool { * @returns The published input schema exposed through the MCP tool definition. */ get inputSchema() { - return gpfWfsGetFeatureByIdPublishedInputSchema; + return gpfGetFeatureByIdPublishedInputSchema; } /** @@ -69,7 +69,7 @@ class GpfWfsGetFeatureByIdTool extends BaseTool { "result_type" in data && data.result_type === "http_post_request" ) { - const payload = gpfWfsGetFeatureByIdHttpPostRequestOutputSchema.parse(data); + const payload = gpfGetFeatureByIdHttpPostRequestOutputSchema.parse(data); return { content: [{ type: "text" as const, text: JSON.stringify(payload) }], @@ -83,7 +83,7 @@ class GpfWfsGetFeatureByIdTool extends BaseTool { "result_type" in data && data.result_type === "http_get_url" ) { - const payload = gpfWfsGetFeatureByIdHttpGetUrlOutputSchema.parse(data); + const payload = gpfGetFeatureByIdHttpGetUrlOutputSchema.parse(data); return { content: [{ type: "text" as const, text: JSON.stringify(payload) }], @@ -104,7 +104,7 @@ class GpfWfsGetFeatureByIdTool extends BaseTool { } throw new Error( - "RĂ©ponse interne inattendue pour gpf_wfs_get_feature_by_id : le rĂ©sultat devrait ĂȘtre une requĂȘte HTTP WFS, une URL GET WFS ou une FeatureCollection.", + "RĂ©ponse interne inattendue pour gpf_get_feature_by_id : le rĂ©sultat devrait ĂȘtre une requĂȘte HTTP, une URL GET ou une FeatureCollection.", ); } @@ -115,7 +115,7 @@ class GpfWfsGetFeatureByIdTool extends BaseTool { * @param input Normalized tool input. * @returns Either an HTTP preview payload or a transformed FeatureCollection containing one feature. */ - async execute(input: GpfWfsGetFeatureByIdInput) { + async execute(input: GpfGetFeatureByIdInput) { logger.info(`[tool] execute ${this.name} ...`, { input: input }); @@ -142,4 +142,4 @@ class GpfWfsGetFeatureByIdTool extends BaseTool { } } -export default GpfWfsGetFeatureByIdTool; +export default GpfGetFeatureByIdTool; diff --git a/src/tools/GpfWfsGetFeaturesTool.ts b/src/tools/GpfGetFeaturesTool.ts similarity index 77% rename from src/tools/GpfWfsGetFeaturesTool.ts rename to src/tools/GpfGetFeaturesTool.ts index 0774f0c..4beb8be 100644 --- a/src/tools/GpfWfsGetFeaturesTool.ts +++ b/src/tools/GpfGetFeaturesTool.ts @@ -10,13 +10,13 @@ import { toWfsHttpPostRequestPayload, } from "../wfs/request.js"; import { - gpfWfsGetFeaturesHitsOutputSchema, - gpfWfsGetFeaturesHttpGetUrlOutputSchema, - gpfWfsGetFeaturesHttpPostRequestOutputSchema, - gpfWfsGetFeaturesInputSchema, - gpfWfsGetFeaturesInputObjectSchema, - type GpfWfsGetFeaturesInput, - gpfWfsGetFeaturesPublishedInputSchema, + gpfGetFeaturesHitsOutputSchema, + gpfGetFeaturesHttpGetUrlOutputSchema, + gpfGetFeaturesHttpPostRequestOutputSchema, + gpfGetFeaturesInputSchema, + gpfGetFeaturesInputObjectSchema, + type GpfGetFeaturesInput, + gpfGetFeaturesPublishedInputSchema, } from "../wfs/schema.js"; import logger from "../logger.js"; @@ -29,12 +29,13 @@ import logger from "../logger.js"; // --- Tool --- -class GpfWfsGetFeaturesTool extends BaseTool { - name = "gpf_wfs_get_features"; - title = "Lecture d’objets WFS"; +class GpfGetFeaturesTool extends BaseTool { + name = "gpf_get_features"; + title = "Lecture d’objets GPF"; annotations = READ_ONLY_OPEN_WORLD_TOOL_ANNOTATIONS; description = [ - "Interroge un type WFS et renvoie des rĂ©sultats structurĂ©s sans demander au modĂšle d'Ă©crire du CQL ou du WFS.", + "Interroge un type GPF et renvoie des rĂ©sultats structurĂ©s.", + "", "Utiliser `select` pour choisir les propriĂ©tĂ©s, `where` pour filtrer, `order_by` pour trier et un filtre spatial dĂ©diĂ© (`bbox_filter`, `intersects_point_filter`, `dwithin_point_filter`, `intersects_feature_filter` ou `travel_time_filter`) pour le spatial. Avec `result_type=\"http_post_request\"` ou `result_type=\"http_get_url\"`, la gĂ©omĂ©trie est automatiquement ajoutĂ©e aux propriĂ©tĂ©s sĂ©lectionnĂ©es pour garantir une requĂȘte cartographiable.", "Exemple attributaire : `where=[{ property: \"code_insee\", operator: \"eq\", value: \"75056\" }]`.", "Exemple bbox : `bbox_filter={ west: 2.1, south: 48.7, east: 2.5, north: 48.9 }`.", @@ -42,14 +43,14 @@ class GpfWfsGetFeaturesTool extends BaseTool { "Exemple distance : `dwithin_point_filter={ lon: 2.35, lat: 48.85, distance_m: 500 }`.", "Exemple rĂ©utilisation : `intersects_feature_filter={ typename, feature_id }` avec `typename` et `feature_id` issus d'une `feature_ref`.", "Exemple temps de trajet : `travel_time_filter={ lon: 2.35, lat: 48.85, minutes: 15, profile: \"pedestrian\" }` pour les objets atteignables en 15 minutes Ă  pied depuis ce point.", - "⚠ Quand `typename` et `intersects_feature_filter.typename` sont identiques, utiliser `gpf_wfs_get_feature_by_id` pour rĂ©cupĂ©rer exactement l'objet ciblĂ©.", - "**OBLIGATOIRE : toujours appeler `gpf_wfs_describe_type` avant ce tool, sauf si `gpf_wfs_describe_type` a dĂ©jĂ  Ă©tĂ© appelĂ© pour ce mĂȘme typename dans la conversation en cours.**", - "Les noms de propriĂ©tĂ©s **ne peuvent pas ĂȘtre devinĂ©s** : ils sont spĂ©cifiques Ă  chaque typename et diffĂšrent systĂ©matiquement des conventions habituelles (ex : pas de nom_officiel, navigabilite sans accent, etc.). Toute tentative sans appel prĂ©alable Ă  `gpf_wfs_describe_type` **provoquera une erreur.**", + "⚠ Quand `typename` et `intersects_feature_filter.typename` sont identiques, utiliser `gpf_get_feature_by_id` pour rĂ©cupĂ©rer exactement l'objet ciblĂ©.", + "**OBLIGATOIRE : toujours appeler `gpf_describe_type` avant ce tool, sauf si `gpf_describe_type` a dĂ©jĂ  Ă©tĂ© appelĂ© pour ce mĂȘme typename dans la conversation en cours.**", + "Les noms de propriĂ©tĂ©s **ne peuvent pas ĂȘtre devinĂ©s** : ils sont spĂ©cifiques Ă  chaque typename et diffĂšrent systĂ©matiquement des conventions habituelles (ex : pas de nom_officiel, navigabilite sans accent, etc.). Toute tentative sans appel prĂ©alable Ă  `gpf_describe_type` **provoquera une erreur.**", ].join("\n"); // The framework requires a plain Zod object here to publish a compatible // input schema. Cross-field runtime validation is applied in `execute`. - schema = gpfWfsGetFeaturesInputObjectSchema; + schema = gpfGetFeaturesInputObjectSchema; /** * Exposes an input schema variant that stays compatible with most MCP integrations. @@ -57,7 +58,7 @@ class GpfWfsGetFeaturesTool extends BaseTool { * @returns The published input schema exposed through the MCP tool definition. */ get inputSchema() { - return gpfWfsGetFeaturesPublishedInputSchema; + return gpfGetFeaturesPublishedInputSchema; } /** @@ -76,7 +77,7 @@ class GpfWfsGetFeaturesTool extends BaseTool { "totalFeatures" in data && typeof data.totalFeatures === "number" ) { - const payload = gpfWfsGetFeaturesHitsOutputSchema.parse(data); + const payload = gpfGetFeaturesHitsOutputSchema.parse(data); return { content: [{ type: "text" as const, text: JSON.stringify(payload) }], @@ -90,7 +91,7 @@ class GpfWfsGetFeaturesTool extends BaseTool { "result_type" in data && data.result_type === "http_post_request" ) { - const payload = gpfWfsGetFeaturesHttpPostRequestOutputSchema.parse(data); + const payload = gpfGetFeaturesHttpPostRequestOutputSchema.parse(data); return { content: [{ type: "text" as const, text: JSON.stringify(payload) }], @@ -104,7 +105,7 @@ class GpfWfsGetFeaturesTool extends BaseTool { "result_type" in data && data.result_type === "http_get_url" ) { - const payload = gpfWfsGetFeaturesHttpGetUrlOutputSchema.parse(data); + const payload = gpfGetFeaturesHttpGetUrlOutputSchema.parse(data); return { content: [{ type: "text" as const, text: JSON.stringify(payload) }], @@ -124,8 +125,8 @@ class GpfWfsGetFeaturesTool extends BaseTool { * @param input Normalized tool input. * @returns Either a compiled request, a hit count, or a transformed FeatureCollection. */ - async execute(input: GpfWfsGetFeaturesInput) { - const validatedInput = gpfWfsGetFeaturesInputSchema.parse(input); + async execute(input: GpfGetFeaturesInput) { + const validatedInput = gpfGetFeaturesInputSchema.parse(input); logger.info(`[tool] execute ${this.name} ...`, { input: validatedInput @@ -142,4 +143,4 @@ class GpfWfsGetFeaturesTool extends BaseTool { } } -export default GpfWfsGetFeaturesTool; +export default GpfGetFeaturesTool; diff --git a/src/tools/GpfWfsSearchTypesTool.ts b/src/tools/GpfSearchTypesTool.ts similarity index 63% rename from src/tools/GpfWfsSearchTypesTool.ts rename to src/tools/GpfSearchTypesTool.ts index 7a2f63f..ddf53fc 100644 --- a/src/tools/GpfWfsSearchTypesTool.ts +++ b/src/tools/GpfSearchTypesTool.ts @@ -11,7 +11,7 @@ import logger from "../logger.js"; // --- Schema --- -const gpfWfsSearchTypesInputSchema = z.object({ +const gpfSearchTypesInputSchema = z.object({ query: z .string() .trim() @@ -28,35 +28,35 @@ const gpfWfsSearchTypesInputSchema = z.object({ // --- Types --- -type GpfWfsSearchTypesInput = z.infer; +type GpfSearchTypesInput = z.infer; -const gpfWfsSearchTypeResultSchema = z.object({ - id: z.string().describe("L'identifiant complet du type WFS."), - title: z.string().describe("Le titre lisible du type WFS."), - description: z.string().describe("La description du type WFS."), +const gpfSearchTypeResultSchema = z.object({ + id: z.string().describe("L'identifiant complet du type GPF."), + title: z.string().describe("Le titre lisible du type GPF."), + description: z.string().describe("La description du type GPF."), score: z.number().describe("Le score de pertinence de la recherche.").optional(), }); -const gpfWfsSearchTypesOutputSchema = z.object({ - results: z.array(gpfWfsSearchTypeResultSchema).describe("La liste ordonnĂ©e des types WFS trouvĂ©s."), +const gpfSearchTypesOutputSchema = z.object({ + results: z.array(gpfSearchTypeResultSchema).describe("La liste ordonnĂ©e des types GPF trouvĂ©s."), }); // --- Tool --- -class GpfWfsSearchTypesTool extends BaseTool { - name = "gpf_wfs_search_types"; - title = "Recherche de types WFS"; +class GpfSearchTypesTool extends BaseTool { + name = "gpf_search_types"; + title = "Recherche de types GPF"; annotations = READ_ONLY_OPEN_WORLD_TOOL_ANNOTATIONS; description = [ - "Recherche des types WFS de la GĂ©oplateforme (GPF) Ă  partir de mots-clĂ©s afin de trouver un identifiant de type (`typename`) valide.", + "Recherche des types de la GĂ©oplateforme (GPF) Ă  partir de mots-clĂ©s afin de trouver un identifiant de type (`typename`) valide.", "La recherche est textuelle (mini-search) et retourne une liste ordonnĂ©e de candidats avec leur identifiant, leur titre, leur description et un score de pertinence Ă©ventuel.", "Le paramĂštre `max_results` permet d'Ă©largir le nombre de candidats retournĂ©s (10 par dĂ©faut).", - "**Important** : Utiliser ce tool avant `gpf_wfs_describe_type` ou `gpf_wfs_get_features` lorsque le nom exact du type n'est pas connu.", + "**Important** : Utiliser ce tool avant `gpf_describe_type` ou `gpf_get_features` lorsque le nom exact du type n'est pas connu.", "**Important** : PrivilĂ©gier des termes mĂ©tier en français pour la recherche." ].join("\n"); - protected outputSchemaShape = gpfWfsSearchTypesOutputSchema; + protected outputSchemaShape = gpfSearchTypesOutputSchema; - schema = gpfWfsSearchTypesInputSchema; + schema = gpfSearchTypesInputSchema; /** * Searches the embedded WFS type catalog from a free-text query. @@ -64,7 +64,7 @@ class GpfWfsSearchTypesTool extends BaseTool { * @param input Normalized tool input. * @returns The ordered search results, optionally enriched with relevance scores. */ - async execute(input: GpfWfsSearchTypesInput) { + async execute(input: GpfSearchTypesInput) { logger.info(`[tool] execute ${this.name} ...`, { input: input }); @@ -82,4 +82,4 @@ class GpfWfsSearchTypesTool extends BaseTool { } } -export default GpfWfsSearchTypesTool; +export default GpfSearchTypesTool; diff --git a/src/tools/UrbanismeTool.ts b/src/tools/UrbanismeTool.ts index da978a7..69cb73c 100644 --- a/src/tools/UrbanismeTool.ts +++ b/src/tools/UrbanismeTool.ts @@ -26,7 +26,7 @@ const urbanismeResultSchema = z type: z.string().describe("Le type d'objet d'urbanisme renvoyĂ©."), id: z.string().describe("L'identifiant de l'objet d'urbanisme."), bbox: z.array(z.number()).describe("La boĂźte englobante de l'objet d'urbanisme.").optional(), - feature_ref: featureRefSchema.describe("RĂ©fĂ©rence WFS rĂ©utilisable, notamment avec `gpf_wfs_get_features` et `intersects_feature_filter`.").optional(), + feature_ref: featureRefSchema.describe("RĂ©fĂ©rence GPF rĂ©utilisable, notamment avec `gpf_get_features` et `intersects_feature_filter`.").optional(), distance: z.number().describe("La distance en mĂštres entre le point demandĂ© et l'objet d'urbanisme retenu."), }) .catchall(z.unknown()); @@ -38,9 +38,9 @@ const urbanismeOutputSchema = z.object({ const URBANISME_TOOL_DESCRIPTION = [ `Renvoie, pour un point donnĂ© par sa \`longitude\` et sa \`latitude\`, la liste des objets d'urbanisme pertinents du GĂ©oportail de l'Urbanisme (document, zones, prescriptions, informations, etc.), avec leurs propriĂ©tĂ©s associĂ©es. (source : ${URBANISME_SOURCE}).`, "Les rĂ©sultats peuvent notamment inclure le document d'urbanisme applicable ainsi que des Ă©lĂ©ments rĂ©glementaires associĂ©s Ă  proximitĂ© du point.", - "Quand un objet correspond Ă  une couche WFS rĂ©utilisable, il expose aussi un `feature_ref` compatible avec `gpf_wfs_get_features` et `intersects_feature_filter`.", + "Quand un objet correspond Ă  une couche GPF rĂ©utilisable, il expose aussi un `feature_ref` compatible avec `gpf_get_features` et `intersects_feature_filter`.", "Le zonage PLU (zone U, AU, A, N...) est inclus dans les zones retournĂ©es et constitue souvent l'information principale recherchĂ©e.", - "Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_wfs_get_feature_by_id`.", + "Pour rĂ©cupĂ©rer exactement l'objet correspondant au `feature_ref`, utiliser `gpf_get_feature_by_id`.", "ModĂšles d'URL GĂ©oportail de l'Urbanisme :", "- fiche document: https://www.geoportail-urbanisme.gouv.fr/document/by-id/{gpu_doc_id}", "- carte: https://www.geoportail-urbanisme.gouv.fr/map/?documentId={gpu_doc_id}", diff --git a/src/wfs/catalog.ts b/src/wfs/catalog.ts index a143d50..006d2c0 100644 --- a/src/wfs/catalog.ts +++ b/src/wfs/catalog.ts @@ -12,7 +12,7 @@ import { getEnv } from '../config/env.js'; // --- Constants --- -export const GPF_WFS_URL = "https://data.geopf.fr/wfs"; +export const GPF_URL = "https://data.geopf.fr/wfs"; // Shared keys used by both `fields` and `boost` in MiniSearchCollectionSearchOptions. const MINISEARCH_INDEXED_OPTION_KEYS = [ diff --git a/src/wfs/features.ts b/src/wfs/features.ts index 3108132..da5fbf1 100644 --- a/src/wfs/features.ts +++ b/src/wfs/features.ts @@ -1,7 +1,7 @@ /** * Shared execution helpers for structured WFS feature search. * - * This module owns the WFS-side execution flow for `gpf_wfs_get_features`: + * This module owns the WFS-side execution flow for `gpf_get_features`: * request preparation, optional reference-geometry lookup, query execution, * hit counting, and FeatureCollection post-processing. */ @@ -30,7 +30,7 @@ import { type CompiledRequest, } from "./request.js"; import { attachFeatureRefs } from "./response.js"; -import type { GpfWfsGetFeaturesInput } from "./schema.js"; +import type { GpfGetFeaturesInput } from "./schema.js"; // --- Types --- @@ -59,7 +59,7 @@ type GeometryLike = { * @param input Normalized tool input. */ export function ensureIntersectsFeatureTargetsOtherTypename( - input: GpfWfsGetFeaturesInput, + input: GpfGetFeaturesInput, ) { const spatialFilter = getSpatialFilter(input); if ( @@ -68,7 +68,7 @@ export function ensureIntersectsFeatureTargetsOtherTypename( ) { throw new Error( "Le filtre `intersects_feature` sur le mĂȘme `typename` retourne potentiellement plusieurs objets. " + - "Utiliser `gpf_wfs_get_feature_by_id` avec `{ typename, feature_id: intersects_feature_filter.feature_id }` pour cibler exactement un objet.", + "Utiliser `gpf_get_feature_by_id` avec `{ typename, feature_id: intersects_feature_filter.feature_id }` pour cibler exactement un objet.", ); } } @@ -100,7 +100,7 @@ function isGeometryLike(value: unknown): value is GeometryLike { * @returns The resolved reference geometry, or `undefined` when no reference feature is needed. */ export async function resolveIntersectsFeatureGeometry( - input: GpfWfsGetFeaturesInput, + input: GpfGetFeaturesInput, ): Promise { const spatialFilter = getSpatialFilter(input); if (!spatialFilter || spatialFilter.operator !== "intersects_feature") { @@ -138,7 +138,7 @@ export async function resolveIntersectsFeatureGeometry( * @returns The resolved isochrone geometry, or `undefined` when no travel-time filter is requested. */ export async function resolveTravelTimeGeometry( - input: GpfWfsGetFeaturesInput, + input: GpfGetFeaturesInput, ): Promise { const spatialFilter = getSpatialFilter(input); if (!spatialFilter || spatialFilter.operator !== "travel_time") { @@ -164,7 +164,7 @@ export async function resolveTravelTimeGeometry( * @returns The resolved geometry, or `undefined` when the selected filter is already self-contained. */ export async function resolveSpatialFilterGeometry( - input: GpfWfsGetFeaturesInput, + input: GpfGetFeaturesInput, ): Promise { const spatialFilter = getSpatialFilter(input); @@ -181,7 +181,7 @@ export async function resolveSpatialFilterGeometry( // --- Request Preparation --- /** - * Prepares the main WFS request for `gpf_wfs_get_features`. + * Prepares the main WFS request for `gpf_get_features`. * * This includes upfront validation of unsupported same-typename * `intersects_feature` requests, feature type lookup, optional @@ -191,7 +191,7 @@ export async function resolveSpatialFilterGeometry( * @returns The compiled query fragments and final WFS request. */ export async function prepareGetFeaturesRequest( - input: GpfWfsGetFeaturesInput, + input: GpfGetFeaturesInput, ): Promise { // TODO: Assess if this guard does not prevent legitimate use cases. ensureIntersectsFeatureTargetsOtherTypename(input); @@ -221,14 +221,14 @@ export async function prepareGetFeaturesRequest( * @param input Normalized tool input. * @returns Either a hit-count payload or a transformed FeatureCollection. */ -export async function executeGetFeatures(input: GpfWfsGetFeaturesInput) { +export async function executeGetFeatures(input: GpfGetFeaturesInput) { const { compiled, request } = await prepareGetFeaturesRequest(input); let featureCollection: WfsFeatureCollectionResponse; try { logger.debug( - `[gpf_wfs_get_features] POST ${request.url}?${new URLSearchParams(request.query).toString()}`, + `[gpf_get_features] POST ${request.url}?${new URLSearchParams(request.query).toString()}`, ); featureCollection = await wfsClient.fetchFeatureCollection(request); } catch (error: unknown) { diff --git a/src/wfs/properties.ts b/src/wfs/properties.ts index 7ff78ff..8598b0e 100644 --- a/src/wfs/properties.ts +++ b/src/wfs/properties.ts @@ -8,7 +8,7 @@ */ import type { Collection, CollectionProperty } from "@ignfab/gpf-schema-store"; -import type { GpfWfsGetFeaturesInput } from "./schema.js"; +import type { GpfGetFeaturesInput } from "./schema.js"; // --- Property Listing --- @@ -65,7 +65,7 @@ function getPropertyOrThrow(featureType: Collection, propertyName: string) { if (!property) { throw new Error( `La propriĂ©tĂ© '${propertyName}' n'existe pas pour '${featureType.id}'. ` + - `Appelle \`gpf_wfs_describe_type\` pour obtenir la liste des propriĂ©tĂ©s disponibles.`, + `Appelle \`gpf_describe_type\` pour obtenir la liste des propriĂ©tĂ©s disponibles.`, ); } return property; @@ -128,7 +128,7 @@ export function validateSelectProperty(featureType: Collection, geometryProperty export function buildSelectList( featureType: Collection, geometryProperty: CollectionProperty, - input: GpfWfsGetFeaturesInput, + input: GpfGetFeaturesInput, ) { // If `select` is specified, only the requested properties are returned // after validation against the embedded catalog. diff --git a/src/wfs/queryPreparation.ts b/src/wfs/queryPreparation.ts index 5c3b234..a35ae20 100644 --- a/src/wfs/queryPreparation.ts +++ b/src/wfs/queryPreparation.ts @@ -19,7 +19,7 @@ import { import { getSpatialFilter } from "./spatialFilter.js"; import type { - GpfWfsGetFeaturesInput, + GpfGetFeaturesInput, OrderByClause, WhereClause, } from "./schema.js"; @@ -183,7 +183,7 @@ function compileOrderByClause(featureType: Collection, geometryProperty: Collect * @returns Compiled query parts used by request builders. */ export function compileQueryParts( - input: GpfWfsGetFeaturesInput, + input: GpfGetFeaturesInput, featureType: Collection, resolvedGeometryRef?: ResolvedFeatureGeometryRef, ): CompiledQuery { diff --git a/src/wfs/request.ts b/src/wfs/request.ts index 8fea5ae..b018fde 100644 --- a/src/wfs/request.ts +++ b/src/wfs/request.ts @@ -7,8 +7,8 @@ * - the compact HTTP payloads exposed by MCP WFS tools */ -import { GPF_WFS_URL } from "./catalog.js"; -import type { GpfWfsGetFeaturesInput } from "./schema.js"; +import { GPF_URL } from "./catalog.js"; +import type { GpfGetFeaturesInput } from "./schema.js"; // --- Transport Types --- @@ -126,7 +126,7 @@ export function buildGetUrl(url: string, query: Record, cqlFilte * @returns A POST request split into base URL, query-string parameters, encoded body, and optional GET variant. */ export function buildMainRequest( - input: GpfWfsGetFeaturesInput, + input: GpfGetFeaturesInput, compiled: { cqlFilter?: string; propertyName?: string; sortBy?: string }, ): CompiledRequest { const query: Record = { @@ -149,10 +149,10 @@ export function buildMainRequest( const body = buildBody(compiled.cqlFilter); return { method: "POST", - url: GPF_WFS_URL, + url: GPF_URL, query, body, - get_url: buildGetUrl(GPF_WFS_URL, query, compiled.cqlFilter), + get_url: buildGetUrl(GPF_URL, query, compiled.cqlFilter), }; } @@ -186,10 +186,10 @@ export function buildGetFeatureByIdRequest( return { method: "POST", - url: GPF_WFS_URL, + url: GPF_URL, query, body: "", - get_url: buildGetUrl(GPF_WFS_URL, query), + get_url: buildGetUrl(GPF_URL, query), }; } @@ -253,9 +253,9 @@ export function buildMultiTypenameRequest( return { method: "POST", - url: GPF_WFS_URL, + url: GPF_URL, query, body, - get_url: buildGetUrl(GPF_WFS_URL, query, expandedCqlFilter), + get_url: buildGetUrl(GPF_URL, query, expandedCqlFilter), }; } diff --git a/src/wfs/schema.ts b/src/wfs/schema.ts index 5bd9c71..f89d6d6 100644 --- a/src/wfs/schema.ts +++ b/src/wfs/schema.ts @@ -19,7 +19,7 @@ export const DEFAULT_LIMIT = 100; export const MAX_LIMIT = 5000; export const WHERE_OPERATORS = ["eq", "ne", "lt", "lte", "gt", "gte", "in", "is_null"] as const; export const ORDER_DIRECTIONS = ["asc", "desc"] as const; -export const GPF_WFS_GET_FEATURES_SPATIAL_FILTER_KEYS = [ +export const GPF_GET_FEATURES_SPATIAL_FILTER_KEYS = [ "bbox_filter", "intersects_point_filter", "dwithin_point_filter", @@ -30,14 +30,14 @@ export const GPF_WFS_GET_FEATURES_SPATIAL_FILTER_KEYS = [ // --- Shared Clauses --- const whereClauseSchema = z.object({ - property: z.string().trim().min(1).describe("Nom exact d'une propriĂ©tĂ© non gĂ©omĂ©trique du type WFS. Utiliser `gpf_wfs_describe_type` pour connaĂźtre les noms exacts disponibles."), + property: z.string().trim().min(1).describe("Nom exact d'une propriĂ©tĂ© non gĂ©omĂ©trique du type GPF. Utiliser `gpf_describe_type` pour connaĂźtre les noms exacts disponibles."), operator: z.enum(WHERE_OPERATORS).describe("OpĂ©rateur de filtre : `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, `in`, `is_null`."), value: z.string().optional().describe("Valeur scalaire sĂ©rialisĂ©e en texte, utilisĂ©e avec tous les opĂ©rateurs sauf `in` et `is_null`."), values: z.array(z.string()).min(1).optional().describe("Liste de valeurs sĂ©rialisĂ©es en texte, utilisĂ©e uniquement avec `operator = \"in\"`."), }).strict().describe("Clause de filtre structurĂ©e. Exemple : `{ property: \"code_insee\", operator: \"eq\", value: \"75056\" }`."); const orderBySchema = z.object({ - property: z.string().trim().min(1).describe("Nom exact d'une propriĂ©tĂ© non gĂ©omĂ©trique Ă  utiliser pour le tri. Utiliser `gpf_wfs_describe_type` pour connaĂźtre les noms exacts disponibles."), + property: z.string().trim().min(1).describe("Nom exact d'une propriĂ©tĂ© non gĂ©omĂ©trique Ă  utiliser pour le tri. Utiliser `gpf_describe_type` pour connaĂźtre les noms exacts disponibles."), direction: z.enum(ORDER_DIRECTIONS).default("asc").describe("Direction de tri : `asc` ou `desc`."), }).strict().describe("CritĂšre de tri structurĂ©. Exemple : `{ property: \"population\", direction: \"desc\" }`."); @@ -60,9 +60,9 @@ const dwithinPointFilterSchema = z.object({ }).strict().describe("Filtre les objets situĂ©s Ă  une distance maximale d'un point."); const intersectsFeatureFilterSchema = z.object({ - typename: z.string().trim().min(1).describe("Type WFS du feature de rĂ©fĂ©rence."), + typename: z.string().trim().min(1).describe("Type GPF du feature de rĂ©fĂ©rence."), feature_id: z.string().trim().min(1).describe("Identifiant du feature de rĂ©fĂ©rence."), -}).strict().describe("Filtre les objets dont la gĂ©omĂ©trie intersecte celle d'un objet WFS de rĂ©fĂ©rence."); +}).strict().describe("Filtre les objets dont la gĂ©omĂ©trie intersecte celle d'un objet GPF de rĂ©fĂ©rence."); const travelTimeFilterSchema = z.object({ lon: lonSchema.describe("Longitude du point de dĂ©part en WGS84 `lon/lat`."), @@ -81,35 +81,35 @@ const travelTimeFilterSchema = z.object({ // --- Shared Compact Outputs --- const wfsHttpPostRequestOutputSchema = z.object({ - result_type: z.literal("http_post_request").describe("Indique que la rĂ©ponse contient une requĂȘte WFS POST robuste Ă  exĂ©cuter par un client HTTP."), + result_type: z.literal("http_post_request").describe("Indique que la rĂ©ponse contient une requĂȘte POST robuste Ă  exĂ©cuter par un client HTTP."), http_post_request: z.object({ method: z.literal("POST").describe("MĂ©thode HTTP Ă  utiliser."), - url: z.string().url().describe("URL WFS avec les paramĂštres query standards, hors `cql_filter`."), + url: z.string().url().describe("L'URL de la requĂȘte."), headers: z.object({ "Content-Type": z.literal("application/x-www-form-urlencoded").describe("Type de contenu du corps POST."), }).strict().describe("En-tĂȘtes HTTP Ă  envoyer avec la requĂȘte POST."), - body: z.string().describe("Corps de la requĂȘte POST, encodĂ© en `application/x-www-form-urlencoded`; contient `cql_filter=...` quand un filtre existe."), - }).strict().describe("RequĂȘte HTTP POST complĂšte Ă  utiliser pour appeler directement le WFS."), + body: z.string().describe("Corps de la requĂȘte POST, encodĂ© en `application/x-www-form-urlencoded`."), + }).strict().describe("RequĂȘte HTTP POST complĂšte Ă  utiliser pour appeler directement le service pourvoyeur."), }); const wfsHttpGetUrlOutputSchema = z.object({ - result_type: z.literal("http_get_url").describe("Indique que la rĂ©ponse contient l'URL GET WFS Ă©quivalente."), - http_get_url: z.string().url().describe("URL GET WFS complĂšte avec tous les paramĂštres, y compris `cql_filter`. Utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant; pour une exĂ©cution HTTP robuste, prĂ©fĂ©rer `http_post_request`."), + result_type: z.literal("http_get_url").describe("Indique que la rĂ©ponse contient l'URL GET Ă©quivalente."), + http_get_url: z.string().url().describe("URL GET complĂšte avec tous les paramĂštres. Utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant; pour une exĂ©cution HTTP robuste, prĂ©fĂ©rer `http_post_request` car l'URL renvoyĂ©e ici peut ĂȘtre trop longue."), }); -export const gpfWfsGetFeaturesHttpPostRequestOutputSchema = wfsHttpPostRequestOutputSchema; -export const gpfWfsGetFeaturesHttpGetUrlOutputSchema = wfsHttpGetUrlOutputSchema; -export const gpfWfsGetFeatureByIdHttpPostRequestOutputSchema = wfsHttpPostRequestOutputSchema; -export const gpfWfsGetFeatureByIdHttpGetUrlOutputSchema = wfsHttpGetUrlOutputSchema; +export const gpfGetFeaturesHttpPostRequestOutputSchema = wfsHttpPostRequestOutputSchema; +export const gpfGetFeaturesHttpGetUrlOutputSchema = wfsHttpGetUrlOutputSchema; +export const gpfGetFeatureByIdHttpPostRequestOutputSchema = wfsHttpPostRequestOutputSchema; +export const gpfGetFeatureByIdHttpGetUrlOutputSchema = wfsHttpGetUrlOutputSchema; -// --- `gpf_wfs_get_features` --- +// --- `gpf_get_features` --- -export const gpfWfsGetFeaturesInputObjectSchema = z.object({ +export const gpfGetFeaturesInputObjectSchema = z.object({ typename: z .string() .trim() .min(1, "le nom du type ne doit pas ĂȘtre vide") - .describe("Nom exact du type WFS Ă  interroger, par exemple `BDTOPO_V3:batiment`. Utiliser `gpf_wfs_search_types` pour trouver un `typename` valide."), + .describe("Nom exact du type GPF Ă  interroger, par exemple `BDTOPO_V3:batiment`. Utiliser `gpf_search_types` pour trouver un `typename` valide."), limit: z .number() .int() @@ -120,12 +120,12 @@ export const gpfWfsGetFeaturesInputObjectSchema = z.object({ result_type: z .enum(["results", "hits", "http_post_request", "http_get_url"]) .default("results") - .describe("`results` renvoie une FeatureCollection avec les propriĂ©tĂ©s attributaires uniquement — **les gĂ©omĂ©tries ne sont pas incluses**, ce mode ne peut donc pas ĂȘtre utilisĂ© directement pour cartographier. `hits` renvoie uniquement le nombre total d'objets correspondant Ă  la requĂȘte. `http_post_request` renvoie une requĂȘte POST WFS robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET WFS Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant. Avec `http_post_request` ou `http_get_url`, la gĂ©omĂ©trie est automatiquement ajoutĂ©e aux propriĂ©tĂ©s du `select` pour garantir l'affichage cartographique."), + .describe("`results` renvoie une FeatureCollection avec les propriĂ©tĂ©s attributaires uniquement — **les gĂ©omĂ©tries ne sont pas incluses**, ce mode ne peut donc pas ĂȘtre utilisĂ© directement pour cartographier. `hits` renvoie uniquement le nombre total d'objets correspondant Ă  la requĂȘte. `http_post_request` renvoie une requĂȘte POST robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant. Avec `http_post_request` ou `http_get_url`, la gĂ©omĂ©trie est automatiquement ajoutĂ©e aux propriĂ©tĂ©s du `select` pour garantir l'affichage cartographique."), select: z .array(z.string().trim().min(1)) .min(1) .optional() - .describe("Liste des propriĂ©tĂ©s non gĂ©omĂ©triques Ă  renvoyer pour chaque objet. Utiliser `gpf_wfs_describe_type` pour connaĂźtre les noms exacts disponibles. Exemple : `[\"code_insee\", \"nom_officiel\"]`."), + .describe("Liste des propriĂ©tĂ©s non gĂ©omĂ©triques Ă  renvoyer pour chaque objet. Utiliser `gpf_describe_type` pour connaĂźtre les noms exacts disponibles. Exemple : `[\"code_insee\", \"nom_officiel\"]`."), order_by: z .array(orderBySchema) .min(1) @@ -147,14 +147,14 @@ export const gpfWfsGetFeaturesInputObjectSchema = z.object({ .describe("Filtre spatial par distance Ă  un point. Exclusif avec les autres filtres spatiaux."), intersects_feature_filter: intersectsFeatureFilterSchema .optional() - .describe("Filtre spatial par intersection avec un feature WFS de rĂ©fĂ©rence. Exclusif avec les autres filtres spatiaux."), + .describe("Filtre spatial par intersection avec un feature GPF de rĂ©fĂ©rence. Exclusif avec les autres filtres spatiaux."), travel_time_filter: travelTimeFilterSchema .optional() .describe("Filtre spatial par temps de trajet depuis un point (`profile` voiture ou piĂ©ton). Exclusif avec les autres filtres spatiaux."), }).strict(); -export const gpfWfsGetFeaturesInputSchema = gpfWfsGetFeaturesInputObjectSchema.superRefine((input, ctx) => { - const usedSpatialFilters = GPF_WFS_GET_FEATURES_SPATIAL_FILTER_KEYS.filter((key) => input[key] !== undefined); +export const gpfGetFeaturesInputSchema = gpfGetFeaturesInputObjectSchema.superRefine((input, ctx) => { + const usedSpatialFilters = GPF_GET_FEATURES_SPATIAL_FILTER_KEYS.filter((key) => input[key] !== undefined); if (usedSpatialFilters.length > 1) { ctx.addIssue({ @@ -165,46 +165,46 @@ export const gpfWfsGetFeaturesInputSchema = gpfWfsGetFeaturesInputObjectSchema.s } }); -// --- `gpf_wfs_get_features` Types --- +// --- `gpf_get_features` Types --- -export type GpfWfsGetFeaturesInput = z.infer; +export type GpfGetFeaturesInput = z.infer; export type SpatialFilter = - | ({ operator: "bbox" } & NonNullable) - | ({ operator: "intersects_point" } & NonNullable) - | ({ operator: "dwithin_point" } & NonNullable) - | ({ operator: "intersects_feature" } & NonNullable) - | ({ operator: "travel_time" } & NonNullable); -export type WhereClause = NonNullable[number]; -export type OrderByClause = NonNullable[number]; + | ({ operator: "bbox" } & NonNullable) + | ({ operator: "intersects_point" } & NonNullable) + | ({ operator: "dwithin_point" } & NonNullable) + | ({ operator: "intersects_feature" } & NonNullable) + | ({ operator: "travel_time" } & NonNullable); +export type WhereClause = NonNullable[number]; +export type OrderByClause = NonNullable[number]; -// --- `gpf_wfs_get_features` Outputs --- +// --- `gpf_get_features` Outputs --- -export const gpfWfsGetFeaturesHitsOutputSchema = z.object({ +export const gpfGetFeaturesHitsOutputSchema = z.object({ result_type: z.literal("hits").describe("Indique que la rĂ©ponse contient uniquement un comptage."), totalFeatures: z.number().describe("Le nombre total d'objets correspondant Ă  la requĂȘte."), }); -// --- `gpf_wfs_get_features` Published Schema --- +// --- `gpf_get_features` Published Schema --- -export const gpfWfsGetFeaturesPublishedInputSchema = generatePublishedInputSchema(gpfWfsGetFeaturesInputObjectSchema); +export const gpfGetFeaturesPublishedInputSchema = generatePublishedInputSchema(gpfGetFeaturesInputObjectSchema); -// --- `gpf_wfs_get_feature_by_id` --- +// --- `gpf_get_feature_by_id` --- -export const gpfWfsGetFeatureByIdInputSchema = z.object({ +export const gpfGetFeatureByIdInputSchema = z.object({ typename: z .string() .trim() .min(1, "le nom du type ne doit pas ĂȘtre vide") - .describe("Nom exact du type WFS Ă  interroger, par exemple `ADMINEXPRESS-COG.LATEST:commune`."), + .describe("Nom exact du type GPF Ă  interroger, par exemple `ADMINEXPRESS-COG.LATEST:commune`."), feature_id: z .string() .trim() .min(1, "le feature_id ne doit pas ĂȘtre vide") - .describe("Identifiant WFS exact de l'objet Ă  rĂ©cupĂ©rer, par exemple `commune.8952`."), + .describe("Identifiant GPF exact de l'objet Ă  rĂ©cupĂ©rer, par exemple `commune.8952`."), result_type: z .enum(["results", "http_post_request", "http_get_url"]) .default("results") - .describe("`results` renvoie une FeatureCollection normalisĂ©e avec exactement un objet. `http_post_request` renvoie une requĂȘte POST WFS robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET WFS Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant."), + .describe("`results` renvoie une FeatureCollection normalisĂ©e avec exactement un objet. `http_post_request` renvoie une requĂȘte POST robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant."), select: z .array(z.string().trim().min(1)) .min(1) @@ -212,10 +212,10 @@ export const gpfWfsGetFeatureByIdInputSchema = z.object({ .describe("Liste des propriĂ©tĂ©s non gĂ©omĂ©triques Ă  renvoyer. Quand `result_type=\"http_post_request\"` ou `result_type=\"http_get_url\"`, la gĂ©omĂ©trie est automatiquement ajoutĂ©e."), }).strict(); -// --- `gpf_wfs_get_feature_by_id` Types --- +// --- `gpf_get_feature_by_id` Types --- -export type GpfWfsGetFeatureByIdInput = z.infer; +export type GpfGetFeatureByIdInput = z.infer; -// --- `gpf_wfs_get_feature_by_id` Published Schema --- +// --- `gpf_get_feature_by_id` Published Schema --- -export const gpfWfsGetFeatureByIdPublishedInputSchema = generatePublishedInputSchema(gpfWfsGetFeatureByIdInputSchema); +export const gpfGetFeatureByIdPublishedInputSchema = generatePublishedInputSchema(gpfGetFeatureByIdInputSchema); diff --git a/src/wfs/spatialFilter.ts b/src/wfs/spatialFilter.ts index c3195d5..3e515fd 100644 --- a/src/wfs/spatialFilter.ts +++ b/src/wfs/spatialFilter.ts @@ -3,10 +3,10 @@ */ import type { - GpfWfsGetFeaturesInput, + GpfGetFeaturesInput, SpatialFilter, } from "./schema.js"; -import { GPF_WFS_GET_FEATURES_SPATIAL_FILTER_KEYS } from "./schema.js"; +import { GPF_GET_FEATURES_SPATIAL_FILTER_KEYS } from "./schema.js"; const FILTER_KEY_TO_OPERATOR = { bbox_filter: "bbox", @@ -14,7 +14,7 @@ const FILTER_KEY_TO_OPERATOR = { dwithin_point_filter: "dwithin_point", intersects_feature_filter: "intersects_feature", travel_time_filter: "travel_time", -} as const satisfies Record<(typeof GPF_WFS_GET_FEATURES_SPATIAL_FILTER_KEYS)[number], string>; +} as const satisfies Record<(typeof GPF_GET_FEATURES_SPATIAL_FILTER_KEYS)[number], string>; /** * Reads the already-validated spatial filter from normalized tool input and @@ -27,8 +27,8 @@ const FILTER_KEY_TO_OPERATOR = { * @param input Normalized tool input. * @returns The spatial filter, or `undefined` when no spatial filter is requested. */ -export function getSpatialFilter(input: GpfWfsGetFeaturesInput): SpatialFilter | undefined { - for (const key of GPF_WFS_GET_FEATURES_SPATIAL_FILTER_KEYS) { +export function getSpatialFilter(input: GpfGetFeaturesInput): SpatialFilter | undefined { + for (const key of GPF_GET_FEATURES_SPATIAL_FILTER_KEYS) { const value = input[key]; if (value) { return { operator: FILTER_KEY_TO_OPERATOR[key], ...value } as SpatialFilter; diff --git a/test/integration/level1-protocol/wfs-describe.test.ts b/test/integration/level1-protocol/describe.test.ts similarity index 81% rename from test/integration/level1-protocol/wfs-describe.test.ts rename to test/integration/level1-protocol/describe.test.ts index e5f0cec..f6c5559 100644 --- a/test/integration/level1-protocol/wfs-describe.test.ts +++ b/test/integration/level1-protocol/describe.test.ts @@ -1,5 +1,5 @@ /** - * Integration test: WFS describe type tool with real API calls. + * Integration test: describe type tool with real API calls. */ import { describe, it, expect } from "vitest"; @@ -8,7 +8,7 @@ import { withMcpServer } from "../helpers/level1-fixtures.js"; import { expectToolCallToThrow } from "../helpers/level1-assertions.js"; import { INTEGRATION_CONFIG } from "../config/shared.js"; -interface WfsDescribeResult { +interface DescribeResult { id: string; namespace: string; name: string; @@ -24,11 +24,11 @@ interface WfsDescribeResult { }>; } -describe("WFS Describe Type (integration)", () => { +describe("GPF Describe Type (integration)", () => { const { getHandle } = withMcpServer(); it("should describe BDTOPO_V3:batiment", async () => { - const result = await callTool(getHandle().client, "gpf_wfs_describe_type", { + const result = await callTool(getHandle().client, "gpf_describe_type", { typename: "BDTOPO_V3:batiment", }); @@ -44,6 +44,6 @@ describe("WFS Describe Type (integration)", () => { }, INTEGRATION_CONFIG.timeout); it("should return an error for empty typename", async () => { - await expectToolCallToThrow(callTool(getHandle().client, "gpf_wfs_describe_type", { typename: "" })); + await expectToolCallToThrow(callTool(getHandle().client, "gpf_describe_type", { typename: "" })); }, INTEGRATION_CONFIG.timeout); }); diff --git a/test/integration/level1-protocol/wfs-getfeaturebyid.test.ts b/test/integration/level1-protocol/getfeaturebyid.test.ts similarity index 88% rename from test/integration/level1-protocol/wfs-getfeaturebyid.test.ts rename to test/integration/level1-protocol/getfeaturebyid.test.ts index 7b1925b..74832bc 100644 --- a/test/integration/level1-protocol/wfs-getfeaturebyid.test.ts +++ b/test/integration/level1-protocol/getfeaturebyid.test.ts @@ -1,8 +1,8 @@ /** - * Integration test: WFS GetFeatureById tool with real API calls. + * Integration test: GetFeatureById tool with real API calls. * * This test first calls adminexpress to get a valid feature_ref, - * then uses gpf_wfs_get_feature_by_id to retrieve it. + * then uses gpf_get_feature_by_id to retrieve it. */ import { describe, it, expect } from "vitest"; @@ -45,7 +45,7 @@ interface GetFeatureByIdResult { numberMatched?: number; } -describe("WFS GetFeatureById (integration)", () => { +describe("GetFeatureById (integration)", () => { let featureRef: { typename: string; feature_id: string }; const { getHandle } = withMcpServer({ @@ -63,7 +63,7 @@ describe("WFS GetFeatureById (integration)", () => { }); it("should retrieve a feature by typename and feature_id", async () => { - const result = await callTool(getHandle().client, "gpf_wfs_get_feature_by_id", { + const result = await callTool(getHandle().client, "gpf_get_feature_by_id", { typename: featureRef.typename, feature_id: featureRef.feature_id, }); @@ -76,7 +76,7 @@ describe("WFS GetFeatureById (integration)", () => { it("should return an error for invalid typename", async () => { await expectToolCallToThrow( - callTool(getHandle().client, "gpf_wfs_get_feature_by_id", { + callTool(getHandle().client, "gpf_get_feature_by_id", { typename: "INVALID:type", feature_id: "nonexistent", }), diff --git a/test/integration/level1-protocol/wfs-getfeatures.test.ts b/test/integration/level1-protocol/getfeatures.test.ts similarity index 88% rename from test/integration/level1-protocol/wfs-getfeatures.test.ts rename to test/integration/level1-protocol/getfeatures.test.ts index b1304cf..4c01bc6 100644 --- a/test/integration/level1-protocol/wfs-getfeatures.test.ts +++ b/test/integration/level1-protocol/getfeatures.test.ts @@ -1,5 +1,5 @@ /** - * Integration test: WFS GetFeatures tool with real API calls. + * Integration test: GetFeatures tool with real API calls. * * Uses BDTOPO_V3:commune to test attribute filtering with a known code INSEE. */ @@ -29,11 +29,11 @@ interface GetFeaturesResult { numberMatched?: number; } -describe("WFS GetFeatures (integration)", () => { +describe("GetFeatures (integration)", () => { const { getHandle } = withMcpServer(); it("should query BDTOPO_V3:commune with attribute filter (code_insee=75056)", async () => { - const result = await callTool(getHandle().client, "gpf_wfs_get_features", { + const result = await callTool(getHandle().client, "gpf_get_features", { typename: "BDTOPO_V3:commune", where: [{ property: "code_insee", operator: "eq", value: "75056" }], select: ["code_insee", "nom_officiel"], @@ -49,7 +49,7 @@ describe("WFS GetFeatures (integration)", () => { it("should return an error for invalid typename", async () => { await expectToolCallToThrow( - callTool(getHandle().client, "gpf_wfs_get_features", { + callTool(getHandle().client, "gpf_get_features", { typename: "INVALID:type", limit: 1, }), diff --git a/test/integration/level1-protocol/wfs-search.test.ts b/test/integration/level1-protocol/search.test.ts similarity index 77% rename from test/integration/level1-protocol/wfs-search.test.ts rename to test/integration/level1-protocol/search.test.ts index f0e4716..caa52eb 100644 --- a/test/integration/level1-protocol/wfs-search.test.ts +++ b/test/integration/level1-protocol/search.test.ts @@ -1,5 +1,5 @@ /** - * Integration test: WFS search types tool with real API calls. + * Integration test: search types tool with real API calls. */ import { describe, it, expect } from "vitest"; @@ -8,7 +8,7 @@ import { withMcpServer } from "../helpers/level1-fixtures.js"; import { expectNonEmptyResults, expectToolCallToThrow } from "../helpers/level1-assertions.js"; import { INTEGRATION_CONFIG } from "../config/shared.js"; -interface WfsSearchResult { +interface SearchResult { results: Array<{ id: string; title: string; @@ -17,11 +17,11 @@ interface WfsSearchResult { }>; } -describe("WFS Search Types (integration)", () => { +describe(" Search Types (integration)", () => { const { getHandle } = withMcpServer(); it("should find 'batiment' types", async () => { - const result = await callTool(getHandle().client, "gpf_wfs_search_types", { + const result = await callTool(getHandle().client, "gpf_search_types", { query: "bĂątiment", }); @@ -32,7 +32,7 @@ describe("WFS Search Types (integration)", () => { }, INTEGRATION_CONFIG.timeout); it("should find cadastral parcel types", async () => { - const result = await callTool(getHandle().client, "gpf_wfs_search_types", { + const result = await callTool(getHandle().client, "gpf_search_types", { query: "parcelle cadastrale", }); @@ -44,6 +44,6 @@ describe("WFS Search Types (integration)", () => { }, INTEGRATION_CONFIG.timeout); it("should return an error for empty query", async () => { - await expectToolCallToThrow(callTool(getHandle().client, "gpf_wfs_search_types", { query: "" })); + await expectToolCallToThrow(callTool(getHandle().client, "gpf_search_types", { query: "" })); }, INTEGRATION_CONFIG.timeout); }); diff --git a/test/integration/level2-agent/level2-agent.test.ts b/test/integration/level2-agent/level2-agent.test.ts index 3a4f296..09f4978 100644 --- a/test/integration/level2-agent/level2-agent.test.ts +++ b/test/integration/level2-agent/level2-agent.test.ts @@ -46,14 +46,14 @@ const mcpScenarios = [ toolMode: "mcp", }, { - testName: "should mention building-related WFS tables", + testName: "should mention building-related GPF tables", userInput: "Dans quelle table peut-on trouver des informations sur les bĂątiments?", expectedResponseFragments: [ "bdtopo_v3:batiment", "cadastralparcels.parcellaire_express:batiment", ], toolMode: "mcp", - requiredToolCalls: ["gpf_wfs_search_types"], + requiredToolCalls: ["gpf_search_types"], }, { testName: "should chain geocode and altitude tools to answer the question", @@ -70,13 +70,13 @@ const mcpScenarios = [ userInput: "Combien de lycĂ©es sont situĂ©s Ă  2km du chateau de vincennes?", expectedResponseFragments: ["14", "lycĂ©es"], toolMode: "mcp", - requiredToolCalls: ["geocode", "gpf_wfs_search_types", "gpf_wfs_describe_type", "gpf_wfs_get_features"], + requiredToolCalls: ["geocode", "gpf_search_types", "gpf_describe_type", "gpf_get_features"], }, { testName: "should find about 1 741 batiments in the commune of Saint MandĂ©", userInput: "Combien y a t il de batiments sur la commune de saint mandĂ©?", toolMode: "mcp", - requiredToolCalls: ["geocode", "gpf_wfs_search_types", "gpf_wfs_describe_type", "gpf_wfs_get_features"], + requiredToolCalls: ["geocode", "gpf_search_types", "gpf_describe_type", "gpf_get_features"], expectedResponseFragments: ["batiments"], assertScenarioResult: ({ normalizedFinalMessage }) => { expect(containsNumberInRange(normalizedFinalMessage, 1700, 1800)).toBe(true); @@ -86,7 +86,7 @@ const mcpScenarios = [ testName: "should find 19 batiments of more than 30 meters in Angouleme", userInput: "Combien y a t il de batiments de plus de 30 mĂštres sur la commune d'AngoulĂȘme?", toolMode: "mcp", - requiredToolCalls: ["geocode", "gpf_wfs_search_types", "gpf_wfs_describe_type", "gpf_wfs_get_features"], + requiredToolCalls: ["geocode", "gpf_search_types", "gpf_describe_type", "gpf_get_features"], // assuming that it won't often change and that the number is correct at the moment of writing // (switch to assertScenarioResult with a range if needed in the future) expectedResponseFragments: ["19", "batiments"] diff --git a/test/integration/samples.ts b/test/integration/samples.ts index 5fda541..7d19139 100644 --- a/test/integration/samples.ts +++ b/test/integration/samples.ts @@ -19,8 +19,8 @@ export const EXPECTED_TOOL_NAMES = [ "cadastre", "urbanisme", "assiette_sup", - "gpf_wfs_search_types", - "gpf_wfs_describe_type", - "gpf_wfs_get_features", - "gpf_wfs_get_feature_by_id", + "gpf_search_types", + "gpf_describe_type", + "gpf_get_features", + "gpf_get_feature_by_id", ] as const; diff --git a/test/scripts/generate-mcp-docs.test.ts b/test/scripts/generate-mcp-docs.test.ts index 7e54d13..392b682 100644 --- a/test/scripts/generate-mcp-docs.test.ts +++ b/test/scripts/generate-mcp-docs.test.ts @@ -40,9 +40,9 @@ describe("generate-mcp-docs helpers", () => { it("should build stable github-like slugs", async () => { const { toAnchorSlug } = await loadDocsHelpers(); - expect(toAnchorSlug("gpf_wfs_get_features")).toEqual("gpf_wfs_get_features"); + expect(toAnchorSlug("gpf_get_features")).toEqual("gpf_get_features"); expect(toAnchorSlug(" Tool Name ")).toEqual("tool-name"); - expect(toAnchorSlug("Lecture d’un objet WFS")).toEqual("lecture-dun-objet-wfs"); + expect(toAnchorSlug("Lecture d’un objet GPF")).toEqual("lecture-dun-objet-gpf"); }); it("should infer schema types from enum and composition keywords", async () => { @@ -62,7 +62,7 @@ describe("generate-mcp-docs helpers", () => { const { sortToolDefinitions } = await loadDocsHelpers(); const sorted = sortToolDefinitions([ - { name: "gpf_wfs_get_features" }, + { name: "gpf_get_features" }, { name: "adminexpress" }, { name: "geocode" }, { name: "unknown_custom_tool" }, @@ -73,7 +73,7 @@ describe("generate-mcp-docs helpers", () => { "geocode", "altitude", "adminexpress", - "gpf_wfs_get_features", + "gpf_get_features", "unknown_custom_tool", ]); }); @@ -124,11 +124,11 @@ describe("generate-mcp-docs helpers", () => { expect(markdown).toContain("| Erreur | oui | oui | `content[0].text` contient `structuredContent.detail`, pas le JSON d'erreur complet de `structuredContent`. |"); }); - it("should document the WFS get features response modes", async () => { + it("should document the get features response modes", async () => { const { renderResponseContractSection } = await loadDocsHelpers(); const markdown = renderResponseContractSection({ - name: "gpf_wfs_get_features", + name: "gpf_get_features", }); expect(markdown).toContain('| SuccĂšs `result_type="results"` | oui | non | `content[0].text` est la FeatureCollection stringifiĂ©e ; aucun `structuredContent` n\'est ajoutĂ© dans ce mode. |'); diff --git a/test/tools/strict-input.test.ts b/test/tools/strict-input.test.ts index c541a3a..b99c8b9 100644 --- a/test/tools/strict-input.test.ts +++ b/test/tools/strict-input.test.ts @@ -5,10 +5,10 @@ import AltitudeTool from "../../src/tools/AltitudeTool"; import AssietteSupTool from "../../src/tools/AssietteSupTool"; import CadastreTool from "../../src/tools/CadastreTool"; import GeocodeTool from "../../src/tools/GeocodeTool"; -import GpfWfsDescribeTypeTool from "../../src/tools/GpfWfsDescribeTypeTool"; -import GpfWfsGetFeatureByIdTool from "../../src/tools/GpfWfsGetFeatureByIdTool"; -import GpfWfsGetFeaturesTool from "../../src/tools/GpfWfsGetFeaturesTool"; -import GpfWfsSearchTypesTool from "../../src/tools/GpfWfsSearchTypesTool"; +import GpfDescribeTypeTool from "../../src/tools/GpfDescribeTypeTool"; +import GpfGetFeatureByIdTool from "../../src/tools/GpfGetFeatureByIdTool"; +import GpfGetFeaturesTool from "../../src/tools/GpfGetFeaturesTool"; +import GpfSearchTypesTool from "../../src/tools/GpfSearchTypesTool"; import UrbanismeTool from "../../src/tools/UrbanismeTool"; const strictInputCases = [ @@ -38,26 +38,26 @@ const strictInputCases = [ validArguments: { text: "10 rue de la Paix Paris" }, }, { - label: "GpfWfsDescribeTypeTool", - tool: new GpfWfsDescribeTypeTool(), + label: "GpfDescribeTypeTool", + tool: new GpfDescribeTypeTool(), validArguments: { typename: "BDTOPO_V3:batiment" }, }, { - label: "GpfWfsGetFeatureByIdTool", - tool: new GpfWfsGetFeatureByIdTool(), + label: "GpfGetFeatureByIdTool", + tool: new GpfGetFeatureByIdTool(), validArguments: { typename: "BDTOPO_V3:batiment", feature_id: "batiment.1", }, }, { - label: "GpfWfsGetFeaturesTool", - tool: new GpfWfsGetFeaturesTool(), + label: "GpfGetFeaturesTool", + tool: new GpfGetFeaturesTool(), validArguments: { typename: "BDTOPO_V3:batiment" }, }, { - label: "GpfWfsSearchTypesTool", - tool: new GpfWfsSearchTypesTool(), + label: "GpfSearchTypesTool", + tool: new GpfSearchTypesTool(), validArguments: { query: "batiment" }, }, { diff --git a/test/tools/wfs/describeType.test.ts b/test/tools/wfs/describeType.test.ts index c1b65a1..a9383ac 100644 --- a/test/tools/wfs/describeType.test.ts +++ b/test/tools/wfs/describeType.test.ts @@ -2,9 +2,9 @@ import { describe, it, expect } from "vitest"; import type { Collection } from "@ignfab/gpf-schema-store"; -import GpfWfsDescribeTypeTool from "../../../src/tools/GpfWfsDescribeTypeTool"; +import GpfDescribeTypeTool from "../../../src/tools/GpfDescribeTypeTool"; -describe("Test GpfWfsDescribeTypeTool",() => { +describe("Test GpfDescribeTypeTool",() => { const mockCollection: Collection = { id: "BDTOPO_V3:batiment", namespace: "BDTOPO_V3", @@ -19,21 +19,21 @@ describe("Test GpfWfsDescribeTypeTool",() => { ], }; - class TestableGpfWfsDescribeTypeTool extends GpfWfsDescribeTypeTool { + class TestableGpfDescribeTypeTool extends GpfDescribeTypeTool { async execute() { return mockCollection; } } - class TestableGpfWfsDescribeTypeToolError extends GpfWfsDescribeTypeTool { + class TestableGpfDescribeTypeToolError extends GpfDescribeTypeTool { async execute(): Promise { - throw new Error("Le type 'BDTOPO_V3:not_found' est introuvable. Utiliser gpf_wfs_search_types pour trouver un type valide."); + throw new Error("Le type 'BDTOPO_V3:not_found' est introuvable. Utiliser gpf_search_types pour trouver un type valide."); } } it("should expose an enriched MCP definition", () => { - const tool = new GpfWfsDescribeTypeTool(); - expect(tool.toolDefinition.title).toEqual("Description d’un type WFS"); + const tool = new GpfDescribeTypeTool(); + expect(tool.toolDefinition.title).toEqual("Description d’un type GPF"); expect(tool.toolDefinition.inputSchema.properties?.typename).toMatchObject({ type: "string", minLength: 1, @@ -42,10 +42,10 @@ describe("Test GpfWfsDescribeTypeTool",() => { }); it("should return both text content and structuredContent", async () => { - const tool = new TestableGpfWfsDescribeTypeTool(); + const tool = new TestableGpfDescribeTypeTool(); const response = await tool.toolCall({ params: { - name: "gpf_wfs_describe_type", + name: "gpf_describe_type", arguments: { typename: "BDTOPO_V3:batiment", }, @@ -70,10 +70,10 @@ describe("Test GpfWfsDescribeTypeTool",() => { }); it("should return isError=true for invalid input", async () => { - const tool = new GpfWfsDescribeTypeTool(); + const tool = new GpfDescribeTypeTool(); const response = await tool.toolCall({ params: { - name: "gpf_wfs_describe_type", + name: "gpf_describe_type", arguments: { typename: "", }, @@ -102,10 +102,10 @@ describe("Test GpfWfsDescribeTypeTool",() => { }); it("should return isError=true when execute fails", async () => { - const tool = new TestableGpfWfsDescribeTypeToolError(); + const tool = new TestableGpfDescribeTypeToolError(); const response = await tool.toolCall({ params: { - name: "gpf_wfs_describe_type", + name: "gpf_describe_type", arguments: { typename: "BDTOPO_V3:not_found", }, @@ -121,7 +121,7 @@ describe("Test GpfWfsDescribeTypeTool",() => { throw new Error("expected text content"); } expect(textContent.text).toContain("Le type 'BDTOPO_V3:not_found' est introuvable"); - expect(textContent.text).toContain("gpf_wfs_search_types"); + expect(textContent.text).toContain("gpf_search_types"); expect(response.structuredContent).toMatchObject({ type: "urn:geocontext:problem:execution-error", }); diff --git a/test/tools/wfs/getFeatureById.test.ts b/test/tools/wfs/getFeatureById.test.ts index 1820a6a..0de6a77 100644 --- a/test/tools/wfs/getFeatureById.test.ts +++ b/test/tools/wfs/getFeatureById.test.ts @@ -11,7 +11,7 @@ const mockFetchJSONPost = vi.fn<( ) => Promise>(); vi.doMock("../../../src/wfs/catalog.js", () => ({ - GPF_WFS_URL: "https://data.geopf.fr/wfs", + GPF_URL: "https://data.geopf.fr/wfs", wfsSchemaStore: { getFeatureType: mockGetFeatureType, }, @@ -22,10 +22,10 @@ vi.doMock("../../../src/helpers/http.js", () => ({ ServiceResponseError, })); -const { default: GpfWfsGetFeatureByIdTool } = await import("../../../src/tools/GpfWfsGetFeatureByIdTool"); +const { default: GpfGetFeatureByIdTool } = await import("../../../src/tools/GpfGetFeatureByIdTool"); -describe("Test GpfWfsGetFeatureByIdTool", () => { - class InvalidSuccessPayloadTool extends GpfWfsGetFeatureByIdTool { +describe("Test GpfGetFeatureByIdTool", () => { + class InvalidSuccessPayloadTool extends GpfGetFeatureByIdTool { async execute() { return { unexpected: true } as never; } @@ -51,26 +51,26 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { }); it("should expose an MCP definition with explicit HTTP result types", () => { - const tool = new GpfWfsGetFeatureByIdTool(); - expect(tool.toolDefinition.title).toEqual("Lecture d’un objet WFS par identifiant"); + const tool = new GpfGetFeatureByIdTool(); + expect(tool.toolDefinition.title).toEqual("Lecture d’un objet GPF par identifiant"); expect(tool.toolDefinition.inputSchema).toEqual({ type: "object", properties: { typename: { type: "string", minLength: 1, - description: "Nom exact du type WFS Ă  interroger, par exemple `ADMINEXPRESS-COG.LATEST:commune`.", + description: "Nom exact du type GPF Ă  interroger, par exemple `ADMINEXPRESS-COG.LATEST:commune`.", }, feature_id: { type: "string", minLength: 1, - description: "Identifiant WFS exact de l'objet Ă  rĂ©cupĂ©rer, par exemple `commune.8952`.", + description: "Identifiant GPF exact de l'objet Ă  rĂ©cupĂ©rer, par exemple `commune.8952`.", }, result_type: { type: "string", enum: ["results", "http_post_request", "http_get_url"], default: "results", - description: "`results` renvoie une FeatureCollection normalisĂ©e avec exactement un objet. `http_post_request` renvoie une requĂȘte POST WFS robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET WFS Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant.", + description: "`results` renvoie une FeatureCollection normalisĂ©e avec exactement un objet. `http_post_request` renvoie une requĂȘte POST robuste Ă  exĂ©cuter directement. `http_get_url` renvoie l'URL GET Ă©quivalente, utile pour les consommateurs URL-first ou pour la visualisation dans un outil la supportant.", }, select: { type: "array", @@ -89,12 +89,12 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { }); it("should return text content and structuredContent for http_post_request", async () => { - const tool = new GpfWfsGetFeatureByIdTool(); + const tool = new GpfGetFeatureByIdTool(); mockGetFeatureType.mockResolvedValue(polygonFeatureType); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_feature_by_id", + name: "gpf_get_feature_by_id", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", feature_id: "commune.1", @@ -127,12 +127,12 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { }); it("should return text content and structuredContent for http_get_url", async () => { - const tool = new GpfWfsGetFeatureByIdTool(); + const tool = new GpfGetFeatureByIdTool(); mockGetFeatureType.mockResolvedValue(polygonFeatureType); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_feature_by_id", + name: "gpf_get_feature_by_id", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", feature_id: "commune.1", @@ -158,7 +158,7 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { }); it("should return exactly one transformed feature for results", async () => { - const tool = new GpfWfsGetFeatureByIdTool(); + const tool = new GpfGetFeatureByIdTool(); const requests: Array<{ url: string; query: Record }> = []; mockGetFeatureType.mockResolvedValue(polygonFeatureType); mockFetchJSONPost.mockImplementation(async (url, _body) => { @@ -187,7 +187,7 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_feature_by_id", + name: "gpf_get_feature_by_id", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", feature_id: "commune.1", @@ -217,13 +217,13 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { }); it("should fail clearly when the feature is missing", async () => { - const tool = new GpfWfsGetFeatureByIdTool(); + const tool = new GpfGetFeatureByIdTool(); mockGetFeatureType.mockResolvedValue(polygonFeatureType); mockFetchJSONPost.mockResolvedValue({ type: "FeatureCollection", features: [], totalFeatures: 0 }); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_feature_by_id", + name: "gpf_get_feature_by_id", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", feature_id: "commune.404", @@ -244,7 +244,7 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { }); it("should fail clearly when multiple features are returned", async () => { - const tool = new GpfWfsGetFeatureByIdTool(); + const tool = new GpfGetFeatureByIdTool(); mockGetFeatureType.mockResolvedValue(polygonFeatureType); mockFetchJSONPost.mockResolvedValue({ type: "FeatureCollection", @@ -257,7 +257,7 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_feature_by_id", + name: "gpf_get_feature_by_id", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", feature_id: "commune.1", @@ -277,7 +277,7 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { }); it("should fail clearly when the returned feature id mismatches", async () => { - const tool = new GpfWfsGetFeatureByIdTool(); + const tool = new GpfGetFeatureByIdTool(); mockGetFeatureType.mockResolvedValue(polygonFeatureType); mockFetchJSONPost.mockResolvedValue({ type: "FeatureCollection", @@ -289,7 +289,7 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_feature_by_id", + name: "gpf_get_feature_by_id", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", feature_id: "commune.1", @@ -309,10 +309,10 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { }); it("should reject invalid result_type values such as hits", async () => { - const tool = new GpfWfsGetFeatureByIdTool(); + const tool = new GpfGetFeatureByIdTool(); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_feature_by_id", + name: "gpf_get_feature_by_id", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", feature_id: "commune.1", @@ -340,10 +340,10 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { }); it("should reject legacy request result_type", async () => { - const tool = new GpfWfsGetFeatureByIdTool(); + const tool = new GpfGetFeatureByIdTool(); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_feature_by_id", + name: "gpf_get_feature_by_id", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", feature_id: "commune.1", @@ -370,7 +370,7 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_feature_by_id", + name: "gpf_get_feature_by_id", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", feature_id: "commune.1", @@ -387,7 +387,7 @@ describe("Test GpfWfsGetFeatureByIdTool", () => { expect(textContent.text).toContain("FeatureCollection"); expect(response.structuredContent).toMatchObject({ type: "urn:geocontext:problem:execution-error", - detail: expect.stringContaining("gpf_wfs_get_feature_by_id"), + detail: expect.stringContaining("gpf_get_feature_by_id"), }); }); }); diff --git a/test/tools/wfs/getFeatures.test.ts b/test/tools/wfs/getFeatures.test.ts index b58a854..b476b80 100644 --- a/test/tools/wfs/getFeatures.test.ts +++ b/test/tools/wfs/getFeatures.test.ts @@ -12,7 +12,7 @@ const mockFetchJSONPost = vi.fn<( const mockFetchJSONGet = vi.fn<(url: string) => Promise>(); vi.doMock("../../../src/wfs/catalog.js", () => ({ - GPF_WFS_URL: "https://data.geopf.fr/wfs", + GPF_URL: "https://data.geopf.fr/wfs", wfsSchemaStore: { getFeatureType: mockGetFeatureType, }, @@ -24,12 +24,12 @@ vi.doMock("../../../src/helpers/http.js", () => ({ ServiceResponseError, })); -const { default: GpfWfsGetFeaturesTool } = await import( - "../../../src/tools/GpfWfsGetFeaturesTool" +const { default: GpfGetFeaturesTool } = await import( + "../../../src/tools/GpfGetFeaturesTool" ); -describe("Test GpfWfsGetFeaturesTool", () => { - class RespondableGpfWfsGetFeaturesTool extends GpfWfsGetFeaturesTool { +describe("Test GpfGetFeaturesTool", () => { + class RespondableGpfGetFeaturesTool extends GpfGetFeaturesTool { respond(data: unknown) { return this.createSuccessResponse(data); } @@ -164,8 +164,8 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should expose an enriched MCP definition", () => { - const tool = new GpfWfsGetFeaturesTool(); - expect(tool.toolDefinition.title).toEqual("Lecture d’objets WFS"); + const tool = new GpfGetFeaturesTool(); + expect(tool.toolDefinition.title).toEqual("Lecture d’objets GPF"); expect(tool.toolDefinition.inputSchema.properties?.typename).toMatchObject({ type: "string", minLength: 1, @@ -188,7 +188,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should publish an LLM-compatible input schema without composition keywords", () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); expect(hasJsonSchemaComposition(tool.toolDefinition.inputSchema)).toBe(false); expect(tool.toolDefinition.inputSchema.properties?.dwithin_point_filter).toMatchObject({ @@ -211,7 +211,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should return a FeatureCollection without structuredContent for results", () => { - const tool = new RespondableGpfWfsGetFeaturesTool(); + const tool = new RespondableGpfGetFeaturesTool(); const response = tool.respond(featureCollection as never); expect("isError" in response).toBe(false); @@ -230,7 +230,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should return text content and structuredContent for hits", () => { - const tool = new RespondableGpfWfsGetFeaturesTool(); + const tool = new RespondableGpfGetFeaturesTool(); const response = tool.respond({ result_type: "hits", totalFeatures: featureCollection.totalFeatures, @@ -255,12 +255,12 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should return text content and structuredContent for http_post_request", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [polygonFeatureType.id]: polygonFeatureType }); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", result_type: "http_post_request", @@ -302,12 +302,12 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should return text content and structuredContent for http_get_url", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [polygonFeatureType.id]: polygonFeatureType }); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", result_type: "http_get_url", @@ -341,13 +341,13 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should compile travel_time_filter into a WFS request using an isochrone geometry", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [polygonFeatureType.id]: polygonFeatureType }); const isochroneUrls = captureIsochroneRequests(); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", result_type: "http_post_request", @@ -381,10 +381,10 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should return isError=true for invalid input", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "", }, @@ -413,10 +413,10 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should reject legacy request result_type", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", result_type: "request", @@ -438,10 +438,10 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should reject multiple spatial filters as invalid tool parameters", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", bbox_filter: { @@ -480,10 +480,10 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should reject legacy inputs removed from the public schema", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", cql_filter: "code_insee = '01001'", @@ -510,13 +510,13 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should build a POST request with query params and encoded body", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [polygonFeatureType.id]: polygonFeatureType }); const requests = captureRequests(featureCollection); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", limit: 7, @@ -537,7 +537,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should report live geometry property mismatches with a catalog desync hint", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [polygonFeatureType.id]: polygonFeatureType }); mockFetchJSONPost.mockRejectedValue( new ServiceResponseError( @@ -557,7 +557,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", }, @@ -577,13 +577,13 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should keep hits independent from limit and omit propertyName", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [polygonFeatureType.id]: polygonFeatureType }); const requests = captureRequests({ numberMatched: 321, totalFeatures: 999 }); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", result_type: "hits", @@ -608,14 +608,14 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should apply travel_time_filter before returning hit counts", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [polygonFeatureType.id]: polygonFeatureType }); captureIsochroneRequests(); const requests = captureRequests({ numberMatched: 12 }); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", result_type: "hits", @@ -645,13 +645,13 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should fail clearly when numberMatched is absent", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [polygonFeatureType.id]: polygonFeatureType }); captureRequests({ totalFeatures: 321 }); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", result_type: "hits", @@ -671,13 +671,13 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should fail clearly when numberMatched is unknown", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [polygonFeatureType.id]: polygonFeatureType }); captureRequests({ numberMatched: "unknown" }); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", result_type: "hits", @@ -697,7 +697,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should return feature_ref for non point layers with geometry set to null", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [polygonFeatureType.id]: polygonFeatureType }); captureRequests({ ...featureCollection, @@ -715,7 +715,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", }, @@ -738,7 +738,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should set point geometry to null and keep feature_ref", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [pointFeatureType.id]: pointFeatureType }); const requests = captureRequests({ type: "FeatureCollection", @@ -756,7 +756,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "BDTOPO_V3:point_d_acces", select: ["cleabs"], @@ -780,7 +780,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should resolve intersects_feature from MultiPoint references", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [polygonFeatureType.id]: polygonFeatureType, [multipointFeatureType.id]: multipointFeatureType, @@ -800,7 +800,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", intersects_feature_filter: { @@ -823,7 +823,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should report missing reference features clearly for intersects_feature", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); mockFeatureTypes({ [polygonFeatureType.id]: polygonFeatureType, [multipointFeatureType.id]: multipointFeatureType, @@ -836,7 +836,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", intersects_feature_filter: { @@ -860,12 +860,12 @@ describe("Test GpfWfsGetFeaturesTool", () => { }); it("should reject intersects_feature on the same typename and guide to by-id tool", async () => { - const tool = new GpfWfsGetFeaturesTool(); + const tool = new GpfGetFeaturesTool(); const requests = captureRequests(featureCollection); const response = await tool.toolCall({ params: { - name: "gpf_wfs_get_features", + name: "gpf_get_features", arguments: { typename: "ADMINEXPRESS-COG.LATEST:commune", intersects_feature_filter: { @@ -881,7 +881,7 @@ describe("Test GpfWfsGetFeaturesTool", () => { if (textContent.type !== "text") { throw new Error("expected text content"); } - expect(textContent.text).toContain("gpf_wfs_get_feature_by_id"); + expect(textContent.text).toContain("gpf_get_feature_by_id"); expect(textContent.text).toContain("intersects_feature"); expect(response.structuredContent).toMatchObject({ type: "urn:geocontext:problem:execution-error", diff --git a/test/tools/wfs/searchTypes.test.ts b/test/tools/wfs/searchTypes.test.ts index 3188cc0..c844f1d 100644 --- a/test/tools/wfs/searchTypes.test.ts +++ b/test/tools/wfs/searchTypes.test.ts @@ -1,9 +1,9 @@ import { describe, it, expect } from "vitest"; -import GpfWfsSearchTypesTool from "../../../src/tools/GpfWfsSearchTypesTool"; +import GpfSearchTypesTool from "../../../src/tools/GpfSearchTypesTool"; -describe("Test GpfWfsSearchTypesTool",() => { - class TestableGpfWfsSearchTypesTool extends GpfWfsSearchTypesTool { +describe("Test GpfSearchTypesTool",() => { + class TestableGpfSearchTypesTool extends GpfSearchTypesTool { async execute() { return { results: [ @@ -18,8 +18,8 @@ describe("Test GpfWfsSearchTypesTool",() => { } it("should expose an enriched MCP definition", () => { - const tool = new GpfWfsSearchTypesTool(); - expect(tool.toolDefinition.title).toEqual("Recherche de types WFS"); + const tool = new GpfSearchTypesTool(); + expect(tool.toolDefinition.title).toEqual("Recherche de types GPF"); expect(tool.toolDefinition.inputSchema.properties?.query).toMatchObject({ type: "string", minLength: 1, @@ -33,10 +33,10 @@ describe("Test GpfWfsSearchTypesTool",() => { }); it("should return both text content and structuredContent", async () => { - const tool = new TestableGpfWfsSearchTypesTool(); + const tool = new TestableGpfSearchTypesTool(); const response = await tool.toolCall({ params: { - name: "gpf_wfs_search_types", + name: "gpf_search_types", arguments: { query: "batiment", max_results: 1, @@ -70,10 +70,10 @@ describe("Test GpfWfsSearchTypesTool",() => { }); it("should return isError=true for invalid input", async () => { - const tool = new GpfWfsSearchTypesTool(); + const tool = new GpfSearchTypesTool(); const response = await tool.toolCall({ params: { - name: "gpf_wfs_search_types", + name: "gpf_search_types", arguments: { query: "", }, diff --git a/test/wfs/execution.test.ts b/test/wfs/execution.test.ts index dfede5f..47b30be 100644 --- a/test/wfs/execution.test.ts +++ b/test/wfs/execution.test.ts @@ -12,7 +12,7 @@ vi.doMock("../../src/wfs/transport.js", () => ({ })); vi.doMock("../../src/wfs/catalog.js", () => ({ - GPF_WFS_URL: "https://data.geopf.fr/wfs", + GPF_URL: "https://data.geopf.fr/wfs", wfsSchemaStore: { getFeatureType: mockGetFeatureType, }, diff --git a/test/wfs/queryPreparation.test.ts b/test/wfs/queryPreparation.test.ts index 648c4df..0d51734 100644 --- a/test/wfs/queryPreparation.test.ts +++ b/test/wfs/queryPreparation.test.ts @@ -2,9 +2,9 @@ import { describe, expect, it } from "vitest"; import type { Collection } from "@ignfab/gpf-schema-store"; import { compileQueryParts, geometryToEwkt } from "../../src/wfs/queryPreparation"; -import type { GpfWfsGetFeaturesInput } from "../../src/wfs/schema"; +import type { GpfGetFeaturesInput } from "../../src/wfs/schema"; -describe("gpfWfsGetFeatures/queryPreparation", () => { +describe("gpfGetFeatures/queryPreparation", () => { const featureType: Collection = { id: "ADMINEXPRESS-COG.LATEST:commune", namespace: "ADMINEXPRESS-COG.LATEST", @@ -22,7 +22,7 @@ describe("gpfWfsGetFeatures/queryPreparation", () => { ], }; - const baseInput: GpfWfsGetFeaturesInput = { + const baseInput: GpfGetFeaturesInput = { typename: "ADMINEXPRESS-COG.LATEST:commune", limit: 100, result_type: "results", diff --git a/test/wfs/spatialFilter.test.ts b/test/wfs/spatialFilter.test.ts index 0c73f7c..4f15598 100644 --- a/test/wfs/spatialFilter.test.ts +++ b/test/wfs/spatialFilter.test.ts @@ -2,11 +2,11 @@ import { describe, expect, it } from "vitest"; import { getSpatialFilter } from "../../src/wfs/spatialFilter"; import { - gpfWfsGetFeaturesInputSchema, - type GpfWfsGetFeaturesInput, + gpfGetFeaturesInputSchema, + type GpfGetFeaturesInput, } from "../../src/wfs/schema"; -const baseInput: GpfWfsGetFeaturesInput = { +const baseInput: GpfGetFeaturesInput = { typename: "ADMINEXPRESS-COG.LATEST:commune", limit: 100, result_type: "results", @@ -18,7 +18,7 @@ describe("getSpatialFilter", () => { }); it("should map a dwithin_point_filter to the compiler spatial filter", () => { - const input: GpfWfsGetFeaturesInput = { + const input: GpfGetFeaturesInput = { ...baseInput, dwithin_point_filter: { lon: 2.3522, @@ -36,7 +36,7 @@ describe("getSpatialFilter", () => { }); it("should map a travel_time_filter to the compiler spatial filter", () => { - const input: GpfWfsGetFeaturesInput = { + const input: GpfGetFeaturesInput = { ...baseInput, travel_time_filter: { lon: 2.3522, @@ -56,9 +56,9 @@ describe("getSpatialFilter", () => { }); }); -describe("gpfWfsGetFeaturesInputSchema spatial filters", () => { +describe("gpfGetFeaturesInputSchema spatial filters", () => { it("should validate bbox filters", () => { - expect(gpfWfsGetFeaturesInputSchema.parse({ + expect(gpfGetFeaturesInputSchema.parse({ ...baseInput, bbox_filter: { west: 2.1, @@ -75,7 +75,7 @@ describe("gpfWfsGetFeaturesInputSchema spatial filters", () => { }); it("should reject incomplete spatial filters", () => { - expect(() => gpfWfsGetFeaturesInputSchema.parse({ + expect(() => gpfGetFeaturesInputSchema.parse({ ...baseInput, intersects_point_filter: { lon: 2.3522, @@ -84,7 +84,7 @@ describe("gpfWfsGetFeaturesInputSchema spatial filters", () => { }); it("should reject fields from another spatial filter mode", () => { - expect(() => gpfWfsGetFeaturesInputSchema.parse({ + expect(() => gpfGetFeaturesInputSchema.parse({ ...baseInput, bbox_filter: { west: 2.1, @@ -97,7 +97,7 @@ describe("gpfWfsGetFeaturesInputSchema spatial filters", () => { }); it("should reject multiple spatial filters", () => { - expect(() => gpfWfsGetFeaturesInputSchema.parse({ + expect(() => gpfGetFeaturesInputSchema.parse({ ...baseInput, bbox_filter: { west: 2.1, @@ -113,7 +113,7 @@ describe("gpfWfsGetFeaturesInputSchema spatial filters", () => { }); it("should validate travel-time filters", () => { - expect(gpfWfsGetFeaturesInputSchema.parse({ + expect(gpfGetFeaturesInputSchema.parse({ ...baseInput, travel_time_filter: { lon: 2.3522, @@ -130,7 +130,7 @@ describe("gpfWfsGetFeaturesInputSchema spatial filters", () => { }); it("should reject invalid travel-time filters", () => { - expect(() => gpfWfsGetFeaturesInputSchema.parse({ + expect(() => gpfGetFeaturesInputSchema.parse({ ...baseInput, travel_time_filter: { lon: 2.3522, @@ -140,7 +140,7 @@ describe("gpfWfsGetFeaturesInputSchema spatial filters", () => { }, })).toThrow(); - expect(() => gpfWfsGetFeaturesInputSchema.parse({ + expect(() => gpfGetFeaturesInputSchema.parse({ ...baseInput, travel_time_filter: { lon: 2.3522, @@ -150,7 +150,7 @@ describe("gpfWfsGetFeaturesInputSchema spatial filters", () => { }, })).toThrow(); - expect(() => gpfWfsGetFeaturesInputSchema.parse({ + expect(() => gpfGetFeaturesInputSchema.parse({ ...baseInput, travel_time_filter: { lon: 2.3522, @@ -162,7 +162,7 @@ describe("gpfWfsGetFeaturesInputSchema spatial filters", () => { }); it("should reject legacy flat spatial parameters", () => { - expect(() => gpfWfsGetFeaturesInputSchema.parse({ + expect(() => gpfGetFeaturesInputSchema.parse({ ...baseInput, spatial_operator: "bbox", })).toThrow();