transit
transit-mcp-server
511.org SF Bay Open Data のトランジットAPI用MCPサーバー。LLMにベイエリアのリアルタイム交通データ(事業者、路線、停留所、リアルタイムの発車情報、車両位置、運行障害情報)を提供します。BART、Muni、AC Transit、Caltrain、VTA、その他511に報告しているすべての事業者に対応しています。
ツールは6つ、すべて読み取り専用です。
要件
Node.js 18+
https://511.org/open-data/token から取得できる無料の511 APIトークン
Related MCP server: mcp-bart
インストール
npm install
npm run build設定
{
"mcpServers": {
"transit": {
"command": "node",
"args": ["/absolute/path/to/transit-mcp-server/dist/index.js"],
"env": { "TRANSIT_511_API_KEY": "your-token-here" }
}
}
}変数 | 必須 | デフォルト | 目的 |
| はい | — | https://511.org/open-data/token から取得するトークン |
| いいえ |
| APIホストを上書き |
| いいえ |
| リクエストごとのタイムアウト |
| いいえ |
|
|
| いいえ |
| HTTPトランスポートのバインドアドレス |
| ホスト時 | — | エンドポイントを |
| いいえ | localhost + claude.ai | カンマ区切りのオリジン許可リスト |
クォータが主な制約です
511ではキーごとに毎時60リクエストが許可されており、すべてのエンドポイントで共有されます。これは、これらのツールの使い方を左右するほど低い値です。
事業者コードと停留所コードは一度解決したら再利用してください。これらは変わりません。
operator_idを指定しないtransit_list_service_alertsを優先してください。1回の呼び出しで全事業者をカバーします。transit_next_departuresをループでポーリングしないでください。通勤中に10回チェックすると、時間あたりの予算の6分の1を消費します。
transit_list_operators は、511がすべてのレスポンスで返す RateLimit-Remaining ヘッダーから、残りの予算を報告します。クォータを超えると429が返ります。増量をリクエストするには transitdata@511.org に連絡してください。
デプロイ(Claude mobile / claude.ai コネクタ用)
ホスト型MCPサーバーと同じ構成です。openssl rand -hex 32 でパスシークレットを生成し、プラットフォームのダッシュボードで TRANSIT_511_API_KEY と MCP_PATH_SECRET を設定します。同梱の Dockerfile と railway.json は、Railway、Render、Flyでそのまま動作します。サーバーはシークレットなしではパブリックインターフェースでの起動を拒否します。/healthz は認証不要の死活監視エンドポイントです。
その後、ブラウザで claude.ai で: Customize → Connectors → Add custom connector を選択し、URL に https://your-app.up.railway.app/mcp/<secret> を指定します。
ツール
ネットワーク — transit_list_operators、transit_list_lines、transit_find_stops
リアルタイム — transit_next_departures、transit_list_vehicles
アラート — transit_list_service_alerts
すべてのツールは response_format: "markdown" | "json" を受け取ります。Markdownがデフォルトで、LLMが読むのに最適化されています。JSONは完全な構造化ペイロードです。structuredContent はフォーマットに関係なく常に設定されます。
例
「次のNジュダはいつ?」
→ operator_id="SF"、query="judah" で transit_find_stops を呼び出して停留所コードを取得し、そのコードと line="N" で transit_next_departures を呼び出します。
「BARTは正常に動いてる?」
→ operator_id="BA" で transit_list_service_alerts を呼び出します。
「通勤経路で何か問題ある?」
→ オペレーターを指定せずに transit_list_service_alerts を呼び出します。1回の呼び出しでベイエリアの全事業者をスキャンします。
「今、電車はどこにいる?」
→ operator_id="BA" で transit_list_vehicles を呼び出します。
設計メモ
構造的に読み取り専用。 511には書き込みエンドポイントがなく、すべてのツールに readOnlyHint: true が付いています。テストでこれを検証しています。
エンドポイントごとに1つの operator_id をマッピング。 511は静的エンドポイントではこのパラメータを operator_id と呼び、リアルタイムエンドポイントでは同じ値に対して agency と呼びます。ここではすべてのツールが operator_id を受け取り、クライアントがそれをマッピングします。この分割は511側の問題であり、呼び出し側の問題ではありません。
2つのリアルタイムエンドポイントは実際には異なるエンベロープを持ちます。 StopMonitoring には Siri ルートラッパーがありません。VehicleMonitoring にはあります。公開されている仕様では両方に1つあるとされていますが、仕様が間違っており、文書化された形で解析すると発車情報がまったく返りません。両方とも実際のAPIが出力する形で解析され、それぞれを固定するテストがあります。
到着時刻にカウントダウンが含まれ、出発時刻には含まれません。 ExpectedDepartureTime は実質的にすべての実データでnullであるため、それに基づいてカウントダウンを行うと、サービスがない停留所として表示されます。ExpectedArrivalTime が信頼できるフィールドです。
UTF-8 BOMは解析前に除去されます。 511はJSONボディの先頭に U+FEFF を付けるため、単純な JSON.parse では有効なペイロードでも例外が発生します。認証失敗はBOMなしのプレーンテキストであるため、ステータスチェック後に除去が行われます。
数値やブール値に見える値は、実際にはそうでないことがよくあります。 座標と方位はJSON文字列として到着し、VehicleAtStop は文字列 "false" であり、nullを意図した場所では "" が使用されます。盲目的に変換すると、欠落した位置がアフリカ沖の有効な0,0に見えてしまうため、空文字列はゼロではなく欠落として扱われます。
エポックゼロのセンチネルはタイムスタンプではありません。 スケジュールされているが車両が割り当てられていない便は、RecordedAtTime が 1970-01-01T00:00:00Z と報告されます。これは「56年前に記録された」ではなく「まだ車両が割り当てられていません」と表示されます。
GTFS-Realtimeの列挙型はデコードされます。 511のJSONアラートレンダリングは "effect": 3 を出力しますが、XMLレンダリングでは SignificantDelays と表示されます。原因と影響の両方が単語にマッピングされます。
511内部の疑似事業者は除外されます。 5E、5F、5O、5S は511の緊急、フラップサイン、運用、スタッフであり、事業者リストにサービスデータなしで表示されます。
すべて太平洋時間です。 タイムスタンプはUTCで到着し、America/Los_Angeles でレンダリングされるため、夏時間はモデルが年に2回処理するのではなく、ここで一度処理されます。511自身の TimeZone フィールドは、ベイエリアのすべての事業者に対して America/Vancouver を報告することに注意してください。これは既知の上流のデータバグであり、意図的に無視されています。
切り捨ては常に明示されます。 511はページネーションを行わず、コレクション全体を返します。大規模な事業者には数千の停留所があります。ツールはクライアント側の limit を受け取り、切り詰められた結果はすべて、保留された量を明示します。黙って短縮されたリストは「それがすべてです」と読めるからです。
注意事項
時間あたりのクォータは全エンドポイントで60リクエストです。これはどのワークフローでも制約となります。
事業者コードは間違えやすいです。VTAは
SC(VTではない)、Capitol CorridorはAM(CCではない)、Tri Deltaは3Dです。transit_list_operatorsはこれらの罠を出力に表示します。停留所コードは1つの事業者に属し、事業者間で交換できません。
transit_find_stopsはこのサーバー上でフィルタリングするため、狭いクエリでもクォータは節約されません。完全な停留所リストはどちらにせよ取得されます。リアルタイム予測は約90分先までで、511は路線の最終到着専用停留所を発車情報フィードから除外します。
tripupdatesとvehiclepositionsはprotobufのみでJSONオプションがないため、意図的に公開していません。サポートするには、SIRIエンドポイントがすでにカバーしているデータのためにprotobuf依存関係を追加する必要があります。
プロジェクト構成
src/
├── index.ts # entry point, transport selection
├── constants.ts # enums, limits, operator-code traps
├── types.ts # interfaces for every 511 entity
├── services/
│ └── transit-client.ts # fetch wrapper, auth, BOM stripping, quota tracking, errors
├── schemas/
│ ├── inputs.ts # Zod input schemas
│ └── outputs.ts # structuredContent schemas
├── formatters/
│ ├── response.ts # limiting, truncation, Pacific-time rendering
│ └── entities.ts # per-entity markdown rendering
└── tools/
├── network.ts # operators, lines, stops
├── departures.ts # real-time arrivals and vehicles
└── alerts.ts # service alertsテスト
npm run build
npm test # 42 checks: handshake, BOM, envelopes, quirks, errors (mocked API)
npm run test:http # 17 checks: config validation, path-secret gating, method handling, origins両方のスイートは、511の実際の癖(BOM、欠落した Siri ラッパー、文字列化されたブール値と座標、エポックセンチネル、プレーンテキストのエラーボディ)を意図的に再現するローカルモックに対して実行されます。これらはまさに、素朴なクライアントが間違える点だからです。
Available Tools
6 toolstransit_find_stopsFind Transit StopsARead-onlyIdempotent
Find an agency's stops by name, and get the stop codes the real-time tools need.
This is the bridge between "Downtown Berkeley" and the code transit_next_departures wants. Pass a query: a large agency has thousands of stops, and 511 returns all of them in one unpaginated response.
Args:
operator_id (string): agency code from transit_list_operators
query (string): substring match on the stop name — strongly recommended
limit (number): maximum stops to return (default: 25)
response_format ('markdown' | 'json'): output format (default: 'markdown')
Returns: { "count": number, "total": number, "truncated": boolean, "stops": [ { "id": string, "Name": string, "Location": { "Latitude": string, "Longitude": string } } ] }
Examples:
"When's the next train from Downtown Berkeley?" -> operator_id='BA', query='downtown berkeley', then pass the id to transit_next_departures
"Find Muni stops on Judah" -> operator_id='SF', query='judah'
Don't use when: you already have the stop code
Error Handling:
Stop codes belong to ONE operator and are not interchangeable between agencies
Filtering happens on this server, so a narrow query does not save quota — the full list is fetched either way
511 allows 60 requests per hour across ALL endpoints
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum results to return (max 200) | |
| query | No | Case-insensitive substring match on the stop name. Strongly recommended — a large agency has thousands of stops | |
| operator_id | Yes | Operator code from transit_list_operators, e.g. 'BA' for BART, 'SF' for Muni | |
| response_format | No | Output format: 'markdown' for human-readable, 'json' for machine-readable | markdown |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| stops | Yes | |
| total | Yes | |
| truncated | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint=false. The description adds meaningful behavior: server-side filtering (no quota savings), unpaginated response from 511, and the 60-request/hour rate limit. It also clarifies that stop codes are operator-specific—valuable context beyond the annotations.
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 longer but well-structured with clear sections (Args, Returns, Examples, Error Handling). Each section contributes distinct value: the main purpose is front-loaded, examples are concise, and error handling is pertinent. No fluff or redundancy.
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 is fully self-contained: it includes the return object structure, examples mapping to actual operator IDs, error-handling notes, rate limits, and the relationship to sibling tools. With an output schema present and annotations covering safety, nothing an agent needs to invoke correctly 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 100%, so the baseline is 3. The description's Args section largely repeats schema descriptions (e.g., 'operator_id' from transit_list_operators, 'query' substring match and 'strongly recommended'). It adds no new per-parameter semantics; the 'strongly recommended' phrasing already appears in the schema. The unpaginated response note is behavioral, not parameter-specific.
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 identifies the tool's purpose: 'Find an agency's stops by name, and get the stop codes the real-time tools need.' It explicitly ties the tool to transit_next_departures, distinguishing it from other transitive tools. The purpose is specific and actionable.
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 explicit usage context: 'This is the bridge between "Downtown Berkeley" and the code transit_next_departures wants.' It also states a clear when-not: 'Don't use when: you already have the stop code.' Examples illustrate typical queries, making the intended conditions unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
transit_list_linesList Transit LinesARead-onlyIdempotent
List an agency's routes.
Use this to resolve a line name a user mentions into the id the real-time tools filter on, or to answer "what routes does this agency run".
Args:
operator_id (string): agency code from transit_list_operators
query (string): substring match on the line name or public code
limit (number): maximum lines to return (default: 25)
response_format ('markdown' | 'json'): output format (default: 'markdown')
Returns: { "count": number, "total": number, "truncated": boolean, "lines": [ { "Id": string, "Name": string, "PublicCode": string, "TransportMode": string, "Monitored": boolean } ] }
Examples:
"What BART lines are there?" -> operator_id='BA'
"Is there an N line on Muni?" -> operator_id='SF', query='N'
Don't use when: you want stops on a line (use transit_find_stops)
Error Handling:
A large agency returns many lines; use query to narrow rather than raising limit
511 allows 60 requests per hour across ALL endpoints
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum results to return (max 200) | |
| query | No | Case-insensitive substring match on the line name or public code | |
| operator_id | Yes | Operator code from transit_list_operators, e.g. 'BA' for BART, 'SF' for Muni | |
| response_format | No | Output format: 'markdown' for human-readable, 'json' for machine-readable | markdown |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| lines | Yes | |
| total | Yes | |
| truncated | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnly, idempotent, and non-destructive hints. The description goes further by disclosing the response shape, the truncation flag, the possibility of many results for large agencies, and the 60-request-per-hour rate limit across all endpoints. This adds behavioral context beyond the annotations and contradicts nothing.
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 well-organized with clear sections (Summary, Args, Returns, Examples, Error Handling). It front-loads the purpose and usage, and every sentence adds value—examples, exclusions, rate limits, and return format. Despite covering many aspects, it remains concise and skimmable.
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 tool has 4 parameters, an output schema, and sits among 5 siblings. The description covers all essentials: purpose, parameter examples, return structure, error handling, and even the rate limit. There is nothing an agent needs to know to call this correctly that is missing, and the output schema already documents the return shape.
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 covers all parameters with detailed descriptions (100% coverage), so the description does not need to duplicate that. It adds a few examples (operator_id='BA', 'SF') and clarifies that query is a case-insensitive substring, but these are minor enhancements. The baseline of 3 is appropriate because the schema already carries the main semantic load.
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 ('List') and resource ('an agency's routes'), and immediately explains its practical use (resolving a line name into an id, answering route questions). It explicitly distinguishes itself from a sibling tool ('Don't use when: you want stops on a line (use transit_find_stops)'), which is a clear differentiation.
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 gives explicit 'Use this to...' guidance and an explicit 'Don't use when...' with a named alternative. Examples illustrate common queries, and the error handling section advises when to prefer query narrowing over raising limit. This leaves no ambiguity about when to invoke this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
transit_list_operatorsList Transit OperatorsARead-onlyIdempotent
List Bay Area transit agencies and their operator codes.
Start here. Every other tool needs an operator code, and this is what produces them — BART is 'BA', Muni is 'SF', AC Transit is 'AC', Caltrain is 'CT'.
The Monitored flag matters: agencies reporting real-time data support live departures and vehicle positions, while schedule-only agencies do not.
Args:
monitored_only (boolean): only agencies publishing real-time data (default: false)
limit (number): maximum operators to return (default: 50)
response_format ('markdown' | 'json'): output format (default: 'markdown')
Returns: { "count": number, "total": number, "truncated": boolean, "operators": [ { "Id": string, "Name": string, "Monitored": boolean, "PrimaryMode": string, "TimeZone": string } ] }
Examples:
"What transit agencies are there?" -> call with no arguments
"Which ones have live tracking?" -> monitored_only=true
Call this first whenever the user names an agency, to resolve its code
Error Handling:
Ignore the TimeZone field: 511 reports "America/Vancouver" for every Bay Area agency, which is a known upstream data bug. Everything here is Pacific time
511-internal pseudo-agencies (5E, 5F, 5O, 5S) are filtered out — they carry no service data
511 allows 60 requests per hour across ALL endpoints, so cache this rather than re-fetching
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum results to return (max 200) | |
| monitored_only | No | Only agencies that publish real-time data, excluding schedule-only ones | |
| response_format | No | Output format: 'markdown' for human-readable, 'json' for machine-readable | markdown |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| total | Yes | |
| operators | Yes | |
| truncated | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, openWorldHint=true, idempotentHint=true, and destructiveHint=false. The description adds substantial behavioral context beyond those: the Monitored flag semantics, the known TimeZone bug (inaccurate 'America/Vancouver' values), the filtering of pseudo-agencies (5E, 5F, 5O, 5S), and the rate limit (60 req/hour across all endpoints) with caching advice. This is exactly the kind of operational detail an agent needs and the annotations do not provide.
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 thorough but efficiently organized into clear sections (Args, Returns, Examples, Error Handling). Every sentence adds value: the opening line is the purpose, the 'Start here' directive is front-loaded, examples are concise, and error handling covers real-world quirks. No redundancy or filler—each element earns its 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 tool with 3 optional parameters and an output schema, the description covers everything needed to call it correctly: it provides default values, return shape (matching the output schema), examples, and critical edge cases (TimeZone bug, filtering, rate limit). Even though the output schema exists, the description's inclusion of the return shape is redundant but not harmful; the error-handling notes are essential and not available anywhere else. The description is fully sufficient for an agent to use this tool without surprises.
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 100%, so baseline is 3. The description goes beyond the schema by explaining the semantic impact of monitored_only (supports live departures/vehicle positions vs schedule-only), giving concrete usage examples for each parameter, and clarifying the response_format default. It also mentions the default for limit in prose. While the schema already has descriptions, the description adds contextual meaning that helps an agent decide parameter values appropriately.
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 clear verb-resource pair: "List Bay Area transit agencies and their operator codes." It immediately differentiates from siblings by noting "Every other tool needs an operator code, and this is what produces them," which is unique among the sibling tools. The specific examples (BART='BA', Muni='SF') further cement 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?
Explicitly instructs when to use: "Start here." and "Call this first whenever the user names an agency, to resolve its code." It also explains the monitored_only flag's relevance for choosing between live vs schedule-only agencies, effectively guiding the agent on when to apply different parameter values. No alternatives are named, but the tool is clearly positioned as the entry point, so exclusion guidance is unnecessary.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
transit_list_service_alertsList Transit Service AlertsARead-onlyIdempotent
Service alerts — delays, outages, detours and planned disruptions.
Answers "is BART running normally", "why is my line delayed" and "anything I should know before I leave". Omit operator_id to sweep every Bay Area agency in a single request, which is also the kinder option against the hourly quota.
Args:
operator_id (string): limit to one agency; omit for all operators
query (string): substring match on the headline or description
active_only (boolean): only alerts whose active period covers now (default: true)
limit (number): maximum alerts to return (default: 25)
response_format ('markdown' | 'json'): output format (default: 'markdown')
Returns: { "count": number, "total": number, "truncated": boolean, "alerts": [ { "id": string, "header": string, "description": string, "effect": string, "cause": string, "routes": [string], "start": number, "end": number } ] // epoch SECONDS }
Examples:
"Any BART delays?" -> operator_id='BA'
"Anything wrong on my commute?" -> omit operator_id, read across agencies
"Weekend track work?" -> active_only=false, query='weekend'
Don't use when: you want a specific stop's arrivals (use transit_next_departures)
Error Handling:
No alerts is genuinely good news, not an error — it means normal service
Timestamps here are epoch seconds, unlike the ISO strings elsewhere in this API
Setting active_only=false surfaces future planned work as well as current problems
511 allows 60 requests per hour across ALL endpoints
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum results to return (max 200) | |
| query | No | Case-insensitive substring match on the alert headline or description | |
| active_only | No | Only alerts whose active period covers now | |
| operator_id | No | Limit to one agency; omit for alerts across every Bay Area operator | |
| response_format | No | Output format: 'markdown' for human-readable, 'json' for machine-readable | markdown |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| total | Yes | |
| alerts | Yes | |
| truncated | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only indicate read-only, idempotent, and non-destructive behavior. The description adds crucial behavioral nuance: no alerts means normal service (not an error), timestamps are epoch seconds unlike the ISO strings in other API endpoints, active_only=false surfaces future planned work, and rate limits. This goes well beyond the annotations.
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 Args, Returns, Examples, and Error Handling sections. The purpose and primary use cases are front-loaded in the first sentence, and every sentence adds value—examples, rate limits, or format distinctions. No redundancy or fluff.
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 output schema is present and the description includes a return format example with field types. It covers error handling (no alerts ≠ error), rate limits, timestamp format, and sibling routing. For a 5-parameter tool with no required parameters, this is fully complete 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 100%, so the baseline is 3, but the description adds substantial meaning: operator_id omission sweeps all agencies and is 'kinder' against quota, active_only=false is tied to 'weekend track work' examples, and query is illustrated with a practical use case. These contextual insights help an agent choose parameter values effectively.
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 ('List') and resource ('Transit Service Alerts'), and grounds it with concrete questions it answers ('is BART running normally', 'why is my line delayed'). It explicitly differentiates from the sibling transit_next_departures by naming when not to use it, making the purpose unmistakable.
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 states when to use the tool (answering delay/outage questions) and when not to ('Don't use when: you want a specific stop's arrivals (use transit_next_departures)'). Also gives operational guidance, such as omitting operator_id to sweep all agencies and conserve quota—practical context an agent needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
transit_list_vehiclesList Transit Vehicle PositionsARead-onlyIdempotent
Live positions of an agency's vehicles currently in service.
Answers "where are the trains right now" and "how many buses are running on this line". For "when does one get to me", use transit_next_departures instead.
Args:
operator_id (string): agency code from transit_list_operators
line (string): only vehicles on this line, matched on name or id
limit (number): maximum vehicles to return (default: 25)
response_format ('markdown' | 'json'): output format (default: 'markdown')
Returns: { "count": number, "total": number, "truncated": boolean, "vehicles": [ { "line": string, "vehicle": string, "destination": string, "latitude": number | null, "longitude": number | null, "bearing": number | null, "recorded_at": string } ] }
Examples:
"How many Muni trains are running on the N?" -> operator_id='SF', line='N'
"Where are the BART trains?" -> operator_id='BA'
Don't use when: you want arrival times at a stop (use transit_next_departures)
Error Handling:
Coordinates arrive as strings and may be empty; those vehicles report "position unavailable" rather than a false 0,0
An agency with no real-time feed returns nothing — check Monitored in transit_list_operators
511 allows 60 requests per hour across ALL endpoints
| Name | Required | Description | Default |
|---|---|---|---|
| line | No | Only vehicles on this line | |
| limit | No | Maximum results to return (max 200) | |
| operator_id | Yes | Operator code from transit_list_operators, e.g. 'BA' for BART, 'SF' for Muni | |
| response_format | No | Output format: 'markdown' for human-readable, 'json' for machine-readable | markdown |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| total | Yes | |
| vehicles | Yes | |
| truncated | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already signal read-only, idempotent, and open-world behavior. The description adds valuable, non-obvious behavioral details: coordinates arrive as strings and may be empty, agencies with no real-time feed return nothing (with a pointer to check 'Monitored'), and a 60-requests-per-hour rate limit. This goes well beyond the annotations and prepares the agent for realistic 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 longer than strictly necessary because it repeats the Args and Returns sections that already exist in the schema. However, it is well-structured with clear sections (purpose, args, returns, examples, error handling) and the most important guidance (purpose and alternative) is front-loaded. Every section adds unique value (examples, error handling, rate limits), so the length is justified despite some redundancy.
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 list tool with an output schema and read-only annotations, the description covers all essential context: proper use cases, alternatives, parameter semantics, edge cases (missing coordinates, empty feeds, rate limits), and concrete examples. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema fully documents each parameter. The description adds some meaning beyond that: it clarifies that 'line' matches on name or id, and provides concrete examples (operator_id='SF', line='N') that illustrate how parameters map to real queries. Defaults for limit and response_format are repeated, but the examples and matching detail provide modest added value.
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 clear, specific statement of what the tool does: 'Live positions of an agency's vehicles currently in service.' It names the resource (vehicles) and action (list positions), and explicitly differentiates from a sibling tool ('For "when does one get to me", use transit_next_departures instead'), so an agent can distinguish it without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: answers 'where are the trains right now' and 'how many buses are running on this line', and gives a direct exclusion: 'Don't use when: you want arrival times at a stop' with the alternative named. Examples reinforce correct usage and operator/line selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
transit_next_departuresNext Transit DeparturesARead-onlyIdempotent
Live arrival predictions for the next vehicles at a stop.
This is the tool for "when is my next train/bus". It returns real-time predictions, not the printed timetable, and reports minutes-from-now alongside Pacific clock time.
Args:
operator_id (string): agency code from transit_list_operators, e.g. 'BA', 'SF'
stop_code (string): stop code from transit_find_stops. Codes belong to ONE operator and are not interchangeable between agencies
line (string): only departures on this line, matched on name or id
limit (number): maximum departures to return (default: 10)
response_format ('markdown' | 'json'): output format (default: 'markdown')
Returns: { "count": number, "total": number, "truncated": boolean, "operator": string, "stop_code": string, "stop_name": string, "retrieved_at": string, "departures": [ { "line": string, "destination": string, "expected": string, "aimed": string, "minutes": number, "vehicle": string | null, "at_stop": boolean } ] }
Examples:
"When's the next N Judah?" -> operator_id='SF', stop_code from transit_find_stops, line='N'
"Next BART from Downtown Berkeley?" -> operator_id='BA', the stop's code
Don't use when: you want the scheduled timetable rather than live predictions
Error Handling:
An empty result usually means service has ended for the night, or the stop is a route's final stop — 511 omits arrival-only terminals from this feed
Predictions extend roughly 90 minutes ahead; nothing beyond that appears
Rows reading "scheduled only, no live prediction" have no vehicle assigned yet
511 allows 60 requests per hour across ALL endpoints — never poll this in a loop
| Name | Required | Description | Default |
|---|---|---|---|
| line | No | Only departures on this line, matched against the line name or id | |
| limit | No | Maximum results to return (max 200) | |
| stop_code | Yes | Stop code from transit_find_stops. Codes are specific to one operator | |
| operator_id | Yes | Operator code from transit_list_operators, e.g. 'BA' for BART, 'SF' for Muni | |
| response_format | No | Output format: 'markdown' for human-readable, 'json' for machine-readable | markdown |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| total | Yes | |
| operator | No | |
| stop_code | No | |
| stop_name | No | |
| truncated | Yes | |
| departures | Yes | |
| retrieved_at | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, and non-destructive hints. The description adds substantial behavioral context: real-time vs static, minutes-from-now plus Pacific clock time, 'scheduled only' rows, a 90-minute prediction horizon, empty results meaning (end of service or arrival-only terminal), and a 60-requests-per-hour rate limit. This goes far beyond the annotations, providing critical operational details an agent needs to interpret results and avoid misuse.
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 (Purpose, Args, Returns, Examples, Error Handling) and front-loaded with the core purpose. Every sentence adds value; error handling is critical for correct agent behavior. It is detailed but not flabby — the length is justified by the operational caveats and edge cases.
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 tool with 5 parameters and an output schema, the description covers all essential usage aspects: parameter semantics, error scenarios (empty results, truncated predictions), time horizon, rate limits, and a full output structure in the Returns section. Even though an output schema is provided, the description's explicit sample JSON and explanation of fields further aid the agent. Nothing needed for correct invocation 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?
Even though schema coverage is 100%, the description enriches every parameter: it provides real examples (operator_id 'BA'/'SF', stop_code from transit_find_stops), warns that stop codes are operator-specific and not interchangeable, clarifies line matching ('matched on name or id'), and explains response_format options. The Args section adds value beyond the schema's terse descriptions, especially for stop_code and line.
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?
Description states a specific verb and resource: 'Live arrival predictions for the next vehicles at a stop' and explicitly positions it as 'the tool for when is my next train/bus'. It distinguishes from the printed timetable and clarifies scope (real-time, not static), making it clearly different from sibling tools that list operators, stops, lines, vehicles, or alerts.
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 when-to-use guidance via examples ('When's the next N Judah?' maps to specific parameters), an explicit 'Don't use when' exclusion for scheduled timetable, and references prerequisite tools (transit_list_operators, transit_find_stops) for parameter sourcing. It also includes rate-limit guidance ('never poll this in a loop'), which is directly relevant to usage.
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.
6 tool updates
v1.0.0- First observed
transit_find_stops - First observed
transit_list_lines - First observed
transit_list_operators - First observed
transit_list_service_alerts - First observed
transit_list_vehicles - First observed
transit_next_departures
TDQS
Scored across 6 tools
Each tool targets a distinct resource: operators, lines, stops, departures, vehicles, and alerts. Descriptions explicitly cross-reference when to use which (e.g., 'Don't use when' sections), eliminating ambiguity. No two tools overlap in purpose.
All tools share the 'transit_' prefix and follow a consistent verb-object pattern: list_operators, list_lines, find_stops, next_departures, list_vehicles, list_service_alerts. Minor variation between 'list', 'find', and 'next' but the style is uniform and predictable.
Six tools are well-scoped for a transit information server, covering discovery (operators, lines, stops), real-time queries (departures, vehicles), and alerts. No tool is redundant, and the count feels neither sparse nor bloated.
The tool surface covers the full workflow of querying transit info: discover agencies, lines, stops, then get live departures, vehicle positions, and alerts. A minor gap is the lack of a dedicated scheduled timetable tool, though the departures endpoint includes aimed times. Overall, the domain is well covered.
Maintenance
Related MCP Connectors
Read-only public transit departures, stop search, and city coverage for bus and train users.
Real-time BART departures, trip planning, fares, stations, and advisories.
Real-time transit stops, routes, arrivals, vehicle positions, and schedules via OneBusAway APIs.
Real-time SF Muni departures, routes, alerts, vehicle positions, and schedules.
Related MCP Servers
- AlicenseBqualityFmaintenanceEnables AI clients to access Boston's MBTA public transit data, including real-time predictions, schedules, route planning, and service alerts.321Apache 2.0
- AlicenseNot gradedqualityBmaintenanceProvides real-time San Francisco Bay Area Rapid Transit data, enabling queries about BART schedules, routes, and station information through natural language.24 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables querying global transit data including agencies, routes, stops, and departures through a GTFS aggregator.22 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables querying real-time BART and SF Muni transit data, including departures, trip planning, fares, advisories, routes, alerts, vehicle positions, and schedules, from any MCP-compatible client.1MIT