Metadata-Version: 2.5
Name: adif-mcp
Version: 1.3.0
Summary: ADIF MCP server — spec parsing, validation, and field enumeration tools
Project-URL: Homepage, https://qso-graph.io
Project-URL: Documentation, https://qso-graph.io/servers/adif-mcp/
Project-URL: Repository, https://github.com/qso-graph/adif-mcp
Project-URL: Issues, https://github.com/qso-graph/adif-mcp/issues
Project-URL: ADIF Specification, https://adif.org.uk/317/ADIF_317.htm
Author-email: KI7MT <maintainers@qso-graph.io>
License: GPL-3.0-or-later
License-File: LICENSE
License-File: NOTICE
Keywords: adif,ai-agents,amateur-radio,eqsl,ham-radio,lotw,mcp,model-context-protocol,qrz,qso-logging
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Ham Radio
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Requires-Dist: aiofiles>=25.1.0
Requires-Dist: fastmcp<5,>=4.0
Requires-Dist: tomli>=2.0; python_version < '3.11'
Provides-Extra: all
Requires-Dist: cli-test-helpers>=0.3.0; extra == 'all'
Requires-Dist: interrogate>=1.7.0; extra == 'all'
Requires-Dist: mypy>=1.11; extra == 'all'
Requires-Dist: pytest>=8.3; extra == 'all'
Requires-Dist: ruff>=0.6.8; extra == 'all'
Provides-Extra: dev
Requires-Dist: interrogate>=1.7.0; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: ruff>=0.6.8; extra == 'dev'
Provides-Extra: test
Requires-Dist: cli-test-helpers>=0.3.0; extra == 'test'
Requires-Dist: pytest>=8.3; extra == 'test'
Description-Content-Type: text/markdown

<!-- mcp-name: io.github.qso-graph/adif-mcp -->
# adif-mcp

