# carto_transactions

Ventes immobilieres officielles (DVF) : ~8 millions de mutations depuis 2018, dedupliquees par vente (1 resultat = 1 vente, valeur totale de la mutation, biens agreges). Filtrer par localisation.commune (nom, code postal ou code INSEE — arrondissements de Paris/Lyon/Marseille inclus), localisation.dept ou localisation.region, plus annee et type_local (maison, appartement, local, dependance). Une localisation est OBLIGATOIRE (pas de listing national). La plus-value (fields.plus_value_eur/pct) n'est renseignee que sur parcelle unique avec une vente precedente fiable. Pour des prix moyens, médians ou comparaisons annuelles, demander mode=statistiques avec une commune seule : agrégats exhaustifs serveur, jamais une moyenne de la page. Exemple : { "localisation": { "commune": "Lyon" }, "annee": 2025, "type_local": "appartement" }.

| | |
|---|---|
| Effets | lecture seule |
| Portée OAuth | `mcp:read` |
| Offre requise | aucune — ouvert à tous les comptes |
| Données lues | la donnée publique du bâti — aucune coordonnée personnelle n'est jamais renvoyée |
| Distribution | MCP historique |

Cet outil n'a pas d'équivalent REST : il s'appelle par le connecteur MCP.

## Paramètres

| Paramètre | Type | Requis | Bornes | Description |
|---|---|---|---|---|
| `annee` | entier | — | min 2014, max 2035 | Annee des ventes (donnees depuis 2018). |
| `cursor` | texte | — | — | Pagination : repasser tel quel le `next_cursor` de la page precedente. Omettre pour la premiere page. |
| `limit` | entier | — | min 1, max 50 | Resultats par page (defaut 20, max 50). |
| `localisation` | objet | — | — | Zone geographique. UNE granularite : { region } (couvre tous ses departements en un appel — ne jamais les enumerer) OU { dept } OU { commune } (combinable avec dept pour lever un homonyme). National = OMETTRE localisation (jamais dept:"all" ni "france"). |
| `localisation.commune` | texte | — | — | Nom de commune (accents/casse ignores), code postal ou code INSEE. Arrondissements de Paris/Lyon/Marseille inclus automatiquement. |
| `localisation.dept` | texte | — | — | Code departement : 2 chiffres ("69"), "2A"/"2B", ou DOM "971"-"976". |
| `localisation.insee` | texte | — | format `^(?:[0-9]{5}\|2[A-Ba-b][0-9]{3})$` | Code INSEE exact issu de la carte. Prioritaire sur commune; jamais interprété comme code postal. |
| `localisation.region` | texte | — | — | Nom officiel de region (ex. "Auvergne-Rhone-Alpes", "Ile-de-France"). |
| `mode` | `liste` · `statistiques` | — | — | liste (défaut) renvoie une page. statistiques calcule sur toutes les ventes de la commune les effectifs, médianes et moyennes par année et type. Pour comparer des prix, utiliser statistiques, jamais une moyenne de la page. |
| `query` | texte | — | — | Raccourci : code postal 5 chiffres (ex. "69006" = Lyon 6e, un arrondissement precis) ou nom de commune. Equivalent a localisation.commune. |
| `type_local` | `maison` · `appartement` · `local` · `dependance` | — | — | Type de bien principal de la vente. |

## Exemple d'appel

_squelette synthétique : les repères `<…>` se remplacent._

```json
{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "mode": "liste"
    },
    "name": "carto_transactions"
  }
}
```

En HTTP, c'est ce corps que l'on poste :

```bash
curl -sS https://prescriptio.fr/api/mcp \
  -H 'Authorization: Bearer <jeton>' \
  -H 'Content-Type: application/json' \
  -d '{"id":1,"jsonrpc":"2.0","method":"tools/call","params":{"arguments":{"mode":"liste"},"name":"carto_transactions"}}'
```

## Forme de la réponse

Le résultat est enveloppé par le protocole : un bloc `content[0].text` qui porte le JSON sérialisé, et `structuredContent` qui porte le même objet. Le squelette ci-dessous donne les **types**, jamais des valeurs réelles.

```json
{
  "_meta": {},
  "next_cursor": "<texte>",
  "results": [
    {
      "fields": {},
      "id": "<texte>",
      "resume": "<texte>",
      "titre": "<texte>"
    }
  ],
  "total_estimated": 0
}
```

Un champ absent ou `null` n'est ni un zéro ni une estimation : la source ne l'a pas renseigné. Les propriétés additionnelles restent autorisées ; ce squelette n'invente pas les champs manquants.

## Erreurs

Le connecteur MCP renvoie une erreur JSON-RPC pour ce qui empêche l'appel, et une erreur *métier* (`isError`) lisible par le modèle pour ce que l'appel n'a pas pu faire.

| Code JSON-RPC | Quand | Que faire |
|---|---|---|
| `-32601` | Outil inconnu | Appeler `tools/list` ; ne pas deviner un nom |
| `-32602` | Argument absent ou hors schéma | Corriger l'argument nommé dans le message |
| `-32003` | Portée OAuth absente de l'autorisation | Reconnecter en acceptant `mcp:read` ou `mcp:write` |
| `-32004` | Quota dépassé | Attendre le délai indiqué ; ne jamais boucler |
| `-32603` | Dépendance indisponible | Réessayer plus tard ; ne pas rejouer une écriture sans vérifier |

Sur les routes REST `/api/mcp/v1/*`, les mêmes situations sont des codes HTTP : `400 invalid_arguments` / `invalid_cursor`, `401 unauthenticated`, `403 insufficient_scope` / `access_denied` / `user_required` / `organization_required` / `role_denied`, `404 not_found`, `409 conflict`, `429 quota_exceeded` (avec `Retry-After` quand il est connu), `503 unavailable`, `504 timeout`. Le corps porte `error.code`, `error.message`, `error.retry_after_seconds` et `api_contract_version`.

⚠ Un message d'erreur n'est jamais une donnée : ne pas le recopier comme un fait, et ne pas inventer un identifiant ou une URL qu'il ne contient pas.

## Quotas

Un appel compte pour **une unité** dans les deux compteurs d'appels, partagés entre le connecteur MCP et les routes REST :

| Compteur | Compte gratuit | Abonné | Accès interne |
|---|---|---|---|
| Appels / 24 h glissantes | 100 | 5000 | illimité |
| Appels / 60 s (anti-rafale) | 5 | 30 | illimité |
| Dossiers distincts / jour civil (Paris) | 5 | 500 | illimité |

Cet outil ne consomme aucune unité de dossier : chercher est gratuit, c'est la lecture d'un dossier (`marches_dce`, `mairies_deliberations` et leurs téléchargements) qui est décomptée.

**Aucun outil n'est réservé à une offre** (décision du 2026-09-17) : un compte gratuit voit et appelle le catalogue entier, ce sont ces plafonds qui bornent son usage.

## Voir aussi

[Référence technique](/docs/reference/concepts.md) · [Tous les outils](/docs/api/index.md) · [OpenAPI](/openapi.json) · [Texte pour agents](/llms-full.txt)
