Malaysia Transit MCP
平台与服务by hithereiamaliff
提供马来西亚12座城市的实时公交与火车信息,支持到站、车辆追踪和线路查询,覆盖 Rapid KL、Rapid Penang 与 BAS.MY 等服务。
什么是 Malaysia Transit MCP?
提供马来西亚12座城市的实时公交与火车信息,支持到站、车辆追踪和线路查询,覆盖 Rapid KL、Rapid Penang 与 BAS.MY 等服务。
核心功能 (43 个工具)
list_service_areasList all available transit service areas in Malaysia (e.g., Klang Valley, Penang, Kuantan)
detect_location_areaAutomatically detect which transit service area a location belongs to using geocoding. Use this when the user mentions a place name without specifying the area (e.g., "KTM Alor Setar", "Komtar", "KLCC"). IMPORTANT: After detecting the area, use find_nearby_stops_with_arrivals or find_nearby_stops with the location parameter - these tools handle geocoding automatically and are more reliable than using coordinates from this tool.
get_area_infoGet detailed information about a specific transit service area
search_stopsSearch for bus or train stops by name in a specific area. The middleware will automatically geocode place names (like "Ideal Foresta") and find nearby stops if no exact stop name match is found. IMPORTANT: If you are unsure which area a location belongs to, use detect_location_area first to automatically determine the correct area.
get_stop_detailsGet detailed information about a specific bus or train stop
get_stop_arrivalsGet real-time arrival predictions for buses/trains at a specific stop
find_nearby_stopsFind bus or train stops near a specific location AND get all routes serving those stops. You can provide EITHER coordinates (lat/lon) OR a location name - the middleware will geocode place names automatically.
find_nearby_stops_with_arrivalsFind bus stops near a location AND get real-time arrival predictions in one call. RECOMMENDED: Use this tool when users ask about nearby bus stops and arrival times together. Accepts a place name (e.g., "Ideal Foresta") - the middleware handles geocoding automatically.
find_nearby_stops_with_routesFind bus stops near a location AND get all routes serving those stops in one call. Accepts a place name (e.g., "Ideal Foresta") - the middleware handles geocoding automatically.
list_routesList all available bus or train routes in a specific area
get_route_stopsGet all stops on a specific route. Use this to find which stops a bus/train route serves. IMPORTANT: First call list_routes to get the correct route_id.
get_route_detailsGet detailed information about a specific route including stops and geometry. IMPORTANT: First call list_routes to get the correct route_id.
get_route_geometryGet the geographic path and stops for a specific route (for map visualization)
get_live_vehiclesGet real-time positions of all buses and trains in a specific area
get_provider_statusCheck the operational status of transit providers in a specific area
get_route_departuresGet the next N departures for a specific route (both directions). Useful for showing upcoming bus/train times. IMPORTANT: Use route_short_name (e.g., "K10", "A32", "R10", "101") NOT the numeric route_id. Works for: penang, ipoh, seremban, kangar, alor-setar, kota-bharu, kuala-terengganu, melaka, johor, kuching.
get_next_departureGet the single next departure for a route in a specific direction. Quick way to find when the next bus/train leaves. IMPORTANT: Use route_short_name (e.g., "101", "K10", "A32") NOT numeric route_id. Works for: penang, ipoh, seremban, kangar, alor-setar, kota-bharu, kuala-terengganu, melaka, johor, kuching.
get_stop_routesGet all routes serving a specific stop with their next departures. Shows which buses/trains stop here and when. Works for: penang, ipoh, seremban, kangar, alor-setar, kota-bharu, kuala-terengganu, melaka, johor, kuching.
get_route_scheduleGet the complete daily schedule for a route. Shows all departure times throughout the day. IMPORTANT: Use route_short_name (e.g., "101", "K10", "A32") NOT numeric route_id. Works for: penang, ipoh, seremban, kangar, alor-setar, kota-bharu, kuala-terengganu, melaka, johor, kuching.
get_route_originGet the origin stop name for a route in a specific direction. Useful for showing where the bus/train starts. IMPORTANT: Use route_short_name (e.g., "101", "K10", "A32") NOT numeric route_id. Works for: penang, ipoh, seremban, kangar, alor-setar, kota-bharu, kuala-terengganu, melaka, johor, kuching.
get_route_statusCheck if a route is currently operating based on its schedule. Shows if buses/trains are running now. IMPORTANT: Use route_short_name (e.g., "101", "K10", "A32") NOT numeric route_id. Works for: penang, ipoh, seremban, kangar, alor-setar, kota-bharu, kuala-terengganu, melaka, johor, kuching.
get_fare_routesGet all routes available for fare calculation in a specific area. MUST call this FIRST before calculate_fare to get valid route_id values. Supports: ipoh, seremban, kangar, alor-setar, kota-bharu, kuala-terengganu, melaka, johor, kuching, penang.
get_route_stops_for_fareGet all stops on a route with their distances for fare calculation. MUST call this SECOND (after get_fare_routes) to get valid stop_id values for calculate_fare. Returns stop IDs and names.
calculate_fareCalculate the bus fare between two stops on a route. IMPORTANT: You MUST first call get_fare_routes to get route_id, then get_route_stops_for_fare to get valid stop_id values. Do NOT guess IDs.
calculate_journey_fareCalculate the total fare for a multi-leg journey with bus transfers. Each leg is a separate fare since BAS.MY does not have integrated transfers.
get_route_directions_for_fareGet available directions for a route when calculating fares. Use this to determine which direction (outbound/inbound) to use for fare calculation.
get_ktm_komuter_stationsGet all 23 KTM Komuter Utara stations (Padang Besar - Butterworth - Ipoh line). Returns station codes, names, and coordinates.
calculate_ktm_komuter_fareCalculate KTM Komuter Utara fare between two stations. Use station codes (e.g., "BU" for Butterworth, "IP" for Ipoh, "PB" for Padang Besar).
get_ktm_komuter_fare_matrixGet the full KTM Komuter Utara fare matrix showing fares between all station pairs.
get_ktm_station_departuresGet departure times for a specific KTM station. Supports both KTM Komuter Utara and KTM Intercity schedules.
get_ktm_stationsGet all KTM stations for a specific schedule type (Komuter Utara or Intercity).
get_ktm_schedulesGet full KTM schedule data for a specific schedule type. Returns complete timetable information.
find_nearby_ktm_stationsFind KTM stations near a specific location. You can provide EITHER coordinates (lat/lon) OR a location name - the middleware will geocode place names automatically.
get_penang_ferry_overviewGet Penang Ferry service overview including terminals, operating hours, frequency, and contact information. The ferry operates between Butterworth and George Town.
get_penang_ferry_scheduleGet full Penang Ferry schedule with departure times. Supports filtering by direction and day type.
get_penang_ferry_next_departureGet next ferry departures from both Butterworth and George Town terminals in real-time. Shows minutes until departure.
get_penang_ferry_terminalsGet detailed information about Penang Ferry terminals including facilities, connections, parking, and nearby attractions.
get_penang_ferry_fareGet Penang Ferry fare information, payment methods, and terminal coordinates.
get_system_healthCheck the health status of the Malaysia Transit middleware service
get_debug_infoGet comprehensive debug information about the middleware service including memory usage and initialized areas
get_api_analyticsGet API usage analytics and statistics from the middleware. Shows total requests, requests per hour, error rates, and usage by service area. Useful for monitoring API health and usage patterns.
get_area_analyticsGet detailed API usage analytics for a specific service area. Shows which endpoints are most used for that area.
helloA simple test tool to verify that the MCP server is working correctly
README
Malaysia Transit MCP
MCP (Model Context Protocol) server for Malaysia's public transit system, providing real-time bus and train information across 10+ cities in Malaysia.
MCP Endpoint: https://mcp.techmavie.digital/malaysiatransit/mcp
Analytics Dashboard: https://mcp.techmavie.digital/malaysiatransit/analytics/dashboard
Data Source: Malaysia Transit Middleware
Table of Contents
- Features
- Architecture
- Quick Start
- Installation
- Configuration
- Available Tools
- Usage Examples
- AI Integration Guide
- Supported Service Areas
- Deployment
- Troubleshooting
- Contributing
Features
- 11 Operational Service Areas + 1 Under Maintenance + 1 Coming Soon across Malaysia
- Klang Valley (Rapid Rail KL, Rapid Bus KL, MRT Feeder)
- Penang (Rapid Penang, Penang Ferry, KTM Komuter Utara)
- Ipoh (BAS.MY Ipoh, KTM Komuter Utara)
- Seremban (BAS.MY Seremban, KTM Intercity)
- Kangar (BAS.MY Kangar, KTM Komuter Utara)
- Alor Setar (BAS.MY Alor Setar, KTM Komuter Utara)
- Kota Bharu (BAS.MY Kota Bharu, KTM Intercity)
- Kuala Terengganu, Melaka, Johor, Kuching (BAS.MY only)
- Kuantan (Under Maintenance)
- Kota Kinabalu (Coming Soon)
- Real-time Vehicle Tracking - Live positions of buses and trains
- Stop Search & Information - Find stops by name or location
- Route Discovery - Browse available routes with destinations
- Arrival Predictions - Get real-time arrival times at stops (shape-based, 40-60% more accurate)
- Schedule Information - Get departure times, route schedules, and operating status
- Fare Calculator - Calculate bus fares for BAS.MY and Rapid Penang routes
- Multi-Modal Support - Bus, rail, and ferry services
- Provider Status Monitoring - Check operational status of transit providers
- Location Detection - Automatically detect service areas using geocoding
- 🆕 KTM Komuter Utara - 23 stations from Padang Besar to Ipoh with fare calculation
- 🆕 KTM Intercity - SH and ERT routes (Tumpat - Gemas - JB Sentral)
- 🆕 Penang Ferry - Butterworth to George Town ferry information
- 🆕 API Analytics - View API usage statistics, endpoint metrics, and client tracking
- 🆕 Analytics Dashboard - Visual dashboard with charts for MCP server usage monitoring
- 🆕 Client Identification - MCP identifies itself to middleware for analytics tracking
- 🆕 Auto-Deployment - GitHub Actions workflow for automatic VPS deployment
Architecture
This MCP server acts as a bridge between AI assistants and the Malaysia Transit Middleware API:
AI Assistant (Claude, GPT, etc.)
↓
Malaysia Transit MCP Server (identifies as "Malaysia-Transit-MCP")
↓
Malaysia Transit Middleware API (tracks usage via X-App-Name header)
↓
Malaysia Open Data Portal (GTFS Static & Realtime)
Client Identification: This MCP automatically sends an X-App-Name: Malaysia-Transit-MCP header with every API request, allowing the middleware to track usage from this MCP separately in its analytics dashboard.
Quick Start
Connect to the MCP Server
Add this configuration to your MCP client (Claude Desktop, Cursor, Windsurf, etc.):
{
"mcpServers": {
"malaysia-transit": {
"transport": "streamable-http",
"url": "https://mcp.techmavie.digital/malaysiatransit/mcp"
}
}
}
Test with MCP Inspector
npx @modelcontextprotocol/inspector
# Select "Streamable HTTP"
# Enter URL: https://mcp.techmavie.digital/malaysiatransit/mcp
Test with curl
# List all available tools
curl -X POST https://mcp.techmavie.digital/malaysiatransit/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# Call the hello tool
curl -X POST https://mcp.techmavie.digital/malaysiatransit/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"hello","arguments":{}}}'
Installation
npm install
Configuration
Environment Variables
The MCP server uses environment variables for configuration:
-
MIDDLEWARE_URL(required): Malaysia Transit Middleware API URL- Default:
https://malaysiatransit.techmavie.digital
- Default:
-
INTERNAL_AUTH_TOKEN(required for production): Shared secret for middleware API authentication- Must match
INTERNAL_APP_AUTH_TOKENin the middleware's environment - Generate with:
openssl rand -hex 32
- Must match
-
GOOGLE_MAPS_API_KEY(optional): Google Maps API key for location detection- If not provided, falls back to Nominatim (free but less accurate)
- Get your API key from Google Cloud Console
-
ANALYTICS_RESET_KEY(optional): Admin key forPOST /analytics/reset- If not set, the reset endpoint is disabled (returns 403)
-
ANALYTICS_IMPORT_KEY(optional): Admin key forPOST /analytics/import- If not set, the import endpoint is disabled (returns 403)
Development
To run the MCP server in development mode:
npm run dev
Build
To build the MCP server for deployment:
npm run build
Available Tools
Service Area Discovery
list_service_areas
List all available transit areas in Malaysia.
Parameters: None
Returns: List of service areas with their IDs, names, and capabilities.
Example:
const areas = await tools.list_service_areas();
get_area_info
Get detailed information about a specific area.
Parameters:
areaId(string): Service area ID (e.g., "penang", "klang-valley")
Example:
const info = await tools.get_area_info({ areaId: "penang" });
Location Detection
detect_location_area ⭐
Automatically detect which transit service area a location belongs to using geocoding.
Parameters:
location(string): Location name or place (e.g., "KTM Alor Setar", "Komtar", "KLCC")
Returns: Detected area ID, confidence level, and location details.
Example:
const result = await tools.detect_location_area({ location: "KTM Alor Setar" });
// Returns: { area: "alor-setar", confidence: "high" }
Stop Information
search_stops
Search for stops by name. Use detect_location_area first if unsure about the area.
Parameters:
area(string): Service area IDquery(string): Search query (e.g., "Komtar", "KLCC")
Example:
const stops = await tools.search_stops({
area: "penang",
query: "Komtar"
});
get_stop_details
Get detailed information about a stop.
Parameters:
area(string): Service area IDstopId(string): Stop ID from search results
get_stop_arrivals ⭐
Get real-time arrival predictions at a stop.
Parameters:
area(string): Service area IDstopId(string): Stop ID from search results
Returns: Includes a comprehensive disclaimer about prediction methodology, followed by arrival data with:
- Calculation method (shape-based or straight-line)
- Confidence level (high, medium, or low)
- ETA in minutes
- Vehicle information
Prediction Methodology:
- Shape-Based Distance (Preferred): Uses actual route geometry, accurate within ±2-4 minutes
- Straight-Line Distance (Fallback): Conservative estimates with 1.4x multiplier
- Includes GPS speed validation, time-of-day adjustments, and stop dwell time
- Conservative bias: Better to arrive early than miss the bus
Example:
const arrivals = await tools.get_stop_arrivals({
area: "penang",
stopId: "stop_123"
});
// Returns disclaimer + arrival data with confidence levels
find_nearby_stops
Find nearby stops and the routes that serve them.
Parameters:
area(string): Service area IDlocation(string, optional): Place name to geocode (e.g., "KLCC", "Komtar")lat(number, optional): Latitude coordinate (required iflocationnot provided)lon(number, optional): Longitude coordinate (required iflocationnot provided)radius(number, optional): Search radius in meters (default: 500)
find_nearby_stops_with_arrivals ⭐
Find nearby stops and get real-time arrivals in one call.
Parameters:
area(string): Service area IDlocation(string, optional): Place name to geocodelat(number, optional): Latitude coordinate (required iflocationnot provided)lon(number, optional): Longitude coordinate (required iflocationnot provided)radius(number, optional): Search radius in meters (default: 500)routeFilter(string, optional): Filter arrivals by route number (e.g., "302", "101")
find_nearby_stops_with_routes
Find nearby stops and get all routes serving those stops in one call.
Parameters:
area(string): Service area IDlocation(string, optional): Place name to geocodelat(number, optional): Latitude coordinate (required iflocationnot provided)lon(number, optional): Longitude coordinate (required iflocationnot provided)radius(number, optional): Search radius in meters (default: 500)
Route Information
list_routes
List all routes in an area.
Parameters:
area(string): Service area ID
get_route_stops
Get all stops served by a specific route.
Parameters:
area(string): Service area IDrouteId(string): Route ID fromlist_routes
get_route_details
Get detailed route information.
Parameters:
area(string): Service area IDrouteId(string): Route ID from list_routes
get_route_geometry
Get route path for map visualization.
Parameters:
area(string): Service area IDrouteId(string): Route ID from list_routes
Real-time Data
get_live_vehicles ⭐
Get real-time vehicle positions.
Parameters:
area(string): Service area IDtype(enum, optional): Filter by type ('bus' or 'rail')
Example:
const vehicles = await tools.get_live_vehicles({ area: "penang" });
get_provider_status
Check provider operational status.
Parameters:
area(string): Service area ID
Schedule Information (NEW)
get_route_departures
Get the next N departures for a specific route (both directions).
Parameters:
area(string): Service area ID (e.g., "ipoh", "seremban", "penang")routeId(string): Route ID from list_routescount(number, optional): Number of departures to return (default: 5)
Example:
const departures = await tools.get_route_departures({
area: "ipoh",
routeId: "A32",
count: 5
});
get_next_departure
Get the single next departure for a route in a specific direction.
Parameters:
area(string): Service area IDrouteId(string): Route ID from list_routesdirection(enum, optional): 'outbound', 'inbound', or 'loop'
get_stop_routes
Get all routes serving a specific stop with their next departures.
Parameters:
area(string): Service area IDstopId(string): Stop ID from search_stopscount(number, optional): Number of departures per route (default: 3)
get_route_schedule
Get the complete daily schedule for a route.
Parameters:
area(string): Service area IDrouteId(string): Route ID from list_routes
get_route_origin
Get the origin stop name for a route in a specific direction.
Parameters:
area(string): Service area IDrouteId(string): Route ID from list_routesdirection(enum, optional): 'outbound' or 'inbound'
get_route_status
Check if a route is currently operating based on its schedule.
Parameters:
area(string): Service area IDrouteId(string): Route ID from list_routes
Fare Calculator (NEW)
get_fare_routes
Get all routes available for fare calculation in a specific area.
Parameters:
area(string): Service area ID (e.g., "ipoh", "seremban", "penang", "kangar")
Supported Areas: BAS.MY areas (Ipoh, Seremban, Kangar, Alor Setar, Kota Bharu, Kuala Terengganu, Melaka, Johor, Kuching) and Rapid Penang.
get_route_stops_for_fare
Get all stops on a route with their distances for fare calculation.
Parameters:
area(string): Service area IDrouteId(string): Route ID from get_fare_routes
calculate_fare ⭐
Calculate the bus fare between two stops on a route.
Parameters:
area(string): Service area IDrouteId(string): Route ID from get_fare_routesfromStop(string): Origin stop IDtoStop(string): Destination stop ID
Returns: Adult, concession, and child fares in MYR with disclaimer.
Example:
const fare = await tools.calculate_fare({
area: "ipoh",
routeId: "A32",
fromStop: "stop_001",
toStop: "stop_015"
});
// Returns: { adult: "1.50", concession: "0.75", child: "FREE" }
calculate_journey_fare
Calculate the total fare for a multi-leg journey with bus transfers.
Parameters:
area(string): Base service area IDlegs(array): Array of journey legs (max 5), each with:routeId(string): Route ID for this legfromStop(string): Origin stop IDtoStop(string): Destination stop IDareaId(string, optional): Area ID for this leg (for inter-area journeys)
Note: Each bus change requires a separate fare payment (BAS.MY does not have integrated transfers).
get_route_directions_for_fare
Get available directions for a route when calculating fares.
Parameters:
area(string): Service area IDrouteId(string): Route ID from get_fare_routes
KTM Komuter Utara Tools (NEW)
get_ktm_komuter_stations
Get all 23 KTM Komuter Utara stations (Padang Besar - Butterworth - Ipoh line).
Parameters: None
Returns: Station codes, names, and coordinates.
Example:
const stations = await tools.get_ktm_komuter_stations();
// Returns: [{ code: "PB", name: "Padang Besar", ... }, ...]
calculate_ktm_komuter_fare ⭐
Calculate KTM Komuter Utara fare between two stations.
Parameters:
from(string): Origin station code (e.g., "BU" for Butterworth)to(string): Destination station code (e.g., "IP" for Ipoh)
Example:
const fare = await tools.calculate_ktm_komuter_fare({
from: "BU",
to: "IP"
});
// Returns: { adult: "12.00", child: "6.00", currency: "MYR" }
get_ktm_komuter_fare_matrix
Get the full KTM Komuter Utara fare matrix showing fares between all station pairs.
Parameters: None
get_ktm_station_departures ⭐
Get departure times for a specific KTM station.
Parameters:
stationName(string): Station name (e.g., "Butterworth", "Ipoh")type(enum): Schedule type -ktm-komuter-utaraorktm-intercity
Example:
const departures = await tools.get_ktm_station_departures({
stationName: "Butterworth",
type: "ktm-komuter-utara"
});
get_ktm_stations
Get all KTM stations for a specific schedule type.
Parameters:
type(enum):ktm-komuter-utaraorktm-intercity
get_ktm_schedules
Get full KTM schedule data for a specific schedule type.
Parameters:
type(enum):ktm-komuter-utaraorktm-intercity
find_nearby_ktm_stations
Find KTM stations near a specific location.
Parameters:
lat(number): Latitude coordinatelon(number): Longitude coordinateradius(number, optional): Search radius in kilometers (default: 10)type(enum):ktm-komuter-utaraorktm-intercity
Penang Ferry Tools (NEW)
get_penang_ferry_overview ⭐
Get Penang Ferry service overview including route, operator, and general info.
Parameters: None
get_penang_ferry_schedule
Get the full Penang Ferry schedule with operating hours and frequency.
Parameters: None
get_penang_ferry_next_departure
Get the next upcoming Penang Ferry departures from both terminals.
Parameters: None
get_penang_ferry_terminals
Get Penang Ferry terminal information including locations and facilities.
Parameters: None
get_penang_ferry_fare
Get Penang Ferry fare information and payment methods.
Parameters: None
Example:
const fare = await tools.get_penang_ferry_fare();
// Returns: { adult: "1.40", child: "0.70", ... }
System Tools
get_system_health
Check the health status of the Malaysia Transit middleware service.
Parameters: None
get_debug_info
Get comprehensive debug information about the middleware service.
Parameters: None
Analytics Tools (NEW)
get_api_analytics
Get API usage analytics and statistics from the middleware.
Parameters:
type(enum, optional): Type of analytics to retrieve:summary(default): Overview with requests/hour, error rate, uptimeendpoints: Per-endpoint statisticsareas: Per-service-area statisticscumulative: All-time totalsclients: App/website usage breakdown
Example:
// Get summary analytics
const summary = await tools.get_api_analytics({ type: "summary" });
// Get client usage (see which apps use the API)
const clients = await tools.get_api_analytics({ type: "clients" });
get_area_analytics
Get detailed API usage analytics for a specific service area.
Parameters:
area(string): Service area ID (e.g., "penang", "klang-valley")
Example:
const penangStats = await tools.get_area_analytics({ area: "penang" });
Testing
hello
Simple test tool to verify server is working.
Usage Examples
Find When Your Bus is Coming
// 1. Detect area from location
const areaResult = await tools.detect_location_area({
location: "KTM Alor Setar"
});
// 2. Search for your stop
const stops = await tools.search_stops({
area: areaResult.area,
query: "KTM Alor Setar"
});
// 3. Get real-time arrivals
const arrivals = await tools.get_stop_arrivals({
area: areaResult.area,
stopId: stops[0].id
});
// Returns: "Bus K100(I) arrives in 1 minute, Bus K100(O) in 2 minutes"
Track Live Buses
// Get all live vehicles in Penang
const vehicles = await tools.get_live_vehicles({
area: "penang"
});
// Filter by bus only
const buses = await tools.get_live_vehicles({
area: "klang-valley",
type: "bus"
});
Discover Routes
// List all routes in Klang Valley
const routes = await tools.list_routes({
area: "klang-valley"
});
// Get detailed route information
const routeDetails = await tools.get_route_details({
area: "klang-valley",
routeId: "LRT-KJ"
});
AI Integration Guide
Key Use Cases
1. "When is my bus coming?" ⭐
This is the PRIMARY use case. Users want to know when their next bus/train will arrive.
Workflow:
1. User asks: "When is the next bus at Komtar?"
2. AI uses: detect_location_area({ location: "Komtar" })
3. AI uses: search_stops({ area: "penang", query: "Komtar" })
4. AI uses: get_stop_arrivals({ area: "penang", stopId: "..." })
5. AI responds: "Bus T101 arrives in 5 minutes, Bus T201 in 12 minutes"
2. "Where is my bus right now?"
Users want to track their bus in real-time.
Workflow:
1. User asks: "Where is bus T101 right now?"
2. AI uses: detect_location_area({ location: "Penang" })
3. AI uses: get_live_vehicles({ area: "penang" })
4. AI filters for route T101
5. AI responds: "Bus T101 is currently at [location], heading towards Airport"
Tool Usage Patterns
Always Start with Location Detection
When a user mentions a location without specifying the area, use location detection:
// User: "When is the next bus at KTM Alor Setar?"
const areaResult = await tools.detect_location_area({
location: "KTM Alor Setar"
});
// Returns: { area: "alor-setar", confidence: "high" }
Search Before Details
Always search for stops/routes before requesting details:
// ✅ CORRECT
const stops = await tools.search_stops({ area: "penang", query: "Komtar" });
const arrivals = await tools.get_stop_arrivals({
area: "penang",
stopId: stops[0].id
});
// ❌ WRONG - Don't guess stop IDs
const arrivals = await tools.get_stop_arrivals({
area: "penang",
stopId: "random_id"
});
Response Formatting
Arrival Times
Format arrival times in a user-friendly way:
// ✅ GOOD
"Bus T101 arrives in 5 minutes"
"Train LRT-KJ arrives in 2 minutes"
"Next bus: T201 in 12 minutes"
// ❌ BAD
"Arrival time: 2025-01-07T14:30:00Z"
"ETA: 1736258400000"
Multiple Arrivals
Present multiple arrivals clearly:
"Upcoming arrivals at Komtar:
• T101 → Airport: 5 minutes
• T201 → Bayan Lepas: 12 minutes
• T102 → Gurney: 18 minutes"
Error Handling
Provider Unavailable
try {
const arrivals = await tools.get_stop_arrivals({ ... });
} catch (error) {
// Check provider status
const status = await tools.get_provider_status({ area: "penang" });
if (status.providers[0].status !== "active") {
"The transit provider is currently unavailable.
Please try again later or check the official transit app."
}
}
Best Practices
- Use location detection when users mention place names
- Always specify area for every tool (except
list_service_areasanddetect_location_area) - Search before details - don't guess IDs
- Handle errors gracefully - providers may have temporary outages
- Format responses clearly - use minutes, not timestamps
- Don't cache real-time data - it updates every 30 seconds
Supported Service Areas
| Area ID | Name | Providers | Transit Types | Fare Calculator |
|---|---|---|---|---|
klang-valley | Klang Valley | Rapid Rail KL, Rapid Bus KL, MRT Feeder | Bus, Rail | ❌ |
penang | Penang | Rapid Penang, Penang Ferry, KTM Komuter Utara | Bus, Ferry, Rail | ✅ |
kuantan | Kuantan | Under Maintenance | Bus | ❌ |
ipoh | Ipoh | BAS.MY Ipoh, KTM Komuter Utara | Bus, Rail | ✅ |
seremban | Seremban | BAS.MY Seremban, KTM Intercity | Bus, Rail | ✅ |
kangar | Kangar | BAS.MY Kangar, KTM Komuter Utara | Bus, Rail | ✅ |
alor-setar | Alor Setar | BAS.MY Alor Setar, KTM Komuter Utara | Bus, Rail | ✅ |
kota-bharu | Kota Bharu | BAS.MY Kota Bharu, KTM Intercity | Bus, Rail | ✅ |
kuala-terengganu | Kuala Terengganu | BAS.MY Kuala Terengganu | Bus | ✅ |
melaka | Melaka | BAS.MY Melaka | Bus | ✅ |
johor | Johor Bahru | BAS.MY Johor Bahru | Bus | ✅ |
kuching | Kuching | BAS.MY Kuching | Bus | ✅ |
kota-kinabalu | Kota Kinabalu | Coming Soon | - | ❌ |
KTM Services
| Service | Route | Stations | Fare Calculator |
|---|---|---|---|
| KTM Komuter Utara | Padang Besar ↔ Butterworth ↔ Ipoh | 23 stations | ✅ |
| KTM Intercity (SH) | JB Sentral ↔ Gemas ↔ Tumpat | Multiple | ❌ (Coming Soon) |
| KTM Intercity (ERT) | JB Sentral ↔ Gemas ↔ Tumpat | Multiple | ❌ (Coming Soon) |
Location to Area Mapping
The detect_location_area tool automatically maps common locations to service areas:
| User Says | Area ID |
|---|---|
| Ipoh, Bercham, Tanjung Rambutan, Medan Kidd | ipoh |
| Seremban, Nilai, Port Dickson | seremban |
| George Town, Butterworth, Bayan Lepas, Penang Sentral | penang |
| KLCC, Shah Alam, Putrajaya | klang-valley |
| Kuantan, Pekan, Bandar Indera Mahkota | kuantan |
| Kangar, Arau, Kuala Perlis, Padang Besar | kangar |
| Alor Setar, Sungai Petani, Pendang, Jitra | alor-setar |
| Kota Bharu, Rantau Panjang, Bachok, Machang, Jeli | kota-bharu |
| Kuala Terengganu, Merang, Marang, Setiu | kuala-terengganu |
| Melaka, Tampin, Jasin, Masjid Tanah | melaka |
| Johor Bahru, Iskandar Puteri, Pasir Gudang, Kulai | johor |
| Kuching, Bau, Serian, Bako, Siniawan, Matang | kuching |
Deployment
Production Server
The MCP server is deployed at:
- Endpoint:
https://mcp.techmavie.digital/malaysiatransit/mcp - Health Check:
https://mcp.techmavie.digital/malaysiatransit/health - Analytics Dashboard:
https://mcp.techmavie.digital/malaysiatransit/analytics/dashboard - Analytics API:
https://mcp.techmavie.digital/malaysiatransit/analytics - Transport: Streamable HTTP
Analytics Dashboard
The MCP server includes a built-in analytics dashboard that tracks:
- Total requests and tool calls
- Tool usage distribution (doughnut chart)
- Hourly request trends (last 24 hours)
- Requests by endpoint (bar chart)
- Top clients by user agent
- Recent tool calls feed
The dashboard auto-refreshes every 30 seconds.
Self-Hosting
To deploy your own instance, see the deployment guide.
# Using Docker
docker compose up -d --build
# Or run directly
npm run build
npm run start:http
Auto-Deployment
This repository includes a GitHub Actions workflow for automatic VPS deployment. When you push to main, the server automatically redeploys.
To set up auto-deployment, add these secrets to your GitHub repository:
VPS_HOST- Your VPS IP addressVPS_USERNAME- SSH username (e.g.,root)VPS_PORT- SSH port (e.g.,22)VPS_SSH_KEY- Private SSH key for authenticationGOOGLE_MAPS_API_KEY- Google Maps API key for geocodingINTERNAL_AUTH_TOKEN- Shared auth token (must match middleware'sINTERNAL_APP_AUTH_TOKEN)ANALYTICS_RESET_KEY(optional) - Enables/analytics/resetadmin endpointANALYTICS_IMPORT_KEY(optional) - Enables/analytics/importadmin endpoint
Troubleshooting
Connection Issues
If you can't connect to the middleware:
- Verify your
MIDDLEWARE_URLis correct - Ensure the middleware is running and accessible
- Check network connectivity
- Verify
INTERNAL_AUTH_TOKENmatches middlewareINTERNAL_APP_AUTH_TOKEN(if middleware auth is enabled) - Test middleware directly:
curl https://your-middleware-url/api/areas
No Data Returned
If tools return empty data:
- Check if the service area is operational using
get_provider_status - Verify the area ID is correct using
list_service_areas - Check middleware logs for errors
Real-time Data Unavailable
Real-time data depends on the upstream GTFS providers:
- Use
get_provider_statusto check provider health - Some providers may have temporary outages
- Check the middleware logs for API issues
Location Detection Not Working
If location detection returns incorrect results:
- Ensure
GOOGLE_MAPS_API_KEYis set in environment variables - Check Google Cloud Console for API quota limits
- Verify the API key has Geocoding API enabled
- Falls back to Nominatim if Google Maps fails
Requirements
- Node.js: >= 18.0.0
- Malaysia Transit Middleware: Running instance (local or deployed)
- Google Maps API Key: Optional, for enhanced location detection
Project Structure
malaysiatransit-mcp/
├── src/
│ ├── index.ts # Main MCP server entry point
│ ├── http-server.ts # Streamable HTTP server with analytics
│ ├── transit.tools.ts # Transit tool implementations
│ ├── geocoding.utils.ts # Location detection utilities
│ ├── inspector.ts # MCP Inspector entry point
│ └── server.ts # HTTP server for testing
├── deploy/
│ ├── DEPLOYMENT.md # VPS deployment guide
│ └── nginx-mcp.conf # Nginx reverse proxy config
├── .github/
│ └── workflows/
│ └── deploy-vps.yml # GitHub Actions auto-deploy
├── docker-compose.yml # Docker deployment config
├── Dockerfile # Container build config
├── package.json # Project dependencies
├── tsconfig.json # TypeScript configuration
├── .env.sample # Environment variables template
├── README.md # This file
└── LICENSE # MIT License
Related Projects
- Malaysia Open Data MCP - MCP for Malaysia's open data portal
Contributing
Contributions are welcome! Please feel free to submit pull requests or open issues.
License
MIT - See LICENSE file for details.
Acknowledgments
- Malaysia Open Data Portal for GTFS data
- Prasarana Malaysia for Rapid KL services
- BAS.MY for regional bus services
- Model Context Protocol for the MCP framework
- Google Maps Platform for geocoding services
- OpenStreetMap Nominatim for fallback geocoding
Made with ❤️ by Aliff
常见问题
Malaysia Transit MCP 是什么?
提供马来西亚12座城市的实时公交与火车信息,支持到站、车辆追踪和线路查询,覆盖 Rapid KL、Rapid Penang 与 BAS.MY 等服务。
Malaysia Transit MCP 提供哪些工具?
提供 43 个工具,包括 list_service_areas、detect_location_area、get_area_info 等。
相关 Skills
MCP构建
by anthropics
聚焦高质量 MCP Server 开发,覆盖协议研究、工具设计、错误处理与传输选型,适合用 FastMCP 或 MCP SDK 对接外部 API、封装服务能力。
✎ 想让 LLM 稳定调用外部 API,就用 MCP构建:从 Python 到 Node 都有成熟指引,帮你更快做出高质量 MCP 服务器。
Slack动图
by anthropics
面向Slack的动图制作Skill,内置emoji/消息GIF的尺寸、帧率和色彩约束、校验与优化流程,适合把创意或上传图片快速做成可直接发送的Slack动画。
✎ 帮你快速做出适配 Slack 的动图,内置约束规则和校验工具,少踩上传与播放坑,做表情包和演示都更省心。
MCP服务构建器
by alirezarezvani
从 OpenAPI 一键生成 Python/TypeScript MCP server 脚手架,并校验 tool schema、命名规范与版本兼容性,适合把现有 REST API 快速发布成可生产演进的 MCP 服务。
✎ 帮你快速搭建 MCP 服务与后端 API,脚手架完善、扩展顺手,尤其适合想高效验证服务能力的开发者。
相关 MCP Server
Slack 消息
编辑精选by Anthropic
Slack 是让 AI 助手直接读写你的 Slack 频道和消息的 MCP 服务器。
✎ 这个服务器解决了团队协作中需要 AI 实时获取 Slack 信息的痛点,特别适合开发团队让 Claude 帮忙汇总频道讨论或发送通知。不过,它目前只是参考实现,文档有限,不建议在生产环境直接使用——更适合开发者学习 MCP 如何集成第三方服务。
by netdata
io.github.netdata/mcp-server 是让 AI 助手实时监控服务器指标和日志的 MCP 服务器。
✎ 这个工具解决了运维人员需要手动检查系统状态的痛点,最适合 DevOps 团队让 Claude 自动分析性能数据。不过,它依赖 NetData 的现有部署,如果你没用过这个监控平台,得先花时间配置。
by d4vinci
Scrapling MCP Server 是专为现代网页设计的智能爬虫工具,支持绕过 Cloudflare 等反爬机制。
✎ 这个工具解决了爬取动态网页和反爬网站时的头疼问题,特别适合需要批量采集电商价格或新闻数据的开发者。不过,它依赖外部浏览器引擎,资源消耗较大,不适合轻量级任务。