# CTA L Train Tracker — MCP Server Documentation

A publicly accessible MCP server that gives AI agents real-time access to Chicago's CTA L train system. Query train arrivals at any of 145 stations across 8 lines, plan multi-leg trips with transfers, check service alerts, and get station metadata including neighborhoods, landmarks, and accessibility. No authentication required — connect and start querying immediately.

This server is designed for AI agents. Both humans and machines are welcome.

## MCP Endpoint

- **URL:** `https://cta-mcp-server.subpaatt.workers.dev/mcp`
- **Transport:** Streamable HTTP (MCP protocol 2025-03-26)
- **Authentication:** None required
- **Rate Limit:** 30 requests/minute per IP

### Connect in 30 seconds

Add this to your Claude Desktop, ChatGPT, or Cursor MCP config:

```json
{
  "mcpServers": {
    "cta-trains": {
      "url": "https://cta-mcp-server.subpaatt.workers.dev/mcp"
    }
  }
}
```

---

## Tools

### get_arrivals

Get upcoming train arrivals at a CTA L station. Supports fuzzy station name matching — handles typos, abbreviations, and neighborhood names.

**Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| station | string | Yes | Station name (e.g. "Belmont", "Clark/Lake", "O'Hare", "Wicker Park") |
| line | string | No | Line filter: Red, Blue, Brown, Green, Orange, Purple, Pink, Yellow |
| max_results | number | No | Max arrivals to return (1-50, default 5) |

**Example:**

```json
// Input
{ "station": "Belmont", "line": "Red" }

// Output
{
  "station": "Belmont (Red/Brown/Purple)",
  "map_id": "41320",
  "arrivals": [
    {
      "line": "Red",
      "destination": "Howard",
      "arrival": "Approaching",
      "minutes": 1,
      "run_number": "921",
      "is_approaching": true,
      "is_delayed": false,
      "is_scheduled": false,
      "platform": "Service toward Howard or Linden"
    },
    {
      "line": "Red",
      "destination": "95th/Dan Ryan",
      "arrival": "7 min",
      "minutes": 7,
      "run_number": "919",
      "is_approaching": false,
      "is_delayed": false,
      "is_scheduled": false,
      "platform": "Service toward 95th or Loop"
    }
  ]
}
```

**Notes:**
- Minutes are calculated as `arrT - prdt` (arrival time minus prediction time), per CTA documentation
- `is_approaching`: train is arriving at the station now
- `is_scheduled`: prediction is schedule-based, not live GPS tracking
- `is_delayed`: train is running behind schedule
- Fuzzy matching handles typos and abbreviations: "belmont", "belmnt", "ohare", "wicker park" all resolve correctly

---

### get_train_locations

Get real-time GPS positions of all trains currently running on a specific line.

**Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| line | string | Yes | Line name: Red, Blue, Brown, Green, Orange, Purple, Pink, Yellow |

**Example:**

```json
// Input
{ "line": "Blue" }

// Output
{
  "line": "Blue",
  "route_code": "Blue",
  "train_count": 17,
  "trains": [
    {
      "run_number": "115",
      "destination": "O'Hare",
      "next_station": "UIC-Halsted",
      "is_approaching_next": false,
      "is_delayed": false,
      "latitude": "41.87522",
      "longitude": "-87.64926",
      "heading": "269"
    }
  ]
}
```

---

### get_alerts

Get active CTA service alerts and advisories. Optionally filter by line.

**Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| line | string | No | Line filter: Red, Blue, Brown, Green, Orange, Purple, Pink, Yellow. Omit for all alerts. |

**Example:**

```json
// Input
{ "line": "Red" }

// Output
{
  "alert_count": 2,
  "alerts": [
    {
      "headline": "Boarding Change, Delays Between 63rd and 79th",
      "description": "Track work between 63rd and 79th...",
      "severity": "25",
      "impact": "Significant",
      "affected_services": ["Red Line"]
    }
  ]
}
```

---

### plan_trip

Plan a trip between two CTA L stations or neighborhoods. Finds direct routes or routes requiring transfers, and fetches real-time departure times at origin and transfer points.

**Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| from_station | string | Yes | Origin station or neighborhood (e.g. "Belmont", "Wicker Park", "O'Hare") |
| to_station | string | Yes | Destination station or neighborhood (e.g. "Midway", "Loop", "Hyde Park") |

**Example — direct route:**

```json
// Input
{ "from_station": "Wicker Park", "to_station": "O'Hare" }

// Output
{
  "from": "Damen (Blue)",
  "to": "O'Hare",
  "route_type": "direct",
  "estimated_total_minutes": 30,
  "line": "Blue",
  "direction": "toward O'Hare",
  "stations": 12,
  "real_time_departure": "Next Blue Line toward O'Hare: 4 min (Run #129 to O'Hare)"
}
```

**Example — transfer route:**

```json
// Input
{ "from_station": "Belmont", "to_station": "Midway" }

// Output
{
  "from": "Belmont (Red/Brown/Purple)",
  "to": "Midway",
  "route_type": "transfer",
  "estimated_total_minutes": 43,
  "leg_1": {
    "line": "Brown",
    "direction": "toward Loop",
    "stations": 9,
    "ride_minutes": 23
  },
  "transfer_at": "Harold Washington Library",
  "transfer_walk_minutes": 0,
  "leg_2": {
    "line": "Orange",
    "direction": "toward Midway",
    "stations": 8,
    "ride_minutes": 20
  },
  "real_time_departure": "Next Brown Line toward Loop: 3 min",
  "transfer_arrivals": "Next Orange Line at Harold Washington Library: 9 min",
  "alternatives": [...]
}
```

**Notes:**
- Understands 30+ Chicago neighborhoods: "Wicker Park" -> Damen Blue, "Hyde Park" -> Cottage Grove Green, "Wrigleyville" -> Addison Red
- Returns up to 3 route alternatives
- Real-time departures fetched for both origin and transfer stations

---

### station_info

Get detailed information about a CTA L station including lines served, neighborhood, nearby landmarks, accessibility, and typical train frequency.

**Parameters:**

| Name | Type | Required | Description |
|------|------|----------|-------------|
| station | string | Yes | Station name or neighborhood (e.g. "Clark/Lake", "Wrigleyville", "Hyde Park") |

**Example:**

```json
// Input
{ "station": "Clark/Lake" }

// Output
{
  "name": "Clark/Lake",
  "map_id": "40380",
  "lines_served": ["Blue", "Brown", "Green", "Orange", "Pink", "Purple"],
  "neighborhood": "Loop",
  "landmarks": ["State Street", "Chicago Theatre"],
  "station_type": "both",
  "ada_accessible": true,
  "typical_headways": {
    "peak": "2-4 min (multiple lines)",
    "offPeak": "5-8 min",
    "weekend": "8-12 min"
  },
  "transfer_to": "Transfer between Blue, Brown, Green, Orange, Pink, Purple lines at this station"
}
```

---

## Rate Limits

| Endpoint | Limit | Response on Exceed |
|----------|-------|--------------------|
| `/mcp` (MCP protocol) | 30 requests/minute per IP | HTTP 429 + Retry-After: 60 |
| `/api/chat` (web demo) | 20 requests/minute per IP | HTTP 429 + Retry-After: 60 |

---

## Data Sources

- **Train arrivals & positions:** CTA Train Tracker API (lapi.transitchicago.com) — real-time
- **Service alerts:** CTA Customer Alerts API (transitchicago.com) — real-time
- **Station data:** City of Chicago Data Portal + embedded dataset (145 stations, 30+ neighborhoods)
- **Caching:** Arrivals cached 60 seconds, alerts cached 5 minutes

---

## All Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/` | GET | Landing page with AI chat demo |
| `/mcp` | POST | MCP server (Streamable HTTP transport) |
| `/api/chat` | POST | AI chat — `{"message": "your question"}` |
| `/.well-known/mcp.json` | GET | MCP discovery manifest |
| `/docs` | GET | This documentation |

---

*Data provided by Chicago Transit Authority. Not affiliated with CTA.*
*Powered by Cloudflare Workers, Workers AI, and the Model Context Protocol.*
