Skip to main content
Glama
ThiagoJacobLannes

carris-metropolitana-mcp

carris-metropolitana-mcp

MCP server for Carris Metropolitana, the bus network of Greater Lisbon (Lisbon Metropolitan Area), so Claude (or any MCP client) can answer "when is the next bus at X?", "where does line 4715 go?" or "is my line disrupted?" in real time.

Read-only, no key, no account. It only reads the official open API. Not affiliated with Carris Metropolitana or TML (Transportes Metropolitanos de Lisboa).

Note: buses inside the city of Lisbon are mostly run by Carris, a different company that this server does not cover. Carris Metropolitana serves the surrounding municipalities and the routes into Lisbon (e.g. from Oriente, Cacilhas, Setúbal).

Tools

find_stops

Find stops by name and/or near a coordinate. Use it first to get a stop_id.

Parameters:

  • query — text in the stop name, accent/case-insensitive (e.g. "Gare do Oriente", "cacilhas terminal"); a 6-digit stop id also works

  • lat / lon — a coordinate; results are sorted by straight-line distance

  • radius_m — only stops within this radius (default 500 m when no query is given)

  • limit — max stops (default 10, max 50)

Each stop has: id, name, locality, municipality, distance, coordinates, the lines serving it, and wheelchair boarding.

next_arrivals

Upcoming buses at a stop, with minutes from now. Each arrival is marked "realtime" (live GPS estimate, with delay vs. timetable) or "scheduled" (timetable only).

Parameters:

  • stop_id — 6-digit stop id (e.g. "060361")

  • line_id — optional, keep only one line (e.g. "2794")

  • limit — max arrivals (default 10, max 50)

line_info

A line's name, municipalities and localities served, and each direction (headsign, first and last stop, number of stops, trips today, first/last departure today). Pass a line number ("4715") or text ("Oriente Setubal"), in which case matching lines are listed. include_stops=True adds the ordered stop list for each direction.

service_alerts

Active (and, by default, upcoming) service alerts: detours, works, strikes, stops not served. Filter by line_id and/or stop_id. Alerts are mostly published in Portuguese.

vehicle_positions

Live positions of the buses currently running on a line: coordinates, heading, speed, headsign, the stop it is at or heading to, and how old the GPS fix is.

All times are Europe/Lisbon. On failure a tool returns {"error": "..."} instead of raising.

Related MCP server: Movilidad Con Prompts

Install

Requires Python 3.10+.

git clone https://github.com/ThiagoJacobLannes/carris-metropolitana-mcp
cd carris-metropolitana-mcp
python -m pip install -e .

On Windows, if you keep code in a cloud-synced folder (Google Drive, OneDrive), create the virtual environment outside it: sync clients drop desktop.ini files into folders, which can crash packages on startup.

Add to Claude Code

claude mcp add carris-metropolitana -- python -m carris_metropolitana_mcp.server

Add to Claude Desktop

Merge claude_desktop_config.snippet.json into your Claude Desktop claude_desktop_config.json, then restart Claude Desktop:

{
  "mcpServers": {
    "carris-metropolitana": {
      "command": "python",
      "args": ["-m", "carris_metropolitana_mcp.server"]
    }
  }
}

Data source

All data comes from the official Carris Metropolitana open API, https://api.carrismetropolitana.pt/v2 (docs, source). It is public, needs no key, and is the same API the operator's own website and apps use. Carris Metropolitana publishes it as open data; its documentation does not state a specific data licence, so check their terms before redistributing the data itself.

The large, slow-changing lists (all ~12,000 stops, ~700 lines, localities, municipalities) are downloaded once and cached in memory for 12 hours; alerts and vehicle positions are cached for 30 seconds. Set CARRIS_METROPOLITANA_BASE_URL to point at a different API base URL.

Real-time estimates only exist for buses that are already running with live tracking; other arrivals are the published timetable. Arrivals cover the current service day only, so late at night the list can be short.

This project is not affiliated with, endorsed by, or connected to Carris Metropolitana or TML.

License

MIT

Available Tools

5 tools
find_stopsA

Find Carris Metropolitana bus stops by name and/or near a coordinate.

Use this first whenever the user names a place, street, station or gives a location, to get the stop_id needed by next_arrivals. Results include the lines serving each stop.

Args: query: Text to match in the stop name (accent/case-insensitive, all words must match), e.g. "Gare do Oriente", "Cacilhas terminal". A 6-digit stop id also works. lat, lon: A coordinate (WGS84). Results are sorted by straight-line distance from it. Note: buses inside Lisbon city are mostly run by Carris (a different company, not covered here); Carris Metropolitana serves the wider region. radius_m: Only stops within this many metres of lat/lon. Default 500 when no query is given; unlimited when a query is given. limit: Max stops to return (default 10, max 50).

