UNPKG

psgc-mcp

Version:

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

446 lines 13.1 kB
import axios from 'axios'; import { API_CONFIG, } from '../types/index.js'; /** * Custom error class for PSGC API errors */ export class PSGCApiError extends Error { statusCode; endpoint; details; constructor(message, statusCode, endpoint, details) { super(message); this.statusCode = statusCode; this.endpoint = endpoint; this.details = details; this.name = 'PSGCApiError'; } } /** * PSGC API Client - A robust HTTP client for the Philippine Standard Geographic Code API */ export class PSGCClient { client; cache = new Map(); config; constructor(config = {}) { this.config = { baseURL: config.baseURL || API_CONFIG.BASE_URL, timeout: config.timeout || API_CONFIG.DEFAULT_TIMEOUT, retries: config.retries || API_CONFIG.MAX_RETRIES, retryDelay: config.retryDelay || API_CONFIG.RETRY_DELAY, cacheTTL: config.cacheTTL || API_CONFIG.CACHE_TTL, }; this.client = axios.create({ baseURL: this.config.baseURL, timeout: this.config.timeout, headers: { Accept: 'application/json', 'User-Agent': 'psgc-mcp-server/1.0.0', }, }); // Add response interceptor for error handling this.client.interceptors.response.use((response) => response, (error) => this.handleError(error)); } /** * Handle API errors with retry logic */ async handleError(error) { if (axios.isAxiosError(error)) { const statusCode = error.response?.status; const endpoint = error.config?.url; if (statusCode === 404) { throw new PSGCApiError('Resource not found', statusCode, endpoint); } if (statusCode && statusCode >= 500) { throw new PSGCApiError('Server error', statusCode, endpoint); } throw new PSGCApiError(error.message || 'Network error', statusCode, endpoint, error.response?.data); } throw new PSGCApiError('Unknown error occurred'); } /** * Generate cache key for request */ getCacheKey(endpoint) { return `psgc:${endpoint}`; } /** * Check if cache entry is valid */ isCacheValid(timestamp) { return Date.now() - timestamp < this.config.cacheTTL; } /** * Get data from cache */ getFromCache(key) { const entry = this.cache.get(key); if (entry && this.isCacheValid(entry.timestamp)) { return entry.data; } return null; } /** * Store data in cache */ setCache(key, data) { this.cache.set(key, { data, timestamp: Date.now(), }); } /** * Clear expired cache entries */ clearExpiredCache() { for (const [key, entry] of this.cache.entries()) { if (!this.isCacheValid(entry.timestamp)) { this.cache.delete(key); } } } /** * Make HTTP request with caching and retry logic */ async request(endpoint, useCache = true) { const cacheKey = this.getCacheKey(endpoint); // Check cache first if (useCache) { const cached = this.getFromCache(cacheKey); if (cached) { return cached; } } let lastError = null; for (let attempt = 0; attempt <= this.config.retries; attempt++) { try { const response = await this.client.get(endpoint); const data = response.data; // Cache successful response if (useCache) { this.setCache(cacheKey, data); } return data; } catch (error) { lastError = error; // Don't retry on 404 if (error instanceof PSGCApiError && error.statusCode === 404) { throw error; } // Wait before retry (exponential backoff) if (attempt < this.config.retries) { const delay = this.config.retryDelay * Math.pow(2, attempt); await new Promise((resolve) => setTimeout(resolve, delay)); } } } throw lastError; } /** * Get all island groups */ async getIslandGroups() { return this.request('/island-groups.json'); } /** * Get specific island group by code */ async getIslandGroup(code) { return this.request(`/island-groups/${code}.json`); } /** * Get regions in an island group */ async getIslandGroupRegions(islandGroupCode) { return this.request(`/island-groups/${islandGroupCode}/regions.json`); } /** * Get provinces in an island group */ async getIslandGroupProvinces(islandGroupCode) { return this.request(`/island-groups/${islandGroupCode}/provinces.json`); } /** * Get districts in an island group */ async getIslandGroupDistricts(islandGroupCode) { return this.request(`/island-groups/${islandGroupCode}/districts.json`); } /** * Get cities in an island group */ async getIslandGroupCities(islandGroupCode) { return this.request(`/island-groups/${islandGroupCode}/cities.json`); } /** * Get municipalities in an island group */ async getIslandGroupMunicipalities(islandGroupCode) { return this.request(`/island-groups/${islandGroupCode}/municipalities.json`); } /** * Get cities and municipalities in an island group */ async getIslandGroupCitiesMunicipalities(islandGroupCode) { return this.request(`/island-groups/${islandGroupCode}/cities-municipalities.json`); } /** * Get sub-municipalities in an island group */ async getIslandGroupSubMunicipalities(islandGroupCode) { return this.request(`/island-groups/${islandGroupCode}/sub-municipalities.json`); } /** * Get barangays in an island group */ async getIslandGroupBarangays(islandGroupCode) { return this.request(`/island-groups/${islandGroupCode}/barangays.json`); } /** * Get all regions */ async getRegions() { return this.request('/regions.json'); } /** * Get specific region by code */ async getRegion(code) { return this.request(`/regions/${code}.json`); } /** * Get provinces in a region */ async getRegionProvinces(regionCode) { return this.request(`/regions/${regionCode}/provinces.json`); } /** * Get districts in a region */ async getRegionDistricts(regionCode) { return this.request(`/regions/${regionCode}/districts.json`); } /** * Get cities in a region */ async getRegionCities(regionCode) { return this.request(`/regions/${regionCode}/cities.json`); } /** * Get municipalities in a region */ async getRegionMunicipalities(regionCode) { return this.request(`/regions/${regionCode}/municipalities.json`); } /** * Get cities and municipalities in a region */ async getRegionCitiesMunicipalities(regionCode) { return this.request(`/regions/${regionCode}/cities-municipalities.json`); } /** * Get sub-municipalities in a region */ async getRegionSubMunicipalities(regionCode) { return this.request(`/regions/${regionCode}/sub-municipalities.json`); } /** * Get barangays in a region */ async getRegionBarangays(regionCode) { return this.request(`/regions/${regionCode}/barangays.json`); } /** * Get all provinces */ async getProvinces() { return this.request('/provinces.json'); } /** * Get specific province by code */ async getProvince(code) { return this.request(`/provinces/${code}.json`); } /** * Get cities in a province */ async getProvinceCities(provinceCode) { return this.request(`/provinces/${provinceCode}/cities.json`); } /** * Get municipalities in a province */ async getProvinceMunicipalities(provinceCode) { return this.request(`/provinces/${provinceCode}/municipalities.json`); } /** * Get cities and municipalities in a province */ async getProvinceCitiesMunicipalities(provinceCode) { return this.request(`/provinces/${provinceCode}/cities-municipalities.json`); } /** * Get sub-municipalities in a province */ async getProvinceSubMunicipalities(provinceCode) { return this.request(`/provinces/${provinceCode}/sub-municipalities.json`); } /** * Get barangays in a province */ async getProvinceBarangays(provinceCode) { return this.request(`/provinces/${provinceCode}/barangays.json`); } /** * Get all districts */ async getDistricts() { return this.request('/districts.json'); } /** * Get specific district by code */ async getDistrict(code) { return this.request(`/districts/${code}.json`); } /** * Get cities in a district */ async getDistrictCities(districtCode) { return this.request(`/districts/${districtCode}/cities.json`); } /** * Get municipalities in a district */ async getDistrictMunicipalities(districtCode) { return this.request(`/districts/${districtCode}/municipalities.json`); } /** * Get cities and municipalities in a district */ async getDistrictCitiesMunicipalities(districtCode) { return this.request(`/districts/${districtCode}/cities-municipalities.json`); } /** * Get sub-municipalities in a district */ async getDistrictSubMunicipalities(districtCode) { return this.request(`/districts/${districtCode}/sub-municipalities.json`); } /** * Get barangays in a district */ async getDistrictBarangays(districtCode) { return this.request(`/districts/${districtCode}/barangays.json`); } /** * Get all cities */ async getCities() { return this.request('/cities.json'); } /** * Get specific city by code */ async getCity(code) { return this.request(`/cities/${code}.json`); } /** * Get barangays in a city */ async getCityBarangays(cityCode) { return this.request(`/cities/${cityCode}/barangays.json`); } /** * Get all municipalities */ async getMunicipalities() { return this.request('/municipalities.json'); } /** * Get specific municipality by code */ async getMunicipality(code) { return this.request(`/municipalities/${code}.json`); } /** * Get barangays in a municipality */ async getMunicipalityBarangays(municipalityCode) { return this.request(`/municipalities/${municipalityCode}/barangays.json`); } /** * Get all sub-municipalities */ async getSubMunicipalities() { return this.request('/sub-municipalities.json'); } /** * Get specific sub-municipality by code */ async getSubMunicipality(code) { return this.request(`/sub-municipalities/${code}.json`); } /** * Get barangays in a sub-municipality */ async getSubMunicipalityBarangays(subMunicipalityCode) { return this.request(`/sub-municipalities/${subMunicipalityCode}/barangays.json`); } /** * Get all cities and municipalities */ async getCitiesMunicipalities() { return this.request('/cities-municipalities.json'); } /** * Get specific city or municipality by code */ async getCityMunicipality(code) { return this.request(`/cities-municipalities/${code}.json`); } /** * Get barangays in a city or municipality */ async getCityMunicipalityBarangays(cityOrMunicipalityCode) { return this.request(`/cities-municipalities/${cityOrMunicipalityCode}/barangays.json`); } /** * Get all barangays */ async getBarangays() { return this.request('/barangays.json'); } /** * Get specific barangay by code */ async getBarangay(code) { return this.request(`/barangays/${code}.json`); } /** * Clear all cached data */ clearCache() { this.cache.clear(); } /** * Clear expired cache entries */ cleanupCache() { this.clearExpiredCache(); } /** * Get cache statistics */ getCacheStats() { return { size: this.cache.size, entries: Array.from(this.cache.keys()), }; } } // Export singleton instance export const psgcClient = new PSGCClient(); //# sourceMappingURL=psgc-client.js.map