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_areas

List all available transit service areas in Malaysia (e.g., Klang Valley, Penang, Kuantan)

detect_location_area

Automatically 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_info

Get detailed information about a specific transit service area

search_stops

Search 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_details

Get detailed information about a specific bus or train stop

get_stop_arrivals

Get real-time arrival predictions for buses/trains at a specific stop

find_nearby_stops

Find 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_arrivals

Find 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_routes

Find 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_routes

List all available bus or train routes in a specific area

get_route_stops

Get 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_details

Get detailed information about a specific route including stops and geometry. IMPORTANT: First call list_routes to get the correct route_id.

get_route_geometry

Get the geographic path and stops for a specific route (for map visualization)

get_live_vehicles

Get real-time positions of all buses and trains in a specific area

get_provider_status

Check the operational status of transit providers in a specific area

get_route_departures

Get 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_departure

Get 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_routes

Get 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_schedule

Get 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_origin

Get 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_status

Check 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_routes

Get 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_fare

Get 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_fare

Calculate 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_fare

Calculate 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_fare

Get available directions for a route when calculating fares. Use this to determine which direction (outbound/inbound) to use for fare calculation.

get_ktm_komuter_stations

Get all 23 KTM Komuter Utara stations (Padang Besar - Butterworth - Ipoh line). Returns station codes, names, and coordinates.

calculate_ktm_komuter_fare

Calculate 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_matrix

Get the full KTM Komuter Utara fare matrix showing fares between all station pairs.

get_ktm_station_departures

Get departure times for a specific KTM station. Supports both KTM Komuter Utara and KTM Intercity schedules.

get_ktm_stations

Get all KTM stations for a specific schedule type (Komuter Utara or Intercity).

get_ktm_schedules

Get full KTM schedule data for a specific schedule type. Returns complete timetable information.

find_nearby_ktm_stations

Find 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_overview

Get Penang Ferry service overview including terminals, operating hours, frequency, and contact information. The ferry operates between Butterworth and George Town.

get_penang_ferry_schedule

Get full Penang Ferry schedule with departure times. Supports filtering by direction and day type.

get_penang_ferry_next_departure

Get next ferry departures from both Butterworth and George Town terminals in real-time. Shows minutes until departure.

get_penang_ferry_terminals

Get detailed information about Penang Ferry terminals including facilities, connections, parking, and nearby attractions.

get_penang_ferry_fare

Get Penang Ferry fare information, payment methods, and terminal coordinates.

get_system_health

Check the health status of the Malaysia Transit middleware service

get_debug_info

Get comprehensive debug information about the middleware service including memory usage and initialized areas

get_api_analytics

Get 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_analytics

Get detailed API usage analytics for a specific service area. Shows which endpoints are most used for that area.

hello

A 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

  • 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:

code
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.):

json
{
  "mcpServers": {
    "malaysia-transit": {
      "transport": "streamable-http",
      "url": "https://mcp.techmavie.digital/malaysiatransit/mcp"
    }
  }
}

Test with MCP Inspector

bash
npx @modelcontextprotocol/inspector
# Select "Streamable HTTP"
# Enter URL: https://mcp.techmavie.digital/malaysiatransit/mcp

Test with curl

bash
# 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

bash
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
  • INTERNAL_AUTH_TOKEN (required for production): Shared secret for middleware API authentication

    • Must match INTERNAL_APP_AUTH_TOKEN in the middleware's environment
    • Generate with: openssl rand -hex 32
  • 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 for POST /analytics/reset

    • If not set, the reset endpoint is disabled (returns 403)
  • ANALYTICS_IMPORT_KEY (optional): Admin key for POST /analytics/import

    • If not set, the import endpoint is disabled (returns 403)

Development

To run the MCP server in development mode:

bash
npm run dev

Build

To build the MCP server for deployment:

bash
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:

typescript
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:

typescript
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:

typescript
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 ID
  • query (string): Search query (e.g., "Komtar", "KLCC")

Example:

typescript
const stops = await tools.search_stops({
  area: "penang",
  query: "Komtar"
});

get_stop_details

Get detailed information about a stop.

Parameters:

  • area (string): Service area ID
  • stopId (string): Stop ID from search results

get_stop_arrivals

Get real-time arrival predictions at a stop.