NameRequiredDescriptionDefault
latNo
lonNo
limitNo
queryNo
radius_mNo

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses sort order (straight-line distance), the conditional default behaviour of radius_m (500m without query, unlimited with query), the limit cap of 50, and that results include serving lines. It stops short of stating read-only/safety semantics or how matching/pagination behaves at scale, but coverage is unusually strong for an unannotated tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the one-line purpose and the routing instruction, followed by a compact per-parameter block where each line adds distinct information. No filler sentences; even the Carris caveat earns its place by preventing wrong-tool use.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be spelled out, and the description still flags the useful field (serving lines). Combined with full parameter documentation and the coverage caveat, an agent has everything needed to call this correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate and it does: every one of the five parameters is documented with matching semantics (accent/case-insensitive, all words must match, 6-digit stop id accepted), example queries, coordinate system (WGS84), defaults and max values for radius_m and limit.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Find Carris Metropolitana bus stops') plus the two lookup modes (name and/or coordinate). It also names the downstream consumer (next_arrivals) and the sibling relationship, so the tool is distinguishable without opening its schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Use this first whenever the user names a place, street, station or gives a location' and explains why (to get the stop_id needed by next_arrivals). It also adds a when-not caveat: Lisbon city buses are run by Carris, a different company not covered here.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

line_infoA

Details of a Carris Metropolitana line: name, route ends, directions, municipalities.

Use for "where does line 4715 go?", "which bus goes from Cacilhas to Almada?", or to check whether a line passes a stop (include_stops=True). If line is not an exact line number it is treated as text (e.g. "Oriente Setubal") and the matching lines are listed instead. trips_today = 0 means the line does not run today (some lines are seasonal or weekday-only). Departure times past midnight are written "24:30", "25:10" (same service day).

Args: line: 4-digit line number ("4715") or free text matched against line names. include_stops: Also list every stop (id + name) in order for each direction.

NameRequiredDescriptionDefault
lineYes
include_stopsNo

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses the fuzzy-text fallback behavior for a non-exact line number, the meaning of trips_today=0 (seasonal/weekday-only lines), and the past-midnight time convention like "24:30". It stops short of stating what happens on zero matches or any error condition.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the resource and return fields, then use cases, then edge-case behavior, then a clean Args block. Every sentence earns its place; the trips_today and midnight-time notes are non-obvious and directly prevent misreading the response.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return formatting need not be re-explained, and both parameters plus the key behavioral quirks are covered. The remaining gap is failure-mode handling — what an agent should expect when a fuzzy `line` matches nothing at all.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate entirely, and it does via a dedicated Args block: `line` is either a 4-digit number or free text matched against line names, and `include_stops` triggers an ordered per-direction stop listing. Both parameters gain meaning well beyond their bare string/boolean types.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific resource (Carris Metropolitana line) and enumerates exactly what is returned: name, route ends, directions, municipalities. The three example questions make it instantly distinguishable from siblings like next_arrivals or find_stops, which cover timing and stop lookup rather than line metadata.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit use cases are given ("where does line 4715 go?", "which bus goes from Cacilhas to Almada?", "check whether a line passes a stop"), and the include_stops flag is tied to the third case. However, it never names an alternative sibling or an exclusion — e.g. it doesn't say to use find_stops when you already have a stop id and want lines serving it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

next_arrivalsA

Upcoming buses at a Carris Metropolitana stop, real-time where available.

Use for "when is the next bus at X?" (get stop_id from find_stops first). Each arrival says whether it is "realtime" (live GPS estimate) or "scheduled" (timetable only: the bus may not have started its trip yet). minutes_from_now uses the real-time estimate when present. Times are Europe/Lisbon. Only today's operational day is covered, so late at night the list may be short or empty.

Args: stop_id: 6-digit stop id, e.g. "060361". line_id: Optional 4-digit line number to keep only that line, e.g. "2794". limit: Max arrivals to return (default 10, max 50).

NameRequiredDescriptionDefault
limitNo
line_idNo
stop_idYes

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does so: it explains the realtime vs scheduled distinction, that minutes_from_now prefers the live estimate, the Europe/Lisbon timezone, and the operational-day limitation. These are non-obvious behavioral traits an agent could not infer from the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and trigger, then behavioral caveats, then per-parameter notes — good ordering with no filler. The args block slightly restates type information that the schema already encodes, but it is earning its place given the 0% schema coverage.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Output schema exists, so return structure need not be explained, yet the description still clarifies how to read each arrival (realtime flag, scheduled caveat, minutes_from_now). For a 3-parameter read tool with no annotations, nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate and it does: stop_id format (6-digit, example "060361"), line_id (optional 4-digit line, example "2794"), and limit with both default and max (default 10, max 50). That is meaningfully more than the bare schema types.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource+scope: upcoming bus arrivals at a Carris Metropolitana stop, real-time where available. It also names the sibling find_stops as the source of the required stop_id, so the agent can orient without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit trigger phrase ("when is the next bus at X?") plus the prerequisite step (get stop_id from find_stops first). It also flags a boundary condition — late at night the list may be short or empty — which tells the agent when results should be interpreted cautiously.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

service_alertsA

Service alerts (detours, works, strikes, stops not served) for Carris Metropolitana.

Use before recommending a trip, or when the user asks "is line X disrupted?". Filter by line and/or stop; with no filter, returns all current alerts (there are often ~100). Alerts are mostly published in Portuguese only.

Args: line_id: 4-digit line number, e.g. "4407". stop_id: 6-digit stop id, e.g. "160584". include_upcoming: Also return alerts that start in the future (default True). language: Preferred language ("pt" or "en"); falls back to what exists. limit: Max alerts to return (default 20, max 100).

NameRequiredDescriptionDefault
limitNo
line_idNo
stop_idNo
languageNopt
include_upcomingNo

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it warns that an unfiltered call returns ~100 alerts, that alerts are mostly Portuguese-only with a language fallback, and that include_upcoming defaults to True. It omits auth/rate-limit context and does not explicitly state the operation is read-only, which keeps it just short of a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The summary line is front-loaded, followed by usage and a scannable Args block, so an agent can extract what it needs quickly. There is minor redundancy in restating schema defaults (limit 20, include_upcoming True), but the added format examples justify most of the length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be described, and the description covers everything else an agent needs: purpose, trigger conditions, all five parameters with examples, the unfiltered-call volume risk, and the Portuguese-only caveat. Nothing material is missing for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, yet the Args block documents all five parameters with formats and defaults: 4-digit line_id with example "4407", 6-digit stop_id with example "160584", include_upcoming default True, language values pt/en with fallback behavior, and limit default 20 / max 100. This fully compensates for the empty schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence names a specific resource (service alerts) and enumerates its content types (detours, works, strikes, stops not served) for a named operator. That is clearly distinguishable from siblings such as next_arrivals, line_info, and vehicle_positions, which cover schedules, line metadata, and vehicles rather than disruptions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states explicit triggers — "Use before recommending a trip, or when the user asks 'is line X disrupted?'" — and explains the filtering decision (filter by line and/or stop) plus the consequence of not filtering. That covers both when to use it and how to scope the call, leaving little to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

vehicle_positionsA

Live GPS positions of the buses currently running on a Carris Metropolitana line.

Use for "where is the 3508 right now?" or to see how many buses are out on a line. Each vehicle includes its position, heading, speed, the stop it is at / heading to, and how many seconds old the fix is.

Args: line_id: 4-digit line number, e.g. "3508".

NameRequiredDescriptionDefault
line_idYes

Output Schema

NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and does well: it discloses the returned fields (position, heading, speed, current/next stop) and, importantly, that each fix includes its age in seconds. It does not mention authentication or rate limits, but for a read-only live-data lookup the behavioral disclosure is solid.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose, followed by usage examples and a clean Args section. Every sentence adds information, including the freshness indicator, with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter read tool with an output schema already present, the description is complete: it explains what the tool does, when to use it, the parameter format, and the shape of returned vehicle data. Nothing needed to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It does: line_id is documented as a 4-digit line number with the concrete example '3508', which is enough to call the single parameter correctly. It could add minor notes about formatting or route variants, but the essential semantics are covered.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: live GPS positions of buses currently running on a Carris Metropolitana line. It is inherently distinguishable from siblings such as next_arrivals, line_info, and find_stops because it returns vehicle-level positions rather than schedules or stop data.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear usage contexts with examples: 'where is the 3508 right now?' or seeing how many buses are out on a line. It does not explicitly name alternatives or when-not-to-use conditions, but the intended scenarios are clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv0.1.0
    • First observedfind_stops
    • First observedline_info
    • First observednext_arrivals
    • First observedservice_alerts
    • First observedvehicle_positions

TDQS

A4.6/5.0

Scored across 5 tools

Disambiguation5/5

Each tool targets a clearly distinct resource: stop discovery (find_stops), real-time arrivals (next_arrivals), line metadata (line_info), disruptions (service_alerts), and live vehicle tracking (vehicle_positions). The descriptions even prescribe the intended workflow (find_stops → next_arrivals), leaving almost no room for misselection.

Naming Consistency4/5

All names are snake_case, lowercase, and readable, with a mostly noun-phrase convention (line_info, service_alerts, vehicle_positions). Only find_stops is verb-led and next_arrivals is adjective-led, so the pattern isn't perfectly uniform, but it is predictable enough to navigate.

Tool Count5/5

Five tools is a tight, well-scoped surface for a transit data server, and each one covers a genuinely different query type. No tool feels redundant or like filler.

Completeness4/5

The surface covers the core lifecycle: locate a stop, get arrivals, inspect a line, check disruptions, and track vehicles. Gaps remain around journey planning between two points and timetables for future days (only today's operational day is covered), but these are documented limits rather than dead ends agents can't work around.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers