Skip to main content
Glama

prt-mcp

MCP server exposing Pittsburgh Regional Transit (PRT) TrueTime operations as typed tools for agent clients.

What this implements

  • MCP tools for core TrueTime queries:

    • prt_get_routes

    • prt_get_directions

    • prt_get_stops

    • prt_get_vehicles

    • prt_get_predictions

    • prt_get_patterns

    • prt_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/bustimePGH

      • aidan2312/prt-api

Prerequisites

  • Node 20+ (Node 22 recommended)

  • A PRT TrueTime API key

Configuration

Copy env template:

cp .env.example .env

Set:

  • PRT_TRUETIME_API_KEY (required)

  • Optional tuning:

    • PRT_TRUETIME_BASE_URL

    • PRT_REQUEST_TIMEOUT_MS

    • PRT_MAX_RETRIES

    • PRT_USER_AGENT

  • Optional HTTP mode security:

    • HOST (default: 127.0.0.1)

    • PORT (default: 3000)

    • MCP_ALLOWED_HOSTS (comma-separated Host allowlist)

    • MCP_ALLOWED_ORIGINS (comma-separated browser Origin allowlist)

    • MCP_AUTH_TOKEN (Bearer token expected on /mcp)

    • MCP_RATE_LIMIT_WINDOW_MS

    • MCP_RATE_LIMIT_MAX

Run

npm install
npm run build
npm start

For local development:

npm run dev

For hosted HTTP mode (remote MCP endpoint):

npm run build
PORT=3000 HOST=0.0.0.0 MCP_AUTH_TOKEN=change-me npm run start:http

Health check:

curl http://localhost:3000/healthz

MCP endpoint:

  • POST /mcp

  • GET /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 /sse

  • GET /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:

  1. Local stdio (npm start)
    Best for Claude Desktop / local agent tools.

  2. Remote Streamable HTTP (npm run start:http)
    Best for hosting on Render/Railway/Fly.io/a VM.

Minimal production checklist:

  • Set PRT_TRUETIME_API_KEY in host environment variables.

  • Use Node 20+ runtime.

  • Expose the PORT your host provides.

  • Keep at least one health check path (/healthz).

  • Set MCP_AUTH_TOKEN.

  • Set MCP_ALLOWED_HOSTS and MCP_ALLOWED_ORIGINS for your deployment.

  • Restrict inbound access (IP allowlist, auth gateway, or private network).

Example Render/Railway start command:

npm run build && npm run start:http

Docker

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:latest

Implementation best practices applied

  • Input validation with zod on every tool schema

  • Fail-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 isError results

  • Tool annotations (readOnlyHint, idempotentHint, openWorldHint) for client safety hints

  • HTTP 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 tools
prt_get_directionsGet route directionsC
Read-onlyIdempotent

Fetch available directions for a route using getdirections.

ParametersJSON Schema
NameRequiredDescriptionDefault
rtYesRoute code
rtpidatafeedNo

TDQS

C2.6/5.0
Behavior2/5

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.

Conciseness3/5

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.

Completeness2/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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 patternsC
Read-onlyIdempotent

Fetch route geometry/pattern data via getpatterns.

ParametersJSON Schema
NameRequiredDescriptionDefault
rtNoRoute code
pidNoPattern id
rtpidatafeedNo

TDQS

C2.8/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness2/5

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.

Parameters3/5

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.

Purpose3/5

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.

Usage Guidelines2/5

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 predictionsC
Read-onlyIdempotent

Fetch arrival predictions for one or more stop IDs.

ParametersJSON Schema
NameRequiredDescriptionDefault
topNoMaximum predictions to return
stpidYesStop ID(s), comma-separated
rtpidatafeedNo

TDQS

C2.9/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness2/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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 routesC
Read-onlyIdempotent

Fetch route metadata from TrueTime getroutes.

ParametersJSON Schema
NameRequiredDescriptionDefault
rtpidatafeedNoExample: Port Authority Bus or Light Rail

TDQS

C2.9/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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 bulletinsC
Read-onlyIdempotent

Fetch service bulletins using getservicebulletins.

ParametersJSON Schema
NameRequiredDescriptionDefault
rtNoOptional route filter
rtpidatafeedNo

TDQS

C2/5.0
Behavior2/5

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.

Conciseness2/5

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.

Completeness2/5

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.

Parameters2/5

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.

Purpose2/5

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.

Usage Guidelines2/5

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 stopsC
Read-onlyIdempotent

Fetch stops for a route with optional direction using getstops.

ParametersJSON Schema
NameRequiredDescriptionDefault
rtYesRoute code
dirNoINBOUND or OUTBOUND, if supported
rtpidatafeedNo

TDQS

C2.9/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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 vehiclesC
Read-onlyIdempotent

Fetch live vehicle data via getvehicles.

ParametersJSON Schema
NameRequiredDescriptionDefault
rtNoOptional route filter
vidNoOptional vehicle id filter
rtpidatafeedNo

TDQS

C2.7/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness2/5

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.

Parameters2/5

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.

Purpose3/5

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.

Usage Guidelines2/5

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.

  1. 7 tool updatesv0.1.0
    • First observedprt_get_directions
    • First observedprt_get_patterns
    • First observedprt_get_predictions
    • First observedprt_get_routes
    • First observedprt_get_service_bulletins
    • First observedprt_get_stops
    • First observedprt_get_vehicles

TDQS

B3.2/5.0

Scored across 7 tools

Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness5/5

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

ActivityInactive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP 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 npm
    MIT
  • F
    license
    A
    quality
    C
    maintenance
    MCP 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
    -