SatisfactoryMCP
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| SATISFACTORY_DOCS | No | Path to the game's CommunityResources/Docs/en-US.json file, overriding install auto-detection. | |
| SATISFACTORY_SAVES | No | Override the save directory (default: %LOCALAPPDATA%\FactoryGame\Saved\SaveGames). |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| factory_mapA | Proposed factories, from power islands and belt topology, plus what is named. Three independent signals are reported rather than one answer, because none is
right alone: power islands separate outposts but leave a grown-together base as one
476-machine blob; belt components shatter that blob into fragments; foundation slabs
are the sharpest of the three but say nothing about the ground-built parts of a
factory. Where they disagree, carve the difference with show=slabs also lists BARE platforms -- poured foundations carrying no machine yet -- with tile count, extent, bounding box and elevation, because a freshly built platform is a real place a build plan refers to. Pads under a stated tile threshold are summarised in one line. |
| factory_queryA | Ask one thing about one factory: what it makes, needs, draws, or touches.
Every rate is printed twice. NAMEPLATE is the machine's recipe rate at its saved clock, which a starved factory still reports in full. MEASURED is that rate scaled by the share of its own productivity window each machine spent producing -- the window that ended when the save was written, so a line idle at that moment measures 0 and is not broken. A machine keeping no monitor is left out of measured entirely and shown separately, because counting it in full there would invent output. |
| factory_healthA | Measured uptime per machine, and WHY each stopped one is stopped. The only measured numbers in this MCP. Every manufacturing building keeps a fixed 300-second productivity window; uptime is seconds-producing over that window. States, worst first: A stalled machine no generator can reach over the wires says so in its For a STARVED machine it also says what physically feeds the input it lacks: the run
that arrives, what stands at its far end and that feeder's own state, ONE hop back --
A missing FLUID is diagnosed on the plumbing manual's own ladder -- (1) connection,
(2) head lift, (3) flow rate -- and the Blocked counts as needing action, alongside dead node, no recipe, starved and
stalled: a full output box means nothing is taking what the machine makes. The sweep's
The sweep over every factory also reports three plumbing faults that belong to no machine set: fluid buffers holding too little to output at their intake rate, pipeline pumps no wire reaches, and points where a line climbs above the head lift pushing it. All three are world-wide there, not scoped to a factory.
|
| propose_factoriesB | One coherence score over every signal, agglomerated into proposed factories. Combines foundation slabs, proximity, belt connectivity, shared products and supply links. Validated leave-one-factory-out against the player's twelve hand-named factories: precision 1.000, recall 0.945, and precision was 1.000 on every fold -- it never merges two factories, it only ever splits one. Use |
| select_machinesA | Preview which machines a selector picks, before naming them. Worth running first on anything product-based: 17 machines make Concrete on the reference save, but 15 of them are a construction feed inside the steel site and only one is the player's "concrete setup".
|
| name_factoryA | Name a set of machines and persist it for this world. The label stores the machine instance ids, which are stable across saves, so it
survives moving machines, adding to the factory, and autosave rotation. Calling
this again with the same name RE-ANCHORS it to the current selection, dropping every
machine the new selector misses -- |
| rename_factoryB | Rename a factory label. The machines it holds are not touched and nothing re-anchors. The label keeps its anchors, notes, centroid, signature and dates -- the name is the only thing here a player picked, and correcting one used to mean naming the whole selection again under a second name and forgetting the first. Stored plans scoped to this factory follow the new name. Renaming onto a name this world already uses is refused and says which label holds it. |
| amend_factoryA | Add or drop individual machines on a label, without re-anchoring the rest of it.
|
| list_factoriesC | Named factories for this world, with how much of each is still standing. |
| forget_factoryB | Delete a factory label. The machines themselves are untouched. |
| trace_upstreamA | What feeds a machine, or what it feeds -- walked on the save's own connections.
Direction is READ, not guessed. Every material edge carries its connector role, and 92.5% of the connectors landing on a machine name their direction outright; the rest are all on extractors or generators, whose own nature settles them. Where even that fails the edge is walked BOTH ways -- over-reporting a feeder is recoverable, missing one is not. Belts and pipes are walked THROUGH and left out of the table: a trace from the
generators touches 331 nodes at depth 72, nearly all of it conveyor. What the route
crossed is named instead in a note -- how many runs of each medium, and the ids
|
| factory_floorsA | How many decks a factory has and what stands on each. Nothing in the save says "floor". Floors are recovered from geometry: a platform is a
4-connected run of 8 m foundation cells, and its storeys are the levels its tops
cluster at, with no assumed storey pitch. A band holding less than a quarter of the
platform's largest deck is marked Whole-world by default: one row per platform, largest first. Narrow with |
| search_itemsC | Find items by name. Returns form, energy and sink points. |
| recipe_detailA | Exact numbers for one recipe: rates, machine, power, unlock source. Takes a class id OR a display name. Refusing the name cost a caller two round trips
to fetch an id this function could resolve itself, which is a poor trade for strictness
that buys nothing -- |
| alternates_for_itemB | Every automatable recipe that makes an item, alternates first. When a save is readable, each row is marked HAVE or LOCKED, and a LOCKED one says which schematic would grant it -- a hard drive and a milestone are different work. |
| search_recipesA | Search recipes by name, or by what they consume/produce. Marks HAVE/LOCKED.
|
| list_buildingsA | Buildings by kind: production, extractor, generator, logistics, foundation, ramp, wall, pillar, beam, architecture (all five families together), or all. Rows are marked HAVE or LOCKED against the save when one can be read. That matters
most for Paged: |
| list_pending_hard_drive_choicesC | The pending hard-drive choices stored in the save, with rerolls left. |
| advise_hard_drive_pickA | Rank one pending drive's options by marginal value, via counterfactual LP. Each option is solved for and against across several objectives, because a recipe can be worthless for power yet excellent for parts. Deltas are reported per objective and never collapsed into one score.
|
| stockA | What you own, by item: spendable stock apart from what merely exists. Spendable is carried + storage + Dimensional Depot -- exactly the set every affordability check in this surface spends. Machine buffers and crate contents get their own columns and are never added in: buffer material is in transit, and a crate exists because something went wrong and deletes itself when emptied.
|
| storageA | Which container holds what, where it stands, and how full it is. Every storage container, Personal Storage Box, Depot uploader and fluid buffer the
player built -- not splitters and mergers, whose one to three items in transit are not
stock, and not machine buffers, which belong to their machine. Fullest first, or by
how much of
|
| cratesB | The crates lying on the ground: what is in each one and where to walk to get it. A death crate is what you dropped when you died; a dismantle crate is the overflow from dismantling with a full inventory. Both are recoverable and NEITHER counts as spendable stock -- a crate deletes itself the moment it is emptied, so a plan that spent it would depend on somebody walking back there first. Whose crate it is the save does not say: the crate's only saved property is its type, so there is no owner, no timestamp and no cause to report. |
| list_plansA | Plans saved for this world, their versions, and whether the world has moved under them.
Every plan carries a version ( |
| forget_planA | Delete a saved plan. Nothing in the world is touched, and plan_log can undo it. |
| rename_planA | Rename a saved plan. Nothing is re-solved and nothing else about it changes. The plan keeps its key, its recorded field, its siting and its notes -- a name is the only thing here a player picked, and it was the only thing they could not correct without saving the plan again under a second name and forgetting the first. |
| site_planA | Record, update or clear WHERE a stored plan stands. Nothing is re-solved. A plan stores what to build; this stores where -- origin (x, y and optionally z, in metres, at the footprint's centre), orientation (yaw about world Z, the same convention the save stores machine facing with), and footprint (width x depth, metres). The footprint defaults to the square plan_layout budgets for the plan's largest floor, and the record keeps track of whether it was measured or derived. Once sited: plan recalls print the siting; The siting is a RECORD of your decision, not a constraint on the solve -- re-running
the plan neither reads nor moves it, and |
| plan_factoryA | Optimise a factory with an LP over this world's unlocked recipes.
Omit it and the whole map is in scope. Use search_resource_nodes to discover ids. Machine counts are whole buildings at a derived clock: a 52.8 machine-equivalent result is reported as 53 machines at 99.6%. That is exact, always a clean ratio, and provably the power-optimal way to run that throughput, so ordinary ratio underclocking is automatic and needs no parameter.
objective: max_mw | max_item | min_raw | min_machines | min_power. Every item is balanced as an EQUALITY, so a byproduct with no consumer makes the plan infeasible rather than silently vanishing.
Two things worth reading twice. The power token is MW (
|
| plan_layoutA | Turn a plan into a buildable schematic: blocks, buses and floors. Same arguments as plan_factory, plus This is a SCHEMATIC, not a blueprint. It gives modules, connections, floor assignment and a space budget. It deliberately does NOT give world coordinates or belt routing -- there is no terrain data here, so those would be invented. Blocks are split by throughput: 46 Refineries needing 1380 m3/min of crude cannot share one manifold when a Mk2 pipe carries 600, so that is 3 blocks. Floors follow chain depth, with a logistics deck between each pair of production floors. |
| diff_vs_saveA | What to change to get from the factory you have to the one plan_factory plans. Takes exactly plan_factory's arguments and re-solves, because the server keeps no state. Both tools print a plan id hashed over the arguments AND the save-derived solve inputs, so two responses carrying the same id are provably the same plan. Machines are matched by IDENTITY, never by position: a manufacturer on (building, recipe), a generator on its building alone since its fuel is piped in rather than set on the machine, an extractor on the node it occupies. A Refinery running some other recipe is busy, not spare, so it never counts toward the plan. Actions are ordered free-first -- UNPAUSE, then SETRECIPE on machines that produce nothing today, then BUILD. Stages follow the plan's own chain depth and the power arithmetic is INCREMENTAL, charging only the machines you have yet to place. Where a machine cannot be identified at all (Water Extractors have no recipe and no resolvable node) the answer is a RANGE, never a number. Recall a stored plan with Built and energised are DIFFERENT states and the save separates them in one direction only: a machine that produced in the last 300s window certainly had power, while one that did not may be unpowered, starved, blocked or idle. Grid membership is not persisted at all, so a stage is never reported as "unpowered" -- only as built with nothing proven running, which is exactly what a finished but not-yet-energised block looks like. Saves are read-only: this never proposes writing one, and there is no dismantle action. Machines standing among the plan but not in it are listed for you to judge. |
| explain_byproductsB | Explain which byproducts stall a plan, and what can legally consume them. Every item balance is an equality, so a byproduct with no consumer makes a plan INFEASIBLE rather than silently vanishing. This says WHICH item is stuck, whether it can be sunk (solids only -- a fluid must be consumed exactly or packaged first), and which recipes would absorb it, split into ones this world has unlocked and ones it does not. Pass |
| compare_recipe_optionsA | Rank whole ROUTES to make an item by what each actually costs. Not a recipe list -- alternates_for_item already does that. Each route is solved end to end with the LP, so the comparison is Crude -> Alt HOR -> Diluted Fuel against Crude -> Fuel, priced in raw resource per unit, whole buildings, net power, and byproducts needing an outlet. |
| bomC | Flattened bill of materials: total raw and intermediate rates for qty/min of an item.
|
| commission_planA | In what order to switch a built plant on, without blowing the fuse. This is a STARTUP order, not a build order, and the difference removes most of the problem. Building costs materials, not power -- a machine draws only when it runs -- so the whole plant can be constructed at leisure, drawing nothing, and then energised block by block. Nothing here tells you what to build first. The constraint is one line, and it is hard: at every step, energised consumer draw must stay under the headroom plus generation from generators already burning fuel. Exceeding it in Satisfactory does not degrade gracefully -- the fuse blows and the whole grid stops until it is reset by hand, including the plant that was feeding it. Generators are free to energise (0 MW draw, read from the dump), so a wave costs its consumers and refunds its generators, and that refund pays for the next wave. Takes plan_factory's arguments, or recall a saved plan with |
| rank_unlocksA | What every locked alternate recipe would be worth to THIS plan. One counterfactual per candidate: solve the plan, solve it again with the recipe added, report the difference. It answers "which unlock should I chase" with a number in the plan's own units instead of a tier list, because a recipe's worth depends entirely on what you already have. A zero is an answer. Most candidates change nothing, and "you are not missing anything here" is a decision -- it is otherwise reached by walking the recipe tree by hand. Deltas are an UPPER bound: a candidate needing a machine you have not built is judged as if you had it, and the machine is named. Alternates currently offered by a pending hard drive are flagged, which is the difference between "worth having" and "claimable now". |
| plan_logA | One plan's versions, newest first: who changed what, from chat or from the page.
|
| ui_contextB | What the web page has open, and what changed in plans since this session last looked. Call it first when the user says "this", "here" or "what I have open": it names the page's view, plan and version, tab and selection, whether the page reads the same save as you, and every plan version and chat solve by someone else since your last look. |
| phase_requirementsC | What the Space Elevator still wants, live record and deprecated record apart. The per-phase item table in the save is DEPRECATED and frozen, so it is shown labelled rather than believed. Read the header line first. |
| power_shardsB | Power Shards held, committed and free, plus what an overclock plan would cost.
|
| mam_researchB | MAM research: what is left, what it costs, and what you can afford right now. The MAM is where CAPABILITIES live, as opposed to recipes -- the Dimensional Depot, the Power Augmenter, and Production Amplifier, which is the one that lets a Somersloop go into a machine at all. That last one has no flag in the save. |
| milestonesA | HUB milestones: what is left, what each costs, and what you can afford right now. The questions READY is about the BILL, not about access: tiers are opened by Space Elevator
deliveries, that gate is in no shipped data, and |
| somersloopsB | Somersloops held, slotted and owned -- the sibling of power_shards.
Free and committed are both exact. Slotted ones live in |
| collected_from_worldA | Map collectibles: how many exist, how many you took, what is left and what is closest. Slugs, somersloops, Mercer spheres and their shrines, mushrooms, drop pods and the loot caches around them. Two sources, and neither is asked the other's question:
Views: A placement in a cell no save has ever loaded is counted as remaining and reported as
|
| list_regionsA | Named map regions, optionally only those containing a given resource. Region names are ADVISORY: the boundaries are the game's own map areas, downsampled to a 256 m grid to publish and a 64 m one to look up in, so a name near a boundary can be one cell out. Use them to talk about places, not to compute with -- every node row also carries an exact grid cell.
|
| describe_locationA | Name the region at a place, sample its elevation, and count what runs through.
Returns 'off-map or ocean' rather than guessing the nearest land region. Elevation is answered two ways and the two are never averaged. Where this machine
carries the extracted 1 m terrain field, Belts and pipes are counted too, measured against the runs' drawn lines rather than
their corner points, so a conduit crossing mid-span is seen. With a readable save,
a zero here means nothing runs through -- absence in this output is absence in the
world. |
| search_conduitsA | Belt and pipe runs near a point or between two areas: ends, length, elevation. The web map has drawn these all along; this is the text answer to "is there a pipe between those extractors and that platform, where does it run, how long is it". A run is one belt CHAIN (consecutive conveyor pieces, split at splitters, mergers and machines) or one placed pipeline piece. Longest first; each row carries both ends with what stands there where known, the drawn length, and the elevation span.
Long lists page with |
| search_resource_nodesA | Resource nodes, in one of three views.
All three views page with Water is the exception to everything above. Open water carries no node, so asking for it returns only the fracking satellites; the bodies already being pumped, the pumps on each and the measured sea level are printed beside them instead. |
| show_on_mapA | Map links centred on something: this project's own map, and the public one. Two links for every place. The LOCAL one opens this project's web map, which draws the reader's own save -- their machines, their belts, their siting. The satisfactory-calculator.com one opens a third-party map of the vanilla world, which knows the terrain and the nodes and nothing the player built.
Only the Crude Oil layer tokens are confirmed; the rest follow the same pattern and are flagged. A wrong token still opens the map in the right place, just without that overlay. |
| rank_build_sitesA | Rank candidate fields for a new extraction site, best first. Scores untapped REACHABLE capacity against spread, distance to your existing buildings, and purity mix. Every raw component is shown so you can re-weight: the single score is a starting point, not a verdict.
A ranking does not page: the rows below the cut score worse by construction, so raise
|
| whereamiB | Where the player is standing, and what is around them. Position comes from the Char_Player_C pawn in the save, so it is wherever you
were when it was written -- an autosave can be several minutes stale. Use
|
| list_worldsA | List save games grouped by world, newest first. Unsupported files are reported separately rather than failing the scan -- pre-1.0 saves cannot be parsed at all. |
| world_summaryC | Progress, power and problems for one world. |
| unlocked_recipesB | Which recipes this world has. Defaults to alternates, never all 872. Sorted by name and paged with |
| power_reportB | Generation capacity vs machine draw, nameplate AND measured. Nameplate is what everything built would draw running at once. Measured weights each machine by the 300 s productivity monitor the save already carries, which on a factory with idle blocks is a very different number -- and it is the one that says what is free right now. Both are shown because they answer different questions. Generation is capacity on both figures, with one exception the answer names: a generator whose fuel or supplemental water has run dry AND whose own monitor read zero is listed as starved, because those MW will not arrive when the grid asks for them. |
| factory_sitesC | Built production buildings clustered into sites, largest first. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| design_factory | Plan a factory for a target item, respecting what this world has unlocked. |
| plan_power_plant | Plan a power plant from a given resource and area. |
| pick_hard_drive | Advise which alternate recipe to take from a pending hard drive. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| docs_summary | One-line census of the normalized game data, plus its content hash. |
| current_save | Which world and file the server would read right now, and its headline state. |
| factory_labels | Factory labels for the current world, as the JSON another tool can consume. Labels are the one thing in this server a player authored by hand, so they are the one thing worth publishing as a stable interface rather than a private cache. The file is per world, keyed by the save header's ``saveIdentifier``, and carries a ``schema`` integer so a reader can refuse a shape it does not know. ``anchors`` are machine instance names, which were verified stable across saves -- 365 of 365 kept id and position between two files. That is what makes a label portable: a consumer can join it against its own read of the same save without needing anything from this server. |
| map_regions | Region names available as source selectors, with their accuracy caveat. |
TDQS
Scored across 54 tools
Descriptions are unusually detailed and most tools have a specific niche, but overlapping factory_* and recipe/plan vocabularies create real selection risk. An agent could confuse factory_map, propose_factories, and factory_sites, or search_recipes, alternates_for_item, and compare_recipe_options without careful reading.
All names use snake_case, but the convention is mixed: some are verb_noun (search_items, plan_factory), while many are bare nouns (stock, bom, factory_map, world_summary). Pluralization and phrasing are uneven, though the set remains readable.
With 54 tools, the server is far beyond the 3-15 sweet spot and well past the 25+ 'too many' threshold. The domain is broad, but the surface feels like several toolsets merged, making discovery and context management difficult.
Coverage is strong for recipes, factories, plans, resources, power, research, collectibles, and map analysis, with lifecycle operations for plans and factory labels. Missing or thin areas include vehicle/train/drone logistics and blueprint editing, but core planning workflows are well supported.