reunion_get_weather_observations
Retrieve synoptic weather observations from Météo-France stations in La Réunion, including temperature, humidity, wind, and rainfall for a specified station and date range.
Instructions
Météo-France SYNOP synoptic weather observations from stations located in La Réunion. SYNOP is the WMO standard for surface observations (every 3 hours typically). Returns: station name, observation timestamp, commune, current temperature (°C), 12h min/max temperature, relative humidity (%), wind speed (m/s) and direction (degrees), sea-level pressure (Pa), 1h and 24h rainfall (mm), present-weather code. Sorted by date descending. Use reunion_list_weather_stations to discover available stations.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| station | No | Station name prefix match (case-sensitive uppercase). Examples: "LE PORT", "GILLOT-AEROPORT" (Saint-Denis airport), "PIERREFONDS" (Saint-Pierre airport), "BELLECOMBE-JACOB" | |
| from | No | Inclusive lower bound on date, ISO format YYYY-MM-DD | |
| to | No | Inclusive upper bound on date, ISO format YYYY-MM-DD | |
| limit | No | Max observations to return (1-100, default 20) |
Implementation Reference
- src/modules/weather.ts:12-55 (handler)The tool registration for 'reunion_get_weather_observations' with the MCP server. The handler queries the 'donnees-synop-essentielles-ommpublic' dataset on data.regionreunion.com, filtering by region (La Réunion), optional station name, date range, and limit. It returns parsed observation fields like temperature, humidity, wind, pressure, rainfall, and present weather code.
server.tool( 'reunion_get_weather_observations', 'Météo-France SYNOP synoptic weather observations from stations located in La Réunion. SYNOP is the WMO standard for surface observations (every 3 hours typically). Returns: station name, observation timestamp, commune, current temperature (°C), 12h min/max temperature, relative humidity (%), wind speed (m/s) and direction (degrees), sea-level pressure (Pa), 1h and 24h rainfall (mm), present-weather code. Sorted by date descending. Use reunion_list_weather_stations to discover available stations.', { station: z.string().optional().describe('Station name prefix match (case-sensitive uppercase). Examples: "LE PORT", "GILLOT-AEROPORT" (Saint-Denis airport), "PIERREFONDS" (Saint-Pierre airport), "BELLECOMBE-JACOB"'), from: z.string().optional().describe('Inclusive lower bound on date, ISO format YYYY-MM-DD'), to: z.string().optional().describe('Inclusive upper bound on date, ISO format YYYY-MM-DD'), limit: z.number().int().min(1).max(100).default(20).describe('Max observations to return (1-100, default 20)'), }, async ({ station, from, to, limit }) => { try { const data = await client.getRecords<RecordObject>(DATASET_SYNOP, { where: buildWhere([ `nom_reg = ${quote('La Réunion')}`, station ? `nom LIKE ${quote(`${station}%`)}` : undefined, from ? `date >= date${quote(from)}` : undefined, to ? `date <= date${quote(to)}` : undefined, ]), order_by: 'date DESC', limit, }); return jsonResult({ total_observations: data.total_count, observations: data.results.map((obs) => ({ station: pickString(obs, ['nom']), date: pickString(obs, ['date']), commune: pickString(obs, ['libgeo']), temperature_c: pickNumber(obs, ['tc']), temp_min_12h_c: pickNumber(obs, ['tn12c']), temp_max_12h_c: pickNumber(obs, ['tx12c']), humidity_pct: pickNumber(obs, ['u']), wind_speed_ms: pickNumber(obs, ['ff']), wind_direction_deg: pickNumber(obs, ['dd']), pressure_sea_level_pa: pickNumber(obs, ['pmer']), rainfall_1h_mm: pickNumber(obs, ['rr1']), rainfall_24h_mm: pickNumber(obs, ['rr24']), present_weather: pickString(obs, ['temps_present']), })), }); } catch (error) { return errorResult(error instanceof Error ? error.message : 'Failed to fetch weather observations'); } } - src/modules/weather.ts:15-20 (schema)Zod schema defining input parameters: station (optional string prefix match), from/to (optional ISO date strings), limit (integer 1-100, default 20).
{ station: z.string().optional().describe('Station name prefix match (case-sensitive uppercase). Examples: "LE PORT", "GILLOT-AEROPORT" (Saint-Denis airport), "PIERREFONDS" (Saint-Pierre airport), "BELLECOMBE-JACOB"'), from: z.string().optional().describe('Inclusive lower bound on date, ISO format YYYY-MM-DD'), to: z.string().optional().describe('Inclusive upper bound on date, ISO format YYYY-MM-DD'), limit: z.number().int().min(1).max(100).default(20).describe('Max observations to return (1-100, default 20)'), }, - src/modules/index.ts:34-56 (registration)Registration of registerWeatherTools(server) in the registerAllTools function, which wires the tool into the MCP server.
registerAdministrationTools(server); registerCatalogTools(server); registerCommuneTools(server); registerCultureTools(server); registerEconomyTools(server); registerEducationTools(server); registerEmploymentTools(server); registerEnvironmentTools(server); registerFacilityTools(server); registerGeographyTools(server); registerHealthTools(server); registerHospitalityTools(server); registerHousingTools(server); registerNationalElectionsTools(server); registerPossessionTools(server); registerSocialTools(server); registerTelecomTools(server); registerTerritoryTools(server); registerTourismTools(server); registerTransportTools(server); registerUrbanismTools(server); registerWeatherTools(server); } - src/utils/helpers.ts:36-55 (helper)buildWhere helper used to construct the ODSQL WHERE clause from optional filter conditions.
export function buildWhere( conditions: Array<string | undefined | null | false> ): string | undefined { const valid = conditions.filter((condition): condition is string => Boolean(condition)); return valid.length > 0 ? valid.join(' AND ') : undefined; } /** * Escape a string literal for ODSQL */ export function escapeOdSqlString(value: string): string { return value.replace(/'/g, "''"); } /** * Quote an ODSQL string literal */ export function quote(value: string): string { return `'${escapeOdSqlString(value)}'`; } - src/client.ts:43-138 (helper)ReunionClient.getRecords method - the API client function that actually fetches records from the OpenDataSoft API.
async getRecords<T extends RecordObject = RecordObject>( datasetId: string, params: ODSQueryParams = {} ): Promise<ODSResponse<T>> { const url = this.buildUrl(`/catalog/datasets/${datasetId}/records`, params); if (REFERENTIAL_DATASETS.has(datasetId)) { const now = Date.now(); const cached = this.recordsCache.get(url); if (cached && cached.expiresAt > now) { return cached.value as ODSResponse<T>; } const value = await this.fetchJson<ODSResponse<T>>(url); this.recordsCache.set(url, { value, expiresAt: now + REFERENTIAL_TTL_MS }); return value; } return this.fetchJson<ODSResponse<T>>(url); } /** * Clear the in-memory caches. Intended for tests. */ clearCaches(): void { this.metadataCache.clear(); this.recordsCache.clear(); } /** * Fetch aggregated data from a dataset */ async getAggregates<T extends RecordObject = RecordObject>( datasetId: string, select: string, options: { where?: string; groupBy?: string; orderBy?: string; limit?: number; } = {} ): Promise<ODSResponse<T>> { const params: Record<string, string | number | undefined> = { select }; if (options.where) params.where = options.where; if (options.groupBy) params.group_by = options.groupBy; if (options.orderBy) params.order_by = options.orderBy; if (options.limit !== undefined) params.limit = options.limit; const url = this.buildUrl(`/catalog/datasets/${datasetId}/aggregates`, params); return this.fetchJson<ODSResponse<T>>(url); } /** * Search across all datasets */ async searchDatasets(query: string): Promise<CatalogResponse> { const url = this.buildUrl('/catalog/datasets', { where: `search(${quote(query)})`, limit: 20, }); return this.fetchJson<CatalogResponse>(url); } /** * List datasets with an optional raw ODSQL where clause. */ async listDatasets( options: { where?: string; limit?: number; offset?: number } = {} ): Promise<CatalogResponse> { const url = this.buildUrl('/catalog/datasets', { where: options.where, limit: options.limit ?? 20, offset: options.offset, }); return this.fetchJson<CatalogResponse>(url); } /** * Fetch dataset metadata from the catalog */ async getDatasetMetadata(datasetId: string): Promise<DatasetMetadata | undefined> { if (!this.metadataCache.has(datasetId)) { const promise = this.fetchJson<CatalogResponse>( this.buildUrl('/catalog/datasets', { where: `dataset_id = ${quote(datasetId)}`, limit: 1, }) ).then((data) => data.results[0]); this.metadataCache.set(datasetId, promise); } return this.metadataCache.get(datasetId); } /** * Check whether a dataset currently exists in the public catalog