Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Capitalism Lab

БесплатноНе проверен

Read-only MCP server for accessing live game data from the Capitalism Lab business simulation game via process memory reading.

GitHubEmbed

Описание

Read-only MCP server for accessing live game data from the Capitalism Lab business simulation game via process memory reading.

README

Read-only MCP server exposing Capitalism Lab live game state to any MCP-compatible client (Claude Desktop, Claude Code, etc.). Attaches to a running CapMain.exe — on Linux via /proc/<pid>/mem (game under Wine/Proton), on Windows via ReadProcessMemory (kernel32 / psapi). No ptrace, no network egress, no binary modification, no modification of anything on disk.

34 read-only tools for pulling game state (calendar, economy, player balance sheet, AI competitors, cities, firms, stocks, products, news), plus exploration primitives (read_raw, read_struct), a pause/resume-friendly snapshot + diff pair, and a human-in-the-loop action gate with a small local web dashboard.

Every struct offset and virtual address below is pinned against Capitalism Lab v11.1.2. Other game versions are not supported.

Requirements

  • Python ≥ 3.10.
  • A legitimately-licensed copy of Capitalism Lab v11.1.2. On Linux the game should be running under Wine/Proton; on Windows it runs natively.
  • The MCP server must run as the same user as the game process — required for /proc/<pid>/mem reads on Linux and for OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION) on Windows. The server never uses ptrace, never requests elevated privileges, and never calls WriteProcessMemory.
  • mcp >= 1.27.0 (installed automatically via pip).

The server speaks stdio — hook it up to any MCP client.

Claude Desktop config

Add the following to your claude_desktop_config.json (~/.config/Claude/claude_desktop_config.json on Linux, see Claude Desktop docs for other platforms). Replace /path/to/caplab-mcp/.venv/bin/python with the actual path to the venv's Python on your machine:

{
  "mcpServers": {
    "caplab": {
      "command": "/path/to/caplab-mcp/.venv/bin/python",
      "args": ["-m", "caplab_mcp"]
    }
  }
}

Restart Claude Desktop; the caplab MCP should appear on the next launch. If the action dashboard's port (default 5599) is free, the HTTP server binds eagerly at startup and is reachable as soon as the MCP process is up.

Tools

Tool Purpose
attach(pid=None) Resolve PID + base + Misc pointer, cache for 30s. Idempotent.
get_game_state() Date (JDN + Gregorian), pause, speed, difficulty, RNG seed, DLC flags.
get_player() Player group (recno 2 by convention) with full balance sheet.
get_company(group_recno) Any group by recno. O(1) chunk math via resolve_pointer_paged.
list_companies(label=None, only_listed=False) Both GroupArrays, summary rows. Filter by recno-based label ("player" / "government" / "ai").
list_firms(owner_recno=None, firm_type=None, limit=200, verbose=False) FirmArray with filters. Default hides idle Government-owned infrastructure firms; pass verbose=True for the full list.
get_firm(firm_recno, include_history=False) One firm; FirmRetail also returns walked Item chain (stock / price / quality / MTD profit). include_history=True adds a 36-month calendar-labelled revenue/profit/expense series.
list_stocks() Listed groups with NationStock fields + computed market cap.
list_cities() / get_city(recno) Town summary with real city names, population, growth %, living_standard, local_inflation, tax rates, business_activity_index.
list_products() ItemResManager catalog (MegaClass / ProductClass / ItemRes).
list_recent_news(since_jdn=None, limit=50) NewsArray records with rich schema (3 subject recnos, 2 int params, 2 double amounts, town context, map coords) + click-dispatch category label. Template-rendered display text is not included.
get_economy() Economy singleton (GDP, cycle phase, interest rates, CPI, 5-year GDP history).
read_raw(va, size=4, format="hex") Dump raw bytes from any VA. Formats: u8/i8/u16/i16/u32/i32/u64/i64/f32/f64/hex/ascii. Scalar formats with size > element width return a list. Always includes raw hex for verification.
read_struct(ptr, fields) Decode many typed fields from one struct in a single sweep. fields = [{name, offset, type, [size]}]. Returns {fields: {...}, covers_range: {start, end, hex}}. Good for validating a hypothesized layout without editing constants.py.
snapshot(tools=None) Run several tools and cache the combined payload under a snap_NNNN id. Last 10 snapshots retained.
diff_from(snapshot_id, tools=None) Re-run the snapshot's tool set and return a structured diff. Lists of recno-keyed dicts are aligned by recno; numeric changes include a delta.
request_action(action, reasoning="", impact="", timeout_sec=600) Human-in-the-loop action gate. The AI describes what it wants done in-game; the user sees it on the local dashboard at http://127.0.0.1:5599 and clicks Done ✓ or Can't do ✗ with optional notes. Blocks until the user responds or the timeout hits. One pending at a time.

