ADIF-MCP demonstrates solid foundational tool design with consistent verb-noun naming (parse_adif, validate_adif, stream_adif, etc.), detailed parameter descriptions, and mostly complete input schemas. However, it suffers from critical gaps in output schema documentation, error handling guidance, and lacks tool annotations. No evidence of return type schemas, error classification, or recovery guidance in the visible code. Descriptions are generally good (100-150 chars average) but some lack context on when to use the tool vs. similar ones. All 11 tools have descriptions and input parameters with types, but output structure is undocumented for the LLM. The eQSL integration tools (fetch_eqsl_inbox, filter_qso_summary) are well-named but lack error handling for credential management and network timeouts.
Tools (11)
calculate_distanceread onlysource verified68/100
Calculate great-circle distance between two coordinates (lat/lon or gridsquare)
calculate_headingread onlysource verified68/100
Calculate great-circle heading (azimuth) from one location to another
fetch_eqsl_inboxread onlyauth50/100
Fetch eQSL inbox records since a date for a given persona (demo tool with mock support)
filter_qso_summaryread only50/100
Summarize QSO records by band or mode with optional date and confirmation filters
get_enumerationread only50/100
Retrieve an enumeration by name (Band, Mode, QSL_Rcvd, Country, etc.) with optional filtering
get_field_specread only50/100
Get detailed specification for a single ADIF field (type, constraints, enumeration, etc.)
parse_adifread onlysource verified78/100
Parse ADIF text and return normalized QSO records with field-level validation
Output schemas completely undocumented. No visible schema definitions for return types from any of the 11 tools. LLMs cannot predict what fields to extract or plan downstream tool chains.
No error handling guidance. Tools like validate_adif, stream_adif, and fetch_eqsl_inbox have failure modes (invalid ADIF, file not found, network timeout, missing credentials) but no documented error responses, error categories, or recovery suggestions.
Parameter 'filters' in stream_adif is typed as 'object' with no inline schema. LLM cannot know valid filter keys (band, mode, call_pattern, date_from, date_to, confirmed_only). Need explicit object schema with typed subfields.
Recommendations
Add explicit output schema for every tool. Define return type as object with typed fields and descriptions. Example: parse_adif returns { qso_records: [{call, band, mode, date, ...}], errors: [{field, message}], record_count: number }. Use this in tool registration.
Add error classification and recovery guidance to tool descriptions or as separate error schema. For parse_adif: 'Validation errors are returned in the response. If errors list is non-empty and strict=true, no records are returned; set strict=false to collect partial results.' For stream_adif: 'If file not found, returns error with available paths. If timeout, increase timeout parameter and retry.'
Add enum constraints to get_enumeration.enumeration parameter. Schema should include enum: ['Band', 'Mode', 'QSL_Rcvd', 'Country', ...] to eliminate hallucinated values.
Add enum constraint to calculate_distance.unit parameter: enum: ['km', 'miles'].
Score history
Overall score trend
↓ 2 points across a rubric change (v1 → v2)
48/100
Scored
Grade
Overall
Spec posture
Rubric
2026-09-22
F
48
2026-07-28+
v2
2026-03-09
D
50
-
v1
read only50/100
Search ADIF field definitions by name, type, or enumeration
stream_adifread only50/100
Stream-parse large ADIF files and yield records incrementally with optional filtering
validate_adifread only50/100
Validate ADIF text against the 3.1.7 specification, returning detailed errors and warnings
validate_enum_fieldread only50/100
Validate a single ADIF field value against its enumeration
Parameter 'from' and 'to' in calculate_distance and calculate_heading are typed as 'object' with description only ('lat/lon or gridsquare'). No schema defining required/optional subfields (lat, lon, gridsquare). LLM must guess structure.
fetch_eqsl_inbox has credential dependency (persona with stored eQSL credentials) but no error guidance if persona is missing, credentials are invalid, or eQSL service is unreachable. No documented recovery path.
Tool composition: parse_adif and filter_qso_summary operate on records but have no shared chaining IDs documented. If parse_adif returns records, does it return enough structure for filter_qso_summary to work? Unknown.
No tool annotations visible (readOnlyHint, destructiveHint, idempotentHint). All tools are READ_ONLY per description, but no formal annotations to guide agent behavior. Protocol supports annotations but server does not use them.
Date parameters (date_from, date_to in stream_adif and filter_qso_summary) have descriptions mentioning format (YYYY-MM-DD) but no formal regex pattern or date type constraint. Description says 'YYYY-MM-DD' but schema allows any string.
Parameter 'enumeration' in get_enumeration lacks enum constraint. Description lists examples ('Band', 'Mode', 'QSL_Rcvd', 'Country') but no formal enum in schema. LLM can hallucinate invalid enumeration names like 'Transmitter' or 'Location'.
Parameter 'unit' in calculate_distance accepts string with description 'km, miles' but no enum constraint. LLM could pass 'meters', 'feet', 'nautical_miles' without validation.
calculate_distance
Formalize date parameters with type and format. For stream_adif.date_from and date_to: type: 'string', format: 'date', pattern: '^\d{4}-\d{2}-\d{2}$' with description 'ISO 8601 date (YYYY-MM-DD)'.
Document credential and persona dependency in fetch_eqsl_inbox. Add guidance: 'This tool requires eQSL credentials stored in the persona keyring. If persona is unknown, call list_personas first. If fetch fails with credential error, verify persona name and update credentials via the eQSL credential manager.'
Add tool annotations to tool registration: readOnlyHint: true for all tools (or pass in tool definition metadata). This explicitly signals to agents that these tools are safe to call without confirmation.
Document tool composition: in parse_adif description, note: 'Output records have fields: call, band, mode, date, qsl_rcvd, etc. Use these records with filter_qso_summary(records=..., by=band) to group results.'
For stream_adif, document result pagination or streaming semantics. Is output streamed incrementally, or does it return all records at once? If paginated, what is max_records per call? Add guidance: 'Results are yielded incrementally. For large files, iterate through chunks. Filters are applied server-side before streaming.'
For filter_qso_summary, document expected input record structure in the 'records' parameter description. State: 'Records must be objects with at least: call, band, mode, date, qsl_rcvd. Pass output from parse_adif or stream_adif directly.'
Add validation examples to descriptions. E.g., get_field_spec: 'Valid field names: BAND, FREQ, MODE, QSL_RCVD, DATE. Pass uppercase names. If field not found, response includes available fields by category.'
Document limits for tools that could return large result sets. For search_fields: 'Returns up to 50 matching fields. Use filter and field_type to narrow results. Offset/limit not supported; refine query instead.'
For calculate_distance and calculate_heading, specify output units. Add to description: 'Returns distance in the specified unit (default km). Heading is given as degrees (0-360, where 0=North, 90=East).'
Consider adding a tool annotation or description note that parse_adif and validate_adif are safe to call on untrusted ADIF input, they only read and validate, do not modify data or trigger side effects. This reassures agents.