pob-mcp

pob-mcp

Enables LLMs to load, inspect, change, and improve Path of Exile 2 builds using the real Path of Building Community calculation engine.

Category
Visit Server

README

pob-mcp

License: MIT Python 3.12+ PRs welcome Support on Ko-fi

pob-mcp is an MCP server. It lets an LLM load, inspect, change, and improve Path of Exile 2 builds. It uses the real calculation engine from Path of Building Community (PoE2 fork). It does not reimplement that engine.

pob-mcp runs a real, headless copy of PoB (a Lua program) as a background process. It talks to that process over a small JSON-RPC protocol. Every stat you get back is a number PoB itself calculated.

How it works

MCP client (Claude Desktop, Cursor, ...)
        |  MCP over stdio
        v
   pob-mcp (Python)  -- tools_*.py, optimizer/
        |  JSON-RPC over stdio
        v
   lua/pob_bridge.lua  (running under `luajit`)
        |  dofile()
        v
   Path of Building - PoE2's own Lua source (Launch.lua, Main.lua, ...)

lua/pob_bridge.lua is a fork of PoB's own src/HeadlessWrapper.lua, which PoB uses for its test suite. pob-mcp does not depend on that file directly. Installed copies of PoB leave HeadlessWrapper.lua out (see manifest.cfg), so pob-mcp brings its own version instead. This means pob-mcp works the same way against a git checkout of PathOfBuilding-PoE2 and against an installed release build.

Before you start

You need four things:

  1. A way to install a Python package. We recommend uv — it's the fastest path and what the rest of this README shows first. Don't want another tool on your machine? Plain pip and a virtual environment work fine too; see the alternate commands below.
  2. LuaJIT, a 5.1-compatible build. Put it on your PATH as luajit, or point to it with POB_MCP_LUAJIT. You need this separately from PoB itself: PoB's own runtime only ships lua51.dll/SimpleGraphic.dll for its graphical app. It does not ship a command-line interpreter you can run on its own.
    • Windows: install it with Scoop (scoop install luajit), Chocolatey (choco install luajit), or a portable build.
    • macOS: brew install luajit.
    • Linux: apt install luajit, the equivalent for your distribution, or build it from source.
  3. A Path of Building - PoE2 install. This can be a git checkout (this repo, or your own clone) or an installed release build. See "Point pob-mcp at a PoB install" below.
  4. zlib. pob-mcp needs this to read and write build codes, and to calculate Timeless Jewel data. On Windows, you already have this: PoB bundles zlib1.dll (in runtime/ for a checkout, or alongside everything else for an installed release). On Linux and macOS, install your system's zlib/libz package if you don't have it already (most systems do). If pob-mcp can't find zlib, everything still works except pasted or shared build codes and Timeless Jewel calculations. Load and export builds as .xml files instead.

Point pob-mcp at a PoB install

pob-mcp needs to know where your Path of Building - PoE2 install keeps its Lua source, because that's what the bridge process runs against. There are two ways to point it there. Note that the two have different layouts on disk — pob-mcp detects which one you're using automatically.

  • Dev checkout mode. Set POB_MCP_SOURCE_DIR to a PathOfBuilding-PoE2 git checkout — either its root folder, or its src folder directly. This layout keeps the Lua source under src/, and keeps the native runtime (LuaJIT DLLs, zlib, the bundled Lua libraries) in a separate runtime/ folder next to it.
  • Release mode. Set POB_MCP_INSTALL_DIR to the root folder of an installed release. On Windows, this is usually %APPDATA%\Path of Building Community (PoE2). An installed release puts everything in one folder — Launch.lua, Modules/, zlib1.dll, the bundled lua/ libraries — instead of splitting it up. (We checked this against a real install. We didn't just guess from the repo's packaging config.)

If you don't set either variable, pob-mcp checks a few common install locations for your operating system, and gives you a clear error if it can't find one. On Windows, this already finds a normal installer-installed copy without any setup on your part.

Install pob-mcp

git clone <this repo, or wherever you put pob-mcp> pob-mcp
cd pob-mcp
uv sync

Don't want to use uv? You don't need it. pob-mcp is a normal Python package — plain pip works too:

git clone <this repo, or wherever you put pob-mcp> pob-mcp
cd pob-mcp
python -m venv .venv
.venv/bin/pip install -e .        # Windows: .venv\Scripts\pip install -e .

Run it on its own (for testing)

POB_MCP_SOURCE_DIR=/path/to/PathOfBuilding-PoE2 uv run pob-mcp
# or, against an installed release:
POB_MCP_INSTALL_DIR="C:\Users\you\AppData\Roaming\Path of Building Community (PoE2)" uv run pob-mcp

With a plain pip install, the same thing looks like:

