#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// ============================================
// CONFIGURACIÓN
// ============================================
const API_BASE_URL = "https://api-colombia.com/api/v1";
const server = new McpServer({
name: "colombia-mcp-server",
version: "1.0.0",
});
// ============================================
// CLIENTE API
// ============================================
async function apiRequest<T>(endpoint: string): Promise<T> {
const url = `${API_BASE_URL}${endpoint}`;
const response = await fetch(url, {
method: "GET",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error(`API Error: ${response.status} ${response.statusText}`);
}
return response.json() as Promise<T>;
}
// ============================================
// TIPOS
// ============================================
interface Region {
id: number;
name: string;
description: string;
}
interface Department {
id: number;
name: string;
description: string;
cityCapitalId: number;
municipalities: number;
surface: number;
population: number;
phonePrefix: string;
regionId: number;
cityCapital?: {
id: number;
name: string;
description: string;
};
}
interface City {
id: number;
name: string;
description: string;
surface: number | null;
population: number | null;
postalCode: string | null;
departmentId: number;
}
interface TouristAttraction {
id: number;
name: string;
description: string;
images: string[];
latitude: string;
longitude: string;
cityId: number;
city?: City;
}
// ============================================
// HERRAMIENTAS
// ============================================
// 1. Obtener todas las regiones
server.registerTool(
"get_regions",
{
title: "Obtener Regiones de Colombia",
description: `Obtiene las 6 regiones naturales de Colombia: Caribe, Pacífico, Orinoquía, Amazonía, Andina e Insular.
Returns:
Lista de regiones con id, nombre y descripción.
Ejemplo de uso:
- "¿Cuáles son las regiones de Colombia?"
- "Dame información sobre las regiones naturales"`,
inputSchema: {},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async () => {
try {
const regions = await apiRequest<Region[]>("/Region");
const resultado = regions.map(r => ({
id: r.id,
nombre: r.name,
descripcion: r.description,
}));
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener regiones: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 2. Obtener todos los departamentos
server.registerTool(
"get_departments",
{
title: "Obtener Departamentos de Colombia",
description: `Obtiene los 32 departamentos de Colombia más Bogotá D.C.
Returns:
Lista de departamentos con: id, nombre, capital, población, superficie y región.
Ejemplo de uso:
- "¿Cuáles son los departamentos de Colombia?"
- "Lista todos los departamentos"`,
inputSchema: {},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async () => {
try {
const departments = await apiRequest<Department[]>("/Department");
const resultado = departments.map(d => ({
id: d.id,
nombre: d.name,
capital: d.cityCapital?.name || "N/A",
poblacion: d.population,
superficie_km2: d.surface,
municipios: d.municipalities,
region_id: d.regionId,
}));
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener departamentos: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 3. Obtener departamento por ID o nombre
server.registerTool(
"get_department",
{
title: "Obtener Departamento Específico",
description: `Obtiene información detallada de un departamento específico por ID o nombre.
Args:
- id (number, opcional): ID del departamento (1-33)
- name (string, opcional): Nombre del departamento (ej: "Antioquia", "Cundinamarca")
Returns:
Información detallada del departamento incluyendo población, superficie, capital y descripción.
Ejemplo de uso:
- "Dame información sobre Antioquia"
- "¿Cuál es la capital de Cundinamarca?"`,
inputSchema: {
id: z.number().int().min(1).max(33).optional().describe("ID del departamento (1-33)"),
name: z.string().optional().describe("Nombre del departamento"),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async ({ id, name }) => {
try {
let department: Department;
if (id) {
department = await apiRequest<Department>(`/Department/${id}`);
} else if (name) {
department = await apiRequest<Department>(`/Department/name/${encodeURIComponent(name)}`);
} else {
return {
content: [
{
type: "text",
text: "Error: Debes proporcionar un ID o nombre del departamento",
},
],
};
}
const resultado = {
id: department.id,
nombre: department.name,
descripcion: department.description,
capital: department.cityCapital?.name || "N/A",
poblacion: department.population,
superficie_km2: department.surface,
municipios: department.municipalities,
prefijo_telefonico: department.phonePrefix,
region_id: department.regionId,
};
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener departamento: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 4. Obtener departamentos por región
server.registerTool(
"get_departments_by_region",
{
title: "Obtener Departamentos por Región",
description: `Obtiene todos los departamentos que pertenecen a una región específica.
Args:
- region_id (number): ID de la región
- 1 = Caribe
- 2 = Pacífico
- 3 = Orinoquía
- 4 = Amazonía
- 5 = Andina
- 6 = Insular
Returns:
Lista de departamentos de esa región.
Ejemplo de uso:
- "¿Cuáles departamentos están en la región Caribe?"
- "Dame los departamentos de la Amazonía"`,
inputSchema: {
region_id: z.number().int().min(1).max(6).describe("ID de la región (1=Caribe, 2=Pacífico, 3=Orinoquía, 4=Amazonía, 5=Andina, 6=Insular)"),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async ({ region_id }) => {
try {
const departments = await apiRequest<Department[]>(`/Region/${region_id}/departments`);
const regionNames: Record<number, string> = {
1: "Caribe",
2: "Pacífico",
3: "Orinoquía",
4: "Amazonía",
5: "Andina",
6: "Insular",
};
const resultado = {
region: regionNames[region_id] || `Región ${region_id}`,
total_departamentos: departments.length,
departamentos: departments.map(d => ({
id: d.id,
nombre: d.name,
capital: d.cityCapital?.name || "N/A",
poblacion: d.population,
})),
};
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener departamentos por región: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 5. Obtener ciudades de un departamento
server.registerTool(
"get_cities",
{
title: "Obtener Ciudades de un Departamento",
description: `Obtiene todas las ciudades/municipios de un departamento específico.
Args:
- department_id (number): ID del departamento
Returns:
Lista de ciudades con nombre, población y código postal.
Ejemplo de uso:
- "¿Qué ciudades hay en Antioquia?"
- "Lista los municipios del Valle del Cauca"`,
inputSchema: {
department_id: z.number().int().min(1).describe("ID del departamento"),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async ({ department_id }) => {
try {
const cities = await apiRequest<City[]>(`/Department/${department_id}/cities`);
const resultado = {
departamento_id: department_id,
total_ciudades: cities.length,
ciudades: cities.map(c => ({
id: c.id,
nombre: c.name,
poblacion: c.population,
superficie_km2: c.surface,
codigo_postal: c.postalCode,
})),
};
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener ciudades: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 6. Obtener atracciones turísticas
server.registerTool(
"get_tourist_attractions",
{
title: "Obtener Atracciones Turísticas",
description: `Obtiene atracciones turísticas de Colombia. Puedes buscar por ciudad o por palabra clave.
Args:
- city_id (number, opcional): ID de la ciudad para filtrar
- search (string, opcional): Palabra clave para buscar (ej: "playa", "museo", "parque")
Returns:
Lista de atracciones con nombre, descripción, ubicación e imágenes.
Ejemplo de uso:
- "¿Qué lugares turísticos hay en Cartagena?"
- "Busca atracciones relacionadas con playas"`,
inputSchema: {
city_id: z.number().int().optional().describe("ID de la ciudad"),
search: z.string().optional().describe("Palabra clave para buscar"),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async ({ city_id, search }) => {
try {
let attractions: TouristAttraction[];
if (city_id) {
attractions = await apiRequest<TouristAttraction[]>(`/City/${city_id}/touristattractions`);
} else if (search) {
attractions = await apiRequest<TouristAttraction[]>(`/TouristAttraction/search/${encodeURIComponent(search)}`);
} else {
attractions = await apiRequest<TouristAttraction[]>("/TouristAttraction");
}
const resultado = {
total: attractions.length,
atracciones: attractions.slice(0, 20).map(a => ({
id: a.id,
nombre: a.name,
descripcion: a.description,
coordenadas: {
latitud: a.latitude,
longitud: a.longitude,
},
imagenes: a.images || [],
ciudad_id: a.cityId,
})),
};
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener atracciones: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 7. Obtener información del país
server.registerTool(
"get_country_info",
{
title: "Obtener Información de Colombia",
description: `Obtiene información general sobre Colombia como país.
Returns:
Información del país incluyendo nombre, capital, bandera, moneda, etc.
Ejemplo de uso:
- "¿Cuál es la capital de Colombia?"
- "Dame información general de Colombia"`,
inputSchema: {},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async () => {
try {
const country = await apiRequest<any>("/Country/Colombia");
const resultado = {
nombre: country.name,
capital: country.stateCapital,
superficie_km2: country.surface,
poblacion: country.population,
idioma: country.languages,
moneda: country.currency,
codigo_iso: country.isoCode,
codigo_telefono: country.phonePrefix,
dominio_internet: country.internetDomain,
bandera: country.flags,
escudo: country.coatOfArms,
himno: country.nationalAnthem,
ave_nacional: country.nationalBird,
flor_nacional: country.nationalFlower,
arbol_nacional: country.nationalTree,
};
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener información del país: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 8. Obtener presidentes
server.registerTool(
"get_presidents",
{
title: "Obtener Presidentes de Colombia",
description: `Obtiene la lista de presidentes de Colombia.
Returns:
Lista de presidentes con nombre, período, partido político y ciudad de nacimiento.
Ejemplo de uso:
- "¿Quiénes han sido los presidentes de Colombia?"
- "Lista de presidentes colombianos"`,
inputSchema: {},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async () => {
try {
const presidents = await apiRequest<any[]>("/President");
const resultado = {
total: presidents.length,
presidentes: presidents.map(p => ({
id: p.id,
nombre: p.name,
apellido: p.lastName,
inicio_periodo: p.startPeriodDate,
fin_periodo: p.endPeriodDate,
partido_politico: p.politicalParty,
descripcion: p.description,
imagen: p.image,
ciudad_nacimiento_id: p.cityId,
})),
};
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener presidentes: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 9. Obtener presidente por ID
server.registerTool(
"get_president",
{
title: "Obtener Presidente Específico",
description: `Obtiene información detallada de un presidente específico.
Args:
- id (number): ID del presidente
Returns:
Información detallada del presidente.
Ejemplo de uso:
- "Dame información sobre el presidente con ID 5"`,
inputSchema: {
id: z.number().int().min(1).describe("ID del presidente"),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async ({ id }) => {
try {
const president = await apiRequest<any>(`/President/${id}`);
const resultado = {
id: president.id,
nombre: president.name,
apellido: president.lastName,
nombre_completo: `${president.name} ${president.lastName}`,
inicio_periodo: president.startPeriodDate,
fin_periodo: president.endPeriodDate,
partido_politico: president.politicalParty,
descripcion: president.description,
imagen: president.image,
};
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener presidente: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 10. Obtener aeropuertos
server.registerTool(
"get_airports",
{
title: "Obtener Aeropuertos de Colombia",
description: `Obtiene la lista de aeropuertos de Colombia. Puede filtrar por departamento.
Args:
- department_id (number, opcional): ID del departamento para filtrar
Returns:
Lista de aeropuertos con nombre, ciudad, tipo y códigos IATA/OACI.
Ejemplo de uso:
- "¿Cuáles son los aeropuertos de Colombia?"
- "Aeropuertos en Antioquia"`,
inputSchema: {
department_id: z.number().int().optional().describe("ID del departamento para filtrar"),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async ({ department_id }) => {
try {
let airports: any[];
if (department_id) {
airports = await apiRequest<any[]>(`/Department/${department_id}/airports`);
} else {
airports = await apiRequest<any[]>("/Airport");
}
const resultado = {
total: airports.length,
aeropuertos: airports.map(a => ({
id: a.id,
nombre: a.name,
ciudad: a.city?.name || "N/A",
departamento: a.department?.name || "N/A",
tipo: a.type,
codigo_iata: a.iataCode,
codigo_oaci: a.oaciCode,
latitud: a.latitude,
longitud: a.longitude,
})),
};
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener aeropuertos: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 11. Obtener emisoras de radio
server.registerTool(
"get_radios",
{
title: "Obtener Emisoras de Radio",
description: `Obtiene la lista de emisoras de radio de Colombia. Puede filtrar por ciudad.
Args:
- city_id (number, opcional): ID de la ciudad para filtrar
Returns:
Lista de emisoras con nombre, frecuencia, ciudad y URL de streaming.
Ejemplo de uso:
- "¿Qué emisoras de radio hay en Colombia?"
- "Emisoras de radio en Bogotá"`,
inputSchema: {
city_id: z.number().int().optional().describe("ID de la ciudad para filtrar"),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async ({ city_id }) => {
try {
let radios: any[];
if (city_id) {
radios = await apiRequest<any[]>(`/City/${city_id}/radios`);
} else {
radios = await apiRequest<any[]>("/Radio");
}
const resultado = {
total: radios.length,
emisoras: radios.slice(0, 50).map(r => ({
id: r.id,
nombre: r.name,
frecuencia: r.frequency,
ciudad: r.city?.name || "N/A",
url_streaming: r.streamingUrl,
})),
};
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener emisoras: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 12. Obtener resguardos indígenas
server.registerTool(
"get_indigenous_reservations",
{
title: "Obtener Resguardos Indígenas",
description: `Obtiene la lista de resguardos indígenas de Colombia. Puede filtrar por departamento.
Args:
- department_id (number, opcional): ID del departamento para filtrar
Returns:
Lista de resguardos indígenas con nombre, comunidad y departamento.
Ejemplo de uso:
- "¿Cuáles son los resguardos indígenas de Colombia?"
- "Resguardos indígenas en el Cauca"`,
inputSchema: {
department_id: z.number().int().optional().describe("ID del departamento para filtrar"),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async ({ department_id }) => {
try {
let reservations: any[];
if (department_id) {
reservations = await apiRequest<any[]>(`/Department/${department_id}/indigenousreservations`);
} else {
reservations = await apiRequest<any[]>("/IndigenousReservation");
}
const resultado = {
total: reservations.length,
resguardos: reservations.slice(0, 50).map(r => ({
id: r.id,
nombre: r.name,
codigo: r.code,
comunidad_nativa: r.nativeCommunity,
departamento_id: r.departmentId,
ciudad_id: r.cityId,
})),
};
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener resguardos indígenas: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 13. Obtener áreas naturales
server.registerTool(
"get_natural_areas",
{
title: "Obtener Áreas Naturales Protegidas",
description: `Obtiene la lista de áreas naturales protegidas de Colombia. Puede filtrar por departamento.
Args:
- department_id (number, opcional): ID del departamento para filtrar
Returns:
Lista de áreas naturales con nombre, categoría y departamento.
Ejemplo de uso:
- "¿Cuáles son los parques naturales de Colombia?"
- "Áreas naturales en el Amazonas"`,
inputSchema: {
department_id: z.number().int().optional().describe("ID del departamento para filtrar"),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async ({ department_id }) => {
try {
let areas: any[];
if (department_id) {
areas = await apiRequest<any[]>(`/Department/${department_id}/naturalareas`);
} else {
areas = await apiRequest<any[]>("/NaturalArea");
}
const resultado = {
total: areas.length,
areas_naturales: areas.map(a => ({
id: a.id,
nombre: a.name,
categoria: a.categoryNaturalArea?.name || "N/A",
departamento_id: a.departmentId,
area_hectareas: a.areaHa,
})),
};
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener áreas naturales: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 14. Obtener mapas de departamentos
server.registerTool(
"get_maps",
{
title: "Obtener Mapas",
description: `Obtiene mapas de Colombia o de un departamento específico.
Args:
- department_id (number, opcional): ID del departamento para obtener su mapa
Returns:
URLs de mapas del departamento o país.
Ejemplo de uso:
- "Dame el mapa de Antioquia"
- "Quiero ver mapas de Colombia"`,
inputSchema: {
department_id: z.number().int().optional().describe("ID del departamento"),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async ({ department_id }) => {
try {
let maps: any[];
if (department_id) {
maps = await apiRequest<any[]>(`/Department/${department_id}/maps`);
} else {
maps = await apiRequest<any[]>("/Map");
}
const resultado = {
total: maps.length,
mapas: maps.map(m => ({
id: m.id,
nombre: m.name,
descripcion: m.description,
url: m.url,
departamento_id: m.departmentId,
})),
};
return {
content: [
{
type: "text",
text: JSON.stringify(resultado, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error al obtener mapas: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// 15. Buscar información general
server.registerTool(
"search_colombia",
{
title: "Buscar en Colombia",
description: `Herramienta de búsqueda general que intenta encontrar información sobre departamentos, ciudades o lugares turísticos.
Args:
- query (string): Término de búsqueda (nombre de departamento, ciudad o lugar)
Returns:
Resultados de búsqueda con la información encontrada.
Ejemplo de uso:
- "Busca Medellín"
- "Información sobre el Parque Tayrona"`,
inputSchema: {
query: z.string().min(2).describe("Término de búsqueda"),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: true,
},
},
async ({ query }) => {
try {
const resultados: {
tipo: string;
datos: unknown;
}[] = [];
// Buscar en departamentos
try {
const dept = await apiRequest<Department>(`/Department/name/${encodeURIComponent(query)}`);
if (dept) {
resultados.push({
tipo: "departamento",
datos: {
id: dept.id,
nombre: dept.name,
descripcion: dept.description,
capital: dept.cityCapital?.name,
poblacion: dept.population,
},
});
}
} catch {
// No encontrado como departamento
}
// Buscar en atracciones turísticas
try {
const attractions = await apiRequest<TouristAttraction[]>(`/TouristAttraction/search/${encodeURIComponent(query)}`);
if (attractions && attractions.length > 0) {
resultados.push({
tipo: "atracciones_turisticas",
datos: attractions.slice(0, 5).map(a => ({
id: a.id,
nombre: a.name,
descripcion: a.description?.substring(0, 200) + "...",
})),
});
}
} catch {
// No encontrado
}
if (resultados.length === 0) {
return {
content: [
{
type: "text",
text: `No se encontraron resultados para "${query}". Intenta con otro término.`,
},
],
};
}
return {
content: [
{
type: "text",
text: JSON.stringify({ query, resultados }, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `Error en la búsqueda: ${error instanceof Error ? error.message : "Error desconocido"}`,
},
],
};
}
}
);
// ============================================
// INICIAR SERVIDOR
// ============================================
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Colombia MCP Server iniciado correctamente");
}
main().catch((error) => {
console.error("Error al iniciar el servidor:", error);
process.exit(1);
});