Skip to main content
Glama
gflerm
by gflerm
README.md
# aprs-mcp

Model Context Protocol (MCP) server exposing the APRS-IS network as tools for
any LLM agent (opencode, Claude Desktop, Cursor, Windsurf, generic MCP
clients). Built on `aprslib` and `fastmcp`.

> [!IMPORTANT]
> APRS transmissions are public. Only transmit with a valid amateur-radio
> callsign and in accordance with the rules that apply in your jurisdiction.

## Tools

| Tool | Purpose |
| --- | --- |
| `aprs_cmd_list()` | List the APRS commands exposed by this MCP server |
| `aprs_send_message(to_callsign, message)` | Send an APRS message (max ~60 chars) |
| `aprs_position(callsign, timeout=120)` | Last known position, aprs.fi API first, live stream fallback |
| `aprs_nearby(lat, lon, radius_km, timeout=8)` | Stations transmitting within a radius |
| `aprs_listen(filter_text, duration=5, max_packets=50, raw=false)` | Stream raw or parsed packets against an APRS-IS filter |

### `aprs_listen` filter syntax

| Example | Meaning |
| --- | --- |
| `s/NOCALL` | Station NOCALL |
| `r/-33.9/18.4/50` | Radius filter around lat/lon, 50 km |
| `m/NOCALL` | Messages to/from NOCALL |
| `t/po` | Positions and objects only |

## Requirements

Requires Python 3.11+ and [uv](https://docs.astral.sh/uv/).

## Installation

```sh
git clone https://github.com/gflerm/aprs-mcp.git
cd aprs-mcp
uv sync

cp credentials.example credentials
```

On Windows PowerShell, the included installer creates the virtual environment
and credentials template:

```powershell
.\install.ps1
```

## MCP client configuration

### Codex

Register the local STDIO server globally:

```powershell
codex mcp add aprs -- ".venv\Scripts\aprs-mcp.exe"
```

On macOS or Linux, use `.venv/bin/aprs-mcp` instead. Restart Codex after
registration, then run `aprs_cmd_list` to verify the connection.

### Other clients

**opencode** — add to `~/.config/opencode/opencode.json`:

```json
{
  "mcp": {
    "aprs": {
      "type": "local",
      "command": [".venv/bin/aprs-mcp"],
      "enabled": true
    }
  }
}
```

**Claude Desktop** — add to `claude_desktop_config.json` under `mcpServers`.
**Any other MCP client** — point it at the `mcp` stdio transport with command
`.venv/bin/aprs-mcp` (Windows: `.venv\Scripts\aprs-mcp.exe`).

Restart the agent after registering the server.

## Credentials

The server reads network settings and station identity from a plain
`key=value` file named `credentials` in the project. Copy
`credentials.example` to `credentials` and fill in your own values.

The server looks for `credentials` in this order:

1. Next to the installed module (site-packages)
2. The project root (the checkout / install directory)
3. The current working directory

Existing environment variables always win; the file never overrides them.

Populate the `credentials` file as follows:

| Key | Type | Meaning |
| --- | --- | --- |
| `APRS_CALLSIGN` | required | Your base callsign, e.g. `ZL1ABC`. **Never transmit with `NOCALL`.** |
| `APRS_PASSCODE` | required | APRS-IS passcode for the callsign. Use `-1` for receive-only |
| `APRS_HOST` | optional | APRS-IS server, default `euro.aprs2.net` |
| `APRS_PORT` | optional | Default `14580` (140 for legacy tin) |
| `APRSFI_API_KEY` | optional | aprs.fi API key for fast `aprs_position` lookups |
| `APRS_NTP_HOST` | optional | NTP server for local timezone, default `xxx.xxx.xxx.xxx` |

**Getting your passcode.** The passcode is a one-way hash of your base
callsign — generate it once with the APRSPASS calculator shipped inside
`aprslib`:

```bash
python -c "from aprslib import passcode; print(passcode('ZL1XXXX'))"
```

Then write the result into `credentials`:

```
APRS_CALLSIGN=ZL1XXXX
APRS_PASSCODE=12345
APRS_HOST=euro.aprs2.net
APRS_PORT=14580
```

Optionally add your aprs.fi key (free, low-volume) so `aprs_position`
answers without waiting on the live stream.

`credentials` is your real file. `credentials.example` is the committed
template. Never commit `credentials`.

## Development

Install the project in its managed virtual environment:

```sh
uv sync
uv run python -m aprs_mcp.server
```

The MCP transport uses standard input/output. Client logs and diagnostics must
go to standard error so they do not corrupt the protocol stream.

## License

Released under the [MIT License](LICENSE).

TDQS

A3.9/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: sending messages, looking up positions, finding nearby stations, streaming raw packets, and listing commands. There is minimal overlap between these operations.

Naming Consistency3/5

All tools share the 'aprs_' prefix, but the pattern is inconsistent: some use verb_noun (send_message), some are bare verbs (listen), and others are nouns or adjectives (position, nearby). The naming is readable but does not follow a single consistent convention.

Tool Count5/5

With 5 tools, the server is well-scoped for its purpose. Each tool serves a distinct APRS-related function, and the count is neither overwhelming nor too sparse.

Completeness4/5

The toolset covers key APRS operations: sending messages, retrieving positions, discovering nearby stations, and listening to filtered packet streams. Missing advanced features like weather data or route history are minor gaps that do not impede core workflows.

Maintenance

ActivitySlowing
ResponsivenessNo issues