[![PyPI](https://img.shields.io/pypi/v/adif-mcp?label=PyPI&color=blue)](https://pypi.org/project/adif-mcp/)
[![MCP Registry](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fregistry.modelcontextprotocol.io%2Fv0%2Fservers%3Fsearch%3Dio.github.qso-graph%2Fadif-mcp%26version%3Dlatest&query=%24.servers%5B0%5D.server.version&label=MCP%20Registry&color=blue)](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.qso-graph/adif-mcp&version=latest)

MCP server for the [ADIF 3.1.7 specification](https://adif.org.uk/317/ADIF_317.htm): validate and parse ADIF records, search the spec's fields, enumerations and data types, and compute distance and heading between Maidenhead locators, through any MCP-compatible AI assistant.

Part of the [qso-graph](https://qso-graph.io/) project. **No authentication required** — the ADIF specification is bundled, so it works offline.

## Install

```bash
uvx adif-mcp            # run it; nothing to install
```

## Tools

| Tool | Description | Key Parameters |
|------|-------------|----------------|
| `validate_adif_record` | Validate a raw ADIF record against the 3.1.7 spec | adif_string |
| `parse_adif` | Streaming parser for large ADIF files, with pagination | file_path, start_at, limit |
| `read_specification_resource` | Raw JSON for any spec module (band, mode, fields, ...) | resource_name |
| `list_enumerations` | All ADIF enumerations with entry counts | — |
| `search_enumerations` | Search enumeration records by keyword | search_term, enumeration |
| `calculate_distance` | Great-circle distance (km) between two Maidenhead locators | start, end |
| `calculate_heading` | Initial beam heading (azimuth) between two locators | start, end |
| `get_version_info` | Service version + upstream spec version (fleet identity attestation) | — |

## What is ADIF?

ADIF (Amateur Data Interchange Format) is the standard file format amateur radio logging programs use to exchange QSO records. adif-mcp is the specification package of qso-graph: it validates, parses and searches ADIF so an assistant can work with logs safely and accurately. Credentials are handled separately by [qso-graph-auth](https://pypi.org/project/qso-graph-auth/), and each logging service has its own MCP server (see [qso-graph.io](https://qso-graph.io/)).

## Quick Start

No credentials needed — just install and configure your MCP client.

### Configure your MCP client

adif-mcp works with any MCP-compatible client. Add the server config and restart. The tools appear automatically.

#### Claude Desktop

Add to `claude_desktop_config.json` (`~/Library/Application Support/Claude/` on macOS, `%APPDATA%\Claude\` on Windows):

```json
{
  "mcpServers": {
    "adif": {
      "command": "uvx",
      "args": ["adif-mcp"]
    }
  }
}
```

#### Claude Code

Add to `.claude/settings.json`:

```json
{
  "mcpServers": {
    "adif": {
      "command": "uvx",
      "args": ["adif-mcp"]
    }
  }
}
```

#### ChatGPT Desktop

Configure via Settings > Apps & Connectors, or in your agent definition:

```json
{
  "mcpServers": {
    "adif": {
      "command": "uvx",
      "args": ["adif-mcp"]
    }
  }
}
```

#### Cursor

Add to `.cursor/mcp.json` (project-level) or `~/.cursor/mcp.json` (global):

```json
{
  "mcpServers": {
    "adif": {
      "command": "uvx",
      "args": ["adif-mcp"]
    }
  }
}
```

#### VS Code / GitHub Copilot

Add to `.vscode/mcp.json` in your workspace:

```json
{
  "servers": {
    "adif": {
      "command": "uvx",
      "args": ["adif-mcp"]
    }
  }
}
```

#### Gemini CLI

Add to `~/.gemini/settings.json` (global) or `.gemini/settings.json` (project):

```json
{
  "mcpServers": {
    "adif": {
      "command": "uvx",
      "args": ["adif-mcp"]
    }
  }
}
```

### Ask questions

> "Is this ADIF record valid? <CALL:5>KI7MT<QSO_DATE:8>20260928<BAND:3>20m<MODE:3>SSB<EOR>"

> "What values does the ADIF MODE enumeration allow for digital modes?"

> "Parse my log file and show me the first 50 QSOs."

> "How far is it from DN13 to JN48, and what heading should I point the beam?"

## What the validator checks

adif-mcp ships ADIF 3.1.7 as ADIF publishes it: 186 fields, 25 enumerations and 28 data types.
`validate_adif_record` checks each field against its ADIF data type and, for enumerated fields,
against the enumeration:

- **Enumeration values** match regardless of case, so LoTW's `BAND=15M` is accepted as `15m`.
- **Import-only values** (42 of the Mode enumeration's 91 entries, for example) give a warning, not an
  error: ADIF allows them on import but not in new records.
- **Compound values** such as `CREDIT_SUBMITTED` (a list of `Credit` or `Credit:Medium` entries) are
  checked entry by entry.
- **SUBMODE** must be a valid Submode; one that belongs to a different MODE gives a warning. FT4, for
  example, is `MODE=MFSK` with `SUBMODE=FT4`.

The server also publishes the MCP resource `adif://system/version`: the service version and the ADIF
specification version it serves.

## Compliance & Provenance

adif-mcp follows the [ADIF Specification](https://adif.org.uk) (currently 3.1.7) and uses **registered Program IDs** to identify all exports:

- `ADIF-MCP` -- Core engine
- `ADIF-MCP-LOTW` -- LoTW server
- `ADIF-MCP-EQSL` -- eQSL server
- `ADIF-MCP-QRZ` -- QRZ server

The project uses **APP_ fields** for provenance when augmenting records:

- `APP_ADIF-MCP_OP` -- operation performed (`normalize`, `validate`, `merge`)
- `APP_ADIF-MCP-LOTW_ACTION` -- LoTW server operation
- `APP_ADIF-MCP-EQSL_TIME` -- timestamp of eQSL merge

## Development

```bash
git clone https://github.com/qso-graph/adif-mcp.git
cd adif-mcp
uv sync --group dev
uv run pytest
```

## License

adif-mcp's own code is GPL-3.0-or-later. See [LICENSE](LICENSE) for details.

**The ADIF specification files are not ours.** The package includes ADIF's published files for two versions, the previous and the current (3.1.6 and 3.1.7 today), unmodified: every file in ADIF's resource zip except its test material, in every format ADIF publishes (JSON, XML with `all.xml` and the XSD, CSV, TSV, XLSX, ODS), with a `MANIFEST.json` of their SHA-256s per version, from the [ADIF Developers Group](https://adif.org.uk/). They are ADIF's work, and our licence doesn't cover them. The only files in that folder that are ours are the derived Country enumeration and two small catalog files. See [NOTICE](NOTICE).
