prt-mcp
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., "@prt-mcpwhen is the next bus at stop 20001?"
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.
prt-mcp
MCP server exposing Pittsburgh Regional Transit (PRT) TrueTime operations as typed tools for agent clients.
What this implements
MCP
toolsfor core TrueTime queries:prt_get_routesprt_get_directionsprt_get_stopsprt_get_vehiclesprt_get_predictionsprt_get_patternsprt_get_service_bulletins
MCP
resources:prt://capabilities(discoverability for clients)
MCP
prompts:transit-arrival-workflow
Related MCP server: marta-mcp
Source references used for design
MCP protocol/spec and server guidance:
modelcontextprotocol.io docs + specification (JSON-RPC, capabilities, safety)
MCP reference implementations:
modelcontextprotocol/servers(everything,fetch,filesystem)
MCP SDK patterns:
modelcontextprotocol/typescript-sdk
PRT data/API entry points:
PRT Developer Resources
TrueTime account/API key workflow
TrueTime v3 endpoint behavior from public community codebases:
juctaposed/bustimePGHaidan2312/prt-api
Prerequisites
Node 20+ (Node 22 recommended)
A PRT TrueTime API key
Configuration
Copy env template:
cp .env.example .envSet:
PRT_TRUETIME_API_KEY(required)Optional tuning:
PRT_TRUETIME_BASE_URLPRT_REQUEST_TIMEOUT_MSPRT_MAX_RETRIESPRT_USER_AGENT
Optional HTTP mode security:
HOST(default:127.0.0.1)PORT(default:3000)MCP_ALLOWED_HOSTS(comma-separatedHostallowlist)MCP_ALLOWED_ORIGINS(comma-separated browserOriginallowlist)MCP_AUTH_TOKEN(Bearer token expected on/mcp)MCP_RATE_LIMIT_WINDOW_MSMCP_RATE_LIMIT_MAX
Run
npm install
npm run build
npm startFor local development:
npm run devFor hosted HTTP mode (remote MCP endpoint):
npm run build
PORT=3000 HOST=0.0.0.0 MCP_AUTH_TOKEN=change-me npm run start:httpHealth check:
curl http://localhost:3000/healthzMCP endpoint:
POST /mcpGET /mcp(SSE stream)DELETE /mcp(close session if stateful)Include
Authorization: Bearer <MCP_AUTH_TOKEN>if auth is enabled.
Alternate SSE endpoint for clients that expect /sse:
POST /sseGET /sse(SSE stream)DELETE /sse(close session if stateful)
Example MCP client config (stdio)
{
"mcpServers": {
"prt": {
"command": "node",
"args": ["/ABSOLUTE/PATH/prt-mcp/dist/index.js"],
"env": {
"PRT_TRUETIME_API_KEY": "YOUR_KEY"
}
}
}
}Hosting options
You now have two transport modes:
Local stdio (
npm start)
Best for Claude Desktop / local agent tools.Remote Streamable HTTP (
npm run start:http)
Best for hosting on Render/Railway/Fly.io/a VM.
Minimal production checklist:
Set
PRT_TRUETIME_API_KEYin host environment variables.Use Node 20+ runtime.
Expose the
PORTyour host provides.Keep at least one health check path (
/healthz).Set
MCP_AUTH_TOKEN.Set
MCP_ALLOWED_HOSTSandMCP_ALLOWED_ORIGINSfor your deployment.Restrict inbound access (IP allowlist, auth gateway, or private network).
Example Render/Railway start command:
npm run build && npm run start:httpDocker
Build image:
docker build -t prt-mcp:latest .Run container:
docker run --rm -p 3000:3000 -e PRT_TRUETIME_API_KEY=YOUR_KEY prt-mcp:latestImplementation best practices applied
Input validation with
zodon every tool schemaFail-fast config checks (missing key)
Retry + timeout behavior to handle unstable upstream API responses
Uniform structured + text output for deterministic agent parsing
Tool execution failures returned as MCP
isErrorresultsTool annotations (
readOnlyHint,idempotentHint,openWorldHint) for client safety hintsHTTP hardening: auth token, origin checks, host allowlist support, and rate limiting
Strict TypeScript + minimal surface area
No secrets in source; env-only configuration
Notes
TrueTime key acquisition is account-gated by PRT.
API responses can differ by data feed (
Port Authority Bus,Light Rail).Some endpoints require additional filters (
rt,stpid,pid).
Available Tools
7 toolsprt_get_directionsGet route directionsCRead-onlyIdempotent
Fetch available directions for a route using getdirections.
| Name | Required | Description | Default |
|---|---|---|---|
| rt | Yes | Route code | |
| rtpidatafeed | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and openWorldHint, so safety is covered. The description adds nothing beyond that — it doesn't clarify what 'directions' means (direction of travel vs. turn-by-turn navigation) or what the response contains.
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?
A single short sentence, reasonably front-loaded, but 'using getdirections' is redundant with the tool name and consumes part of an already very thin description.
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 read-only lookup with one undocumented parameter and no output schema, the description should at least say what 'directions' returns and what the data-feed parameter does. Neither is provided, leaving the agent under-informed.
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 only 50%: 'rt' is documented as 'Route code' but 'rtpidatafeed' is undocumented in both schema and description. The description does not mention any parameters, so it fails to compensate for the coverage gap.
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 (Fetch) and resource (directions for a route), which is enough to distinguish it from sibling tools like get_routes or get_predictions. The trailing 'using getdirections' is redundant restatement and adds no clarity.
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?
There is no guidance on when to use this tool versus siblings such as prt_get_routes or prt_get_patterns, nor any prerequisites. The agent must infer that this returns directional variants of a route.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
prt_get_patternsGet route patternsCRead-onlyIdempotent
Fetch route geometry/pattern data via getpatterns.
| Name | Required | Description | Default |
|---|---|---|---|
| rt | No | Route code | |
| pid | No | Pattern id | |
| rtpidatafeed | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, openWorldHint, and idempotentHint, so the safety and repeatability profile is covered. The description adds only the nature of the returned data (geometry/pattern), saying nothing about auth, rate limits, or payload shape, which is a modest contribution given the annotation coverage.
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?
A single short sentence that front-loads the verb and resource. It is efficient, though the trailing "via getpatterns" is redundant with the tool name and earns no 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?
With no output schema and 0 required parameters across three params, the description should say what the returned pattern/geometry data looks like and when the optional filters matter. As written, an agent cannot tell what a pattern record contains or why it might omit rt/pid.
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 67%: rt and pid are documented in the schema while rtpidatafeed is not described anywhere. The description contributes no parameter meaning at all, so it neither compensates for the undocumented parameter nor adds beyond the schema — baseline territory for a partially-covered 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?
The description names a specific verb ("Fetch") and resource ("route geometry/pattern data"), which is more informative than the bare name. However, "via getpatterns" merely restates the tool name, and it does not distinguish patterns from adjacent siblings like prt_get_routes or prt_get_directions.
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?
There is no when-to-use guidance, no statement of prerequisites, and no reference to an alternative among the six sibling tools. The agent is left to infer that this is the pattern/geometry-specific variant on its own.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
prt_get_predictionsGet stop predictionsCRead-onlyIdempotent
Fetch arrival predictions for one or more stop IDs.
| Name | Required | Description | Default |
|---|---|---|---|
| top | No | Maximum predictions to return | |
| stpid | Yes | Stop ID(s), comma-separated | |
| rtpidatafeed | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, openWorldHint, and idempotentHint, covering the basic safety profile. The description adds no further behavioral context such as rate limits, real-time data freshness, or return format.
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 a single front-loaded sentence with no redundant or wasted words. It is appropriately sized for a concise statement of purpose.
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 prediction tool with no output schema, the description omits the return format, data freshness, and the purpose of the undocumented rtpidatafeed parameter. While annotations cover safety, the definition leaves significant gaps for an agent to fully leverage the tool.
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 67% (2 of 3 parameters documented), so the baseline is 3. The description's mention of 'one or more stop IDs' mirrors the schema's own description for stpid and adds no new semantic detail, while rtpidatafeed remains undocumented.
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 verb ('Fetch') and resource ('arrival predictions') scoped to 'stop IDs,' making the tool's purpose clear. It does not explicitly differentiate itself from the six sibling tools, so it falls short of a 5.
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?
There is no guidance on when to use this tool versus alternatives, nor any exclusions or prerequisites. The phrase 'for one or more stop IDs' provides minimal context but does not constitute usage guidelines.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
prt_get_routesGet PRT routesCRead-onlyIdempotent
Fetch route metadata from TrueTime getroutes.
| Name | Required | Description | Default |
|---|---|---|---|
| rtpidatafeed | No | Example: Port Authority Bus or Light Rail |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and openWorldHint, so the safety profile is covered externally. The description adds only the upstream service name and discloses nothing about return shape, datafeed scoping behavior, or result size; with annotations doing the heavy lifting this is minimal added value.
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?
A single front-loaded sentence with no filler, clearly leading with the action and resource. It is efficient, though extremely terse.
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 read-only, one-optional-param tool with no output schema, the description is minimally adequate. It still leaves unclear what 'route metadata' contains and how omitting rtpidatafeed affects results, but the annotations and schema cover the essentials.
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 100% and there is a single optional parameter, so the schema already documents rtpidatafeed with an example. The description adds no syntax or semantic detail beyond the schema, which is the baseline expectation.
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 (Fetch) and resource (route metadata) plus the upstream source (TrueTime getroutes), so the purpose is unmistakable. However, it offers no differentiation from siblings like prt_get_directions or prt_get_stops, which an agent must infer from the resource noun.
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?
There is no guidance on when to use this tool versus the many sibling get_* tools, nor any stated prerequisites or context of use. The agent must guess from the name alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
prt_get_service_bulletinsGet service bulletinsCRead-onlyIdempotent
Fetch service bulletins using getservicebulletins.
| Name | Required | Description | Default |
|---|---|---|---|
| rt | No | Optional route filter | |
| rtpidatafeed | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, openWorldHint, and idempotentHint, so the safety profile is covered. The description adds nothing beyond that: no return format, no rate limits, no note on what 'openWorld' means for the upstream feed, and no hint about how the two parameters interact.
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?
It is one short sentence, but the trailing 'using getservicebulletins' is pure redundancy that adds no value. Brevity here stems from under-specification rather than disciplined editing.
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?
With two undocumented-in-description parameters, one unexplained param name, no output schema, and a feed-based open-world data source, the definition leaves the agent without enough context to call the tool confidently. The surrounding structure does not fill the 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?
Schema description coverage is only 50% – 'rt' is documented as 'Optional route filter' but 'rtpidatafeed' has no description at all. The description text provides no compensating detail, so the meaning of rtpidatafeed remains entirely opaque.
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 essentially restates the name and title: 'Fetch service bulletins using getservicebulletins' adds no information beyond the tool identifier. It does not differentiate this tool from siblings such as prt_get_routes, prt_get_predictions, or prt_get_stops, nor does it clarify what a 'service bulletin' is in this API's context.
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?
There is no guidance on when to use this tool versus the six sibling getters, no mention of prerequisites, and no note on whether filters are applied server-side. The only implicit signal is that it retrieves bulletins, which the name already conveys.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
prt_get_stopsGet route stopsCRead-onlyIdempotent
Fetch stops for a route with optional direction using getstops.
| Name | Required | Description | Default |
|---|---|---|---|
| rt | Yes | Route code | |
| dir | No | INBOUND or OUTBOUND, if supported | |
| rtpidatafeed | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and openWorldHint, so the safety profile is covered. The description adds nothing beyond them: 'optional direction' merely restates the schema's own 'if supported' note, and there is no mention of return format, empty-result behavior, or feed selection.
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?
One short, front-loaded sentence that puts the resource first. The only waste is the 'using getstops' clause, which is redundant internal jargon.
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 read-only lookup with no output schema, the definition is minimally adequate, but the undocumented 'rtpidatafeed' parameter and the absence of any guidance on direction resolution or result shape leave clear gaps for an agent.
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 67%, so most parameters are self-documented. The description names route and direction but says nothing about 'rtpidatafeed', the one parameter lacking a schema description, so it only partially compensates for the gap. Baseline 3 is appropriate.
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 verb ('Fetch') and resource ('stops for a route'), which cleanly separates it from siblings like get_routes, get_directions, and get_vehicles. The trailing 'using getstops' is a redundant API-method reference that adds no distinguishing value but does not obscure the purpose.
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?
There is no guidance on when to use this tool versus alternatives, nor any mention that direction values may need to be resolved via prt_get_directions first. Usage is only implied by the resource name, leaving the agent to infer the context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
prt_get_vehiclesGet active vehiclesCRead-onlyIdempotent
Fetch live vehicle data via getvehicles.
| Name | Required | Description | Default |
|---|---|---|---|
| rt | No | Optional route filter | |
| vid | No | Optional vehicle id filter | |
| rtpidatafeed | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, openWorldHint, and idempotentHint, so the safety and side-effect profile is covered. The description adds only that the data is "live," which is a small but real piece of context; it says nothing about rate limits, refresh cadence, or return volume.
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?
A single short sentence, front-loaded with the core action, with no wasted preamble. The trailing "via getvehicles" is redundant with the tool name and marginally costs it a point.
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 three-parameter tool with an undocumented parameter, no output schema, and no annotation-independent behavior notes, the description is far too thin. It does not tell the agent what the live feed returns or how filtering params interact.
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 67%: rt and vid are documented as optional filters, but rtpidatafeed has no description anywhere. The description adds no parameter meaning at all, so it fails to compensate for the uncovered parameter.
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 verb and resource ("Fetch live vehicle data"), and the resource name does loosely distinguish it from siblings like get_routes or get_predictions. However, it leans on the tool name and appends a redundant API endpoint reference ("via getvehicles") instead of clarifying scope, so it is vague rather than specific.
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?
No guidance on when to use this versus the six sibling prt_get_* tools, no mention of prerequisites, filters, or common workflows. The agent must infer that this is the vehicle-position endpoint purely from the name.
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.
7 tool updates
v0.1.0- First observed
prt_get_directions - First observed
prt_get_patterns - First observed
prt_get_predictions - First observed
prt_get_routes - First observed
prt_get_service_bulletins - First observed
prt_get_stops - First observed
prt_get_vehicles
TDQS
Scored across 7 tools
Each tool targets a distinct TrueTime data endpoint: routes, directions, stops, vehicles, predictions, patterns, and service bulletins. The nouns alone make the intended data type unambiguous, so an agent can reliably select the correct tool.
Every tool follows the same prt_get_<plural_noun> snake_case pattern with a shared prt prefix. There are no deviations in verb style, casing, or structure, making the set highly predictable.
Seven read-only tools map cleanly to the seven TrueTime API endpoints. The count is well-scoped for a transit data wrapper, with no redundant or filler tools.
The surface covers the full TrueTime read-only endpoint set: routes, directions, stops, vehicles, predictions, patterns, and service bulletins. All standard transit-data queries are reachable with no obvious dead ends for the stated purpose.
Maintenance
Related MCP Connectors
- UnifAPIOAuthcom.unifapi
Hosted MCP server for live public-data APIs and Skills for AI agents.
MCP server exposing the Backtest360 engine API as tools for AI agents.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
POC MCP server. Tool say_hello returns 'Welcome' (agent -> MCP -> API path).
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMCP server that provides tools for querying live transit data (stops, departures, routes, vehicles, alerts) from any WP GTFS Pro site, enabling AI assistants to answer rider questions.11 npmGPL 2.0
- AlicenseAqualityBmaintenanceMCP server for Atlanta MARTA real-time transit data, enabling queries about train arrivals and bus positions via natural language.4MIT
- AlicenseNot gradedqualityDmaintenanceMCP server that integrates with the transport12 API to provide tools for searching stops, routes, arrivals, and vehicle forecasts, enabling natural language interaction with public transport data.4 npmMIT
- FlicenseAqualityCmaintenanceMCP server for St. Louis transit developer tooling, providing 45 tools for GTFS and GTFS-Realtime inspection, feed surveillance, schedule and arrival queries, assertions, drift detection, and golden-fixture generation for the Light Phone 3 Kotlin transit app, all backed by public unauthenticated sources.45-