MCP server for Renfe train schedules and prices for Spanish railways
Server has 3 tools with reasonable naming (verb_noun pattern: search_trains, find_station, get_train_prices). Tool descriptions are present and moderately detailed (124 - 268 chars), exceeding the minimum but below the 50-200 char sweet spot for LLM clarity. Input schemas are visible and include types + descriptions for most parameters. However, critical gaps exist: (1) output schema is NOT documented for any tool, responses are returned as plain strings, not structured objects; (2) pagination parameters (page, per_page) lack constraints (min/max), inviting unbounded input; (3) date parameter accepts 'flexible formats' but validation logic is hidden in ScheduleSearcher.format_date(), with no visible enum or regex constraint; (4) error handling returns narrative strings rather than structured error codes or recovery guidance; (5) no tool annotations (readOnlyHint, idempotentHint) are present despite all three tools being read-only. These are read-only data lookups, which is appropriate for initial scope, but the response format and parameter validation fall short of production quality.
Search for train stations in a city and return matching options. Useful for checking what stations are available in a city before searching for journeys.
Check actual ticket prices for trains between two cities using web scraping with pagination. NOTE: This tool scrapes the Renfe website and may take a few seconds to complete. It complements the search_trains tool by providing real-time price information. This endpoint has stricter rate limits due to web scraping.
Search for train journeys between two cities on a specific date.
Output schema not documented. All three tools return plain strings (narrative story format) instead of structured JSON objects. LLMs cannot parse unstructured text reliably or chain results to downstream tools. Violates pattern:response-shaper and pattern:tool.
Pagination parameters lack constraints. page and per_page have no visible min/max validation in schema; per_page has max=50 in description but not enforced in input schema type. Unbounded numeric parameters invite oversized requests. pattern:constrained-input violated.
Date parameter accepts 'flexible formats' (ISO, European, Written) but no regex pattern, enum, or formal constraint visible in schema. Validation is hidden in ScheduleSearcher.format_date(). If validation fails, error is a narrative string, not actionable guidance. Violates pattern:constrained-input and pattern:recovery-guide.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-21 | D | 53 | <=2025-11-25 | v2 |
| 2026-03-09 | D | 52 | - | v1 |
Error handling returns narrative text, not structured codes. Example: date validation error appended to story string with no machine-parseable error code or guidance. LLM cannot programmatically detect error type (retryable, user-fixable, fatal). Violates pattern:error-classification.
Tool annotations missing. All three tools are read-only but no readOnlyHint present in definition. Idempotent operations (search, find) should be marked idempotentHint=true. These annotations enable safe agent retries and caching. Violates MCP spec 2026-07-28 structured tool output pattern.
api_key exposed as tool parameter despite @require_auth decorator. Credentials should never appear in tool signatures, they should be injected server-side via environment. If an agent logs calls, api_key leaks into traces. Violates pattern:secret-injection.