# eqsl-mcp [![PyPI](https://img.shields.io/pypi/v/eqsl-mcp?label=PyPI&color=blue)](https://pypi.org/project/eqsl-mcp/) [![MCP Registry](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fregistry.modelcontextprotocol.io%2Fv0%2Fservers%3Fsearch%3Dio.github.qso-graph%2Feqsl-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/eqsl-mcp&version=latest) MCP server for [eQSL.cc](https://www.eqsl.cc/) — download incoming eQSLs, verify QSOs, check AG status, and query upload history through any MCP-compatible AI assistant. Part of the [qso-graph](https://qso-graph.io/) project. Uses [qso-graph-auth](https://pypi.org/project/qso-graph-auth/) for credential management. ## Install ```bash uvx eqsl-mcp # run it; nothing to install ``` ## Tools | Tool | Auth | Description | |------|------|-------------| | `eqsl_inbox` | Yes | Download incoming eQSLs with date/confirmation filters | | `eqsl_verify` | No | Check if a specific QSO exists in eQSL | | `eqsl_ag_check` | No | Check if a callsign has AG (Authenticity Guaranteed) status | | `eqsl_download` | Yes | Download your eQSL inbox as raw ADIF, to import into your logger | | `eqsl_last_upload` | No | When did a persona last upload to eQSL | | `get_version_info` | No | Service version + upstream eQSL.cc endpoint version (fleet identity attestation) | ## Quick Start ### 1. Set up credentials eqsl-mcp uses [qso-graph-auth](https://qso-graph.io/servers/qso-graph-auth/) personas for credential management: ```bash # Install qso-graph-auth if you haven't uv tool install qso-graph-auth # A persona (your callsign and the dates it covers), then eQSL for it qso-auth persona add --name ki7mt --callsign KI7MT --start 2020-01-01 qso-auth provider enable ki7mt eqsl qso-auth creds set ki7mt eqsl # asks for your username, then your password (hidden) ``` All three steps are needed: without `provider enable`, the server reports that the persona has no `eqsl` ref. ### 2. Configure your MCP client eqsl-mcp works with any MCP-compatible client. Add the server config and restart — tools appear automatically. #### Claude Desktop Add to `claude_desktop_config.json` (`~/Library/Application Support/Claude/` on macOS, `%APPDATA%\Claude\` on Windows): ```json { "mcpServers": { "eqsl": { "command": "uvx", "args": ["eqsl-mcp"] } } } ``` #### Claude Code Add to `.claude/settings.json`: ```json { "mcpServers": { "eqsl": { "command": "uvx", "args": ["eqsl-mcp"] } } } ``` #### ChatGPT Desktop ChatGPT supports MCP via the [OpenAI Agents SDK](https://developers.openai.com/api/docs/mcp/). Add under Settings > Apps & Connectors, or configure in your agent definition: ```json { "mcpServers": { "eqsl": { "command": "uvx", "args": ["eqsl-mcp"] } } } ``` #### Cursor Add to `.cursor/mcp.json` (project-level) or `~/.cursor/mcp.json` (global): ```json { "mcpServers": { "eqsl": { "command": "uvx", "args": ["eqsl-mcp"] } } } ``` #### VS Code / GitHub Copilot Add to `.vscode/mcp.json` in your workspace: ```json { "servers": { "eqsl": { "command": "uvx", "args": ["eqsl-mcp"] } } } ``` #### Gemini CLI Add to `~/.gemini/settings.json` (global) or `.gemini/settings.json` (project): ```json { "mcpServers": { "eqsl": { "command": "uvx", "args": ["eqsl-mcp"] } } } ``` ### 3. Ask questions > "Show me all eQSLs received this week" > "How many unconfirmed eQSLs do I have on 20m FT8?" > "Does W1AW have AG status on eQSL?" > "Verify my QSO with KI7MT on 20m on March 1, 2026" ## Testing Without Credentials The two public tools (`eqsl_verify` and `eqsl_ag_check`) work without any credentials. For `eqsl_inbox` testing, set the mock environment variable: ```bash EQSL_MCP_MOCK=1 eqsl-mcp ``` Or point to a local ADIF file: ```bash EQSL_MCP_MOCK=1 EQSL_MCP_ADIF=/path/to/test.adi eqsl-mcp ``` ## MCP Inspector ```bash eqsl-mcp --transport streamable-http --port 8001 ``` Then open the MCP Inspector at `http://localhost:8001`. ## Development ```bash git clone https://github.com/qso-graph/eqsl-mcp.git cd eqsl-mcp uv sync --group dev uv run pytest ``` ## Date Formats eQSL uses different date formats across endpoints. eqsl-mcp normalizes everything — you always use `YYYY-MM-DD`: | You provide | eqsl-mcp sends | Endpoint | |-------------|----------------|----------| | `2026-03-01` | `202603010000` | DownloadInBox (RcvdSince) | | `2026-03-01` | `03/01/2026` | VerifyQSO (QSODate) | ## Mode Matching eQSL requires exact mode matching. `SSB` won't match `USB`/`LSB`. `PSK` won't match `PSK31`. Use the exact mode logged by the other station. ## Known Quirks - **AG list cache:** the Authenticity Guaranteed member list is cached for 4 hours (eQSL updates it about six times a day), in `~/.cache/eqsl-mcp/ag_members.txt` (`$XDG_CACHE_HOME` if set; `%LOCALAPPDATA%\eqsl-mcp\` on Windows). - **Inbox download:** eQSL returns an HTML page that links to the `.adi` file, so the inbox is fetched in two steps. If eQSL answers with ADIF directly, that is used as is. ## License GPL-3.0-or-later