POB_MCP_SOURCE_DIR=/path/to/PathOfBuilding-PoE2 .venv/bin/pob-mcp   # Windows: .venv\Scripts\pob-mcp.exe

This starts the MCP server over stdio. You won't see much happen — MCP servers talk to MCP clients, not directly to you. See "Check that it works," below, for a way to try it out without a full client.

Use it with Claude Desktop, Cursor, or another MCP client

Add an entry to your client's MCP server config. For Claude Desktop, this is claude_desktop_config.json. For Cursor, it's mcp.json.

{
  "mcpServers": {
    "pob-mcp": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/pob-mcp", "run", "pob-mcp"],
      "env": {
        "POB_MCP_SOURCE_DIR": "/absolute/path/to/PathOfBuilding-PoE2"
      }
    }
  }
}

For release mode, use POB_MCP_INSTALL_DIR instead. Point it to the root folder of your installed release — on Windows, usually %APPDATA%\Path of Building Community (PoE2):

{
  "mcpServers": {
    "pob-mcp": {
      "command": "uv",
      "args": ["--directory", "C:\\path\\to\\pob-mcp", "run", "pob-mcp"],
      "env": {
        "POB_MCP_INSTALL_DIR": "C:\\Users\\you\\AppData\\Roaming\\Path of Building Community (PoE2)"
      }
    }
  }
}

Restart your client after you edit its config. You don't need to close Path of Building itself. pob-mcp only reads game data from the install folder. It never writes to it, so it runs fine alongside the app.

With a plain pip install (no uv), point command straight at the executable pip created in your virtual environment instead — no args needed:

{
  "mcpServers": {
    "pob-mcp": {
      "command": "C:\\path\\to\\pob-mcp\\.venv\\Scripts\\pob-mcp.exe",
      "env": {
        "POB_MCP_INSTALL_DIR": "C:\\Users\\you\\AppData\\Roaming\\Path of Building Community (PoE2)"
      }
    }
  }
}

