Skip to main content
Glama
Hug0x0

mcp-reunion

reunion_commune_profile

Fetch a complete profile of a commune in La Réunion by merging data from 8 sources including population, schools, businesses, and accidents. Get a single structured snapshot with inline error reporting for missing data.

Instructions

Comprehensive cross-dataset snapshot of one La Réunion commune. Joins 8 sources in parallel (population, QPV count and list, IRIS count, school count, priority-education school count, active SIRENE establishments, 2019 road-accidents count, museums count and list) into a single structured response. Failures on individual dimensions are reported inline rather than failing the whole call. Use reunion_find_commune first if the commune name might be misspelled. For side-by-side multi-commune comparison use reunion_compare_communes.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
communeYesCommune name, used as case-sensitive prefix match. Examples: "Saint-Denis", "Le Tampon", "Saint-Pierre", "L'Étang-Salé"

Implementation Reference

  • Registration: registerCommuneTools registers 'reunion_commune_profile' on the MCP server via server.tool()
    export function registerCommuneTools(server: McpServer): void {
      server.tool(
        'reunion_commune_profile',
        'Comprehensive cross-dataset snapshot of one La Réunion commune. Joins 8 sources in parallel (population, QPV count and list, IRIS count, school count, priority-education school count, active SIRENE establishments, 2019 road-accidents count, museums count and list) into a single structured response. Failures on individual dimensions are reported inline rather than failing the whole call. Use reunion_find_commune first if the commune name might be misspelled. For side-by-side multi-commune comparison use reunion_compare_communes.',
        {
          commune: z.string().describe('Commune name, used as case-sensitive prefix match. Examples: "Saint-Denis", "Le Tampon", "Saint-Pierre", "L\'Étang-Salé"'),
        },
        async ({ commune }) => {
          const prefix = `${commune}%`;
    
          const [
            population,
            qpv,
            iris,
            schools,
            priorityEd,
            sirene,
            accidents,
            museums,
          ] = await Promise.all([
            settle(
              'population',
              // Dataset is national — scope to département 974 to avoid matches
              // like "Villiers-Saint-Denis" (Aisne) when user types "Saint-Denis".
              client.getRecords<RecordObject>('population-francaise-communespublic', {
                where: `code_departement = ${quote('974')} AND nom_de_la_commune LIKE ${quote(prefix)}`,
                order_by: 'annee_recensement DESC',
                limit: 1,
              })
            ),
            settle(
              'qpv',
              client.getRecords<RecordObject>('quartiers-prioritaires-de-la-politique-de-la-ville-qpv', {
                where: `commune_qp LIKE ${quote(prefix)}`,
                limit: 50,
              })
            ),
            settle(
              'iris',
              client.getRecords<RecordObject>('iris-millesime-france', {
                where: `com_name LIKE ${quote(prefix)}`,
                limit: 100,
              })
            ),
            settle(
              'schools',
              client.getRecords<RecordObject>(
                'adresse-et-geolocalisation-des-etablissements-d-enseignement-du-premier-et-secon',
                {
                  where: `libelle_commune LIKE ${quote(prefix)}`,
                  limit: 1,
                }
              )
            ),
            settle(
              'priority_education',
              client.getRecords<RecordObject>('etablissements-de-l-education-prioritaire-a-la-reunion', {
                where: `nom_commune LIKE ${quote(prefix)}`,
                limit: 1,
              })
            ),
            settle(
              'sirene',
              client.getRecords<RecordObject>('base-sirene-v3-lareunion', {
                where: `libellecommuneetablissement LIKE ${quote(prefix)} AND etatadministratifetablissement = ${quote('Actif')}`,
                limit: 1,
              })
            ),
            settle(
              'accidents_2019',
              client.getRecords<RecordObject>(
                'bases-de-donnees-annuelles-des-accidents-corporels-de-la-circulation-routiere',
                {
                  where: `com_name LIKE ${quote(prefix)} AND an = 2019`,
                  limit: 1,
                }
              )
            ),
            settle(
              'museums',
              client.getRecords<RecordObject>('liste-des-musees-de-la-reunion', {
                where: `commune LIKE ${quote(prefix)}`,
                limit: 20,
              })
            ),
          ]);
    
          const populationRow = population.value?.results[0];
          const dimensions = {
            resolved: {
              query: commune,
              matched_commune: populationRow ? pickString(populationRow, ['nom_de_la_commune']) : null,
              insee_code: populationRow ? pickString(populationRow, ['code_insee']) : null,
              epci: populationRow ? pickString(populationRow, ['libepci']) : null,
            },
            population: population.error
              ? { error: population.error }
              : populationRow
                ? {
                    census_year: pickNumber(populationRow, ['annee_recensement']),
                    total: pickNumber(populationRow, ['population_totale']),
                    municipal: pickNumber(populationRow, ['population_municipale']),
                    area_km2: pickNumber(populationRow, ['superficie']),
                  }
                : null,
            territory: {
              iris_count: iris.error ? null : iris.value?.total_count ?? 0,
              qpv_count: qpv.error ? null : qpv.value?.total_count ?? 0,
              qpv_list: qpv.value?.results.map((row) => ({
                code: pickString(row, ['code_qp']),
                name: pickString(row, ['nom_qp']),
              })) ?? [],
            },
            education: {
              schools_count: schools.error ? null : schools.value?.total_count ?? 0,
              priority_education_schools: priorityEd.error
                ? null
                : priorityEd.value?.total_count ?? 0,
            },
            economy: {
              active_sirene_establishments: sirene.error ? null : sirene.value?.total_count ?? 0,
            },
            safety: {
              accidents_2019: accidents.error ? null : accidents.value?.total_count ?? 0,
            },
            culture: {
              museums_count: museums.error ? null : museums.value?.total_count ?? 0,
              museums: museums.value?.results.map((row) => ({
                name: pickString(row, ['nom_officiel_du_musee']),
                museofile_id: pickString(row, ['identifiant_museofile']),
              })) ?? [],
            },
            errors: [population, qpv, iris, schools, priorityEd, sirene, accidents, museums]
              .filter((r) => r.error)
              .map((r) => ({ dimension: r.label, error: r.error })),
          };
    
          if (!populationRow && population.error) {
            return errorResult(
              `Commune "${commune}" not found or population dataset unreachable: ${population.error}`
            );
          }
    
          return jsonResult(dimensions);
        }
      );
  • Handler: The async callback receiving {commune}, querying 8 data sources in parallel via settle() and returning a structured JSON profile
      async ({ commune }) => {
        const prefix = `${commune}%`;
    
        const [
          population,
          qpv,
          iris,
          schools,
          priorityEd,
          sirene,
          accidents,
          museums,
        ] = await Promise.all([
          settle(
            'population',
            // Dataset is national — scope to département 974 to avoid matches
            // like "Villiers-Saint-Denis" (Aisne) when user types "Saint-Denis".
            client.getRecords<RecordObject>('population-francaise-communespublic', {
              where: `code_departement = ${quote('974')} AND nom_de_la_commune LIKE ${quote(prefix)}`,
              order_by: 'annee_recensement DESC',
              limit: 1,
            })
          ),
          settle(
            'qpv',
            client.getRecords<RecordObject>('quartiers-prioritaires-de-la-politique-de-la-ville-qpv', {
              where: `commune_qp LIKE ${quote(prefix)}`,
              limit: 50,
            })
          ),
          settle(
            'iris',
            client.getRecords<RecordObject>('iris-millesime-france', {
              where: `com_name LIKE ${quote(prefix)}`,
              limit: 100,
            })
          ),
          settle(
            'schools',
            client.getRecords<RecordObject>(
              'adresse-et-geolocalisation-des-etablissements-d-enseignement-du-premier-et-secon',
              {
                where: `libelle_commune LIKE ${quote(prefix)}`,
                limit: 1,
              }
            )
          ),
          settle(
            'priority_education',
            client.getRecords<RecordObject>('etablissements-de-l-education-prioritaire-a-la-reunion', {
              where: `nom_commune LIKE ${quote(prefix)}`,
              limit: 1,
            })
          ),
          settle(
            'sirene',
            client.getRecords<RecordObject>('base-sirene-v3-lareunion', {
              where: `libellecommuneetablissement LIKE ${quote(prefix)} AND etatadministratifetablissement = ${quote('Actif')}`,
              limit: 1,
            })
          ),
          settle(
            'accidents_2019',
            client.getRecords<RecordObject>(
              'bases-de-donnees-annuelles-des-accidents-corporels-de-la-circulation-routiere',
              {
                where: `com_name LIKE ${quote(prefix)} AND an = 2019`,
                limit: 1,
              }
            )
          ),
          settle(
            'museums',
            client.getRecords<RecordObject>('liste-des-musees-de-la-reunion', {
              where: `commune LIKE ${quote(prefix)}`,
              limit: 20,
            })
          ),
        ]);
    
        const populationRow = population.value?.results[0];
        const dimensions = {
          resolved: {
            query: commune,
            matched_commune: populationRow ? pickString(populationRow, ['nom_de_la_commune']) : null,
            insee_code: populationRow ? pickString(populationRow, ['code_insee']) : null,
            epci: populationRow ? pickString(populationRow, ['libepci']) : null,
          },
          population: population.error
            ? { error: population.error }
            : populationRow
              ? {
                  census_year: pickNumber(populationRow, ['annee_recensement']),
                  total: pickNumber(populationRow, ['population_totale']),
                  municipal: pickNumber(populationRow, ['population_municipale']),
                  area_km2: pickNumber(populationRow, ['superficie']),
                }
              : null,
          territory: {
            iris_count: iris.error ? null : iris.value?.total_count ?? 0,
            qpv_count: qpv.error ? null : qpv.value?.total_count ?? 0,
            qpv_list: qpv.value?.results.map((row) => ({
              code: pickString(row, ['code_qp']),
              name: pickString(row, ['nom_qp']),
            })) ?? [],
          },
          education: {
            schools_count: schools.error ? null : schools.value?.total_count ?? 0,
            priority_education_schools: priorityEd.error
              ? null
              : priorityEd.value?.total_count ?? 0,
          },
          economy: {
            active_sirene_establishments: sirene.error ? null : sirene.value?.total_count ?? 0,
          },
          safety: {
            accidents_2019: accidents.error ? null : accidents.value?.total_count ?? 0,
          },
          culture: {
            museums_count: museums.error ? null : museums.value?.total_count ?? 0,
            museums: museums.value?.results.map((row) => ({
              name: pickString(row, ['nom_officiel_du_musee']),
              museofile_id: pickString(row, ['identifiant_museofile']),
            })) ?? [],
          },
          errors: [population, qpv, iris, schools, priorityEd, sirene, accidents, museums]
            .filter((r) => r.error)
            .map((r) => ({ dimension: r.label, error: r.error })),
        };
    
        if (!populationRow && population.error) {
          return errorResult(
            `Commune "${commune}" not found or population dataset unreachable: ${population.error}`
          );
        }
    
        return jsonResult(dimensions);
      }
    );
  • Schema: Zod schema defining the 'commune' input parameter (string, described as case-sensitive prefix match)
    {
      commune: z.string().describe('Commune name, used as case-sensitive prefix match. Examples: "Saint-Denis", "Le Tampon", "Saint-Pierre", "L\'Étang-Salé"'),
    },
  • Helper: The settle() function that wraps promises to report individual dimension errors inline
    async function settle<T>(label: string, p: Promise<T>): Promise<{ label: string; value?: T; error?: string }> {
      try {
        return { label, value: await p };
      } catch (error) {
        return { label, error: error instanceof Error ? error.message : String(error) };
      }
    }
  • Registration: registerAllTools in modules/index.ts calls registerCommuneTools(server) to activate the tool
    export function registerAllTools(server: McpServer): void {
      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);
    }
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description discloses key behavioral traits: joining 8 sources in parallel, inline error handling for individual failures, and the specific data included. There is no contradiction with annotations since none exist.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is informative and well-structured, listing the 8 sources compactly and including usage notes. While it is somewhat long, every sentence adds necessary context, so it earns a 4.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (8 parallel sources, inline error handling) and lack of output schema, the description is remarkably complete. It explains what data is returned, how errors are reported, and provides cross-references to related tools.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds value by clarifying that the commune parameter is a 'case-sensitive prefix match' and provides examples, which goes beyond the schema description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it provides a 'Comprehensive cross-dataset snapshot of one La Réunion commune' and lists the 8 sources joined, making the purpose unmistakable. It also distinguishes itself from sibling tools like reunion_compare_communes and reunion_find_commune.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says to 'use reunion_find_commune first if the commune name might be misspelled' and 'For side-by-side multi-commune comparison use reunion_compare_communes', offering clear when-to-use and when-not-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Hug0x0/mcp-reunion'

If you have feedback or need assistance with the MCP directory API, please join our Discord server