UNPKG

psgc-mcp

Version:

Philippine Standard Geographic Code MCP Server - provides hierarchical geographic data for the Philippines

273 lines 12 kB
import { z } from 'zod'; import { GeographicCodeSchema, EntityTypeSchema, SearchNameSchema, } from '../types/validation.schemas.js'; export function registerSearchTools(server, psgcClient) { // 4.8.1 Search entities by name across all levels server.tool('search_by_name', 'Search for geographic entities by name across all levels (regions, provinces, cities, municipalities, barangays)', { name: SearchNameSchema, type: EntityTypeSchema.optional(), limit: z.number().int().positive().max(100).default(10).optional(), }, async ({ name, type, limit }) => { try { const searchLimit = limit || 10; const results = []; // Search in regions if (!type || type === 'region') { const regions = await psgcClient.getRegions(); const matching = regions .filter((r) => r.name.toLowerCase().includes(name.toLowerCase())) .slice(0, searchLimit); results.push(...matching.map((r) => ({ ...r, entityType: 'region' }))); } // Search in provinces if (!type || type === 'province') { const provinces = await psgcClient.getProvinces(); const matching = provinces .filter((p) => p.name.toLowerCase().includes(name.toLowerCase())) .slice(0, searchLimit); results.push(...matching.map((p) => ({ ...p, entityType: 'province' }))); } // Search in cities if (!type || type === 'city') { const cities = await psgcClient.getCities(); const matching = cities .filter((c) => c.name.toLowerCase().includes(name.toLowerCase())) .slice(0, searchLimit); results.push(...matching.map((c) => ({ ...c, entityType: 'city' }))); } // Search in municipalities if (!type || type === 'municipality') { const municipalities = await psgcClient.getMunicipalities(); const matching = municipalities .filter((m) => m.name.toLowerCase().includes(name.toLowerCase())) .slice(0, searchLimit); results.push(...matching.map((m) => ({ ...m, entityType: 'municipality' }))); } // Search in barangays if (!type || type === 'barangay') { const barangays = await psgcClient.getBarangays(); const matching = barangays .filter((b) => b.name.toLowerCase().includes(name.toLowerCase())) .slice(0, searchLimit); results.push(...matching.map((b) => ({ ...b, entityType: 'barangay' }))); } return { content: [ { type: 'text', text: JSON.stringify(results.slice(0, searchLimit), null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }); // 4.8.2 Get complete hierarchy for a code server.tool('get_hierarchy', 'Get complete geographic hierarchy for a specific code (shows parent entities)', { code: GeographicCodeSchema, }, async ({ code }) => { try { const hierarchy = { code, levels: [], }; // Determine entity type based on code pattern let entityType; const codeStr = String(code); if (codeStr.length === 9) { if (codeStr.endsWith('000000')) { entityType = 'region'; } else if (codeStr.endsWith('000')) { if (codeStr.substring(3, 6) === '000') { entityType = 'province'; } else { entityType = 'city/municipality'; } } else { entityType = 'barangay'; } } else { throw new Error('Invalid code format - must be 9 digits'); } hierarchy.entityType = entityType; // Build hierarchy based on entity type switch (entityType) { case 'barangay': { const barangay = await psgcClient.getBarangay(codeStr); hierarchy.levels.unshift({ type: 'barangay', data: barangay }); // Get city/municipality const cityOrMunicipalityCode = codeStr.substring(0, 6) + '000'; try { const cityMunicipality = await psgcClient.getCityMunicipality(cityOrMunicipalityCode); hierarchy.levels.unshift({ type: 'city/municipality', data: cityMunicipality }); // Get province const provinceCode = codeStr.substring(0, 3) + '000000'; const province = await psgcClient.getProvince(provinceCode); hierarchy.levels.unshift({ type: 'province', data: province }); // Get region const regionCode = codeStr.substring(0, 2) + '0000000'; const region = await psgcClient.getRegion(regionCode); hierarchy.levels.unshift({ type: 'region', data: region }); // Get island group const islandGroup = await psgcClient.getIslandGroup(region.islandGroupCode); hierarchy.levels.unshift({ type: 'island_group', data: islandGroup }); } catch { // Handle cases where parent entities might not exist } break; } case 'city': case 'municipality': { const cityMunicipality = await psgcClient.getCityMunicipality(codeStr); hierarchy.levels.unshift({ type: 'city/municipality', data: cityMunicipality }); // Get province const provinceCode2 = codeStr.substring(0, 3) + '000000'; const province2 = await psgcClient.getProvince(provinceCode2); hierarchy.levels.unshift({ type: 'province', data: province2 }); // Get region const regionCode2 = codeStr.substring(0, 2) + '0000000'; const region2 = await psgcClient.getRegion(regionCode2); hierarchy.levels.unshift({ type: 'region', data: region2 }); // Get island group const islandGroup2 = await psgcClient.getIslandGroup(region2.islandGroupCode); hierarchy.levels.unshift({ type: 'island_group', data: islandGroup2 }); break; } case 'province': { const province3 = await psgcClient.getProvince(codeStr); hierarchy.levels.unshift({ type: 'province', data: province3 }); // Get region const regionCode3 = codeStr.substring(0, 2) + '0000000'; const region3 = await psgcClient.getRegion(regionCode3); hierarchy.levels.unshift({ type: 'region', data: region3 }); // Get island group const islandGroup3 = await psgcClient.getIslandGroup(region3.islandGroupCode); hierarchy.levels.unshift({ type: 'island_group', data: islandGroup3 }); break; } case 'region': { const regionData = await psgcClient.getRegion(codeStr); hierarchy.levels.unshift({ type: 'region', data: regionData }); // Get island group const islandGroupData = await psgcClient.getIslandGroup(regionData.islandGroupCode); hierarchy.levels.unshift({ type: 'island_group', data: islandGroupData }); break; } } return { content: [ { type: 'text', text: JSON.stringify(hierarchy, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }); // 4.8.3 Validate if a geographic code exists server.tool('validate_code', 'Validate if a geographic code exists and return its type', { code: GeographicCodeSchema, }, async ({ code }) => { try { const codeStr = String(code); let entityType = null; let exists = false; let data = null; // Determine entity type based on code pattern if (codeStr.length === 9) { if (codeStr.endsWith('000000')) { entityType = 'region'; try { data = await psgcClient.getRegion(codeStr); exists = true; } catch { exists = false; } } else if (codeStr.endsWith('000')) { if (codeStr.substring(3, 6) === '000') { entityType = 'province'; try { data = await psgcClient.getProvince(codeStr); exists = true; } catch { exists = false; } } else { entityType = 'city/municipality'; try { data = await psgcClient.getCityMunicipality(codeStr); exists = true; } catch { exists = false; } } } else { entityType = 'barangay'; try { data = await psgcClient.getBarangay(codeStr); exists = true; } catch { exists = false; } } } return { content: [ { type: 'text', text: JSON.stringify({ code: codeStr, valid: exists, type: entityType, data: exists ? data : null, }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }); } //# sourceMappingURL=search.js.map