cats-mcp
cats-mcp
An MCP server for live Charlotte Area Transit System (CATS) bus and light rail data, built on the agency's public GTFS-Realtime feeds. It runs over stdio, launched by the MCP client that uses it.
Tools
Tool | Purpose |
| Locate one bus/train by vehicle number, or every vehicle on a route, and return GPS coordinates. |
| Current GPS coordinates of every bus and train in service. |
| Estimated arrival times at a specific stop or station. |
find_vehicle
Argument | Type | Notes |
| string | Vehicle number as shown on the bus/train, e.g. |
| string | Route to locate: |
|
| Optional filter. |
At least one of vehicle or route is required. Returns position, heading, speed,
occupancy, headsign, and the next scheduled stop.
list_vehicles
Argument | Type | Notes |
|
| Optional filter. |
| string | Optional single-route filter. |
| integer | Max vehicles to return (default and cap: 250). |
Includes countsByMode and totalInService so the total is visible even when the
list is truncated.
get_arrivals
Argument | Type | Notes |
| string | Required. Stop id ( |
| string | Optional route filter. |
|
| Optional filter. |
| integer | Max arrivals (default 10, cap 50). |
Returns minutes away, predicted and scheduled times, schedule deviation, the vehicle
number, and that vehicle's live position. When a name query is ambiguous, the best
match is used and the runners-up are listed under otherStopsMatchingQuery. Service
alerts affecting the stop or its routes are attached when present.
Install
Requires Python 3.11+.
python3 -m venv .venv
.venv/bin/pip install .Run it
Register it with Claude Code:
claude mcp add cats -- /absolute/path/to/cats-mcp/.venv/bin/cats-mcpOr in an MCP client config file:
{
"mcpServers": {
"cats": {
"command": "/absolute/path/to/cats-mcp/.venv/bin/cats-mcp"
}
}
}python -m cats_mcp runs the same server, so any interpreter with the package
installed works as the command.
stdout carries MCP protocol traffic only; all diagnostics go to stderr.
Data sources
Realtime (GTFS-Realtime protobuf, refreshed every 20s):
https://gtfsrealtime.ridetransit.org/GTFSRealTime/Vehicle/VehiclePositions.pbhttps://gtfsrealtime.ridetransit.org/GTFSRealTime/TripUpdate/TripUpdates.pbhttps://gtfsrealtime.ridetransit.org/GTFSRealTime/Alert/Alerts.pb
Static schedule (cached 6h), used to turn feed identifiers into route names, stop names, and coordinates:
https://gtfsrealtime.ridetransit.org/GTFSStatic/api/GTFSDownload/GTFS.zip
Only routes.txt, stops.txt, and trips.txt are read; stop_times.txt and
shapes.txt are the bulk of the archive and are not needed.
Feed quirks this server works around
Verified against live feed captures:
VehiclePosition.stop_idandcurrent_stop_sequenceare unusable. None of the 158 vehicle stop ids in a sample capture matched any stop in the published schedule, and reported sequence numbers exceeded the trip's own stop count (e.g. sequence 192 on a 52-stop trip). This server never surfaces them; next-stop data comes from the TripUpdates feed instead, whose stop ids resolve 100%.StopTimeEvent.delayis never populated. Schedule deviation is computed fromtimeminusscheduled_time, which are both present.TripUpdates cover ~83% of active vehicles, so
nextStopis omitted rather than guessed for the remainder.Route matching is exact-first, so a query of
5returns route 5, not 501 or 510.
Behavior notes
Arrival predictions already in the past are filtered out; no negative ETAs.
Feed responses are capped in size and time-bounded; one slow feed cannot hang a call.
Concurrent calls share a single in-flight fetch per feed, and one call giving up does not abort a fetch the others are awaiting.
If a refresh fails but cached data exists, the last good data is served rather than an error.
feedAgeSecondson every response shows how stale it is.The alerts feed is supplementary: if it fails,
get_arrivalsstill returns arrivals.Times are ISO 8601 UTC; coordinates are WGS84 decimal degrees.
Configuration
All optional; defaults target the CATS feeds above. Durations are in milliseconds.
Variable | Default |
| CATS vehicle positions feed |
| CATS trip updates feed |
| CATS alerts feed |
| CATS static GTFS zip |
|
|
|
|
|
|
|
|
|
|
Feed URLs must be http or https; anything else is rejected at startup.
Layout
Module | Role |
| Environment parsing and validation |
| Bounded, time-limited HTTP fetch |
| TTL cache with single-flight refresh |
| GTFS-flavored CSV reading |
| Static schedule: routes, stops, trips |
| GTFS-Realtime protobuf decoding |
| Domain layer: joins realtime to schedule, resolves queries |
| The three tools' behavior and JSON payloads |
| MCP tool registration and schemas |
| stdio entry point |
Development
.venv/bin/pip install -e '.[dev]'
.venv/bin/pytest # 80 tests, offline against recorded feed fixtures
.venv/bin/mypy # strict
.venv/bin/ruff check .
.venv/bin/ruff format .Tests run against protobuf and GTFS fixtures captured from the live feeds, so they are
deterministic and make no network calls. tests/test_feed_http.py is the exception: it
serves canned responses from a loopback socket so the byte cap and timeout are exercised
for real.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ajwann/CATS-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server