nexus-mcp
# nexus-mcp
[](LICENSE)
[](pyproject.toml)
[](https://github.com/Talya1412/nexus-mcp/actions/workflows/ci.yml)
[](https://pypi.org/project/nexus-mods-mcp/)
[](https://modelcontextprotocol.io)
**140 MCP tools** for [Nexus Mods](https://www.nexusmods.com) — wraps the official
**REST API v1** and **GraphQL API v2** as a Model Context Protocol server (Python +
FastMCP, stdio transport). Lets any MCP client (Claude Desktop, opencode, Cursor, ...)
browse games, inspect mods and files, run free-text searches, download mod files with
checksum verification, manage endorsements, comments, collections, and user preferences.
## Highlights
- **Full API coverage** — 74 read tools + 66 mutations across v1 REST and v2 GraphQL.
Includes things v1 doesn't offer: free-text mod search, batch mod lookups, comment
threads, collection lifecycle, and quota-free GraphQL reads.
- **Built-in TTL cache** — repeated identical GETs within a session don't consume quota
(games 1h, mod/file data 5 min, GraphQL POSTs 60 s). Personal state (`/user/*`) is
never cached.
- **Dual authentication** — personal API key out of the box; optional OAuth2 (PKCE S256)
with auto-refresh for user-context mutations that API keys cannot perform.
- **Safe downloads** — `nexus_download_mod_file` streams from the CDN to disk, verifies
MD5 + SHA-256 checksums against the expected hashes, and enforces a configurable size cap.
- **Rate-limit transparency** — every v1 response carries an `_rl` snapshot of Nexus'
hourly/daily limit headers.
## Install
```bash
# uv (recommended for MCP servers)
uvx --from nexus-mods-mcp nexus-mcp
# pipx
pipx install nexus-mods-mcp
# pip
pip install nexus-mods-mcp
```
Or from a cloned repository:
```bash
pip install -r requirements.txt
python -m nexus_mcp
```
## Works with every MCP harness
Create an API key at <https://www.nexusmods.com/users/myaccount?tab=api%20access>,
then pick your harness below. The server is a single stdio process — no ports,
no daemons, no database.
**Zero-install (recommended):** run from PyPI with `uvx` — nothing to clone,
no venv to manage, auto-fetched on first run:
```json
{
"mcpServers": {
"nexus": {
"command": "uvx",
"args": ["--from", "nexus-mods-mcp", "nexus-mcp"],
"env": { "NEXUS_API_KEY": "<your-key>" }
}
}
}
```
If you installed locally (`pipx install nexus-mods-mcp`),
use `"command": "nexus-mcp"` without the `uvx` wrapper instead.
<details>
<summary>Claude Code (one-liner)</summary>
```bash
claude mcp add nexus -e NEXUS_API_KEY=<your-key> -- uvx --from nexus-mods-mcp nexus-mcp
```
</details>
<details>
<summary>opencode (<code>opencode.json</code>)</summary>
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"nexus": {
"type": "local",
"command": ["uvx", "--from", "nexus-mods-mcp", "nexus-mcp"],
"enabled": true,
"environment": {
"NEXUS_API_KEY": "<your-key>"
}
}
}
}
```
</details>
<details>
<summary>Claude Desktop (<code>claude_desktop_config.json</code>)</summary>
```json
{
"mcpServers": {
"nexus": {
"command": "uvx",
"args": ["--from", "nexus-mods-mcp", "nexus-mcp"],
"env": {
"NEXUS_API_KEY": "<your-key>"
}
}
}
}
```
</details>
<details>
<summary>Cursor / Windsurf / Cline (any <code>mcpServers</code> JSON)</summary>
Same shape as the generic JSON above. Paste the `mcpServers` block into:
Cursor — `.cursor/mcp.json` · Windsurf — `~/.codeium/windsurf/mcp_config.json` ·
Cline — extension MCP server settings.
</details>
<details>
<summary>VS Code (<code>.vscode/mcp.json</code>)</summary>
```json
{
"servers": {
"nexus": {
"type": "stdio",
"command": "uvx",
"args": ["--from", "nexus-mods-mcp", "nexus-mcp"],
"env": {
"NEXUS_API_KEY": "<your-key>"
}
}
}
}
```
</details>
<details>
<summary>Gemini CLI (<code>~/.gemini/settings.json</code>)</summary>
```json
{
"mcpServers": {
"nexus": {
"command": "uvx",
"args": ["--from", "nexus-mods-mcp", "nexus-mcp"],
"env": {
"NEXUS_API_KEY": "<your-key>"
}
}
}
}
```
</details>
<details>
<summary>Codex CLI (<code>~/.codex/config.toml</code>)</summary>
```toml
[mcp_servers.nexus]
command = "uvx"
args = ["--from", "nexus-mods-mcp", "nexus-mcp"]
env = { "NEXUS_API_KEY" = "<your-key>" }
```
</details>
### Environment variables
| Variable | Required | Description |
|---|---|---|
| `NEXUS_API_KEY` | yes | Personal API key from Nexus Mods |
| `NEXUS_OAUTH_CLIENT_ID` | no | OAuth client ID (see below) |
| `NEXUS_OAUTH_CLIENT_SECRET` | no | Only for non-public OAuth apps |
| `NEXUS_OAUTH_REDIRECT_URI` | no | Defaults to `http://localhost/callback` |
| `NEXUS_OAUTH_TOKEN_FILE` | no | Token store path, defaults to `~/.nexus-mcp/oauth-tokens.json` |
| `NEXUS_MCP_TOOLS` | no | Tool profile: `all` (default), `read` (74 read-only tools only), or `rw` (all but the 12 destructive tools) |
## Authentication
**API key** works for all public reads and most mutations. Note that the key itself is
*not* an authorization scope: some user-context mutations (e.g.
`nexus_update_mod_direct_download`) are rejected with API-key auth even for the mod's
owner — Nexus requires an OAuth user context for those.
**OAuth (optional).** Nexus has no self-service OAuth app registration; email
`support@nexusmods.com` with your app name, description, logo, source link, and callback
URI to obtain a `client_id`. Then run the two-step flow:
1. `nexus_oauth_login` → returns an `authorize_url` (state + PKCE challenge); open it,
log in, and copy the `code` from the redirect URL.
2. `nexus_oauth_exchange(code)` → exchanges the code for tokens, persists them to
`NEXUS_OAUTH_TOKEN_FILE`, and validates your identity.
Tokens last ~6 h and auto-refresh (a 4xx refresh response is treated as revocation and
falls back to API-key auth). Companion tools: `nexus_oauth_status`, `nexus_oauth_refresh`,
`nexus_oauth_logout`. The `public` scope is sufficient; Bearer auth is accepted on both
v1 REST and v2 GraphQL.
## Rate limits & quota
- v1 REST: **2000 requests/hour**, **20 000/day** per key. Every v1 response includes an
`_rl` snapshot (`X-RL-*` headers) so agents can self-throttle.
- v2 GraphQL has its **own rate-limit pool** and does not consume v1 quota — prefer v2
tools (`nexus_search_mods`, `nexus_get_mod_v2`, `nexus_get_mods_batch`, ...) for
searches and public data.
- The server-side TTL cache (see Highlights) further reduces quota usage for repeated
reads within a session.
## Tool catalog
### v1 REST (core)
| Tool | Description |
|---|---|
| `nexus_validate_key` | Check API key + account info (exempt from rate limits) |
| `nexus_get_games` / `nexus_get_game` | Game catalog (substring filter) / one game + categories |
| `nexus_get_mod` / `nexus_get_mod_changelogs` | Mod details (endorsements, downloads, description) / changelog per version |
| `nexus_get_latest_added` / `nexus_get_latest_updated` / `nexus_get_trending` | 10 newest / updated / trending mods for a game |
| `nexus_get_updated_mods` | Mods with activity in the last `1d`/`1w`/`1m` |
| `nexus_get_mod_files` / `nexus_get_file_info` | Mod file list (category filter) / one file's details (MD5, size, version) |
| `nexus_get_download_link` | Short-lived CDN download URL (non-premium needs `key`+`expires` from a `.nxm` link) |
| `nexus_download_mod_file` | Stream a file to disk (MD5+SHA-256 verification, `max_bytes` cap) |
| `nexus_search_by_md5` | Identify a mod/file from an MD5 hash |
| `nexus_get_tracked_mods` / `nexus_track_mod` / `nexus_untrack_mod` | Manage tracked mods |
| `nexus_get_endorsements` / `nexus_endorse_mod` / `nexus_abstain_endorsement` | Manage endorsements |
### v2 GraphQL (does not consume v1 quota)
| Tool | Description |
|---|---|
| `nexus_search_mods` | Free-text mod search (wildcard) + game/endorsement/download filters + sort + pagination |
| `nexus_get_mod_v2` | Full mod details: raw BBCode description, tags, requirements, complete file list |
| `nexus_get_mods_batch` | Resolve many mods in one query: `"domain:modId,domain:modId"` |
| `nexus_get_mod_endorsers` | Users who endorsed a mod (cursor pagination) |
| `nexus_search_games` / `nexus_get_game_v2` | Game search / rich game details (genre, forum, counts, Vortex support) |
| `nexus_resolve_domain` | Resolve a game display name (e.g. 'Skyrim Special Edition') to its `domain_name` slug — call before any tool needing `domain_name` |
| `nexus_get_files_v2` / `nexus_get_files_by_uid` | File lists via v2 (by domain/modId or by uid) |
| `nexus_search_users` / `nexus_get_user_v2` / `nexus_get_user_by_name` | User search (fuzzy) / public profile by id or username / exact username lookup |
| `nexus_search_collections` / `nexus_get_collection` / `nexus_get_collection_revision` | Collection search / details by slug / single revision |
| `nexus_get_categories` / `nexus_get_category_by_id` / `nexus_get_collection_games` | Collection categories (per-game or global) |
| `nexus_get_tags` / `nexus_get_tags_v2` / `nexus_get_tag_by_id` / `nexus_get_tag_categories` / `nexus_get_tag_category_by_id` | Tag taxonomy |
| `nexus_get_comment_thread` / `nexus_get_comment` / `nexus_search_comments` | Comment reads (⚠️ `search_comments` is 500-ing server-side at Nexus; use `get_comment_thread` instead) |
| `nexus_get_badges` | Static catalog of earnable mod badges |
| `nexus_get_news` | Site/game news, interviews, features (filter by category/game) |
| `nexus_graphql_query` / `nexus_graphql_introspect` | Raw GraphQL escape hatch / schema introspection |
| `nexus_get_age_verification_info` / `nexus_get_api_applications` / `nexus_get_current_warnings` | Account state reads |
| `nexus_get_external_video` / `nexus_get_game_artwork` / `nexus_get_legacy_mods` / `nexus_get_file_hash(es)` | Media + legacy lookups |
| `nexus_search_media` | Site-wide media search (⚠️ Nexus endpoint is intermittently flaky — retry) |
| `nexus_get_preferences` / `nexus_update_preferences` | Site preferences (emails, default tabs, download location...) |
| `nexus_get_user_donation_preferences` / `nexus_update_user_donation_preferences` | Donation Points preferences |
| `nexus_get_favourite_games` / `nexus_add_favourite_game` / `nexus_remove_favourite_game` | Favourite games |
| `nexus_get_ignored_users` / `nexus_ignore_user` / `nexus_unignore_user` | Ignored users |
| `nexus_get_blocked_tags` / `nexus_block_tag` / `nexus_unblock_tag` | Blocked tags |
| `nexus_track_user` / `nexus_untrack_user` | Track/untrack users for update notifications |
| `nexus_give_kudos` / `nexus_remove_kudos` | Kudos |
| `nexus_like_comment` / `nexus_remove_comment_like` | Comment likes |
| `nexus_create_comment` / `nexus_edit_comment` / `nexus_discard_comment` / `nexus_restore_comment` | Comment mutations (nested replies via `reply_to_id`) |
| `nexus_create_message` / `nexus_upload_attachment` | Private messages (multipart attachments) |
| `nexus_update_about_me` / `nexus_update_country` | Profile updates |
| `nexus_get_speedtest_urls` | CDN speedtest endpoints |
| `nexus_get_opted_in_mods` / `nexus_get_transactions` / `nexus_get_user_monthly_summary` / `nexus_get_user_monthly_report` / `nexus_get_user_monthly_report_by_id` | Donation Points reporting (⚠️ some data is hidden under API-key auth — OAuth required) |
| `nexus_get_uploads` | Upload activity with scan status |
### Collections, moderation & misc mutations
| Tool | Description |
|---|---|
| `nexus_create_collection` / `nexus_edit_collection` | Create/edit collections (manifest, BBCode description) |
| `nexus_create_or_update_revision` / `nexus_update_revision` / `nexus_publish_revision` / `nexus_retract_revision` / `nexus_discard_revision` | Revision lifecycle |
| `nexus_list_collection` / `nexus_unlist_collection` / `nexus_discard_collection` | Collection visibility lifecycle |
| `nexus_create_changelog` / `nexus_update_changelog` | Revision changelogs |
| `nexus_create_tag` / `nexus_update_tag` / `nexus_discard_tag` | Tag management (moderator) |
| `nexus_add_badge_to_collection` / `nexus_remove_badge_from_collection` / `nexus_reorder_item` | Collection content management |
| `nexus_close_collection_bug_report` | Close bug reports on your collections |
| `nexus_submit_moderation_fix` | Submit fixes for moderated content |
| `nexus_hide_comment` / `nexus_lock_comment` / `nexus_lock_comment_thread` / `nexus_pin_comment` / `nexus_unpin_comment` / `nexus_reorder_pinned_comments` / `nexus_clear_comment_moderation_status` / `nexus_clear_comment_thread_moderation_status` | Comment moderation (moderator/owner) |
| `nexus_block_mods_from_earning_dp` / `nexus_unblock_mods_from_earning_dp` | Donation Points earning control (moderator) |
| `nexus_track_app_metric` | App metrics (e.g. Vortex collection installs) |
| `nexus_update_mod_direct_download` | Toggle direct downloads on your own mods (**OAuth required**) |
| `nexus_start_age_verification_flow` / `nexus_start_age_verification_appeal_flow` | Age verification flows |
| `nexus_request_media_upload_url` / `nexus_get_collection_revision_upload_url` | Presigned upload URLs |
| `nexus_oauth_login` / `nexus_oauth_exchange` / `nexus_oauth_status` / `nexus_oauth_refresh` / `nexus_oauth_logout` | OAuth2 flow (PKCE S256, auto-refresh, apikey fallback) |
## API quirks worth knowing
- `domain_name` is the lowercase URL slug (e.g. `forzahorizon6`, `skyrimse`) — **not**
the display name.
- Download links are short-lived; don't cache them. Non-premium accounts must pass the
`key`/`expires` pair extracted from a `.nxm` link generated on the website.
- Preference mutations (`ignore_user`, `block_tag`, ...) apply immediately but list reads
can lag a few seconds behind (eventual consistency).
- `nexus_discard_comment` is effectively one-way under API-key auth: restoring requires
OAuth Bearer auth.
- The v1 categories endpoint (`/v1/games/{domain}/categories.json`) was removed by Nexus
— categories are only available via v2.
- `nexus_search_comments` currently returns HTTP 500 from Nexus itself, regardless of
parameters; `nexus_get_comment_thread` is the working alternative.
- Web scraping (nexus_scrape_page, nexus_scrape_mod_page, nexus_scrape_mod_posts): read public
nexusmods.com pages without an API key and consuming no quota - HTTPS only, SSRF-guarded;
some pages may still be Cloudflare-blocked, in which case use the API tools instead.
## Development
```bash
pip install -e ".[dev]" # editable install with pytest + ruff
python -m pytest # 95-test suite,, fully offline (mocked HTTP)
python -m ruff check nexus_mcp tests
python -m nexus_mcp # run on stdio
```
CI runs on every push: ruff lint, pytest across ubuntu/windows (Python 3.10–3.12),
pip-audit, build + twine check + wheel smoke test, and an optional live API smoke test.
Tagged releases (`v*`) publish to PyPI automatically.
## Contributing
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, the
tool-authoring checklist, and conventions. Bug reports and feature requests use the
issue templates; questions and ideas go to
[Discussions](https://github.com/Talya1412/nexus-mcp/discussions); security issues go
through
[private vulnerability reporting](https://github.com/Talya1412/nexus-mcp/security/advisories/new).
## License
[MIT](LICENSE)
TDQS
Scored across 135 tools
Several clusters of tools have nearly identical purposes: mod file listings (nexus_get_mod_files, nexus_get_files_v2, nexus_get_files_by_uid), MD5 lookups (nexus_search_by_md5, nexus_get_file_hash, nexus_get_file_hashes), and exact user lookups (nexus_get_user_v2, nexus_get_user_by_name). The descriptions are thorough and often point out differences, but agents must read carefully to avoid mis-selecting, and v1/v2 duplicates create genuine boundary ambiguity.
Almost every tool follows a nexus_verb_noun snake_case pattern, and subdomain prefixes like oauth_ and graphql_ are internally consistent. Minor deviations from strict verb-first structure and inconsistently applied _v2 suffixes prevent a perfect score.
135 tools is an extreme count by any MCP standard, far above the 50+ threshold for a mismatch. Even for a broad platform API, this creates severe navigation and selection burden and would be better split into focused servers or consolidated tool groups.
The surface covers lifecycle operations for mods, files, collections, revisions, comments, tags, users, preferences, OAuth, moderation, and more, plus a raw GraphQL introspect/query escape hatch. There are no obvious dead ends for the domain this server targets.