Map / save-file tools

These read the most-recent .SAV in your Capitalism Lab save folder (default: ~/Documents/My Games/Capitalism Lab/SAVE), decompress it, and run useful queries. Results are small payloads — the full 12 MB per-town JSON never crosses the wire. Cells, firms, groups, and raw sites are cached in process memory; subsequent calls reuse the cache until the file mtime changes.

Tool Purpose
get_map_dimensions(town_index) Width × height + cell_count for one town (1-based).
get_tile(town_index, x, y) Full decoded cell — type_flags, every is_* predicate, walker_terrain, feature_recno, site_recno, land_value, dev_level, traffic_index, etc.
get_map_summary(town_index) Aggregate stats: cell-category counts, ownership distribution (top 10), land_value & traffic max/mean, dev_level max, purchasable_cells. No per-cell data.
get_terrain_grid(town_index) W*H bytes (one CATEGORY_CODE per cell) base64-encoded. Includes the codes legend so the caller can decode it. ~53 KB for a 200 × 200 map.
list_tiles_matching(town_index, filter, limit=100, offset=0, sort_by=None, sort_desc=True) Filter every cell, return compact summaries. Filter keys: any boolean predicate (is_road, is_plant, is_firm, purchasable, …), any recno (owner_recno, feature_recno, site_recno, …), category, tile_type, plus numeric ranges (land_value_min/max, dev_level_min/max, traffic_min/max, x_min/max, y_min/max).
find_buildable_land(town_index, limit=20, min_land_value=0) Tiles you could build on: purchasable + no firm/road/building/plant overlay, sorted by land_value desc.
top_land_value_tiles(town_index, limit=20) Tiles ranked by land_value desc.
traffic_hotspots(town_index, threshold=50, limit=50) Tiles where traffic_index > threshold.
ownership_breakdown(town_index, limit=25) Distribution of owner_recno values + per-owner category breakdown.
road_components(town_index, min_size=3, limit=20) Connected components of road cells (4-neighbor flood fill). Returns size + bbox, largest first.
get_firm_at_cell(town_index, x, y) Resolve a firm cell through FirmArray → returns firm_type_name (specific, e.g. "Airport", "City Hall", "University"), firm_class (coarse C++ class), owner Group with label (player/government/ai), nation, bounding box. Returns is_firm: false for non-firm cells.
list_firms_in_town(town_index, firm_type=None, owner_group_recno=None, owner_group_label=None, limit=200) Every distinct firm whose footprint touches the town, with full owner + type details.
group_ownership_in_town(town_index) Cell counts grouped by resolved owner Group. Firm cells are dereferenced through FirmArray, so e.g. "Government owns 401 cells" instead of 50 separate firm-recno buckets. Includes a per-group firm_types breakdown.
get_site_at_cell(town_index, x, y) Resolve the raw-material site (Aluminum / Chemical Minerals / Coal / Gold / Iron Ore / Lithium / Oil / Silica / Silver / Timber) at cell (x, y). Returns {has_site: false} for cells without a deposit.
list_raw_deposits(town_index, item_name=None) Every raw-material deposit (mine / oil well / forest tile) inside one town. Optionally filter by item_name (e.g. "Coal").

