A secure web content fetching MCP server with SSRF protection, DNS validation, rate limiting, and optional JavaScript rendering via Playwright
Single tool 'fetch_url' has a clear action verb and reasonable description (127 chars, within 10-1024 range). Input schema is present with three parameters, all typed as string/integer. However, parameter descriptions are minimal (e.g., 'Caller identifier for rate limiting' lacks context on format/constraints). Output schema is not documented in the tool definition, the FetchResult dataclass exists in code but is not exposed as a formal schema in the MCP tool registration. Error handling is implemented server-side (SSRF validation, DNS checks, rate limiting) but error responses are not documented in the tool description, leaving LLMs unaware of recovery paths. No tool annotations (readOnlyHint, destructiveHint, idempotentHint) despite the tool being read-only.
Fetch and extract content from a URL with SSRF protection, DNS validation, rate limiting, and optional JavaScript rendering
Output schema not documented. FetchResult dataclass exists in code but is not declared in the MCP tool schema. LLMs cannot plan downstream operations or extract specific fields from the response.
Parameter descriptions lack actionable constraints. 'max_tokens' has no range (min/max), 'caller_id' has no format guidance, 'url' does not document supported schemes or length limits.
Error handling not documented in tool description. Server implements SSRF protection, DNS validation, rate limiting, and Playwright fallback, but LLMs are not told what errors to expect or how to recover (e.g., 'dns_resolution_failed', 'ip_blocked', 'rate_limit_exceeded').
No tool annotations. Tool is read-only and idempotent but lacks readOnlyHint and idempotentHint annotations to signal this to clients.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 54 | <=2025-11-25 | v2 |
Response field naming not aligned with parameter naming. Tool accepts 'url' parameter but response includes 'final_url', 'content_markdown', 'content_chars', inconsistent naming forces LLM field mapping reasoning.