Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ Les fonctionnalités correspondent aux outils MCP documentés dans [`docs/mcp-to
| 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/) + [isochrone](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/calcul-isochrone-isodistance/) | Extraire des objets |
| Télécharger une isochrone ou une isodistance | `gpf_isoline_layer` | [isochrone](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/calcul-isochrone-isodistance/) | Cartographier une desserte |
| Télécharger un itinéraire | `gpf_itinerary_layer` | [itinéraire](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/calcul-itineraire/) | Cartographier un trajet |
| Compter les objets résultats d'une interrogation de couche GPF | `gpf_count_features` | [WFS](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/diffusion/wfs/) | Compter les bâtiments d'une zone |
| 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 |
| Télécharger le résultat d'une interrogation de couche GPF | `gpf_get_features_layer` | [WFS](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/diffusion/wfs/) + [isochrone](https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/calcul-isochrone-isodistance/) | Cartographier un résultat |
Expand Down
8 changes: 4 additions & 4 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
| `GPF_WFS_RATE_LIMIT` | Nombre maximum de requêtes par seconde sur le WFS de la Géoplateforme. | 30 |
| `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_NAVIGATION_RATE_LIMIT` | Nombre maximum de requêtes par seconde sur le service d'isochrone/navigation de la Géoplateforme, pour les appels émis par le serveur MCP. | 5 |
| `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" |
Expand All @@ -28,12 +28,12 @@
| `PROXY_ENDPOINT` | Chemin exposé par le proxy geodata. | `/api/v1/proxy` |
| `PROXY_PUBLIC_BASE_URL` | URL de base publiquement joignable du proxy, utilisée pour construire la `data_url` absolue transmise à Carto. Derrière un reverse-proxy, elle diffère de l'adresse d'écoute ; en développement local, c'est typiquement `http://localhost:3002`. Requise avec `PROXY_URL_SECRET` pour activer les tools `*_layer`. | Aucune |
| `GPF_WFS_PROXY_RATE_LIMIT` | Limite de requêtes/s du proxy vers le WFS, distincte de `GPF_WFS_RATE_LIMIT`. Les deux comptent sur le même service IGN : répartir une seule allocation entre les deux. | `10` |
| `GPF_NAVIGATION_PROXY_RATE_LIMIT` | Limite de requêtes/s du proxy vers le service d'isochrone (filtre `travel_time`), distincte de `GPF_NAVIGATION_RATE_LIMIT`. Les deux comptent sur le même service IGN : répartir une seule allocation entre les deux. | `5` |
| `PROXY_UPSTREAM_TIMEOUT` | Délai (secondes) des appels amont du proxy (WFS **et** isochrone), plus court que `HTTP_TIMEOUT` pour qu'une requête à 2 appels (`intersects_feature` ou `travel_time`) reste sous le délai du navigateur/Carto. | `10` |
| `GPF_NAVIGATION_PROXY_RATE_LIMIT` | Limite de requêtes/s du proxy vers le service de navigation de la Géoplateforme, distincte de `GPF_NAVIGATION_RATE_LIMIT`. Budget partagé par le filtre `isoline` et les couches `gpf_isoline_layer` et `gpf_itinerary_layer`. Les deux limites comptent sur le même service IGN : répartir une seule allocation entre les deux. | `5` |
| `PROXY_UPSTREAM_TIMEOUT` | Délai (secondes) des appels amont du proxy (WFS, isochrone et itinéraire), plus court que `HTTP_TIMEOUT` pour qu'une requête à 2 appels (`intersects_feature` ou `isoline`) reste sous le délai du navigateur/Carto. | `10` |

## Génération de `PROXY_URL_SECRET`

Pour produire des URLs opaques d'affichage cartographique (tools `gpf_isoline_layer`, `gpf_get_features_layer` et `gpf_get_feature_by_id_layer`), geocontext chiffre les paramètres de requête avec une clé symétrique AES-256, fournie via `PROXY_URL_SECRET`. La même clé est utilisée par le MCP (pour signer) et par le proxy geodata (pour déchiffrer).
Pour produire des URLs opaques d'affichage cartographique (tools `gpf_isoline_layer`, `gpf_itinerary_layer`, `gpf_get_features_layer` et `gpf_get_feature_by_id_layer`), geocontext chiffre les paramètres de requête avec une clé symétrique AES-256, fournie via `PROXY_URL_SECRET`. La même clé est utilisée par le MCP (pour signer) et par le proxy geodata (pour déchiffrer).

La clé doit être une valeur aléatoire de **32 octets encodée en hexadécimal** (soit 64 caractères `0-9a-f`). Générez-la avec :

Expand Down
2 changes: 1 addition & 1 deletion docs/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Avec certains clients MCP, vous serez amené à éditer un fichier JSON. Par exe

## Activer les tools cartographiques en local

Les tools `gpf_isoline_layer`, `gpf_get_features_layer` et `gpf_get_feature_by_id_layer` renvoient une `data_url` opaque, servie par le **proxy geodata**, un processus séparé du serveur MCP. Ils sont listés dans tous les transports mais échouent tant qu'aucun proxy joignable n'est configuré. Comme le proxy est **indépendant du transport**, on peut les activer en local — **même en `stdio`** — en lançant les deux composants côte à côte, sans Docker.
Les tools `gpf_isoline_layer`, `gpf_itinerary_layer`, `gpf_get_features_layer` et `gpf_get_feature_by_id_layer` renvoient une `data_url` opaque, servie par le **proxy geodata**, un processus séparé du serveur MCP. Ils sont listés dans tous les transports mais échouent tant qu'aucun proxy joignable n'est configuré. Comme le proxy est **indépendant du transport**, on peut les activer en local — **même en `stdio`** — en lançant les deux composants côte à côte, sans Docker.

Il faut une clé partagée (`PROXY_URL_SECRET`) entre les deux processus, et pointer le MCP vers le proxy local via `PROXY_PUBLIC_BASE_URL`.

Expand Down
127 changes: 127 additions & 0 deletions docs/mcp-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Tous les tools exposent les mêmes annotations MCP dans leur définition `tools/
- [`gpf_describe_type`](#gpf_describe_type)
- [`gpf_get_features`](#gpf_get_features)
- [`gpf_isoline_layer`](#gpf_isoline_layer)
- [`gpf_itinerary_layer`](#gpf_itinerary_layer)
- [`gpf_get_features_layer`](#gpf_get_features_layer)
- [`gpf_count_features`](#gpf_count_features)
- [`gpf_get_feature_by_id`](#gpf_get_feature_by_id)
Expand Down Expand Up @@ -1531,6 +1532,132 @@ L'URL est opaque et doit être transmise telle quelle à un outil cartographique
| 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_itinerary_layer`

Code Source : [src/tools/GpfItineraryLayerTool.ts](../src/tools/GpfItineraryLayerTool.ts)

### Titre

Couche cartographiable d'itinéraire GPF

### Description du tool

```
Renvoie une **URL de couche cartographiable** (`data_url`) représentant l'itinéraire entre deux points calculé par la Géoplateforme.
Utiliser `departure_lon`/`departure_lat` pour le départ, `arrival_lon`/`arrival_lat` pour l'arrivée, `profile` pour le mode de déplacement (`car` ou `pedestrian`) et `optimize` pour choisir entre l'itinéraire le plus rapide (`time`, défaut) ou le plus court (`distance`).
La couche GeoJSON retournée contient une Feature LineString avec les propriétés `distance_meters` et `duration_minutes`.
Le départ et l'arrivée doivent être distants de moins de 100 km à vol d'oiseau.
L'URL est opaque et doit être transmise telle quelle à un outil cartographique (MCP Carto, ...).
(source : Géoplateforme (calcul d'itinéraire)).
```

### Schéma d’entrée

| Champ | Type | Requis | Description |
| --- | --- | --- | --- |
| `arrival_lat` | number | oui | Latitude du point d'arrivée en WGS84 `lon/lat`. |
| `arrival_lon` | number | oui | Longitude du point d'arrivée en WGS84 `lon/lat`. |
| `departure_lat` | number | oui | Latitude du point de départ en WGS84 `lon/lat`. |
| `departure_lon` | number | oui | Longitude du point de départ en WGS84 `lon/lat`. |
| `optimize` | string (enum) | non | Métrique d'optimisation : `time` (itinéraire le plus rapide, défaut) ou `distance` (le plus court). Valeurs : time, distance. |
| `profile` | string (enum) | oui | Mode de déplacement : `car` ou `pedestrian`. Valeurs : car, pedestrian. |

<details>
<summary>Schéma d’entrée brut</summary>

```json
{
"type": "object",
"properties": {
"departure_lon": {
"type": "number",
"minimum": -180,
"maximum": 180,
"description": "Longitude du point de départ en WGS84 `lon/lat`."
},
"departure_lat": {
"type": "number",
"minimum": -90,
"maximum": 90,
"description": "Latitude du point de départ en WGS84 `lon/lat`."
},
"arrival_lon": {
"type": "number",
"minimum": -180,
"maximum": 180,
"description": "Longitude du point d'arrivée en WGS84 `lon/lat`."
},
"arrival_lat": {
"type": "number",
"minimum": -90,
"maximum": 90,
"description": "Latitude du point d'arrivée en WGS84 `lon/lat`."
},
"profile": {
"type": "string",
"enum": [
"car",
"pedestrian"
],
"description": "Mode de déplacement : `car` ou `pedestrian`."
},
"optimize": {
"type": "string",
"enum": [
"time",
"distance"
],
"description": "Métrique d'optimisation : `time` (itinéraire le plus rapide, défaut) ou `distance` (le plus court)."
}
},
"required": [
"departure_lon",
"departure_lat",
"arrival_lon",
"arrival_lat",
"profile"
],
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
}
```

</details>

### Schéma de sortie

| Champ | Type | Requis | Description |
| --- | --- | --- | --- |
| `data_url` | string | oui | URL renvoyant une FeatureCollection GeoJSON (géométries complètes) prête à être affichée dans un outil cartographique. |

<details>
<summary>Schéma de sortie brut</summary>

```json
{
"type": "object",
"properties": {
"data_url": {
"type": "string",
"description": "URL renvoyant une FeatureCollection GeoJSON (géométries complètes) prête à être affichée dans un outil cartographique.",
"format": "uri"
}
},
"required": [
"data_url"
]
}
```

</details>

### Réponse MCP

| Cas | `content` | `structuredContent` | Relation entre `content` et `structuredContent` |
| --- | --- | --- | --- |
| 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_get_features_layer`

Code Source : [src/tools/GpfGetFeaturesLayerTool.ts](../src/tools/GpfGetFeaturesLayerTool.ts)
Expand Down
1 change: 1 addition & 0 deletions scripts/generate-mcp-docs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const toolDisplayOrder = [
"gpf_describe_type",
"gpf_get_features",
"gpf_isoline_layer",
"gpf_itinerary_layer",
"gpf_get_features_layer",
"gpf_count_features",
"gpf_get_feature_by_id",
Expand Down
3 changes: 3 additions & 0 deletions src/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ const envSchema = z.object({
GPF_WFS_RATE_LIMIT: z.preprocess(emptyToUndefined, positiveIntegerSchema.default(30)),
GPF_GEOCODE_RATE_LIMIT: z.preprocess(emptyToUndefined, positiveIntegerSchema.default(50)),
GPF_ALTI_RATE_LIMIT: z.preprocess(emptyToUndefined, positiveIntegerSchema.default(50)),
// Budget for MCP calls to the GPF navigation service, shared through a single
// RateLimiter instance (see getNavigationRateLimiter). The proxy draws on
// GPF_NAVIGATION_PROXY_RATE_LIMIT instead.
GPF_NAVIGATION_RATE_LIMIT: z.preprocess(emptyToUndefined, positiveIntegerSchema.default(5)),
// GPF
GPF_WFS_MINISEARCH_OPTIONS: z
Expand Down
Loading