The dumper resolves three lookup tables on top of the raw cell bytes: FirmRes (142 entries — Airport, City Hall, Police Station, …), FirmArray::create_firm firm-type → C++ class (44 entries), and SiteArray raw-material item_type (1..10 → 10 raw materials). See Documentation/engine/Engine_Land.txt in the research repo for the canonical bit definitions and offsets.

Response envelope

Every tool response is wrapped:

{
  "_meta": {
    "game_date_jdn": 2448282,
    "current_month_index": 12,
    "paused": true,
    "rng_seed": 2566239387
  },
  "data": { ... }
}

_meta is enough for a consumer to detect when two reads straddle a game-day tick boundary. Compaction is applied to data for most tools — fields with exact-zero numeric values, empty strings, and empty collections are dropped by default to keep payloads small and signal-dense. Identity fields (recno, name, group_label, …) are always preserved.

Monthly history format

When a tool returns a 36-entry monthly array (e.g. get_firm(..., include_history=True)) each entry is labelled with the calendar month:

{
  "slot": 12,
  "year": 1991,
  "month": 1,
  "label": "1991-01",
  "is_current": true,
  "value": 48293.50
}

Entries are oldest-to-newest. slot maps back to the engine's raw index into the double[36] rolling window.

Action dashboard

When request_action is called, a local web dashboard is served at http://127.0.0.1:5599 (or the next free port if 5599 is busy — printed to stderr on startup). Open it in any browser:

  • The pending action appears as a card with its title, the AI's reasoning, and the expected impact. A running timer shows age + remaining time.
  • Done ✓ records the action as executed. The notes textarea carries any observations back to the AI.
  • Can't do ✗ records a rejection. The AI is expected to read your reason out of the notes field and adjust its plan.
  • History of past decisions lives at the bottom.

One pending action at a time. The AI's second concurrent request_action call errors with "another action is still pending".

The dashboard binds to 127.0.0.1 only — it is not exposed to any other host on your network. The HTTP server is stdlib-only (http.server.ThreadingHTTPServer). History is in-memory across the lifetime of the MCP process only.

Project layout

caplab-mcp/
├── README.md                     # this file
├── pyproject.toml                # packaging; single dep on `mcp`
└── caplab_mcp/                   # the MCP server package
    ├── __init__.py
    ├── __main__.py               # `python -m caplab_mcp`
    ├── server.py                 # FastMCP server + all 18 tool bodies
    ├── reader.py                 # LiveReader — /proc/<pid>/mem on Linux,
    │                             #   ReadProcessMemory on Windows,
    │                             #   plus the paged-array walkers
    ├── constants.py              # every VA and struct offset, with provenance
    └── action_inbox.py           # request_action dashboard (HTTP on localhost)

Disclaimer

This project reverse-engineers the in-memory layout of Capitalism Lab for interoperability and research purposes. It is not affiliated with, endorsed by, or sponsored by Enlight Software or any of its affiliates. Capitalism Lab and CapLab are trademarks of their respective owner.

You need your own legitimately-licensed copy of the game.

Use at your own risk. Every address in this repository is pinned against CapMain.exe v11.1.2. Different versions — past or future — will not work until the address table is re-pinned.

from github.com/jaykobdetar/caplab_mcp

Установка Capitalism Lab

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/jaykobdetar/caplab_mcp

FAQ

Capitalism Lab MCP бесплатный?

Да, Capitalism Lab MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Capitalism Lab?

Нет, Capitalism Lab работает без API-ключей и переменных окружения.

Capitalism Lab — hosted или self-hosted?

Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.

Как установить Capitalism Lab в Claude Desktop, Claude Code или Cursor?

Открой Capitalism Lab на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Capitalism Lab with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории development