@siva-sub/mcp-public-transport
Version:
A Model Context Protocol server for Singapore transport data with real-time information and routing
121 lines (120 loc) • 5.05 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BusStopsTool = void 0;
const zod_1 = require("zod");
const base_js_1 = require("../base.js");
const validation_js_1 = require("../../utils/validation.js");
const logger_js_1 = require("../../utils/logger.js");
const haversine_distance_1 = __importDefault(require("haversine-distance"));
const BusStopsInputSchema = zod_1.z.intersection(validation_js_1.LocationSchema, zod_1.z.object({
radius: zod_1.z.number().min(100).max(5000).default(500),
limit: zod_1.z.number().min(1).max(50).default(10),
}));
class BusStopsTool extends base_js_1.BaseTool {
constructor(ltaService, oneMapService) {
super();
this.ltaService = ltaService;
this.oneMapService = oneMapService;
}
getDefinitions() {
return [
{
name: 'find_bus_stops',
description: 'Find bus stops by location name, coordinates, or road name',
inputSchema: this.createSchema({
location: {
type: 'string',
description: 'Location name (e.g., "Marina Bay", "Orchard Road")',
},
lat: {
type: 'number',
description: 'Latitude coordinate',
},
lng: {
type: 'number',
description: 'Longitude coordinate',
},
radius: {
type: 'number',
minimum: 100,
maximum: 5000,
default: 500,
description: 'Search radius in meters',
},
limit: {
type: 'number',
minimum: 1,
maximum: 50,
default: 10,
description: 'Maximum number of results',
},
}),
},
];
}
canHandle(toolName) {
return toolName === 'find_bus_stops';
}
async execute(toolName, args) {
try {
const { location, lat, lng, radius, limit } = (0, validation_js_1.validateInput)(BusStopsInputSchema, args);
let searchLat = lat;
let searchLng = lng;
// Geocode location if coordinates not provided
if (!searchLat || !searchLng) {
if (!location) {
throw new Error('Either location name or coordinates must be provided');
}
logger_js_1.logger.info(`Geocoding location: ${location}`);
const geocoded = await this.oneMapService.geocode(location);
if (!geocoded) {
return {
error: `Could not find coordinates for location: ${location}`,
timestamp: new Date().toISOString(),
};
}
searchLat = geocoded.latitude;
searchLng = geocoded.longitude;
}
logger_js_1.logger.info(`Finding bus stops near ${searchLat}, ${searchLng}`, { radius, limit });
// Get all bus stops (cached)
const allStops = await this.ltaService.getAllBusStops();
// Calculate distances and filter
const nearbyStops = allStops
.map(stop => ({
...stop,
distance: (0, haversine_distance_1.default)({ latitude: searchLat, longitude: searchLng }, { latitude: stop.latitude, longitude: stop.longitude }),
}))
.filter(stop => stop.distance <= (radius || 500))
.sort((a, b) => a.distance - b.distance)
.slice(0, limit);
return {
searchLocation: {
latitude: searchLat,
longitude: searchLng,
name: location,
},
radius,
totalFound: nearbyStops.length,
stops: nearbyStops.map(stop => ({
busStopCode: stop.busStopCode,
description: stop.description,
roadName: stop.roadName,
latitude: stop.latitude,
longitude: stop.longitude,
distanceMeters: Math.round(stop.distance),
walkingTimeMinutes: Math.ceil(stop.distance / 80), // Assume 80m/min walking speed
})),
timestamp: new Date().toISOString(),
};
}
catch (error) {
logger_js_1.logger.error(`Bus stops tool failed: ${toolName}`, error);
return this.formatError(error, toolName);
}
}
}
exports.BusStopsTool = BusStopsTool;