psgc-mcp
Version:
Philippine Standard Geographic Code MCP Server - provides hierarchical geographic data for the Philippines
446 lines • 13.1 kB
JavaScript
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