mountainproject
# mp-mcp
MCP server for [Mountain Project](https://www.mountainproject.com) — search routes and areas, run route-finder filters, and query your personal tick list live from MP.
Built for [Cursor](https://cursor.com) and any MCP-compatible agent.
## Disclaimer
**This project is not affiliated with, endorsed by, or connected to [Mountain Project](https://www.mountainproject.com) or [onX Maps, Inc.](https://www.onxmaps.com/)** Mountain Project is a trademark of onX Maps, Inc.
- **Personal use only.** This tool is intended for individual climbers to access their own tick data and browse public route information — not for bulk scraping, republishing MP content, or building a competing database.
- **Terms of service.** Users are responsible for complying with [Mountain Project's terms of service](https://www.mountainproject.com/about/terms). Automated access may be restricted or blocked by MP at any time.
- **Session security.** Your `ap_session` cookie is a login credential. Do not commit `~/.mp-mcp/session.json`, paste cookies into public chats, or store cookies in shared config files. The session file is written with restrictive permissions (`0600`), but treat it like a password.
- **No warranty.** This software is provided as-is under the MIT license. Route data accuracy remains the responsibility of Mountain Project and its community contributors.
## Features
- **Public route discovery** — search, route/area details, route-finder filters (no auth)
- **Personal logbook** — list, filter, and aggregate your ticks pulled live from MP
- **Browser login** — one-time `npm run login` captures your session automatically (no CSV export, no DevTools)
- **Polite by default** — rate limiting between requests
## Quick start (humans)
### 1. Install
```bash
git clone https://github.com/defevan/mp-mcp.git
cd mp-mcp
npm install
npm run build
npx playwright install chromium # first time only, for login
```
### 2. Log in to Mountain Project
```bash
npm run login
```
A browser opens to Mountain Project. Sign in normally — the script detects when you're logged in and saves `~/.mp-mcp/session.json`. The login tab will **not** reload while you sign in.
Re-run when the session expires (~30 days).
### 3. Add to Cursor
Add to `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"mountainproject": {
"command": "node",
"args": ["/absolute/path/to/mp-mcp/dist/server.js"]
}
}
}
```
Restart Cursor or reload MCP servers.
### 4. Try it
Ask in chat:
- "Search trad 5.10 routes in Yosemite Valley"
- "Get beta on The Nose"
- "List my ticks from 2024"
- "How many days out did I have this summer?"
---
## For agents
This section is for LLM agents using the MCP tools directly.
### Server
- **Name:** `mountainproject`
- **Transport:** stdio
- **Entry:** `node dist/server.js` (run `npm run build` first)
### Auth model
| Priority | Source | Notes |
|----------|--------|-------|
| 1 | In-memory session | Set via `mp_set_session` |
| 2 | `~/.mp-mcp/session.json` | Written by `npm run login` |
| 3 | Env vars | `MP_SESSION_COOKIE`, `MP_USER_ID` (CI fallback) |
Tick tools call `GET /user/{id}/{username}/tick-export` with the session cookie. Ticks are parsed in memory and cached (default 15 min). **Never ask the user to export a CSV manually.**
If auth fails, tell the user to run `npm run login` in the project directory or use `mp_set_session`.
### Tool reference
#### Public (no auth)
| Tool | Args | Returns |
|------|------|---------|
| `mp_search` | `query`, `types?` (`routes`/`areas`), `limit?` | Matching routes/areas with id, grade, stars, url, lat/lng |
| `mp_get_route` | `routeId` | Full route detail: beta, protection, FA, parent area |
| `mp_get_area` | `areaId` | Area description, access notes, route counts |
| `mp_find_routes` | `areaId`, `gradeMin?`, `gradeMax?`, `type?`, `styles?`, `minStars?`, `sort?`, `limit?` | Filtered routes from route-finder (CSV export, max 1000) |
| `mp_resolve_id` | `urlOrId` | Parse numeric ID from MP URL or `routes.123` / `areas.456` |
#### Authenticated
| Tool | Args | Returns |
|------|------|---------|
| `mp_auth_status` | — | `{ authenticated, userId, username, tickCount? }` |
| `mp_set_session` | `apSession`, `xsrfToken?`, `userId?`, `username?`, `persist?` | Update session at runtime |
| `mp_fetch_ticks` | — | Force-refresh tick list from MP |
| `mp_list_ticks` | `year?`, `gradeMin?`, `gradeMax?`, `style?`, `areaContains?`, `limit?` | Filtered ticks (auto-fetches if cache stale) |
| `mp_tick_stats` | `groupBy` (`year`/`grade`/`style`/`area`), `yearFrom?`, `yearTo?` | Aggregated counts |
### Agent workflow examples
**Trip planning (no auth):**
```
mp_search → mp_resolve_id → mp_find_routes / mp_get_route
```
**Logbook analysis (auth required):**
```
mp_auth_status → mp_list_ticks / mp_tick_stats
```
**Area deep-dive:**
```
mp_search(query, types=["areas"]) → mp_get_area → mp_find_routes(areaId, gradeMin, gradeMax)
```
### Grade filters for `mp_find_routes`
Use YDS strings: `5.8`, `5.10a`, `5.11d`, etc. The server maps these to MP's internal route-finder rank codes.
### Rate limiting
Default 500 ms between MP requests (`MP_RATE_LIMIT_MS`). Prefer fewer, broader tool calls over many narrow ones.
### Data source notes
- Public data: MP `/api/v2/*` and `/route-finder-export`
- Ticks: authenticated `/user/{id}/{username}/tick-export`
- MP's official Data API is deprecated; this server uses the same endpoints the website uses
- Respect MP's terms of service; this tool is for personal use
---
## Environment variables
| Variable | Default | Description |
|----------|---------|-------------|
| `MP_SESSION_FILE` | `~/.mp-mcp/session.json` | Session storage path |
| `MP_TICKS_CACHE_TTL_MS` | `900000` | Tick cache TTL (15 min) |
| `MP_RATE_LIMIT_MS` | `500` | Delay between MP requests |
| `MP_USER_AGENT` | `mp-mcp/1.0` | HTTP User-Agent |
| `MP_SESSION_COOKIE` | — | Fallback session cookie |
| `MP_USER_ID` | — | Fallback user ID |
| `MP_XSRF_TOKEN` | — | Optional CSRF cookie |
| `MP_USERNAME` | — | Optional username slug |
---
## Development
```bash
npm run build # compile TypeScript → dist/
npm run dev # run server via tsx (stdio)
npm run login # browser auth flow
```
Smoke test:
```bash
node -e "import('./dist/mp-client.js').then(m => m.getRoute(105924807).then(console.log))"
```
---
## Session refresh
When tools return auth errors:
```bash
npm run login
```
Or call `mp_set_session` with a fresh `ap_session` cookie value.
---
## License
MIT — Copyright (c) 2026 Evan Jones. See [LICENSE](LICENSE).
TDQS
Scored across 10 tools
Most tools are clearly distinguished by resource and action (search, get_route, get_area, find_routes, resolve_id, tick operations). Minor overlap exists between mp_search and mp_find_routes (both find routes, though in different contexts) and between mp_fetch_ticks and mp_list_ticks (list auto-fetches, fetch forces refresh), but descriptions clarify the boundaries.
All tools share the mp_ prefix and most follow verb_noun structure (mp_search, mp_get_route, mp_set_session, mp_fetch_ticks, mp_list_ticks). Two names deviate: mp_auth_status (noun phrase) and mp_tick_stats (noun phrase), which break the pattern slightly but remain predictable and readable.
Ten tools is well-scoped for a climbing data and personal tick-list server. Each tool covers a distinct aspect of the domain—searching, retrieving, resolving IDs, session management, and tick lifecycle—without unnecessary redundancy or bloat.
The tool surface covers the core read-only workflows for Mountain Project: searching and fetching routes/areas, resolving URLs to IDs, and managing personal tick data (auth, session, refresh, list, stats). No obvious dead ends; the main missing operations like creating/deleting ticks or routes are external to the API's likely scope.