(On macOS/Linux, that's /path/to/pob-mcp/.venv/bin/pob-mcp.)

Environment variables

Variable What it does
POB_MCP_SOURCE_DIR Path to a PathOfBuilding-PoE2 git checkout (its root folder or src/)
POB_MCP_INSTALL_DIR Path to the root folder of an installed release
POB_MCP_LUAJIT Path to a luajit executable, if it isn't on PATH
POB_MCP_ZLIB_PATH Path or name to load zlib from, if pob-mcp can't find it on its own
POB_MCP_BUILDS_DIR Path to your PoB Builds folder, for list_local_builds
POB_MCP_LOG_LEVEL Log level for the Python side (default INFO); the bridge's own output is logged at DEBUG

What you can do with it

Once your client is connected, start with load_build. Then use the other tools to inspect, change, and improve the build. Every tool that changes the build also returns its updated stats, so you don't need a separate get_stats call to see the effect of a change. Each tool's full description (parameters, behavior, edge cases) shows up in your MCP client — the lists below are just names and a one-line summary, to help you find the right one.

A note on ids: gems and classes are identified by an internal id, not their display name. Fireball's gem id, for example, is "Metadata/Items/Gems/SkillGemFireball", and select_class takes an internal class id, not a simple 0-based index. Use list_gems and list_classes to look these up rather than guessing — a wrong gem id doesn't raise an error, it just silently fails to resolve, so the gem does nothing.

<details> <summary><strong>Load a build</strong> (3 tools)</summary>

Tool What it does
load_build Load a build from a PoB export code, a pobb.in/Maxroll/poe.ninja/poe2db.tw/Pastebin.com/Rentry.co link, a local .xml path, or raw XML text
new_build Start a brand-new, blank build (default class, no items or skills)
list_local_builds List .xml files in your PoB Builds folder

</details>

<details> <summary><strong>Inspect a build</strong> (13 tools)</summary>

Tool What it does
get_stats Get calculated stats (life, ES, mana, resistances, DPS, EHP, etc.) from PoB's real engine
list_stat_keys List every stat key available from get_stats for this build
get_character Get class, ascendancy, and level
list_classes List every class and its ascendancies, for use with select_class
get_tree_state Get allocated passive tree node ids and count
node_info Get details for one passive tree node
search_tree Search the passive tree by name, stat text, type, or ascendancy
get_items List every gear/jewel slot and what's in it
get_skills List skill/socket groups and their gems
list_gems Look up a gem's internal id, for use with add_gem
get_config Get current configuration option values
list_config_options List every configuration option PoB supports
sanity_check Run defensive sanity checks (uncapped resists, low life, etc.)

</details>

<details> <summary><strong>Change a build</strong> (13 tools)</summary>

Tool What it does
alloc_node / dealloc_node Allocate or deallocate a passive tree node (path auto-computed)
node_path_cost Get the point cost to reach a node, without allocating it
select_class Change class and/or ascendancy
equip_item_raw / unequip_item Equip raw in-game item text into a slot, or remove what's there
add_socket_group Create a new, empty skill/socket group
set_main_skill Set which socket group is used for DPS calculations
add_gem / remove_gem / set_gem Add, remove, or edit a gem's level/quality/enabled state
list_valid_supports List support gems PoB considers valid for a skill
set_config Set a configuration option

</details>

<details> <summary><strong>Manage tree specs and gear sets</strong> (12 tools)</summary>

A build can hold several named passive tree specs and several named gear sets, and switch between them. Once you switch one, every other tool (get_tree_state, get_items, and so on) acts on the one you switched to.

Tool What it does
list_specs List the build's passive tree specs
select_spec Switch the active passive tree spec
create_spec Create a new, blank passive tree spec
copy_spec Duplicate a passive tree spec
rename_spec Rename a passive tree spec
delete_spec Delete a passive tree spec (a build always needs at least one)
list_item_sets List the build's gear sets
select_item_set Switch the active gear set
create_item_set Create a new, empty gear set
copy_item_set Duplicate a gear set
rename_item_set Rename a gear set
delete_item_set Delete a gear set (a build always needs at least one)

</details>

<details> <summary><strong>Improve a build</strong> (1 tool)</summary>

Tool What it does
optimize_build Run a goal-directed (damage/defence/balanced) search over the passive tree, support gems, and local unique items, scoring every candidate change against PoB's real engine

</details>

<details> <summary><strong>Compare or export</strong> (2 tools)</summary>

Tool What it does
compare_builds Diff two builds side by side, without touching the build loaded in this session
export_build Export the loaded build as XML or a shareable code

</details>

What this doesn't do (on purpose)

These are choices, not bugs:

  • The optimizer never changes configuration options (buffs, curses, enemy stats, map mods). If it could, it could raise its own score by assuming an unrealistic scenario. Call set_config yourself first if you want to optimize for one specific scenario.
  • Item and jewel search only uses PoB's local database. The items scope of optimize_build tries items from PoB's own bundled unique database, for the same slot. It doesn't check trade-site prices, and it doesn't search rare-item crafting options.
  • The optimizer doesn't search jewels on its own. Matching a jewel to the right socket isn't reliable enough yet. You can still try a specific jewel by hand: use list_uniques_for_slot, then equip_item_raw.
  • The optimizer is a greedy search, not a perfect solver. It only adds tree nodes — it never removes or replaces existing ones — and it only swaps one gem or item at a time. It can get stuck on a good-but-not-best answer that a wider search might beat.
  • pob-mcp can't import a live poe.ninja character profile. It can import a poe.ninja pob-link just like any other supported site, but a live character profile is different: it needs the official character API, and this version doesn't talk to that API yet. Export the character to a PoB code or link first, and use that instead.
  • pob-mcp doesn't watch your Builds folder for changes. list_local_builds lists what's there when you call it. It doesn't push updates when something changes. For an LLM-driven session, calling the tool again is simpler, and works just as well.

Check that it works

Automated tests (run with uv run pytest) come in two groups:

  • Tests that don't touch PoB at all (test_importers.py, test_optimizer_goals.py, test_optimizer_moves.py, test_locate.py). These run anywhere — you don't need LuaJIT or a PoB install.
  • test_bridge_protocol.py runs a real bridge process from start to finish: it starts a new build, searches the tree, allocates and deallocates nodes, saves and reloads, lists config options, and runs a sanity check. If it can't find POB_MCP_SOURCE_DIR, POB_MCP_INSTALL_DIR, or a luajit executable, it skips itself and tells you why. Set those environment variables to actually run it.

To try the bridge by hand, without a full MCP client:

cd /path/to/PathOfBuilding-PoE2/src
luajit /absolute/path/to/pob-mcp/lua/pob_bridge.lua

Then type (or pipe in) JSON-RPC requests, one per line:

{"id": 1, "method": "new_build", "params": {}}
{"id": 2, "method": "get_stats", "params": {}}

Each one should print back a {"id": ..., "result": {...}} line.

Where things live

pob-mcp/
  lua/
    json.lua          # self-contained JSON codec for the bridge protocol
    pob_bridge.lua     # the headless PoB bridge + JSON-RPC loop
  src/pob_mcp/
    server.py          # MCP server entrypoint, tool registration
    bridge.py           # subprocess + JSON-RPC client for pob_bridge.lua
    locate.py           # finds a PoB install + luajit
    sites.py            # pobb.in/Maxroll/poe.ninja/etc. URL -> build code
    importers.py         # unifies code/URL/file/XML into one load_build path
    tools_*.py            # MCP tool definitions, grouped by area
    optimizer/             # goal-directed build search
  tests/

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured