Skip to content

straps-eq/eqemu-mcp-server

Repository files navigation

Version Python License Tools

EQEmu MCP Server

Give your AI assistant full context on your EverQuest Emulator server — source code, quest API, database schema, documentation, and live game data — so it can write accurate queries, debug issues, and manage content without guessing.

A Model Context Protocol (MCP) server for EverQuest Emulator servers. Works with Windsurf, VS Code, Cursor, OpenAI Codex, Claude Desktop, and any MCP-compatible AI client.


What's Included

Your AI assistant gets access to everything it needs to understand and operate your EQEmu server:

Data Source What Your AI Can Access
📚 EQEmu Documentation Full-text search across all 638 pages of docs.eqemu.io — server operation guides, configuration references, quest tutorials, and more
🗄️ Database Schema Docs Detailed documentation for all 261 tables across 48 categories — column names, types, descriptions, and ER relationship diagrams
🔍 Live Database Read-only SQL queries against your actual game database — NPCs, items, spawns, loot, characters, accounts, zones, spells, factions, tasks, tradeskills
💻 C++ Source Code Full-text search across the EQEmu C++ codebase — find how mechanics work, trace calculations, understand game systems
📜 Quest Scripts Browse and search all Perl/Lua quest scripts across every zone — read quest logic, find event handlers, trace quest flows
🔧 Quest API Reference Complete Lua and Perl quest API documentation — method signatures, event lists, and examples for all 30+ method classes
⚙️ Server Configuration Server rules, content flags, expansion settings, eqemu_config.json (with passwords auto-redacted)
📋 Server Logs Recent server logs and crash logs for debugging
👤 Characters & Accounts Character inspection (stats, AAs, inventory, guild), account details, IP history, alt/multibox detection
🗺️ Zone Details Zone information with spawn counts, NPC lists, doors/portals, ground spawns, forage/fishing tables, patrol grids

No guessing. Your AI looks up the exact schema, follows the correct relationships, and writes accurate queries on the first try.


Why?

When you ask an AI assistant to write a query, inspect spawn data, or debug a quest issue, it has to guess table names, column names, relationships, and valid values. It gets things wrong. You end up copy-pasting schema docs and correcting hallucinated SQL.

This MCP server eliminates that. Your AI assistant can:

  • Look up exact schemaget_schema_doc("npc_types") returns all 80+ columns with types and descriptions
  • Follow relationshipstable_relationships("spawn2") shows FK links through the entire spawn chain
  • Search documentationsearch_docs("loottable_id") finds every doc page referencing that column
  • Query live datarun_query("SELECT * FROM npc_types WHERE name LIKE '%Nagafen%'") hits your actual database
  • Inspect full chains — NPC → faction → loot → spawn group → grid path, all from structured tools

The result: correct queries on the first try, accurate quest scripts, and faster debugging.


Tools (60+)

Read-Only (always available)

Category Tools Description
C++ Source search_source get_source_file list_source_files Search and browse the EQEmu C++ codebase
Quest API list_quest_api_classes get_quest_api_methods Browse Lua/Perl quest API method signatures
Quest Scripts list_quest_zones list_quest_files read_quest_file search_quests Browse and search quest scripts across all zones
Server Files list_server_files read_server_file get_server_config Server files, plugins, config (passwords redacted)
Server Info get_server_rules get_server_logs get_crash_logs get_content_flags get_expansion_info Rules, logs, crash analysis, content flags
Database list_tables describe_table run_query table_relationships Schema inspection and read-only SQL
NPCs search_npcs get_npc NPC search by name/zone/level with spawn locations
Items search_items get_item search_items_by_stat Item search by name/type/level or stat thresholds
Spawns get_zone_spawns get_spawngroup Spawn points, spawn groups, placeholder/named setups
Loot get_npc_loot Full loot chain: NPC → loottable → lootdrop → items
Merchants get_merchant_items Merchant inventories
Zones search_zones get_zone_info Zone lookup with spawn/NPC/door counts
Spells search_spells get_spell Spell search and full effect/class breakdown
Factions search_factions get_npc_faction Faction search and NPC faction kill-hit details
Tasks search_tasks get_task Task search and full activity/reward breakdown
Characters list_characters get_character get_online_characters Character inspection, stats, AAs, inventory, online players
Accounts get_account_info find_associated_accounts Account investigation, IP history, alt detection
Tradeskills search_recipes get_recipe Recipe search and component/result breakdown
Grids get_npc_grid NPC patrol paths with waypoint coordinates
Doors get_zone_doors Zone doors/portals with destinations, keys, lockpick
Ground Spawns get_ground_spawns Clickable ground items in a zone
Forage/Fishing get_zone_forage_fishing Zone forage and fishing loot tables
Documentation search_docs read_doc list_doc_sections Full-text search across docs.eqemu.io
Schema Docs get_schema_doc list_schema_tables Table docs with column descriptions and ER relationships
Quest API Docs get_quest_api_doc Official quest API docs with signatures and examples
Server Docs get_server_doc Server operation guides and command references

Write Tools (opt-in)

Enable with EQEMU_ACCESS_MODE=readwrite:

Category Tools Description
Quest Editing write_quest_file delete_quest_file Create, edit, or delete quest scripts
Server Rules set_server_rule Change server rule values
Content Flags set_content_flag Enable/disable content flags
NPC Management create_npc update_npc Create or modify NPCs
Spawn Management create_spawn delete_spawn Add/remove spawn points
Loot Management add_loot_to_npc Add items to NPC loot tables
Merchants add_merchant_item remove_merchant_item Manage merchant inventories
Data Buckets get_data_buckets set_data_bucket Read/write data buckets
Database run_write_query Execute INSERT/UPDATE/DELETE queries

Quick Start

Prerequisites

  • Python 3.10+
  • Access to an EQEmu server (akk-stack recommended)
  • ripgrep (optional — for fast source code search, falls back to grep)

Option A: Docker (Recommended)

git clone https://github.com/straps-eq/eqemu-mcp-server.git
cd eqemu-mcp-server

cp .env.example .env
# Edit .env with your database credentials and paths

docker compose up -d

The server starts on port 8888. Connect your AI client to http://YOUR_SERVER_IP:8888/sse.

akk-stack Integration

If you're running akk-stack, the MCP server can join your existing Docker network and talk to MariaDB directly. No separate .env file is needed — the compose overlay reads credentials from your akk-stack .env automatically.

Step 1: Clone into your akk-stack directory

cd /opt/akk-stack
git clone https://github.com/straps-eq/eqemu-mcp-server.git

Step 2 (optional): Add token authentication

To require a token for connections, add this to your akk-stack .env:

echo "EQEMU_MCP_TOKEN=$(openssl rand -hex 32)" >> /opt/akk-stack/.env
# View the generated token:
grep EQEMU_MCP_TOKEN /opt/akk-stack/.env

Step 3: Build and start the MCP container

cd /opt/akk-stack

# Build the image
docker compose -f docker-compose.yml \
  -f eqemu-mcp-server/docker-compose.akk-stack.yml \
  build eqemu-mcp

# Start (only the MCP container — does NOT restart your game server)
docker compose -f docker-compose.yml \
  -f eqemu-mcp-server/docker-compose.akk-stack.yml \
  up -d --no-deps eqemu-mcp

⚠️ Important: Always use --no-deps eqemu-mcp to start only the MCP container. Without --no-deps, Docker Compose may recreate your MariaDB and EQEmu server containers, causing a server restart.

Step 4: Open the firewall

sudo ufw allow 8888/tcp

Step 5: Verify

docker logs akk-stack-eqemu-mcp-1 --tail 5
# Should show: "Uvicorn running on http://0.0.0.0:8888"

The MCP server is read-only by default. It automatically:

  • Connects to MariaDB via the backend network (no external IP needed)
  • Mounts your code/ and server/ directories read-only
  • Uses your existing MARIADB_PASSWORD from the akk-stack .env

Configuration: All settings go in your akk-stack .env (/opt/akk-stack/.env) — you do not create a separate .env inside the eqemu-mcp-server/ folder. Available variables:

Variable Default Description
MARIADB_PASSWORD (from akk-stack) Database password — already in your .env
IP_ADDRESS 0.0.0.0 Bind address — already in your .env
EQEMU_DB_NAME peq Database name — set this if your DB isn't named peq
EQEMU_DB_USER eqemu Database user — set to a read-only user for extra safety (see Read-Only DB User)
EQEMU_DB_PASSWORD ${MARIADB_PASSWORD} Database password — override if using a separate read-only user
EQEMU_MCP_TOKEN (empty) Set to require token auth on connections
MCP_ACCESS_MODE read Set to readwrite to enable write tools

Option B: Manual Install (No Docker)

git clone https://github.com/straps-eq/eqemu-mcp-server.git
cd eqemu-mcp-server

python3 -m venv venv
source venv/bin/activate
pip install -e .

cp .env.example .env
# Edit .env with your server paths and database credentials

# Start SSE server
./start.sh --sse 8888

Finding Your Database Credentials

akk-stack users:

cd /opt/akk-stack && make info

Or read eqemu_config.json:

cat /opt/akk-stack/server/eqemu_config.json | python3 -m json.tool

Note: If running the MCP server outside Docker (directly on the host), use the host's external IP for EQEMU_DB_HOST, not 127.0.0.1 or mariadb.


Connecting Your AI Client

Option 1: SSE (Recommended for Remote)

Start the server:

./start.sh --sse 8888

Then configure your AI client:

Windsurf

Edit ~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "eqemu": {
      "serverUrl": "http://YOUR_SERVER_IP:8888/sse"
    }
  }
}

With token authentication enabled:

{
  "mcpServers": {
    "eqemu": {
      "serverUrl": "http://YOUR_SERVER_IP:8888/sse",
      "headers": {
        "Authorization": "Bearer YOUR_TOKEN"
      }
    }
  }
}
Cursor

In Settings → MCP Servers, add:

{
  "mcpServers": {
    "eqemu": {
      "url": "http://YOUR_SERVER_IP:8888/sse"
    }
  }
}
VS Code (GitHub Copilot)

Open Command Palette → "MCP: Open User Configuration" (or edit .vscode/mcp.json in your workspace):

{
  "servers": {
    "eqemu": {
      "type": "sse",
      "url": "http://YOUR_SERVER_IP:8888/sse"
    }
  }
}

With token authentication:

{
  "servers": {
    "eqemu": {
      "type": "sse",
      "url": "http://YOUR_SERVER_IP:8888/sse",
      "headers": {
        "Authorization": "Bearer YOUR_TOKEN"
      }
    }
  }
}
OpenAI Codex CLI

Run codex mcp add or edit ~/.codex/config.toml:

[mcp_servers.eqemu]
url = "http://YOUR_SERVER_IP:8888/sse"
enabled = true

[mcp_servers.eqemu.env]
MCP_TOKEN = "YOUR_TOKEN"

Or with token via environment variable:

[mcp_servers.eqemu]
url = "http://YOUR_SERVER_IP:8888/sse"
bearer_token_env_var = "MCP_TOKEN"
enabled = true

Then set export MCP_TOKEN=YOUR_TOKEN in your shell.

Claude Desktop

Claude Desktop doesn't natively support SSE. Use stdio mode instead (Option 2), or use mcp-proxy to bridge SSE to stdio.

Option 2: stdio (Local)

If the MCP server is on the same machine as your AI client:

{
  "mcpServers": {
    "eqemu": {
      "command": "/path/to/venv/bin/python",
      "args": ["/path/to/eqemu-mcp-server/server.py"],
      "env": {
        "EQEMU_SOURCE_PATH": "/opt/akk-stack/code",
        "EQEMU_QUESTS_PATH": "/opt/akk-stack/server/quests",
        "EQEMU_SERVER_PATH": "/opt/akk-stack/server",
        "EQEMU_DB_HOST": "YOUR_SERVER_IP",
        "EQEMU_DB_PORT": "3306",
        "EQEMU_DB_USER": "eqemu",
        "EQEMU_DB_PASSWORD": "YOUR_DB_PASSWORD",
        "EQEMU_DB_NAME": "peq",
        "RG_PATH": "/path/to/rg",
        "EQEMU_ACCESS_MODE": "read"
      }
    }
  }
}

Permission Model

Mode EQEMU_ACCESS_MODE Tools Use Case
Read-Only read (default) 50+ read-only Safe for sharing — SQL restricted to SELECT, passwords redacted
Read-Write readwrite All 60+ Server admins actively managing content
# In .env
EQEMU_ACCESS_MODE=read      # safe default
EQEMU_ACCESS_MODE=readwrite  # full access

Security

By default, the MCP server accepts connections from anyone who can reach the port. Use one or both of these methods to restrict access.

Option 1: Firewall (IP Restriction)

Use UFW to only allow specific IP addresses to connect:

# Remove any existing open rule
sudo ufw delete allow 8888/tcp

# Allow only your IP
sudo ufw allow from YOUR_HOME_IP to any port 8888 proto tcp

# Allow additional users
sudo ufw allow from FRIEND_IP to any port 8888 proto tcp

# Verify
sudo ufw status | grep 8888

This is the simplest approach — no code changes needed. To find your IP, visit https://whatismyip.com.

Option 2: Token Authentication

Require a secret token for all SSE connections. Set EQEMU_MCP_TOKEN in your .env:

# Generate a random token
EQEMU_MCP_TOKEN=$(openssl rand -hex 32)
echo "EQEMU_MCP_TOKEN=$EQEMU_MCP_TOKEN" >> .env
echo "Your token: $EQEMU_MCP_TOKEN"

Then restart the server. Clients must include the token in the URL:

Windsurf:

{
  "mcpServers": {
    "eqemu": {
      "serverUrl": "http://YOUR_SERVER_IP:8888/sse",
      "headers": {
        "Authorization": "Bearer YOUR_TOKEN"
      }
    }
  }
}

The token can also be passed as a query parameter (?token=YOUR_TOKEN) for clients that don't support custom headers. Without a valid token, the server returns 401 Unauthorized.

Option 3: Read-Only Database User (Recommended)

By default, the MCP server connects to MariaDB using the eqemu user, which has full read/write access. For production or shared deployments, create a dedicated read-only MySQL user so that no database writes are possible, even if a bug or prompt injection bypasses the application-level SQL validation.

Step 1: Create the read-only user

# For akk-stack (Docker):
docker exec -it akk-stack-mariadb-1 mysql -u root -p${MARIADB_PASSWORD} -e "
  CREATE USER 'eqemu_readonly'@'%' IDENTIFIED BY 'PICK_A_STRONG_PASSWORD';
  GRANT SELECT, SHOW DATABASES, SHOW VIEW ON \`YOUR_DB_NAME\`.* TO 'eqemu_readonly'@'%';
  FLUSH PRIVILEGES;
"

Replace YOUR_DB_NAME with your database name (e.g. peq, e9profusion_production) and PICK_A_STRONG_PASSWORD with a password of your choice.

Step 2: Add to your .env

EQEMU_DB_USER=eqemu_readonly
EQEMU_DB_PASSWORD=PICK_A_STRONG_PASSWORD

Step 3: Restart the MCP container

cd /opt/akk-stack
docker compose -f docker-compose.yml \
  -f eqemu-mcp-server/docker-compose.akk-stack.yml \
  up -d --no-deps --force-recreate eqemu-mcp

The MCP server now connects with SELECT-only permissions. Any write attempt — whether from the application, an AI, or a crafted query — will be rejected by MySQL itself.

Combining All Three

For maximum security, use all three layers:

  1. Firewall — restricts which IPs can connect
  2. Token auth — ensures only authorized clients can use the tools
  3. Read-only DB user — guarantees no database writes at the MySQL level, regardless of application behavior

Example Conversations

Once connected, your AI assistant can handle requests like:

Querying & Inspecting:

"Write me a query to find all NPCs in crushbone that drop cloth items"

"What's in Lord Nagafen's loot table?"

"Show me all items with haste >= 30"

"Who's online right now?"

Debugging & Investigation:

"Find all accounts that share IPs with the character Soandso"

"Show me the patrol path for the guard NPCs in Qeynos"

"What faction hits do you get for killing a Freeport Militia member?"

"Show me the most recent crash logs"

Quest Development:

"What Lua quest methods are available on the Client object?"

"Search the source for how spell resistance is calculated"

"Find all quest scripts that use eq.spawn2"

Content Management (write mode):

"Create a new NPC named Test_Merchant at level 30"

"Add a Cloth Cap to merchant 123's inventory"

"Set the AA:ExpPerPoint rule to 50000000"


Deployment on akk-stack

cd /opt/akk-stack

# Create venv
python3 -m venv eqemu-mcp-venv
source eqemu-mcp-venv/bin/activate
pip install mcp[cli] mysql-connector-python

# Install ripgrep (optional but recommended)
apt-get install -y ripgrep

# Clone documentation (for docs tools)
git clone --depth 1 https://github.com/EQEmu/eqemu-docs-v2.git eqemu-docs

# Clone and configure
git clone https://github.com/straps-eq/eqemu-mcp-server.git
cd eqemu-mcp-server
cp .env.example .env
# Edit .env with: DB host/password (from `make info`), paths, access mode

# Test
python server.py  # Ctrl+C to stop

# Run SSE
./start.sh --sse 8888

# Open the firewall port
sudo ufw allow 8888/tcp

Running as a systemd Service

sudo tee /etc/systemd/system/eqemu-mcp.service > /dev/null << 'EOF'
[Unit]
Description=EQEmu MCP Server
After=docker.service

[Service]
Type=simple
WorkingDirectory=/opt/akk-stack/eqemu-mcp-server
ExecStart=/opt/akk-stack/eqemu-mcp-server/start.sh --sse 8888
Restart=always
User=root

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl enable eqemu-mcp
sudo systemctl start eqemu-mcp

Architecture

eqemu-mcp-server/
  server.py                          # Entry point — registers tools based on access mode
  start.sh                           # Startup script (loads .env, activates venv)
  Dockerfile                         # Docker image for containerized deployment
  docker-compose.yml                 # Standalone Docker Compose
  docker-compose.akk-stack.yml       # akk-stack integration overlay
  eqemu_mcp/
    config.py                        # Centralized configuration from env vars
    helpers.py                       # Shared utilities (DB connections, ripgrep, file I/O)
    tools_source.py                  # C++ source code search and browsing
    tools_quest_api.py               # Lua/Perl quest API method parsing
    tools_quests.py                  # Quest script browsing + editing (write)
    tools_server.py                  # Server config, rules, logs, content flags
    tools_database.py                # Schema inspection and SQL queries
    tools_entities.py                # NPC, item, spawn, loot, zone, spell lookups
    tools_entities_write.py          # NPC, spawn, loot, merchant write operations
    tools_docs.py                    # EQEmu documentation search (docs.eqemu.io)
    tools_lookup.py                  # Characters, accounts, recipes, doors, grids, factions

Self-Hosting for Other Server Operators

Each EQEmu server operator runs their own instance:

  1. Clone this repo on the server
  2. Configure .env with their paths and DB credentials
  3. Set EQEMU_ACCESS_MODE=read (safe default)
  4. Run via stdio or SSE
  5. Connect their AI client

The server is completely self-contained — no external services, no cloud dependencies, no API keys. All data stays on your machine.


License

MIT

About

MCP server for EverQuest Emulator servers — 60+ tools for AI-assisted server management, quest development, and database inspection

Topics

Resources

License

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages