Skip to main content
Glama
greenhat616

factorio-recipes

by greenhat616

recipe-mcp

Factorio recipe, technology and save-progress queries plus Helmod / Factory Planner style production planning, exposed as MCP tools. Built against Factorio 2.0.77 and the Nullius mod set in this workspace, but the data comes from whatever mods are active when you export.

Entry points

The same 24 tools are reachable two ways:

Entry

Command

Use

MCP server

recipe-mcp-server (or python -m recipe_mcp)

Launched by an MCP client over stdio. No HTTP port.

CLI

recipe-mcp-cli [TOOL] [JSON_ARGS] [--force NAME]

Calls one tool from the shell, prints the JSON result and exits. With no TOOL it lists the tools.

The CLI starts the server with the same interpreter and talks to it over a real stdio session, so its output and errors match what an MCP client sees. It exits non-zero when the tool returns an error.

Both accept --force NAME to set the default force for stage-aware queries. Without it, a save with several non-system forces needs force in every stage-aware call.

Two more commands refresh the data both entries read: recipe-mcp-export (prototypes) and recipe-mcp-export-save (save progress). See Data export.

Related MCP server: SatisfactoryMCP

Setup

uv sync --directory recipe-mcp

This builds .venv from uv.lock and installs the package editable, so the four commands above exist. Every command below is run as uv run --directory recipe-mcp <command>.

MCP client configuration

mcp-config.json:

{
  "mcpServers": {
    "factorio-recipes": {
      "command": "G:/Programs/factorio/hotfix/recipe-mcp/.venv/Scripts/python.exe",
      "args": ["-m", "recipe_mcp"]
    }
  }
}

To pin a default force, append "--force", "faction-a632079" to args. start.ps1 [-ForceName NAME] is an alternative launcher. It runs the server in the foreground through uv run, and stdout carries only the protocol.

CLI examples

uv run --directory recipe-mcp recipe-mcp-cli
uv run --directory recipe-mcp recipe-mcp-cli get_progress_context
uv run --directory recipe-mcp recipe-mcp-cli --force faction-a632079 get_recipe '{"name":"nullius-methanol"}'
uv run --directory recipe-mcp recipe-mcp-cli list_technologies '{"force":"faction-a632079","state":"researched","limit":20}'
uv run --directory recipe-mcp recipe-mcp-cli get_technology '{"name":"nullius-high-pressure-chemistry","force":"faction-a632079"}'
uv run --directory recipe-mcp recipe-mcp-cli related_recipes '{"material":"nullius-methanol","direction":"producers","available_only":true,"force":"faction-a632079"}'
uv run --directory recipe-mcp recipe-mcp-cli validate_plan '{"recipe_rates":{"nullius-fermentation":1},"force":"faction-a632079"}'

Tools

Tool

Purpose

get_progress_context

Save source, tick, compatibility, forces, research counts, current research and queue

list_technologies

Paged technologies with name filter, state filter and optional hidden ones

get_technology

Effects, direct prerequisites, cost or trigger, saved level and progress

technology_requirements

Recursive prerequisites, science packs, Nullius checkpoint tokens, target state

search_recipes

Recipe search; available_only keeps recipes usable by the force

get_object_names

Batch object names in a chosen language, with stable IDs and explicit fallback status

get_recipe

Amounts, time, productivity permission, unlocking technologies, availability

related_recipes

Producers and/or consumers of an item or fluid

production_chain

Upstream expansion, by default only through enabled recipes, with depth/count limits and cycle protection

compatible_machines

Machines for a recipe's category: speed, power, slots, fluid boxes, buildability

validate_plan

Checks recipes, and optionally machines/modules, against research

net_balance

Base net item flow for recipe rates; rejects locked recipes unless validate_stage=false

solve_production

LP or matrix production solver: machine counts, module/beacon effects, power, pollution, imports, surplus, shadow prices

machine_stats

One recipe in one machine: effective speed/productivity/power multipliers, per-machine flow, machines for a rate

production_matrix

Item × line stoichiometric matrix, rank, item roles and determinacy diagnostics

plan_list

Saved plans with revision, update time and staleness

plan_get

One saved plan: block requests and last result summaries

plan_save

Create or overwrite a named plan of blocks

plan_edit

Atomic, revision-checked edit operations on a plan

plan_solve

Solve a plan's blocks in dependency order and sum them into a factory ledger

plan_compare

Saved results of two plans or blocks side by side: totals, imports, surplus

plan_delete

Move a plan to the trash folder

helmod_import

Save a Helmod model string as a plan

helmod_export

A plan as a Helmod model string

Every result is a pydantic model, so each tool publishes an output schema and returns structuredContent alongside the JSON text. Object IDs stay in name; recipe, technology and machine queries also return display_name, language, resolved_language and name_status. The original localised_name expression remains available. net_balance ignores modules and force productivity bonuses and does not treat fluids at different temperatures as interchangeable.

Object names and languages

Queries and searches accept language="en" (default), "zh-CN", "de", or another Factorio language code. Searches match both IDs and translated names. get_object_names takes e.g. {"objects":[{"kind":"fluid","name":"nullius-methanol"}],"language":"zh-CN"} and returns a typed name catalog. Kinds include recipe, item (including modules), fluid, entity, technology, and exact prototype types.

solve_production and plan_solve accept language and include a names catalog keyed by typed IDs. Locale selection never changes recipe choices, flow keys or solver arithmetic. Requested-language keys fall back to English, then the raw ID; name_status is translated, fallback or raw. Unsupported player controls/plural expressions remain visibly unresolved.

Prototype export also saves data/locales.json, tied to the prototype SHA-256, using the exported mod versions and loading order. To add locales to an existing export without launching the game:

uv run python -m recipe_mcp.export.locales

Missing source versions are reported in catalog warnings; an unavailable recorded version is an error. Static locale resolution covers prototype names, not runtime script/player text. A missing or stale locale snapshot leaves IDs readable. The current snapshot contains 55 languages, though individual mods may only translate a subset.

Research gating

Modeled on Recipe Book 4.0.8 (scripts/database/researched.lua), which checks a force's researched technologies and its actually enabled recipes separately.

  • Prototype unlocks: unlock-recipe effects, prerequisites, science packs or research_trigger. Several technologies unlocking one recipe are alternatives, not all required.

  • Technology state comes from the save per force: researched, enabled, level, saved_progress, current research and queue. enabled means researchable, not researched.

  • Recipe state follows the force's LuaRecipe.enabled, so mod scripts that toggle recipes are respected. Researched but disabled is script_disabled; enabled by script without research is still usable.

  • Hidden is not locked: hidden affects display only.

  • Unknown is not locked: no compatible snapshot, missing recipe or no force selected yields unknown, never locked.

  • Infinite and trigger technologies report the real level and progress and keep research_trigger and Nullius checkpoint requirements instead of judging by science pack colour.

Recipe availability.state is one of unlocked, locked, script_disabled, virtual, unknown. usable_at_stage is true, false or null and means research permission only. Machines, ingredients and surface conditions are separate checks. Technology state is one of researched, researching, available_to_research, locked, disabled, unknown. available_to_research checks prerequisites and enabled only, not inventory or trigger completion.

Logistics-request and creative-mode pseudo-recipes are excluded by default and never pass stage validation. Ordinary hidden recipes can still be queried.

A machine or module counts as buildable when its item's crafting recipe is usable. Boxing/unboxing and self-returning loop recipes are ignored. When the mapping is insufficient the result is unknown. Inventory is not scanned, so equipment you already own but cannot yet craft must be allowed explicitly.

Production planning (recipe_mcp.planner)

A line is {recipe, id?, machine?, modules?, beacons?: [{beacon, count, modules, per_machine?, interference?}], fixed_machines?, max_machines?, cost_weight?, ignore_module_rules?}; recipe may be mining:<resource>. A bare recipe name is also accepted. Line specs, defaults and beacons are pydantic models (LineSpec, Defaults, BeaconSpec): the tools publish them in their input schema, and unknown keys are rejected rather than ignored.

Effects follow the 2.0 prototype docs:

  • Module effects are added to beacon effects, which are distribution_effectivity × profile[beacon count] with beacon_counter total or same_type.

  • The machine's effect_receiver.base_effect and the force's recipe productivity bonus from the save are also added.

  • Speed, consumption and pollution multipliers are floored at 20%.

  • Productivity is clamped to [0, maximum_productivity], and is 0 when the recipe disallows it.

  • A beneficial module effect the machine, beacon (allowed_effects) or recipe (allow_*) does not permit is an error, and so is a module whose category the machine or beacon excludes (allowed_module_categories).

  • Electric crafting machines without a declared drain draw energy_usage / 30 (CraftingMachinePrototype).

Two solvers:

  • lp (default): HiGHS linear program.

    • Discovery: with empty lines, it walks upstream from the targets through recipes usable at the current stage, up to max_lines. Barrelling/boxing, virtual, recycling, legacy, creative, power generation, void and hidden recipes are skipped. It then optimizes across the alternatives it found.

    • Imports and surplus: only items with no producing line, or those listed in imports, can be imported. Any item may be overproduced, at a penalty.

    • Objectives: balanced weighs machines plus 0.01 per unit/s imported plus 0.1 per unit/s surplus. The others are machines, power and imports, and weights / import_costs override the presets.

    • Output adds shadow_prices (the marginal objective cost of one more unit/s), lines_for_matrix and matrix_args.

  • matrix: Factory Planner style square solve. It requires lines, with one recipe per intermediate.

    • Item roles: consumed-only items become import unknowns; produced-only non-targets become byproduct unknowns; intermediates balance to zero. surplus_items and imports free more items.

    • Diagnostics: an underdetermined system returns null-space directions, and an inconsistent one returns the items that cannot balance. Negative solutions produce a warning, not a silent fix.

Typical flow: let lp pick the routes, then pass lines_for_matrix and matrix_args to matrix to pin the plan, and adjust machines, modules and beacons line by line. per is second, minute or hour.

Calculation modes (lp)

  • limits: {imports: {item: rate}, power_MW, machines, machines_by_type: {machine: n}, beacons, beacons_by_type: {beacon: n}, modules: {module: n}, pollution_per_minute}. Rates are per per; counts are fractional. machines excludes beacons and disposal machines. A capped import is allowed even when the item has a producing line. Every limit comes back in limits_usage; binding ones are listed in bottlenecks with their marginal value.

  • mode="maximize": targets are ratios. The LP first finds the largest scale (output = scale × ratio) that fits the limits, then the cheapest plan at that scale. Bottleneck marginals are the scale gained per unit of limit. Without any limit the problem is unbounded and is rejected.

  • consume: {item: rate} supplied externally and used up exactly, never imported or left over. Works with targets or ratios in lp; in matrix the targets may be empty and the outputs come back as byproducts (Helmod input mode).

  • Infeasibility: when limits or consume amounts cannot all hold, the result has status: "infeasible", the relaxations the elastic LP needed in infeasibility (a limit raised, a target or consume amount lowered), suggestions, and the relaxed plan in lines. The relaxations minimise the total relative violation together, so they are one combination, not each quantity's own maximum. fixed_machines and max_machines stay hard.

  • integer_machines: whole machine counts per line carry the machine cost and the machine, power, pollution, beacon and module limits (a MILP; time_limit seconds per stage, after which the best plan comes back with status: "time_limit" and mip_gap). No marginal values and no elastic diagnosis. At most 400 lines, so pin the route continuously first for large plans.

  • Disposal (disposal="report", default): each surplus byproduct gets the void recipe and machine that vents it with the fewest machines, preferring the most power-efficient machine (disposal_defaults overrides, including modules). They are reported in disposal, disposal_totals and totals_with_disposal; totals and the route stay unchanged.

Modules and beacons

  • A beacon's count is how many beacons affect each machine (the effect); per_machine is how many beacon entities each machine accounts for (power and counts), lower than count when beacons are shared.

  • interference: k uses the Nullius small-beacon variant <beacon>-k (k large-beacon interference fields); writing the variant name directly works too. Counts use the item that places the beacon, so variants count as their base beacon.

  • Results give beacon_count, beacon_count_ceil and beacon_power_MW per line and in totals, totals.beacon_count_by_type, and module_inventory (machine slots plus beacon slots, disposal machines included; beacon modules count once per beacon entity).

  • Several lines of one recipe with different modules (distinct ids) plus limits.modules let the LP allocate a limited module stock.

  • defaults.module_options (automatic module selection) and the beacons / modules objective weights are reserved and rejected as not implemented.

Plans

A plan is a named JSON file in data/plans/ with blocks, each a solve_production request without force and per (those are plan-wide) plus a summary of its last result.

  • plan_save validates every block like solve_production would, without solving, before writing. Names are 1-64 characters of A-Z a-z 0-9 _ . -.

  • plan_edit applies a list of operations all or nothing and bumps the revision; pass expected_revision to fail on a concurrent change. pin replaces a block's lines with its last result's lines_for_matrix (optionally switching solver); set_modules, set_beacons and replace_module change modules and beacons on lines or block defaults.

  • A target may be a link {"from": ["block", ...] | "*", "plus": n}: the sum of what those blocks import of the item. plan_solve solves the blocks in dependency order (cycles are reported with their path) and returns per-block results and a factory ledger: net_inputs, net_outputs, internal_transfers, totals, beacons, modules, and disposal for the surplus other blocks do not take. It assumes a shared bus and solves blocks independently.

  • plan_compare puts the saved results of two plans (enabled blocks summed) or blocks (plan/block) side by side; it refuses missing or stale results.

  • Plans record the prototype hash, save tick and save copy hash and report fresh, stage_changed or prototypes_changed. Stale plans still solve with the current data, with a warning. plan_delete moves the file to data/plans/.trash/.

Not modeled:

  • Quality and surface effects.

  • Belt and pipe throughput.

  • Fluid-resource depletion (yield is taken as 100%).

  • Mining drill drain (0 unless declared).

  • Fuel chains of burner/heat machines. Their fuel is reported per line as fuel_MW (next to energy_type) and in total as non_electric_fuel_MW.

Fluid temperatures only raise warnings.

uv run --directory recipe-mcp recipe-mcp-cli solve_production '{"targets":{"nullius-methanol":600},"per":"minute","force":"faction-a632079"}'
uv run --directory recipe-mcp recipe-mcp-cli machine_stats '{"recipe":"nullius-methanol","rate":10,"force":"faction-a632079"}'
uv run --directory recipe-mcp recipe-mcp-cli solve_production '{"targets":{"nullius-methanol":10},"objective":"power","defaults":{"modules":["nullius-yield-module-2","nullius-speed-module-2"]},"force":"faction-a632079"}'
uv run --directory recipe-mcp recipe-mcp-cli solve_production '{"targets":{"nullius-methanol":1},"mode":"maximize","limits":{"power_MW":5,"imports":{"nullius-box-limestone":2}},"force":"faction-a632079"}'
uv run --directory recipe-mcp recipe-mcp-cli machine_stats '{"recipe":"nullius-methanol","beacons":[{"beacon":"nullius-beacon-2","interference":1,"modules":["nullius-speed-module-1"]}],"force":"faction-a632079","validate_stage":false}'
uv run --directory recipe-mcp recipe-mcp-cli plan_save '{"name":"methanol-10ps","force":"faction-a632079","blocks":[{"id":"methanol","request":{"targets":{"nullius-methanol":10}}}]}'
uv run --directory recipe-mcp recipe-mcp-cli plan_solve '{"name":"methanol-10ps"}'
uv run --directory recipe-mcp recipe-mcp-cli plan_edit '{"name":"methanol-10ps","ops":[{"op":"pin","block_id":"methanol"}]}'
uv run --directory recipe-mcp recipe-mcp-cli plan_delete '{"name":"methanol-10ps","confirm":"methanol-10ps"}'

Helmod exchange

helmod_import reads the string from Helmod's Upload Production line dialog (Helmod 2.x: helpers.encode_string(serpent.dump(model))) and saves it as a plan; helmod_export writes a plan as a string for the Download Production line dialog, which adds it as a new Helmod model. The string is parsed as Lua table literals plus serpent's shared-reference fixups; nothing is executed.

Both take a file instead of the string: helmod_import reads path, and helmod_export with path writes the string there and leaves text empty (an existing file needs overwrite=true). Relative paths are under data/helmod/, since the server's working directory depends on the MCP client.

Import:

  • Each Helmod block with recipes becomes a plan block whose lines keep Helmod's machine, modules, beacons (combo → count, per_factory → per_machine), fuel and neighbour bonus, with auto_discover=false. resource recipes become mining:<resource>; energy recipes become generate:, solar:, heat: or wind: lines in energy_mode="balance". Helmod's base time of 1, 60 or 3600 s sets the plan's per.

  • Block inputs become targets (consume for input mode, by_product=false); by_factory blocks keep factory.input as fixed_machines. A block without an input uses the outputs Helmod computed for its recipes' pivots.

  • Plans are flat, so nested blocks are linked: a linked child block targets what its parent and earlier siblings import ({"from": [...]}), or in input mode consumes their surplus. Inputs set on a block without recipes go to the first linked descendant that makes the item.

  • Helmod's algebra solver balances one pivot per recipe and turns other shortfalls and excess into block inputs and products. Those blocks import with solver="matrix", the pivots balanced and Helmod's inputs and products as imports and surplus_items. Simplex blocks, and blocks where the matrix is underdetermined or negative, use the LP over the same lines (with a warning).

  • mining_productivity is read from the effects Helmod stored on its mining lines unless given.

  • helmod_machines returns Helmod's own machine counts for comparison with plan_solve. Quality, product constraints, production shares, display limits, assembler limitation, global effects and spoilage are not imported and are listed in warnings.

Export solves the plan first. Each block becomes an unlinked Helmod block whose inputs are the resolved targets (links frozen at their current amounts), or its consume amounts in input mode, or its fixed_machines as by_factory. Auto-discovered blocks export the lines the solve chose. lp maps to Helmod's simplex solver and matrix to its algebra solver. Limits, objectives, import rules and other LP-only settings are listed in warnings. Helmod recomputes with the force's current research, mining productivity included.

uv run --directory recipe-mcp recipe-mcp-cli helmod_import '{"text":"eNrt...","name":"from-helmod","force":"faction-a632079"}'
uv run --directory recipe-mcp recipe-mcp-cli helmod_export '{"name":"methanol-10ps"}'
uv run --directory recipe-mcp recipe-mcp-cli helmod_export '{"name":"methanol-10ps","path":"methanol-10ps.txt"}'
uv run --directory recipe-mcp recipe-mcp-cli helmod_import '{"name":"from-file","path":"methanol-10ps.txt"}'

Data export

Export the final recipe, machine and technology prototypes of the active mod set. This runs factorio --dump-data against your mods directory and does not touch a running game:

uv run --directory recipe-mcp recipe-mcp-export

Export research progress from the newest save, or from an explicit one:

uv run --directory recipe-mcp recipe-mcp-export-save
uv run --directory recipe-mcp recipe-mcp-export-save --save 'C:/Users/a6320/AppData/Roaming/Factorio/saves/Nullius-Next.zip'

Both accept --factorio and --mods to override the default Steam install and mods paths.

How recipe-mcp-export-save reads the save:

  1. It picks the newest .zip in saves by modification time, unless --save is given.

  2. It copies the save, the enabled mods and their settings into an isolated directory under data/progress-*.

  3. It adds factorio-recipe-progress-helper to that copy only, never to the mods directory you play with.

  4. It runs a 1-tick benchmark that writes the progress export.

The original save is never modified, and no command is sent to a running game. The chosen path, the copy's SHA256, the tick and the export time are recorded as provenance, and the benchmark input is hash-checked before and after.

Adding the helper triggers mod configuration-change handlers on load, followed by one simulated tick. The snapshot is therefore the save as loaded under the current mod set, which may include other mods' migration handling.

Compatibility check. Progress-based filtering is only enabled when the snapshot matches the prototype export:

  • Every recipe's time, category, ingredients and results match the prototype export. Amounts are normalized to 6 decimals to absorb the game's fixed-point rounding of tiny logistics placeholder amounts.

  • Every mod's data-stage checksum matches, ignoring the helper's own empty checksum.

  • The raw prototype dump's SHA256 matches.

Otherwise state is unknown and stage calculations are refused. Never mix old progress with updated mod data.

After re-exporting, restart the server in your MCP client. The CLI starts a fresh server on every call, so it picks up new data immediately.

Files

Path

Content

data/script-output/data-raw-dump.json

All final prototypes

data/recipes.json, data/technologies.json

Readable summaries

data/manifest.json, data/mod-list.snapshot.json

Prototype export provenance and hashes

data/progress.json

Per-force technology and recipe state, provenance and consistency checks

data/progress-*/

Isolated load directory, save copy, helper output and logs, kept for auditing

data/plans/

Saved plans; .trash/ holds deleted ones

../factorio-recipe-progress-helper_0.1.0.zip

Helper mod package

data/ is not tracked by git. RECIPE_MCP_HOME overrides the project root and RECIPE_MCP_DATA only the data directory.

Project layout

recipe-mcp/
├── pyproject.toml / uv.lock     uv project (uv_build, src layout)
├── mcp-config.json / start.ps1  MCP client config example / foreground server launcher
├── src/recipe_mcp/              the installable package
│   ├── server.py                MCP server: tool registration (create_server), recipe-mcp-server entry
│   ├── cli.py                   CLI: recipe-mcp-cli entry
│   ├── __main__.py              python -m recipe_mcp → server
│   ├── paths.py                 project root, data directory, dump/progress paths, exporter defaults
│   ├── models.py                pydantic models of query results (raw prototypes and the snapshot stay JSON)
│   ├── database.py              prototype index and save-based research gating
│   ├── planner/                 production planning
│   │   ├── schema.py            pydantic models: line specs, defaults, normalised lines, solutions, results
│   │   ├── model.py             line model: machines, modules, beacons, productivity, power, gating, discovery
│   │   ├── lp.py                LP builder and solver (HiGHS): limits, maximize, consume, elastic diagnosis
│   │   ├── limits.py            limits -> LP rows; usage, bottleneck and infeasibility reports
│   │   ├── matrix.py            exact matrix solver and stoichiometric analysis
│   │   ├── disposal.py          void machines for surplus byproducts
│   │   ├── report.py            result shaping: lines, item flows, totals, beacons, modules, shadow prices
│   │   └── api.py               plan / machine_stats / production_matrix
│   ├── plans/                   saved plans
│   │   ├── models.py            plan file schema, block requests, result summaries, edit operations
│   │   ├── store.py             PlanStore: names, validation, atomic writes, revisions, fingerprints, trash
│   │   ├── ops.py               plan_edit operations and pin
│   │   └── factory.py           plan_solve and plan_compare: dependency order, linked targets, factory ledger
│   ├── helmod/                  Helmod exchange: codec.py (export strings, serpent reader), convert.py (import/export)
│   └── export/                  prototypes.py (recipe-mcp-export), save.py (recipe-mcp-export-save)
├── scripts/                     one-off analyses, not part of the package; they import recipe_mcp
│   ├── analysis/                methanol.py, science_fluids.py, pressure_transition.py
│   └── pressure_report/         build.py, zones.py, check.py (needs playwright), templates/
├── tests/                       pytest: synthetic math, gating semantics, real data, stdio end to end
├── docs/spec/                   requirement / design / task specs
└── data/                        exports and analysis outputs (untracked)

Development

uv run --directory recipe-mcp pytest
uv run --directory recipe-mcp mypy
uv run --directory recipe-mcp ruff check
uv run --directory recipe-mcp ruff format

Tests marked realdata skip when data/ has no compatible export. They use force faction-a632079; set RECIPE_MCP_TEST_FORCE to override. mypy runs with disallow_untyped_defs and the pydantic plugin over src, tests and scripts. Command lines are built with click.

Methanol analysis script

scripts/analysis/methanol.py filters recipes, machines and modules by the progress snapshot and writes data/methanol-current-stage.json, including each stage check and the save provenance:

uv run --directory recipe-mcp scripts/analysis/methanol.py --force faction-a632079
uv run --directory recipe-mcp scripts/analysis/methanol.py --theoretical
uv run --directory recipe-mcp pytest tests/test_analysis_outputs.py

--theoretical ignores current research and recomputes the 14 comparison scenarios of the earlier assumed stage into data/methanol-analysis.json.

The current-stage run keeps the exported recipe productivity bonuses. Passing the research filter does not make a factory buildable: fluid boxes, supply and surface conditions still apply. The analysis covers only the declared air/water synthesis chain and the listed machine and module tiers. It is not a global optimum over all biological routes, beacon layouts or integer machine counts.

References

