mcp-server-unhcr
Version:
MCP server for accessing UNHCR refugee data and statistics API
388 lines (355 loc) • 11.2 kB
JavaScript
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ErrorCode,
ListToolsRequestSchema,
McpError,
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
// UNHCR API endpoints based on their documentation
const API_BASE_URL = 'https://api.unhcr.org/population/v1';
class UNHCRServer {
constructor() {
this.server = new Server(
{
name: 'unhcr-api',
version: '1.0.1',
},
{
capabilities: {
tools: {},
},
}
);
this.setupToolHandlers();
this.axiosInstance = axios.create({
baseURL: API_BASE_URL,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
timeout: 30000,
});
}
setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'get_population_statistics',
description: 'Get population statistics for refugees, asylum seekers, and persons of concern',
inputSchema: {
type: 'object',
properties: {
year_from: {
type: 'integer',
description: 'Start year for the statistics (e.g., 2022)',
default: 2022,
},
year_to: {
type: 'integer',
description: 'End year for the statistics (e.g., 2023)',
default: 2023,
},
coo_iso: {
type: 'string',
description: 'Country of origin ISO3 code (e.g., "SYR" for Syria)',
},
coa_iso: {
type: 'string',
description: 'Country of asylum ISO3 code (e.g., "TUR" for Turkey)',
},
population_type: {
type: 'string',
description: 'Type of population',
enum: ['REF', 'ASY', 'IDP', 'STA', 'OOC', 'VDA'],
},
limit: {
type: 'integer',
description: 'Number of results to return (default: 100, max: 10000)',
default: 100,
},
},
required: [],
},
},
{
name: 'get_demographics',
description: 'Get demographic breakdown by age and gender',
inputSchema: {
type: 'object',
properties: {
year_from: {
type: 'integer',
description: 'Start year for demographics',
default: 2022,
},
year_to: {
type: 'integer',
description: 'End year for demographics',
default: 2023,
},
coo_iso: {
type: 'string',
description: 'Country of origin ISO3 code',
},
coa_iso: {
type: 'string',
description: 'Country of asylum ISO3 code',
},
},
required: [],
},
},
{
name: 'get_countries',
description: 'Get list of countries with their ISO codes',
inputSchema: {
type: 'object',
properties: {
region: {
type: 'string',
description: 'Filter by region (e.g., "Africa", "Asia", "Europe")',
},
},
required: [],
},
},
{
name: 'get_solutions',
description: 'Get data on durable solutions (resettlement, returns, etc.)',
inputSchema: {
type: 'object',
properties: {
year_from: {
type: 'integer',
description: 'Start year',
default: 2022,
},
year_to: {
type: 'integer',
description: 'End year',
default: 2023,
},
coo_iso: {
type: 'string',
description: 'Country of origin ISO3 code',
},
solution_type: {
type: 'string',
description: 'Type of solution',
enum: ['RET', 'RST', 'NAT'],
},
},
required: [],
},
},
{
name: 'get_idps',
description: 'Get internally displaced persons (IDP) statistics',
inputSchema: {
type: 'object',
properties: {
year_from: {
type: 'integer',
description: 'Start year',
default: 2022,
},
year_to: {
type: 'integer',
description: 'End year',
default: 2023,
},
coa_iso: {
type: 'string',
description: 'Country ISO3 code',
},
},
required: [],
},
},
],
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
switch (request.params.name) {
case 'get_population_statistics':
return this.getPopulationStatistics(request.params.arguments);
case 'get_demographics':
return this.getDemographics(request.params.arguments);
case 'get_countries':
return this.getCountries(request.params.arguments);
case 'get_solutions':
return this.getSolutions(request.params.arguments);
case 'get_idps':
return this.getIDPs(request.params.arguments);
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${request.params.name}`
);
}
});
}
async getPopulationStatistics(args) {
try {
const params = new URLSearchParams();
// Add parameters if provided
if (args.year_from) params.append('yearFrom', args.year_from);
if (args.year_to) params.append('yearTo', args.year_to);
if (args.coo_iso) params.append('coo', args.coo_iso);
if (args.coa_iso) params.append('coa', args.coa_iso);
if (args.population_type) params.append('populationType', args.population_type);
if (args.limit) params.append('limit', args.limit);
// Default to showing data grouped by year
params.append('aggregate', 'year');
const url = `/population?${params.toString()}`;
console.error(`Fetching from: ${API_BASE_URL}${url}`);
const response = await this.axiosInstance.get(url);
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
} catch (error) {
console.error('Error details:', error.response?.data || error.message);
return {
content: [
{
type: 'text',
text: `Error fetching population statistics: ${error.response?.data?.message || error.message}\nStatus: ${error.response?.status}\nAPI URL: ${API_BASE_URL}`,
},
],
isError: true,
};
}
}
async getDemographics(args) {
try {
const params = new URLSearchParams();
if (args.year_from) params.append('yearFrom', args.year_from);
if (args.year_to) params.append('yearTo', args.year_to);
if (args.coo_iso) params.append('coo', args.coo_iso);
if (args.coa_iso) params.append('coa', args.coa_iso);
params.append('aggregate', 'age,sex');
const url = `/demographics?${params.toString()}`;
const response = await this.axiosInstance.get(url);
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error fetching demographics: ${error.response?.data?.message || error.message}`,
},
],
isError: true,
};
}
}
async getCountries(args) {
try {
const params = new URLSearchParams();
if (args.region) params.append('region', args.region);
const url = `/countries?${params.toString()}`;
const response = await this.axiosInstance.get(url);
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error fetching countries: ${error.response?.data?.message || error.message}`,
},
],
isError: true,
};
}
}
async getSolutions(args) {
try {
const params = new URLSearchParams();
if (args.year_from) params.append('yearFrom', args.year_from);
if (args.year_to) params.append('yearTo', args.year_to);
if (args.coo_iso) params.append('coo', args.coo_iso);
if (args.solution_type) params.append('solutionType', args.solution_type);
const url = `/solutions?${params.toString()}`;
const response = await this.axiosInstance.get(url);
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error fetching solutions data: ${error.response?.data?.message || error.message}`,
},
],
isError: true,
};
}
}
async getIDPs(args) {
try {
const params = new URLSearchParams();
if (args.year_from) params.append('yearFrom', args.year_from);
if (args.year_to) params.append('yearTo', args.year_to);
if (args.coa_iso) params.append('coa', args.coa_iso);
params.append('populationType', 'IDP');
const url = `/population?${params.toString()}`;
const response = await this.axiosInstance.get(url);
return {
content: [
{
type: 'text',
text: JSON.stringify(response.data, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error fetching IDP statistics: ${error.response?.data?.message || error.message}`,
},
],
isError: true,
};
}
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('UNHCR MCP server running on stdio');
}
}
// Auto-start server when script is run directly
if (import.meta.url === `file://${process.argv[1]}`) {
const server = new UNHCRServer();
server.run().catch(console.error);
}
export default UNHCRServer;