reunion_search_real_estate_transactions
Search the DVF database for real estate transactions in La Réunion by year, commune, property type, and value range. Get mutation details for price analysis and market trends.
Instructions
Search the DVF (Demande de Valeurs Foncières) database — France's open record of real-estate transactions registered with notaires — restricted to La Réunion. Each row is one mutation (sale, exchange, etc.) with date, value, property characteristics. Returns mutation ID, date, year, nature of mutation, VEFA flag (sale of future state of completion), sale value (EUR), INSEE codes, land area, built area, counts of houses/apartments/commercial premises, type code and label, department. Sorted by date descending. Use for price analysis, market trends, comparable sales.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| year | No | Year of mutation (4 digits, e.g. 2023) | |
| insee | No | INSEE commune code (5 digits as string, e.g. "97411" for Saint-Denis). Substring match supported | |
| type | No | Property type label prefix match (libtypbien). Examples: "MAISON", "APPARTEMENT", "DEPENDANCE", "TERRAIN" | |
| min_value | No | Minimum sale value in EUR (inclusive) | |
| max_value | No | Maximum sale value in EUR (inclusive) | |
| limit | No | Max transactions to return (1-200, default 50) |
Implementation Reference
- src/modules/territory.ts:15-64 (registration)The tool 'reunion_search_real_estate_transactions' is registered via server.tool() on line 16 within registerTerritoryTools(). The Zod schema for inputs (year, insee, type, min_value, max_value, limit) is defined on lines 19-26. The handler function is the async callback on lines 27-63 which queries the DVF dataset and returns structured transaction data.
export function registerTerritoryTools(server: McpServer): void { server.tool( 'reunion_search_real_estate_transactions', 'Search the DVF (Demande de Valeurs Foncières) database — France\'s open record of real-estate transactions registered with notaires — restricted to La Réunion. Each row is one mutation (sale, exchange, etc.) with date, value, property characteristics. Returns mutation ID, date, year, nature of mutation, VEFA flag (sale of future state of completion), sale value (EUR), INSEE codes, land area, built area, counts of houses/apartments/commercial premises, type code and label, department. Sorted by date descending. Use for price analysis, market trends, comparable sales.', { year: z.number().int().optional().describe('Year of mutation (4 digits, e.g. 2023)'), insee: z.string().optional().describe('INSEE commune code (5 digits as string, e.g. "97411" for Saint-Denis). Substring match supported'), type: z.string().optional().describe('Property type label prefix match (libtypbien). Examples: "MAISON", "APPARTEMENT", "DEPENDANCE", "TERRAIN"'), min_value: z.number().optional().describe('Minimum sale value in EUR (inclusive)'), max_value: z.number().optional().describe('Maximum sale value in EUR (inclusive)'), limit: z.number().int().min(1).max(200).default(50).describe('Max transactions to return (1-200, default 50)'), }, async ({ year, insee, type, min_value, max_value, limit }) => { try { const data = await client.getRecords<RecordObject>(DATASET_DVF, { where: buildWhere([ year !== undefined ? `datemut >= date'${year}-01-01' AND datemut < date'${year + 1}-01-01'` : undefined, insee ? `l_codinsee LIKE ${quote(`%${insee}%`)}` : undefined, type ? `libtypbien LIKE ${quote(`${type}%`)}` : undefined, min_value !== undefined ? `valeurfonc >= ${min_value}` : undefined, max_value !== undefined ? `valeurfonc <= ${max_value}` : undefined, ]), order_by: 'datemut DESC', limit, }); return jsonResult({ total_transactions: data.total_count, transactions: data.results.map((row) => ({ mutation_id: pickString(row, ['idmutation']), date: pickString(row, ['datemut']), year: pickNumber(row, ['anneemut']), nature: pickString(row, ['libnatmut']), vefa: pickString(row, ['vefa']), value_eur: pickNumber(row, ['valeurfonc']), insee_codes: pickString(row, ['l_codinsee']), land_area_m2: pickNumber(row, ['sterr']), built_area_m2: pickNumber(row, ['sbati']), nb_houses: pickNumber(row, ['nblocmai']), nb_apartments: pickNumber(row, ['nblocapt']), nb_commercial: pickNumber(row, ['nblocact']), type_code: pickString(row, ['codtypbien']), type_label: pickString(row, ['libtypbien']), department: pickString(row, ['dep_name']), })), }); } catch (error) { return errorResult(error instanceof Error ? error.message : 'Failed to search real-estate transactions'); } } ); - src/modules/territory.ts:27-63 (handler)Handler function for reunion_search_real_estate_transactions: builds ODSQL WHERE clause from filters, queries the 'demande-de-valeurs-foncierespublic' dataset via client.getRecords(), maps results to a structured response with mutation_id, date, year, nature, vefa, value_eur, insee_codes, land_area_m2, built_area_m2, nb_houses, nb_apartments, nb_commercial, type_code, type_label, department. Sorted by date descending. Returns total_transactions count.
async ({ year, insee, type, min_value, max_value, limit }) => { try { const data = await client.getRecords<RecordObject>(DATASET_DVF, { where: buildWhere([ year !== undefined ? `datemut >= date'${year}-01-01' AND datemut < date'${year + 1}-01-01'` : undefined, insee ? `l_codinsee LIKE ${quote(`%${insee}%`)}` : undefined, type ? `libtypbien LIKE ${quote(`${type}%`)}` : undefined, min_value !== undefined ? `valeurfonc >= ${min_value}` : undefined, max_value !== undefined ? `valeurfonc <= ${max_value}` : undefined, ]), order_by: 'datemut DESC', limit, }); return jsonResult({ total_transactions: data.total_count, transactions: data.results.map((row) => ({ mutation_id: pickString(row, ['idmutation']), date: pickString(row, ['datemut']), year: pickNumber(row, ['anneemut']), nature: pickString(row, ['libnatmut']), vefa: pickString(row, ['vefa']), value_eur: pickNumber(row, ['valeurfonc']), insee_codes: pickString(row, ['l_codinsee']), land_area_m2: pickNumber(row, ['sterr']), built_area_m2: pickNumber(row, ['sbati']), nb_houses: pickNumber(row, ['nblocmai']), nb_apartments: pickNumber(row, ['nblocapt']), nb_commercial: pickNumber(row, ['nblocact']), type_code: pickString(row, ['codtypbien']), type_label: pickString(row, ['libtypbien']), department: pickString(row, ['dep_name']), })), }); } catch (error) { return errorResult(error instanceof Error ? error.message : 'Failed to search real-estate transactions'); } } - src/modules/territory.ts:19-26 (schema)Zod input schema for the tool: year (int, optional), insee (string, optional, substring match on INSEE code), type (string, optional, prefix match on property type label), min_value (number, optional), max_value (number, optional), limit (int, 1-200, default 50).
{ year: z.number().int().optional().describe('Year of mutation (4 digits, e.g. 2023)'), insee: z.string().optional().describe('INSEE commune code (5 digits as string, e.g. "97411" for Saint-Denis). Substring match supported'), type: z.string().optional().describe('Property type label prefix match (libtypbien). Examples: "MAISON", "APPARTEMENT", "DEPENDANCE", "TERRAIN"'), min_value: z.number().optional().describe('Minimum sale value in EUR (inclusive)'), max_value: z.number().optional().describe('Maximum sale value in EUR (inclusive)'), limit: z.number().int().min(1).max(200).default(50).describe('Max transactions to return (1-200, default 50)'), }, - src/modules/index.ts:22-22 (registration)Import of registerTerritoryTools from './territory.js', which contains the registration of reunion_search_real_estate_transactions.
import { registerTerritoryTools } from './territory.js'; - src/modules/index.ts:51-51 (registration)Call to registerTerritoryTools(server) inside registerAllTools(), which triggers the registration of all territory tools including reunion_search_real_estate_transactions.
registerTerritoryTools(server);