Caltopo Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Caltopo Mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
HTTP MCP server for managing CalTopo maps from headless agents.
This server is designed for a Hermes/Codex-style agent that needs to list maps, create folders, manage markers, and import/export GPX/KML without using a browser session.
CalTopo does not publicly document this private-account API for general third-party use. Treat the integration as pragmatic and test bulk writes on disposable maps first.
Canonical repository:
https://github.com/sttts/caltopo-mcpDo not commit local credentials. Runtime secrets belong in .data/, environment variables, or a local secret manager.
On the Hermes Mac mini, the bundled Node runtime is expected at:
/Users/hermes/.hermes/node/bin/nodeThe server supports two credential modes.
Preferred for local operation:
CALTOPO_ACTIVATION_FILE=.data/activation.local.jsonThe activation file has this shape:
{
"account": {
"id": "..."
},
"code": "...",
"key": "..."
}Keep the file outside Git:
mkdir -p .data
chmod 700 .data
chmod 600 .data/activation.local.json.data/ is ignored by Git.
Alternative for service managers or secret stores:
CALTOPO_ACCOUNT_ID=...
CALTOPO_CREDENTIAL_ID=...
CALTOPO_CREDENTIAL_KEY=...Never hardcode these values in source files, plist files, shell scripts, README examples, or committed config.
Open an activation URL in a browser where you are logged in to CalTopo:
https://caltopo.com/app/activate/localhost/client/finish-activate?code=<CODE>&name=mcpThen resolve the code locally:
CALTOPO_ACTIVATION_CODE=<CODE> python3 - <<'PY'
import os
import urllib.request
code = os.environ["CALTOPO_ACTIVATION_CODE"]
path = ".data/activation.local.json"
os.makedirs(".data", exist_ok=True)
with urllib.request.urlopen(f"https://caltopo.com/api/v1/activate?code={code}", timeout=20) as response:
data = response.read()
fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
with os.fdopen(fd, "wb") as handle:
handle.write(data)
print(f"wrote {path}")
PYnpm install
npm run typecheck
npm run build
CALTOPO_ACTIVATION_FILE=.data/activation.local.json npm startDefault endpoint:
http://127.0.0.1:8788/mcpHealth check:
curl -sS http://127.0.0.1:8788/healthThe health endpoint intentionally does not return credential values or account IDs.
Environment variables:
CALTOPO_ACTIVATION_FILE=.data/activation.local.json
CALTOPO_ACCOUNT_ID=...
CALTOPO_CREDENTIAL_ID=...
CALTOPO_CREDENTIAL_KEY=...
CALTOPO_DOMAIN=caltopo.com
CALTOPO_MCP_HOST=127.0.0.1
CALTOPO_MCP_PORT=8788
CALTOPO_MCP_TOKEN=optional-bearer-tokenCALTOPO_MCP_TOKEN protects the MCP endpoint with:
Authorization: Bearer <token>Keep the server bound to 127.0.0.1 unless you also configure authentication and network-level access control.
caltopo_auth_statuscaltopo_list_mapscaltopo_get_mapcaltopo_list_folderscaltopo_create_foldercaltopo_update_foldercaltopo_delete_foldercaltopo_list_markerscaltopo_create_markercaltopo_update_markercaltopo_delete_markercaltopo_import_markerscaltopo_export_markersCreate a marker:
{
"map_id": "ABCDE12",
"title": "Camp",
"lat": 65.90993,
"lon": -23.84598,
"description": "Overnight stop",
"color": "#00AAFF",
"symbol": "point"
}Import GPX waypoints:
{
"map_id": "ABCDE12",
"format": "gpx",
"content": "<?xml version=\"1.0\"?><gpx><wpt lat=\"65.9\" lon=\"-23.8\"><name>Test</name></wpt></gpx>",
"dry_run": true
}Use dry_run: true before bulk imports.
Manual operation:
bin/start-caltopo-mcp.sh
bin/status-caltopo-mcp.sh
bin/stop-caltopo-mcp.shDo not use cron for this service. On macOS, use the LaunchDaemon in deploy/local.caltopo-mcp.plist if the server must survive reboots without an active UI login.
Recommended layout on the Mac mini:
/Users/hermes/Quellen/caltopo-mcp
/Users/hermes/Quellen/caltopo-mcp/.data/activation.local.jsonClone and build:
cd /Users/hermes/Quellen
git clone https://github.com/sttts/caltopo-mcp.git caltopo-mcp
cd caltopo-mcp
PATH=/Users/hermes/.hermes/node/bin:$PATH npm install
PATH=/Users/hermes/.hermes/node/bin:$PATH npm run buildStart manually:
cd /Users/hermes/Quellen/caltopo-mcp
bin/start-caltopo-mcp.shRegister with Hermes:
/Users/hermes/.local/bin/hermes mcp add caltopo --url http://127.0.0.1:8788/mcp
/Users/hermes/.local/bin/hermes mcp test caltopoFor reboot-safe headless operation, install the LaunchDaemon as root:
cd /Users/hermes/Quellen/caltopo-mcp
sudo bin/install-launchdaemon.shThe plist runs the process as hermes:
/Library/LaunchDaemons/local.caltopo-mcp.plistCheck the service:
launchctl print system/local.caltopo-mcp
bin/status-caltopo-mcp.sh
/Users/hermes/.local/bin/hermes mcp test caltopoRemove it:
sudo launchctl bootout system /Library/LaunchDaemons/local.caltopo-mcp.plist
sudo rm -f /Library/LaunchDaemons/local.caltopo-mcp.plistcd /Users/hermes/Quellen/caltopo-mcp
git pull --ff-only
PATH=/Users/hermes/.hermes/node/bin:$PATH npm install
PATH=/Users/hermes/.hermes/node/bin:$PATH npm run build
bin/stop-caltopo-mcp.sh
bin/start-caltopo-mcp.shIf the LaunchDaemon is installed:
sudo launchctl kickstart -k system/local.caltopo-mcp.data/ is ignored by Git.127.0.0.1.CALTOPO_MCP_TOKEN before exposing the endpoint beyond loopback.dry_run for imports before writing many markers.Before publishing:
git status --short
git ls-files
rg -n "activation\\.local|CALTOPO_CREDENTIAL|credentialKey|Bearer|token|gmail|account_id" . \
-g '!node_modules' -g '!dist' -g '!.data' -g '!.git'~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.