Parameters:

  • area (string): Service area ID
  • stopId (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:

typescript
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 ID
  • location (string, optional): Place name to geocode (e.g., "KLCC", "Komtar")
  • lat (number, optional): Latitude coordinate (required if location not provided)
  • lon (number, optional): Longitude coordinate (required if location not 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 ID
  • location (string, optional): Place name to geocode
  • lat (number, optional): Latitude coordinate (required if location not provided)
  • lon (number, optional): Longitude coordinate (required if location not 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 ID
  • location (string, optional): Place name to geocode
  • lat (number, optional): Latitude coordinate (required if location not provided)
  • lon (number, optional): Longitude coordinate (required if location not 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 ID
  • routeId (string): Route ID from list_routes

get_route_details

Get detailed route information.

Parameters:

  • area (string): Service area ID
  • routeId (string): Route ID from list_routes

get_route_geometry

Get route path for map visualization.

Parameters:

  • area (string): Service area ID
  • routeId (string): Route ID from list_routes

Real-time Data

get_live_vehicles

Get real-time vehicle positions.

Parameters:

  • area (string): Service area ID
  • type (enum, optional): Filter by type ('bus' or 'rail')

Example:

typescript
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_routes
  • count (number, optional): Number of departures to return (default: 5)

Example:

typescript
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 ID
  • routeId (string): Route ID from list_routes
  • direction (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 ID
  • stopId (string): Stop ID from search_stops
  • count (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 ID
  • routeId (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 ID
  • routeId (string): Route ID from list_routes
  • direction (enum, optional): 'outbound' or 'inbound'

get_route_status

Check if a route is currently operating based on its schedule.

Parameters:

  • area (string): Service area ID
  • routeId (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 ID
  • routeId (string): Route ID from get_fare_routes

calculate_fare

Calculate the bus fare between two stops on a route.

Parameters:

  • area (string): Service area ID
  • routeId (string): Route ID from get_fare_routes
  • fromStop (string): Origin stop ID
  • toStop (string): Destination stop ID

Returns: Adult, concession, and child fares in MYR with disclaimer.

Example:

typescript
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 ID
  • legs (array): Array of journey legs (max 5), each with:
    • routeId (string): Route ID for this leg
    • fromStop (string): Origin stop ID
    • toStop (string): Destination stop ID
    • areaId (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 ID
  • routeId (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:

typescript
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:

typescript
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-utara or ktm-intercity

Example:

typescript
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-utara or ktm-intercity

get_ktm_schedules

Get full KTM schedule data for a specific schedule type.

Parameters:

  • type (enum): ktm-komuter-utara or ktm-intercity

find_nearby_ktm_stations

Find KTM stations near a specific location.

Parameters:

  • lat (number): Latitude coordinate
  • lon (number): Longitude coordinate
  • radius (number, optional): Search radius in kilometers (default: 10)
  • type (enum): ktm-komuter-utara or ktm-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:

typescript
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, uptime
    • endpoints: Per-endpoint statistics
    • areas: Per-service-area statistics
    • cumulative: All-time totals
    • clients: App/website usage breakdown

Example:

typescript
// 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:

typescript
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

typescript
// 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

typescript
// 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

typescript
// 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:

code
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:

code
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:

typescript
// 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:

typescript
// ✅ 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:

typescript
// ✅ 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:

typescript
"Upcoming arrivals at Komtar:
• T101 → Airport: 5 minutes
• T201 → Bayan Lepas: 12 minutes
• T102 → Gurney: 18 minutes"

Error Handling

Provider Unavailable

typescript
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

  1. Use location detection when users mention place names
  2. Always specify area for every tool (except list_service_areas and detect_location_area)
  3. Search before details - don't guess IDs
  4. Handle errors gracefully - providers may have temporary outages
  5. Format responses clearly - use minutes, not timestamps
  6. Don't cache real-time data - it updates every 30 seconds

Supported Service Areas

Area IDNameProvidersTransit TypesFare Calculator
klang-valleyKlang ValleyRapid Rail KL, Rapid Bus KL, MRT FeederBus, Rail
penangPenangRapid Penang, Penang Ferry, KTM Komuter UtaraBus, Ferry, Rail
kuantanKuantanUnder MaintenanceBus
ipohIpohBAS.MY Ipoh, KTM Komuter UtaraBus, Rail
serembanSerembanBAS.MY Seremban, KTM IntercityBus, Rail
kangarKangarBAS.MY Kangar, KTM Komuter UtaraBus, Rail
alor-setarAlor SetarBAS.MY Alor Setar, KTM Komuter UtaraBus, Rail
kota-bharuKota BharuBAS.MY Kota Bharu, KTM IntercityBus, Rail
kuala-terengganuKuala TerengganuBAS.MY Kuala TerengganuBus
melakaMelakaBAS.MY MelakaBus
johorJohor BahruBAS.MY Johor BahruBus
kuchingKuchingBAS.MY KuchingBus
kota-kinabaluKota KinabaluComing Soon-

KTM Services

ServiceRouteStationsFare Calculator
KTM Komuter UtaraPadang Besar ↔ Butterworth ↔ Ipoh23 stations
KTM Intercity (SH)JB Sentral ↔ Gemas ↔ TumpatMultiple❌ (Coming Soon)
KTM Intercity (ERT)JB Sentral ↔ Gemas ↔ TumpatMultiple❌ (Coming Soon)

Location to Area Mapping

The detect_location_area tool automatically maps common locations to service areas:

User SaysArea ID
Ipoh, Bercham, Tanjung Rambutan, Medan Kiddipoh
Seremban, Nilai, Port Dicksonseremban
George Town, Butterworth, Bayan Lepas, Penang Sentralpenang
KLCC, Shah Alam, Putrajayaklang-valley
Kuantan, Pekan, Bandar Indera Mahkotakuantan
Kangar, Arau, Kuala Perlis, Padang Besarkangar
Alor Setar, Sungai Petani, Pendang, Jitraalor-setar
Kota Bharu, Rantau Panjang, Bachok, Machang, Jelikota-bharu
Kuala Terengganu, Merang, Marang, Setiukuala-terengganu
Melaka, Tampin, Jasin, Masjid Tanahmelaka
Johor Bahru, Iskandar Puteri, Pasir Gudang, Kulaijohor
Kuching, Bau, Serian, Bako, Siniawan, Matangkuching

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.

bash
# 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 address
  • VPS_USERNAME - SSH username (e.g., root)
  • VPS_PORT - SSH port (e.g., 22)
  • VPS_SSH_KEY - Private SSH key for authentication
  • GOOGLE_MAPS_API_KEY - Google Maps API key for geocoding
  • INTERNAL_AUTH_TOKEN - Shared auth token (must match middleware's INTERNAL_APP_AUTH_TOKEN)
  • ANALYTICS_RESET_KEY (optional) - Enables /analytics/reset admin endpoint
  • ANALYTICS_IMPORT_KEY (optional) - Enables /analytics/import admin endpoint

Troubleshooting

Connection Issues

If you can't connect to the middleware:

  1. Verify your MIDDLEWARE_URL is correct
  2. Ensure the middleware is running and accessible
  3. Check network connectivity
  4. Verify INTERNAL_AUTH_TOKEN matches middleware INTERNAL_APP_AUTH_TOKEN (if middleware auth is enabled)
  5. Test middleware directly: curl https://your-middleware-url/api/areas

No Data Returned

If tools return empty data:

  1. Check if the service area is operational using get_provider_status
  2. Verify the area ID is correct using list_service_areas
  3. Check middleware logs for errors

Real-time Data Unavailable

Real-time data depends on the upstream GTFS providers:

  1. Use get_provider_status to check provider health
  2. Some providers may have temporary outages
  3. Check the middleware logs for API issues

Location Detection Not Working

If location detection returns incorrect results:

  1. Ensure GOOGLE_MAPS_API_KEY is set in environment variables
  2. Check Google Cloud Console for API quota limits
  3. Verify the API key has Geocoding API enabled
  4. 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

code
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

Contributing

Contributions are welcome! Please feel free to submit pull requests or open issues.

License

MIT - See LICENSE file for details.

Acknowledgments


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

Universal
热门

聚焦高质量 MCP Server 开发,覆盖协议研究、工具设计、错误处理与传输选型,适合用 FastMCP 或 MCP SDK 对接外部 API、封装服务能力。

想让 LLM 稳定调用外部 API,就用 MCP构建:从 Python 到 Node 都有成熟指引,帮你更快做出高质量 MCP 服务器。

平台与服务
未扫描111.8k

Slack动图

by anthropics

Universal
热门

面向Slack的动图制作Skill,内置emoji/消息GIF的尺寸、帧率和色彩约束、校验与优化流程,适合把创意或上传图片快速做成可直接发送的Slack动画。

帮你快速做出适配 Slack 的动图,内置约束规则和校验工具,少踩上传与播放坑,做表情包和演示都更省心。

平台与服务
未扫描111.8k

MCP服务构建器

by alirezarezvani

Universal
热门

从 OpenAPI 一键生成 Python/TypeScript MCP server 脚手架,并校验 tool schema、命名规范与版本兼容性,适合把现有 REST API 快速发布成可生产演进的 MCP 服务。

帮你快速搭建 MCP 服务与后端 API,脚手架完善、扩展顺手,尤其适合想高效验证服务能力的开发者。

平台与服务
未扫描9.8k

相关 MCP Server

Slack 消息

编辑精选

by Anthropic

热门

Slack 是让 AI 助手直接读写你的 Slack 频道和消息的 MCP 服务器。

这个服务器解决了团队协作中需要 AI 实时获取 Slack 信息的痛点,特别适合开发团队让 Claude 帮忙汇总频道讨论或发送通知。不过,它目前只是参考实现,文档有限,不建议在生产环境直接使用——更适合开发者学习 MCP 如何集成第三方服务。

平台与服务
83.1k

by netdata

热门

io.github.netdata/mcp-server 是让 AI 助手实时监控服务器指标和日志的 MCP 服务器。

这个工具解决了运维人员需要手动检查系统状态的痛点,最适合 DevOps 团队让 Claude 自动分析性能数据。不过,它依赖 NetData 的现有部署,如果你没用过这个监控平台,得先花时间配置。

平台与服务
78.3k

by d4vinci

热门

Scrapling MCP Server 是专为现代网页设计的智能爬虫工具,支持绕过 Cloudflare 等反爬机制。

这个工具解决了爬取动态网页和反爬网站时的头疼问题,特别适合需要批量采集电商价格或新闻数据的开发者。不过,它依赖外部浏览器引擎,资源消耗较大,不适合轻量级任务。

平台与服务
34.9k

评论