Runtime fields were checked against the runtime-api.json shipped with the local 2.0.77 install, to avoid APIs from newer versions.

Energy balance and repeated blocks

solve_production accepts energy_mode="balance": electric and heat flows use energy:electric and energy:heat, always in MW even with per="minute". Energy cannot be imported unless explicitly listed in imports or limits.imports. Use heat:<reactor>, generate:<generator>, solar:<panel> or wind:<interface> as lines, or let discovery find them. Solar output defaults to 0.7 of peak; wind requires an explicit wind_factor. Choose burner fuel with lines[].fuel or the priority list defaults.fuel; burnt results enter the balance.

Example: {"targets":{"energy:electric":10},"energy_mode":"balance"}. copies=4 reports four identical construction modules; input rates and limits are totals, while per_copy shows each module and copies_totals includes its individual construction rounding. Integer solving applies the limits per copy.

Plan targets and consume values accept {from:["block"], of:"surplus", factor:0.5}; of defaults to imports for targets and surplus for consume. plan_edit supports add_supply_block and add_consumer_block with for_block, item, new_id and optional request overrides. Consumers need request.lines or request.targets. Factory suggestions contain supply edits and consumer templates.

Nullius turbine generator rows represent internal helper entities created by the mod, alongside the turbine recipe rows. Their machine counts are model capacities, not two separate purchasable entities. Geothermal heat assumes suitable placement; solar collector input uses full-flow consumption. These assumptions appear in warnings.

Helmod 2.2.14 parity checks execute the original Lua methods with controlled prototype stubs (scripts/validation/helmod_reference.py). Set defaults.productivity_model="helmod" (or the same field on a line) to reproduce its probability/productivity deduction order. The default expected model retains existing results. Pinned lines preserve the choice.

graph="bipartite" returns a versioned production flow graph; plan_solve accepts graph="blocks" or "full". graph_options.allocate=true includes proportional allocation edges as an explicit mixing assumption. Graphs use achieved flows, including relaxed infeasible results. Fluid temperatures retain separate keys and zero-cost matching edges; internal Nullius turbine energy channels are isolated by turbine type and tier.

Offline planning viewer

Export any solve_production result with graph="bipartite", or a plan_solve result with detail="full", graph="full", as JSON, then run:

uv run python -m recipe_mcp.viewer result.json report.html --language en --language zh-CN

MCP CLI response envelopes are also accepted. Open the resulting HTML directly: no server, CDN or additional project dependency is required. It shows factory and block flow graphs, searchable line/material tables, reference values, node details, power/heat summaries, snapshot provenance and assumptions. You can load another JSON result, download the embedded data, and zoom the diagram. The object-name selector switches between embedded languages and prototype IDs; tables retain IDs below translated names. Name and ID searches both work. Repeat --language to embed more languages; the first is selected initially. Omit the options to retain the result’s embedded catalog without opening the prototype database.

Generate the real Nullius examples and performance measurements:

uv run python scripts/validation/planning_examples.py

Outputs are local under data/reports/helmod-gaps/: factory.html for a linked methanol/utility factory, power.html for 10 MW generation, their JSON files, and metrics.json. See implementation evidence for test coverage, Helmod reference provenance and model boundaries.

Compare the resolver against Factorio's own name export (uses isolated write directories, checks mod/settings hashes and data-stage checksums):

uv run python scripts/validation/locale_reference.py

This checks every exported recipe, item, fluid, entity and technology name in English and Simplified Chinese and writes data/reports/helmod-gaps/locale-parity.json.

Check the Helmod exchange against the Helmod models in your newest save (two one-tick benchmarks of a save copy):

uv run python scripts/validation/helmod_exchange.py

It imports every model, compares plan_solve with Helmod's machine counts, exports each plan and has a copy of Helmod (with one added test interface that runs its Download dialog's import code) recompute it. It exits non-zero on any mismatch.

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    A
    maintenance
    An MCP server for Satisfactory that plans factories using your actual save files, reads game data, and runs LP/MILP optimization. It provides tools for game data lookup, factory planning, hard drive advice, and a local web map of your world.
    54
    6
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables users to look up Monster Hunter World game data such as monsters, weapons, armor, and skills, including weaknesses, stats, and build optimization, through MCP-compatible clients.
    62 npm
    MIT