Read-only MCP server for Chinese railway schedules, fares, stops, and seat availability.
Strong tool naming (all verb-prefixed), comprehensive parameter schemas with proper Zod validation, and clear descriptions. All 6 tools have explicit descriptions (avg ~120 chars) and input schemas with type constraints. Tool annotations present (readOnlyHint, idempotentHint, destructiveHint). Key gaps: output schemas are not formally documented (tools return text-wrapped JSON without declared structure), error handling is minimal (toolError() function exists but recovery guidance is absent), and parameter descriptions in schema are sparse (e.g., 'query' param has description but many filter params lack inline guidance). Pagination is well-implemented (limit/offset with defaults). No security issues detected (read-only operations, no secrets in params).
Filter, sort, and paginate journeys; this does not make subjective recommendations.
Get normalized official seat availability for an exact station pair, train, and date.
Return the official provider, its verified capabilities, and anonymous-session policy.
Get a published official stop sequence for an exact train number and date, including booking status. A timetable may be available before tickets go on sale.
Search public Chinese railway stations. City names are not silently resolved to a specific station.
Search official read-only 12306 route, fare, and availability data within the current ticket-query window. Results are paginated after filtering. Times are Asia/Shanghai.
Output schemas not formally documented. Tools return text-wrapped JSON via text() helper, but no structured schema is declared for LLM parsing. LLMs cannot plan downstream calls or extract fields reliably.
Error handling lacks recovery guidance. toolError() function wraps exceptions but does not categorize errors (retryable vs user-fixable) or suggest next steps. LLMs receive raw error messages without actionable context.
Parameter descriptions in Zod schemas are minimal. Filter parameters (departAfter, departBefore, trainTypes, seatClass, onlyAvailable) lack inline descriptions explaining their purpose and constraints. LLMs must infer meaning from names alone.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 63 | <=2025-11-25 | v2 |
compare_trains description is vague ('Filter, sort, and paginate journeys; this does not make subjective recommendations'). Does not explain when to use it vs search_trains or what 'subjective recommendations' means. LLMs may conflate the two tools.
No dry-run or confirmation pattern for potential future write operations. While current tools are read-only, the server instructions warn against polling, but no mechanism prevents accidental repeated calls or misuse.