TfNSW Trip Planner MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@TfNSW Trip Planner MCP ServerPlan a trip from Central Station to Circular Quay at 5pm"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
TfNSW Trip Planner MCP Server
An MCP server exposing the
Transport for NSW trip planning APIs to
LLM clients, built on the
tfnsw-trip-planner library.
Ten tools cover stop search, journey planning, live departure boards, service alerts, nearby-stop lookup and live vehicle positions.
Authentication
The server stores no credentials. Every caller supplies their own TfNSW Open Data API key on every request:
X-API-Key: <your TfNSW API key>Get a free key from the TfNSW Open Data portal.
A request without the header gets an error naming the header rather than a
silent failure. apikey <key> and Bearer <key> forms are accepted too, since
TfNSW's own docs use the former.
Each tool call builds a client from that request's key and discards it when the call returns, so one caller's key is never reused for another's request.
HTTP connections are nevertheless pooled process-wide, which cuts roughly
93ms — a TCP and TLS handshake — off every call. The split is deliberate: a
connection pool is keyed by host, not by credential, so it can be shared safely,
whereas the library writes the API key into session.headers and a shared
session would let one caller's key overwrite another's mid-flight.
Related MCP server: Transport NSW API Client MCP
Endpoints
Path | Purpose |
| Streamable HTTP transport — use this |
| Legacy SSE transport, for clients that need it |
| Unauthenticated liveness probe |
| Service description |
Listens on 0.0.0.0:6401; override with the HOST and PORT environment
variables.
Connecting a client
Claude Code
Header support is built in:
claude mcp add --transport http tfnsw https://your-host/mcp --header "X-API-Key: YOUR_KEY"Claude Desktop
Claude Desktop's (and claude.ai's) native "Add custom connector" UI accepts a
URL and OAuth credentials only — it has no field for a custom header, so it
cannot be used with this server. Connect through the
mcp-remote bridge instead (needs Node):
{
"mcpServers": {
"tfnsw": {
"command": "npx",
"args": [
"mcp-remote",
"https://your-host/mcp",
"--transport", "http-only",
"--header", "X-API-Key:${TFNSW_KEY}"
],
"env": { "TFNSW_KEY": "YOUR_KEY" }
}
}
}mcp-remote needs Name:value with no space after the colon. Config lives at
~/Library/Application Support/Claude/claude_desktop_config.json on macOS, or
%APPDATA%\Claude\claude_desktop_config.json on Windows. Restart the app after
editing.
For a client that only speaks the older transport, point it at /sse and pass
--transport sse-only.
Tools
plan_trip takes plain place names and resolves them itself. The other tools
are addressed by numeric stop ID, so resolve a name with find_stop or
best_stop first and pass the ID onwards.
Tool | What it does |
| Search stops, wharves, POIs and addresses by name |
| Look up one stop by its numeric ID |
| Return only the single best-matching location for a name |
| Plan a journey between two place names or stop IDs |
| Plan a journey starting from a GPS coordinate |
| Plan a cycling route, optionally mixed with transit |
| Live departure board for a stop or platform |
| Service alerts: disruptions, trackwork, planned changes |
| Stops and POIs near a coordinate, with distances |
| Live GPS positions of vehicles on a network |
Notes:
Times. Tools taking a
whenaccept ISO 8601, e.g.2026-08-30T09:15. Without an offset the value is Australia/Sydney local time. An unparseable value is rejected rather than ignored.Capped results.
get_alerts,find_nearbyandget_vehicle_positionscan each answer with far more than a caller can use — an unfiltered alert fetch returns every alert in NSW (~280, 1.3MB of JSON), and a 500m nearby search can return 600+ locations. They take amax_results(20, 50 and 100 respectively), constrained to>= 1.This is not only about context budget: MCP sends each result twice (as text content and as structured content) and clients cap a single SSE event at 1MiB, so an oversized reply fails outright with "SSE stream ended without a response".
Place names.
plan_tripacceptsorigin/destinationas free text ("100 Harris Street Pyrmont", "Bondi Junction") and resolves them server-side, so a model does not spend twobest_stopround trips before planning can start. Passorigin_id/destination_idinstead when you already hold an ID — giving both for the same end is an error rather than a silent preference. A resolved result echoes what each name matched:{"origin": {"id": "...", "name": "100 Harris St, Pyrmont"}}Resolution refuses a weak match rather than guessing. TfNSW's stop finder practically never returns nothing — it returns its nearest guess with a score, and
"Zzzqqxnowhere Placeton"really does resolve to Iceton Pl, Yass. Measured on the live API, real places score 250 (street addresses) to 996 (stations) while nonsense scores 47–154, so anything under 200 is rejected with an error naming the candidate it declined to use.origin_typedefaults to"any", not"stop". An address resolves to astreetID:...ID of typesinglehouse, and planning it withtype_origin=stopreturns zero journeys — verified against the live API. The old"stop"default silently failed every address-to-address trip.Journey detail. The journey tools take a
detaillevel, because the raw response is overwhelmingly data a model never reads. Journey totals (departure,arrival,duration_min,changes,via) are always present, so the commonest questions need no leg data at all.detailincludes
real Pyrmont → Engadine trip
answertotals only
1.4 KB
summary(default)+ legs: times, route, platform, alerts
11.4 KB
stops+ intermediate stop names
—
full+ route polyline
—
Every result reports the level it used, so a model can see it was trimmed and ask for more rather than assuming the data does not exist.
fullonly fits when paired withmax_resultsof 1–3.Legs are built from an allowlist, not by dropping known-bad fields. The old blocklist removed
coordsandstop_sequenceand passed everything else through, including the raw upstreampropertiesbag — lift equipment heights,AREA_NIVEAU_DIVA,areaGid,pbyb, and the platform name repeated under three separate keys. On a real trip that passthrough was 28% of the payload. An allowlist cannot regress that way when TfNSW adds a field. Also:the four planned/estimated time fields collapse to the time that will actually happen, plus
scheduled_*only when there is a real delay (20 of 22 timed stops on a real trip had the two exactly equal);null times, empty alert lists and the empty
transportationblock that every walking leg carries are omitted rather than serialized;stop IDs are echoed for chaining only when they are real stop IDs, not 129-byte composite
streetID:...blobs that no other tool accepts.
Together with the resolution above, the question "when do I arrive at 32 Geelong Rd if I leave now from 100 Harris Street Pyrmont" went from 3 calls and ~10,800 tokens to 1 call and ~356 tokens (
detail="answer").find_stoptakeslimit, notmax_results— deliberately a different name, because it bounds the upstream query rather than truncating a fetched list, socountis exact and no bandwidth is wasted.get_vehicle_positionsreads the GTFS-Realtime feed, which is a separate product on the Open Data portal — your key must be subscribed to it as well. An unrecognisedmodeis rejected with the list of valid feeds rather than being passed through to an opaque upstream 404. The nine valid feeds arebuses,sydneytrains,metro,nswtrains,ferries/sydneyferries,lightrail/cbdandsoutheast,lightrail/innerwest,lightrail/newcastleandlightrail/parramatta.The list is taken from the library rather than restated here, so the two cannot drift. TfNSW serves
sydneytrainsandlightrail/innerwestfrom a v2 endpoint and the rest from v1;tfnsw-trip-planner1.4.0 routes each feed to the version that actually serves it.
Results are returned as structured JSON. Every list-returning tool answers with
the same shape, so returned is always present and count is always the true
total before any capping:
{"count": 618, "returned": 50, "locations": [...]}Single lookups (find_stop_by_id, best_stop) return {"location": {...}},
or {"location": null} when nothing matches.
Running it
Docker (how it is deployed)
docker compose up --build -dcurl -sf localhost:6401/healthSee DEPLOYMENT.md for the Coolify setup.
Local development
uv syncuv run python -m tfnsw_trip_planner_mcpStdio (for local process-spawning clients and directory scanners)
MCP_TRANSPORT=stdio uv run python -m tfnsw_trip_planner_mcpThere are no HTTP headers under stdio, so every tool call fails with a missing-API-key
error — this mode only serves the initialize/tools-list handshake (e.g. for MCP
directory scanners that spawn the container and speak stdio rather than HTTP). The
default deployment above (Docker/Coolify, /mcp and /sse) is unaffected either way.
Tests
The default suite is fully offline — the library client is mocked, so no key is needed and no request leaves the machine:
uv run pytestSmoke tests against the real API are opt-in and skipped unless a key is present:
TFNSW_API_KEY=your_key uv run pytest -m liveCI
GitHub Actions runs on every push and pull request: ruff, the offline suite,
and a Docker job that builds the image, waits for /health, and checks the
running container lists all 10 tools.
The live tests run on main and on manual dispatch. They skip themselves
unless a TFNSW_API_KEY repository secret exists, so CI is green without one —
add it under Settings → Secrets and variables → Actions to enable them. Fork
pull requests never receive the secret, so they always skip.
Layout
File | Role |
| The 10 tools and their argument mapping |
|
|
| Library dataclasses → JSON-safe structures |
| ASGI app wiring both transports plus |
Available Tools
10 toolsbest_stopA
Return only the single best-matching location for a name.
A shortcut for find_stop when you just need one stop ID and do not want to
weigh alternatives. Returns `{"location": null}` if nothing matches.
Args:
query: The place name to resolve, e.g. "Bondi Junction".
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the full burden. It discloses the return behavior (including null case) but does not explicitly state side effects or whether the operation is read-only; however, the wording strongly implies a lookup.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is succinct, with no filler, and front-loads the core purpose while keeping parameter details in a natural place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple lookup tool, it covers the main context: purpose, when to use, parameter meaning, and return null behavior. It does not mention edge cases or errors, but the scope is narrow enough that this is not a significant gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides only a title for 'query', but the description adds a meaningful explanation with an example ('The place name to resolve, e.g. "Bondi Junction"'), compensating for the otherwise bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Return'), a specific resource ('single best-matching location for a name'), and explicitly contrasts with the sibling 'find_stop', so its purpose is immediately clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use it ('when you just need one stop ID and do not want to weigh alternatives') and names the alternative tool 'find_stop', leaving no ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_nearbyA
Find stops and points of interest near a GPS coordinate.
Each result carries its distance in metres from the coordinate. A dense area
can return hundreds of locations within 500m, so results are capped:
`count` is the true total and `returned` is how many are included. Narrow
`radius_m` rather than raising `max_results` to get more relevant results.
Args:
latitude: Latitude in decimal degrees, e.g. -33.8613.
longitude: Longitude in decimal degrees, e.g. 151.2107.
radius_m: Search radius in metres.
type_1: TfNSW result category. "GIS_POINT" covers stops and POIs.
draw_class: Optional TfNSW sub-category filter.
max_results: Maximum locations to return.
| Name | Required | Description | Default |
|---|---|---|---|
| type_1 | No | GIS_POINT | |
| latitude | Yes | ||
| radius_m | No | ||
| longitude | Yes | ||
| draw_class | No | ||
| max_results | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description explains that results are capped, with count representing the true total and returned the included count. It also recommends adjusting radius_m over max_results, giving insight into expected behavior. Since no annotations are provided, this textual description is the sole source and is fairly informative.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise but includes necessary behavioral details and a clear parameter list. It is well-organized, with the main purpose first, then capping explanation, then parameter descriptions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the tool's purpose, key behaviors (capping, distance in results), and parameter meanings. It does not mention error handling or authentication, but for a simple geospatial search tool this is sufficient. The output schema is referenced indirectly via count and returned.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description includes an Args section that gives meaning to all parameters: latitude/longitude as coordinates, radius_m as search radius, type_1 as TfNSW category, draw_class as optional sub-category filter, and max_results as maximum returned items. This adds value beyond the schema, which only has types and defaults.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it finds stops and points of interest near a GPS coordinate, which distinguishes it from sibling tools like find_stop (likely by name) and find_stop_by_id (by ID).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides usage guidance by advising to narrow radius_m rather than raising max_results for better relevance, and explains the result capping behavior. It does not explicitly contrast with all siblings, but the purpose is clear enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_stopA
Search for stops, stations, wharves, points of interest and addresses by name.
Use this to turn a place name into the stop ID that the trip planning and
departure tools require.
Args:
query: What to search for, e.g. "Circular Quay" or "Town Hall Station".
location_type: Restrict results — "any", "stop", "platform", "poi",
"address", "street" or "locality".
limit: Ask the TfNSW API to return at most this many matches. Unlike
max_results on the capped tools, this bounds the upstream query
rather than truncating a fetched list, so `count` is exact.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes | ||
| location_type | No | any |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses some behavioral detail (e.g., limit bounds the upstream query so count is exact) but does not mention side effects, permissions, or rate limits. With no annotations to rely on, the description carries the full burden but only partially covers behavioral traits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and well-organized, with a clear opening sentence, a purpose statement, and a structured list of parameter explanations. No redundant or irrelevant information is present.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple search tool, the description fully covers the required input and purpose. The existence of an output schema means return values need not be described, and the note about 'count is exact' provides a relevant behavioral detail without over-explaining.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
All three parameters (query, location_type, limit) are explicitly described with examples and allowed values, compensating fully for the lack of schema descriptions. The explanation of limit's distinct behavior adds meaningful context beyond the parameter names.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'Search' and the resource 'stops, stations, wharves, points of interest and addresses', and explains the primary purpose of converting a place name into a stop ID for use by trip planning and departure tools. This makes the tool's function unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit guidance on when to use the tool ('Use this to turn a place name into the stop ID...') and includes a helpful note about the limit parameter differing from max_results in other tools. This gives the agent clear direction on selection and parameter behavior.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_stop_by_idA
Look up a single stop by its numeric TfNSW stop ID.
Returns `{"location": null}` if no stop carries that ID.
Args:
stop_id: The stop ID, e.g. "10101331".
| Name | Required | Description | Default |
|---|---|---|---|
| stop_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description implies a read-only operation through the verb 'look up' and explicitly describes the not-found behavior (returning null). While it doesn't state side effects explicitly, the nature of the operation makes it clear that it does not modify data, covering the essential behavioral trait.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise, using two sentences and a simple args list. It conveys the essential information without unnecessary verbosity, making it easy for an agent to parse.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the not-found case but omits the structure of a successful return. It does not specify what fields the returned stop object contains, which could be necessary for downstream processing. The availability of an output schema is noted in context but not detailed in the description, leaving a gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter stop_id is described with an example ('e.g., 10101331') and clarified as a 'numeric TfNSW stop ID'. This adds meaningful context to the schema, which only indicates a string type, by implying the expected format and purpose.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the specific action: looking up a single stop by its numeric TfNSW stop ID. It distinguishes this tool from the sibling tools by emphasizing the use of the numeric ID, making the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description does not provide explicit guidance on when to use this tool versus alternatives like find_stop or best_stop. It implies that having a numeric stop ID is the trigger, but it does not contrast with other tools or explain scenarios where this tool is preferred.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_alertsA
Retrieve service alerts: disruptions, trackwork and planned changes.
Pass a stop_id whenever you can. A network-wide fetch returns every alert in
NSW — hundreds of them — so results are capped: `count` is the true total
and `returned` is how many are included.
Args:
when: Optional ISO 8601 date/time to check alerts for; Sydney local time
if no offset given. Defaults to now.
stop_id: Restrict to alerts affecting one stop. Omit for network-wide.
current_only: Only alerts in effect now. Set false to include future ones.
max_results: Maximum alerts to return.
| Name | Required | Description | Default |
|---|---|---|---|
| when | No | ||
| stop_id | No | ||
| max_results | No | ||
| current_only | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses that results are capped with count and returned fields, explains timezone handling for the when parameter, and notes default behaviors. It does not mention authentication or rate limits, but for a read-only retrieval tool, this is acceptable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and well-structured. It opens with a one-line purpose, gives a critical usage hint, then lists args in a clear block. Every sentence adds value; no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
All four parameters are covered with practical detail, the cap behavior and return fields are explained, and the timezone handling is specified. Given the output schema exists (which presumably details the full response), this description is complete enough for an agent to call correctly without further clarification.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. Each parameter is clearly explained: when (ISO 8601 format, default now, timezone), stop_id (restrict to stop or omit), current_only (effect period), max_results (cap). This adds significant meaning beyond the bare schema definitions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool retrieves service alerts (disruptions, trackwork, planned changes) for NSW, which is a specific verb-resource pairing. It is easily distinguishable from sibling tools like get_departures or plan_trip, which serve different purposes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear guidance on when to pass a stop_id versus omitting it for network-wide fetch, and explains the current_only parameter's effect. While it doesn't explicitly name alternative tools (there are none for alerts), it gives solid context on usage scenarios, so no exclusion guidance is needed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_departuresA
List upcoming departures from a stop — the live departure board.
Args:
stop_id: Stop ID to read departures for. Resolve names with find_stop.
when: Optional ISO 8601 date/time to board from; Sydney local time if no
offset given. Defaults to now.
platform_id: Restrict to a single platform or stand.
realtime: Include live delay information alongside scheduled times.
| Name | Required | Description | Default |
|---|---|---|---|
| when | No | ||
| stop_id | Yes | ||
| realtime | No | ||
| platform_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full responsibility for disclosing behavior. It does not mention whether the operation is read-only, whether it has side effects, or any potential failure modes. This omission leaves the agent uncertain about safety.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is succinct and well-structured, with a clear opening sentence and a neat bullet-like list of parameters. No redundant information is included, and every word adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that an output schema is present, the description does not need to explain return values. It provides sufficient context about the input parameters and the core functionality. However, it could mention that the output is a list of departures, but that is already implied by the purpose.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The Args section covers all four parameters with concise explanations: stop_id (required, resolve names), when (optional, ISO 8601, Sydney time), platform_id (restrict), and realtime (include live delays). This provides meaningful context beyond the raw schema, though it could elaborate on edge cases or default behaviors.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: listing upcoming departures from a stop and referring to it as the live departure board. It uses a specific verb ('List') and resource ('departures from a stop'), but does not explicitly differentiate from sibling tools like plan_trip or get_vehicle_positions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage by requiring stop_id and suggesting resolving names with find_stop. However, it lacks explicit guidance on when to use this tool instead of alternatives, such as when not to use it or what scenarios it is best suited for.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_vehicle_positionsA
Fetch live GPS positions of vehicles currently running on a network.
Unlike the other tools, which return timing estimates, this returns where
each vehicle physically is. Note this feed is a separate product on the
TfNSW Open Data portal — your API key must be subscribed to it as well.
Feeds can carry thousands of vehicles, so results are capped: `count` is the
true feed size and `returned` is how many are included.
Args:
mode: Which feed to read. One of "buses", "sydneytrains", "metro",
"nswtrains", "ferries/sydneyferries", "lightrail/cbdandsoutheast",
"lightrail/innerwest", "lightrail/newcastle",
"lightrail/parramatta".
max_results: Maximum vehicles to return.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | Yes | ||
| max_results | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden. It discloses subscription requirements and the capping behavior with count/returned distinction, which are important runtime behaviors not inferable 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Structured with clear sections. Contains a few extra phrases like 'Unlike...' and 'Note...' that add context but could be tightened. Overall efficient and well-organized.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return format is not required. The description still mentions the count/returned fields and the cap, which are important contextual details. Missing explicit mention of coordinate units or timestamp format, but not critical.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides basic types but lacks enumerations for mode. The description adds the explicit list of allowed modes and explains max_results default, which adds meaning beyond the raw schema. Slight redundancy but helpful.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states the verb 'Fetch' and the resource 'live GPS positions of vehicles'. Explicitly contrasts with sibling tools that return timing estimates, making its unique purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Mentions that it is a separate product requiring subscription and that results are capped. Implies usage when location data is needed, though it does not explicitly enumerate alternative tools. Slight room for more direct 'use when' guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
plan_cycling_tripA
Plan a cycling route, optionally combined with public transport.
Args:
origin_id: Stop ID to start from.
destination_id: Stop ID to finish at.
profile: Route preference — "EASIER" (gentler gradients and quieter
roads), "MODERATE", or "MORE_DIRECT" (fastest, busier roads).
when: Optional ISO 8601 date/time; Sydney local time if no offset given.
bike_only: Cycle the whole way. Set false to allow mixed bike + transit.
max_time_minutes: Reject routes longer than this.
cycle_speed: Assumed cycling speed in km/h.
detail: How much to return per journey. "answer" gives only departure,
arrival, duration, changes and the mode summary — use it for "when
do I get there" and "how long does it take", which is most questions.
"summary" (default) adds the legs, each with its times, route,
platform and any alerts. "stops" adds intermediate stop names.
"full" adds the map polyline and is very large — pair it with
max_results=1 or 2 or the call may exceed the client's size limit.
max_results: Maximum journeys to return.
| Name | Required | Description | Default |
|---|---|---|---|
| when | No | ||
| detail | No | summary | |
| profile | No | MODERATE | |
| bike_only | No | ||
| origin_id | Yes | ||
| cycle_speed | No | ||
| max_results | No | ||
| destination_id | Yes | ||
| max_time_minutes | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden, and it does well: it discloses bike_only behavior, route rejection based on max_time_minutes, assumed cycle_speed, and warns that 'full' detail can exceed client size limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The one-sentence summary is followed by a clean, structured Args list. Every parameter earns its place with concrete meaning, and the detail-level guidance is especially useful without being verbose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 9-parameter tool with no annotations, this description is remarkably complete: it explains all inputs, key behavioral constraints, output detail granularity, and even size-limit implications. The output schema covers return-value structure, so nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description compensates by explaining every parameter: origin/destination stop IDs, profile meanings, ISO 8601 time format with Sydney local time, bike_only, max_time_minutes, cycle_speed, detail levels, and max_results.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Plan a cycling route, optionally combined with public transport.' This clearly distinguishes it from the sibling plan_trip tool by emphasizing cycling and mixed-mode capability.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The context for use is clear: route planning with cycling, optionally including transit. It does not explicitly name sibling alternatives or state when not to use it, so it falls just short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
plan_tripA
Plan a public transport journey between two places.
Prefer passing plain place names as `origin` and `destination` — addresses,
stations, suburbs and landmarks all work, and the server resolves them
itself. You do NOT need to call find_stop or best_stop first; doing so costs
two extra round trips for no benefit. Use `origin_id`/`destination_id` only
when you already hold a stop ID from an earlier call.
Args:
origin: Place to depart from, e.g. "100 Harris Street Pyrmont" or
"Circular Quay". Resolved server-side.
destination: Place to arrive at, e.g. "32 Geelong Rd Engadine".
origin_id: Stop ID to depart from. Alternative to `origin`, not both.
destination_id: Stop ID to arrive at. Alternative to `destination`.
when: Optional ISO 8601 date/time, e.g. "2026-08-30T09:15". Without an
offset this is Sydney local time. Defaults to now.
arrive_by: Treat `when` as the desired arrival time instead of departure.
origin_type: Kind of the origin ID. Leave as "any", which resolves stops,
addresses and POIs alike; "stop" rejects address IDs and returns
nothing for them.
destination_type: Kind of the destination ID. Leave as "any".
realtime: Include live delay information.
wheelchair: Return only wheelchair-accessible journeys.
detail: How much to return per journey. "answer" gives only departure,
arrival, duration, changes and the mode summary — use it for "when
do I get there" and "how long does it take", which is most questions.
"summary" (default) adds the legs, each with its times, route,
platform and any alerts. "stops" adds intermediate stop names.
"full" adds the map polyline and is very large — pair it with
max_results=1 or 2 or the call may exceed the client's size limit.
max_results: Maximum journeys to return.
| Name | Required | Description | Default |
|---|---|---|---|
| when | No | ||
| detail | No | summary | |
| origin | No | ||
| realtime | No | ||
| arrive_by | No | ||
| origin_id | No | ||
| wheelchair | No | ||
| destination | No | ||
| max_results | No | ||
| origin_type | No | any | |
| destination_id | No | ||
| destination_type | No | any |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full responsibility. It discloses server-side resolution of place names, timezone handling for `when`, the size risk of `full` detail, and the effect of `origin_type` rejecting address IDs. This is transparent about behavior without hiding edge cases.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but dense with purpose. It front-loads the key guidance (prefer place names, don't call find_stop) before the parameter list. Each parameter explanation earns its place, though a few could be trimmed (e.g., repeating 'leave as any' for both type params). Overall, it's structured and efficient for the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 12 parameters and an output schema (which covers return structure), the description covers everything an agent needs: how to select inputs, what each option does, and practical cautions like the size limit. No gaps in decision-making or invocation remain.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must explain every parameter, and it does. It clarifies `origin`/`destination` as free-form place names, `when` as ISO 8601 with Sydney local time default, `detail` levels with concrete usage examples, and the mutual exclusivity of ID versus name params. All 12 parameters are meaningfully described.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('Plan a public transport journey') and explicitly distinguishes it from sibling tools by telling the agent not to call find_stop or best_stop first, and by covering place-name vs ID inputs. This makes the tool's purpose unmistakable and separates it from plan_trip_from_coordinate and others.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit guidance on when to use place names versus stop IDs, which alternatives to avoid (find_stop/best_stop), and how to set detail levels based on the question type (e.g., 'answer' for time/duration, 'full' with max_results). This is clear when-to-use and when-not-to-use direction.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
plan_trip_from_coordinateA
Plan a journey starting from a GPS coordinate rather than a stop.
Use this when the starting point is a user's current location or an
arbitrary address, and only the destination is a known stop.
Args:
latitude: Starting latitude in decimal degrees, e.g. -33.8613.
longitude: Starting longitude in decimal degrees, e.g. 151.2107.
destination_id: Stop ID to arrive at.
when: Optional ISO 8601 date/time; Sydney local time if no offset given.
arrive_by: Treat `when` as the desired arrival time.
realtime: Include live delay information.
wheelchair: Return only wheelchair-accessible journeys.
detail: How much to return per journey. "answer" gives only departure,
arrival, duration, changes and the mode summary — use it for "when
do I get there" and "how long does it take", which is most questions.
"summary" (default) adds the legs, each with its times, route,
platform and any alerts. "stops" adds intermediate stop names.
"full" adds the map polyline and is very large — pair it with
max_results=1 or 2 or the call may exceed the client's size limit.
max_results: Maximum journeys to return.
| Name | Required | Description | Default |
|---|---|---|---|
| when | No | ||
| detail | No | summary | |
| latitude | Yes | ||
| realtime | No | ||
| arrive_by | No | ||
| longitude | Yes | ||
| wheelchair | No | ||
| max_results | No | ||
| destination_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the full burden. It explains query behavior and warns that 'full' detail can exceed client size limits, but it does not mention side effects, read-only status, or failure modes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence adds necessary parameter or usage context; no filler or repetition beyond the useful 'rather than a stop' distinction.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema is present and the description further explains the different detail levels and size implications, so an agent has enough context to invoke the tool and interpret results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although the schema has no per-parameter descriptions, the tool description individually explains every parameter, including units, defaults, ISO 8601 format, boolean semantics, the detail hierarchy, and the max_results caveat.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Begins with a specific verb and resource ('Plan a journey starting from a GPS coordinate') and explicitly contrasts with planning from a stop, distinguishing it from sibling plan_trip.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
States an explicit condition: use when the starting point is a current location or arbitrary address and the destination is a known stop; the 'rather than a stop' contrast provides a clear alternative.
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.
10 tool updates
v0.1.0- First observed
best_stop - First observed
find_nearby - First observed
find_stop - First observed
find_stop_by_id - First observed
get_alerts - First observed
get_departures - First observed
get_vehicle_positions - First observed
plan_cycling_trip - First observed
plan_trip - First observed
plan_trip_from_coordinate
TDQS
Scored across 10 tools
Most tools target clearly separate actions: searching stops, planning trips, departures, alerts, and vehicle positions. The main ambiguity is best_stop, which largely duplicates find_stop in shortcut form, and plan_trip_from_coordinate overlaps somewhat with plan_trip, though the descriptions clarify the intended input types.
Tool names generally follow a verb_noun pattern: find_stop, plan_trip, get_departures, get_alerts, find_nearby. The one outlier is best_stop, which uses an adjective instead of a verb, and plan_trip_from_coordinate / plan_cycling_trip are longer but still consistent in style.
Ten tools is a well-scoped size for a transit trip planning server. Each tool addresses a distinct need—stop resolution, journey planning, departures, alerts, vehicle positions, cycling, and nearby search—without redundancy or bloat.
The server covers the core domain well: stop lookup, trip planning, departures, alerts, vehicle positions, and nearby discovery. Minor gaps exist, such as no coordinate-to-coordinate trip planning or detailed route/stop schedule exploration, but agents can accomplish most user journeys with the available tools.
Maintenance
Related MCP Connectors
The Ferryhopper MCP server is a connector for LLMs and AI Agents in maritime travel that exposes ferry routes, schedules, and booking options. It enables AI assistants to search ports and connections across 33 countries and 190+ ferry operators, provide real-time ferry itineraries with indicative prices, and assist users with planning island-hopping or multi-leg journeys by processing natural language queries about ferry times, passenger counts, and travel durations.
- geoOAuthco.thinair
Geocoding, routing, isochrones, traffic, weather, and place search for AI agents. 19 MCP tools.
Gateway between LLM agents and world data through eight tools and a bundled endpoint catalog.
Turn any task into the right API calls: discover, evaluate, and integrate public APIs.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceThis server enables large language models to access and interact with real-time transport alerts from Transport for NSW's network, supporting filtering by transport mode and returning formatted alert information about disruptions and planned works.8 npm7MIT
- AlicenseBqualityDmaintenanceAn MCP service for interacting with Transport NSW's API that enables users to find transport stops around locations and retrieve information about transport alerts and disruptions.3MIT
- FlicenseNot gradedqualityDmaintenanceProvides access to the TripGo API for multi-modal trip planning, public transport departures, and transport-related location searches. It enables users to calculate routes and retrieve travel information through the Model Context Protocol.5-
- AlicenseBqualityDmaintenanceProvides real-time Transport for London journey data, including routes, alerts, and disruptions, allowing AI assistants to search journeys and get station information.120 npm4ISC