Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | 130x 130x 1x 129x 1x 128x 128x 6x 122x 1x 121x 101x 90x 128x 128x 126x 125x 125x 125x 2x 123x | // SPDX-FileCopyrightText: 2024-2026 Hack23 AB
// SPDX-License-Identifier: Apache-2.0
/**
* @module MCP/ep/error-classifier
* @description Error classification and feed-unavailability detection for EP MCP tools.
*/
import type { MCPToolResult } from '../../types/index.js';
import { _parseResultPayload } from './parse.js';
/**
* Classify an error message into a diagnostic error category.
*
* Maps EP MCP Server v1.3.10 structured error codes and generic HTTP/network
* errors into one of six broad categories used for logging and retry decisions:
*
* Returned categories (priority order):
* 1. `INTERNAL_ERROR` — EP MCP `INTERNAL_ERROR` (catch-all for DNS, TLS, unclassified upstream failures)
* 2. `SERVER_ERROR` — EP MCP `UPSTREAM_500`/`UPSTREAM_503`/`SERVER_ERROR`, or gateway 5xx patterns
* 3. `TIMEOUT` — EP MCP `UPSTREAM_TIMEOUT`, or generic "timeout" strings
* 4. `RATE_LIMIT` — EP MCP `RATE_LIMITED`, HTTP 429, or "rate limit"/"too many requests" strings
* 5. `NOT_FOUND` — EP MCP `UPSTREAM_404`, or generic "404" strings
* 6. `UNKNOWN` — everything else
*
* @param message - Raw error message
* @returns Diagnostic error category string
*/
export function classifyToolError(message: string): string {
const lowerMsg = message.toLowerCase();
if (lowerMsg.includes('internal_error')) {
return 'INTERNAL_ERROR';
}
if (
lowerMsg.includes('upstream_500') ||
lowerMsg.includes('upstream_503') ||
lowerMsg.includes('server_error')
) {
return 'SERVER_ERROR';
}
Iif (lowerMsg.includes('upstream_timeout')) {
return 'TIMEOUT';
}
if (
lowerMsg.includes('gateway timeout') ||
lowerMsg.includes('gateway error 500') ||
lowerMsg.includes('gateway error 502') ||
lowerMsg.includes('gateway error 503') ||
lowerMsg.includes('gateway error 504')
) {
return 'SERVER_ERROR';
}
if (
lowerMsg.includes('429') ||
lowerMsg.includes('rate limit') ||
lowerMsg.includes('too many requests') ||
lowerMsg.includes('rate_limited')
) {
return 'RATE_LIMIT';
}
if (lowerMsg.includes('404') || lowerMsg.includes('upstream_404')) return 'NOT_FOUND';
if (lowerMsg.includes('timeout')) return 'TIMEOUT';
return 'UNKNOWN';
}
/**
* Detect whether an MCP feed result represents an "unavailable" response,
* covering the two shapes historically emitted by the EP MCP server.
*
* 1. **Uniform envelope** (all feeds as of
* `european-parliament-mcp-server@1.3.10`) —
* `{status:"unavailable", items:[], generatedAt:"..."}` established by
* Hack23/European-Parliament-MCP-Server#301 and extended to
* `get_events_feed`/`get_procedures_feed` by
* Hack23/European-Parliament-MCP-Server#380 (which closed #378).
* 2. **Pre-v1.2.13 raw upstream 404 shape** (historically emitted pre-v1.2.13 by
* `get_events_feed` / `get_procedures_feed`, fixed upstream in PR #380) —
* `{"@id":"https://data.europarl.europa.eu/eli/dl/...","error":"404 N..."}`.
* Retained purely as defense-in-depth for older pinned server versions or
* any future regression of #378, so such payloads do not silently poison
* downstream analysis.
*
* Returning `true` from this helper lets callers treat both shapes as
* "known-empty" rather than "success with garbage payload".
*
* @param result - Raw MCP tool result
* @returns `true` when the payload matches either unavailable envelope
*/
export function isFeedUnavailable(result: MCPToolResult | undefined): boolean {
const envelope = _parseResultPayload(result);
if (!envelope) return false;
if (envelope['status'] === 'unavailable') return true;
const error = envelope['error'];
const idField = envelope['@id'];
if (
typeof error === 'string' &&
typeof idField === 'string' &&
idField.startsWith('https://data.europarl.europa.eu/') &&
error.includes('404')
) {
return true;
}
return false;
}
|