Skip to main content
Glama
bayraak

Load Planner MCP

by bayraak

load-planner-mcp

CI

A deterministic load-planning solver, plus an MCP stdio server that exposes it as two tools. The solver decides freight mode for a cohort of orders — all-air, consolidated sea-LCL, or full container — prices each option, and attaches a risk read (ETA percentiles, deadline-miss probability, CVaR of the delay tail).

What is in here

Path

Contents

load_planner/geometry.py

3D bin-packing (py3dbp): do the pallets fit a 20GP/40GP/40HC, and at what fill rate

load_planner/solver.py

Fixed-charge MILP over OR-Tools CP-SAT: cheapest feasible assignment per enumerated mode-pattern

load_planner/risk.py

Delay-distribution convolution: p50/p90 ETA, deadline-miss probability, CVaR(alpha)

load_planner/scenarios.py

Enumerates the cards, scores them on one identical vector, ranks them, picks a recommendation

load_planner/cli.py

A plain stdin/stdout JSON boundary, for hosts that prefer a child process to an import

solver_mcp/server.py

The MCP stdio server: JSON-RPC 2.0, two tools, and verify_narration()

fixtures/cohort_urgent.json

A synthetic 20-pallet cohort with a synthetic rate card, used by the tests

The two sub-problems are layered rather than merged. Solving true 3D placement inside the MILP explodes combinatorially, so the packer runs first and hands CP-SAT a fill rate and a feasibility flag.

Related MCP server: freight-pulse

The two-tool contract

solve(problem) runs the solver and returns its output verbatim — not reformatted, not rounded, not summarised — together with a result_id and a SHA-256 content_hash of the result. The result is kept in an in-process store.

explain(result_id) returns a narration built from a fixed template whose slots are filled from that stored result.

The split is the design. A language model calling this server chooses what to solve and puts the answer into words; it does not originate any figure. Three properties make that structural rather than aspirational:

  1. explain takes one opaque id and nothing else. Its input schema has a single property. It cannot be handed a rate, a weight or a deadline, so it cannot restate one.

  2. The stored record holds the solver result only. solve discards the problem payload once the solver has run. There is no field on the stored record that could carry an input value.

  3. There is no arithmetic on the explain path. Values are looked up by key and rendered as strings. Selecting the recommended card reads a flag the solver already set; it does not compare or compute.

verify_narration(narration, stored) turns the claim into a check. It scans the text for digit runs and rejects any token absent from the stored result, so a narration that rounds 11480.0 to "11500", converts 0.1246 to "12.46%", or adds two stored costs together fails — each of those is a number the solver never produced. A host can apply it to any narration before display, including one the model wrote in its own words rather than one explain returned. explain runs it on its own output before returning.

What the guard deliberately allows, so it is not mistaken for a stronger claim: digits inside stored strings and dict keys count as present, because a narration may quote those strings verbatim. "ortools-cpsat+py3dbp" admits 3, and the field name eta_p90_days admits 90. It is a lexical check on numeric tokens, not a proof of semantic correctness: a narration that attaches the right number to the wrong label still passes.

Install

Python 3.10 or newer.

python -m venv .venv
./.venv/bin/pip install ortools py3dbp pytest

Or install the package itself, which pulls the two runtime dependencies:

pip install -e .

ortools is the CP-SAT engine; py3dbp is the 3D packer, MIT-licensed. The MCP server adds nothing beyond the standard library — the stdio protocol surface is small enough to implement directly, which keeps the dependency list auditable.

Run

The server speaks newline-delimited JSON-RPC 2.0 on stdin/stdout:

python -m solver_mcp

The solver is also usable without MCP, as a library or as a child process:

from load_planner import generate_scenarios
result = generate_scenarios(problem)
cat fixtures/cohort_urgent.json | python -m load_planner.cli

Wiring into an MCP client

Any client that launches stdio servers takes a command and arguments. For a client using the common mcpServers configuration shape:

{
  "mcpServers": {
    "load-planner": {
      "command": "/absolute/path/to/.venv/bin/python",
      "args": ["-m", "solver_mcp"],
      "env": { "PYTHONPATH": "/absolute/path/to/load-planner-mcp" }
    }
  }
}

PYTHONPATH is only needed when the package has not been installed into the interpreter's environment; after pip install -e . the env block can go.

The handshake is the standard one: initialize, then the notifications/initialized notification, then tools/list. The server reports protocol version 2025-06-18 and accepts 2025-03-26 and 2024-11-05, echoing back whichever the client asked for if it is one of those.

Tests

python -m pytest tests/ -q

Three suites: the solver's own tests against the fixture; the MCP handshake and both tools, exercised in process and over a real stdio subprocess; and the tamper suite, which asserts that a narration containing a value absent from the stored result is rejected.

Limits

  • Single machine, single process. The result store is in memory and lives for the life of the server process. A result_id from one process resolves in another only because ids are content-addressed and the solver is deterministic — re-solving the same problem reproduces the id. There is no shared store, no eviction, and no bound on the number of retained results.

  • Solver runtimes. CP-SAT is called once per enumerated mode-pattern, at most three times per solve, on a model with one integer variable and two constraints; it returns in milliseconds. The cost that scales is the 3D packer, which places one item per pallet — a few hundred pallets is fine, tens of thousands is not. solve runs synchronously and has no internal timeout, so a host should impose its own.

  • Whole-order assignment only. Splitting one order across two modes (a base-plus-surge policy) is not modelled; each order is assigned entirely to one mode.

  • Seeded risk inputs. The delay distributions in risk.py are published carrier-reliability seeds, not measurements from your own lane history. They are the right shape and the wrong precision until a caller replaces them.

  • The guard is lexical. See the note above: verify_narration checks that every number in a narration appears in the result. It does not check that the number was used to mean the right thing.

Available Tools

2 tools
explainNarrate a stored solver resultA

Return a narration template populated only from a stored solver result. Takes a result_id and nothing else: it has no access to the problem inputs and performs no arithmetic, so it cannot state a number the solver did not produce.

ParametersJSON Schema
NameRequiredDescriptionDefault
result_idYesA result_id returned by solve.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the transparency burden. It explicitly discloses key behavioral constraints: no access to problem inputs, no arithmetic, and inability to state numbers the solver did not produce. It does not cover failure modes or detailed return structure, but the core limitation is clearly communicated.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, front-loaded with the primary action and followed by a concise constraint. Every sentence earns its place, and there is no repetition of schema fields or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter tool with no output schema, the description explains the input source, what the tool returns, and its limitations. It could specify the narration template's structure, but the tool's simplicity and clear boundaries make it adequately complete for selection and invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single parameter is already described as 'A result_id returned by solve.' The description adds value by emphasizing that it takes 'a result_id and nothing else' and by clarifying the tool's isolation from problem inputs, reinforcing the parameter's purpose beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Return a narration template populated only from a stored solver result,' which is a specific verb and object. It clearly distinguishes this tool from the sibling solve by emphasizing that it consumes a stored result rather than producing one.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies use after solve by stating it takes a result_id and nothing else, and the schema's parameter description says the result_id is 'returned by solve.' It also provides a clear exclusion: it has no access to problem inputs and performs no arithmetic, so it should not be used to derive or verify numbers.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

solveSolve a load planA

Run the CP-SAT load-planning solver on a problem and return its numeric result verbatim, with a result_id and a content hash. This is the only tool that produces numbers.

ParametersJSON Schema
NameRequiredDescriptionDefault
problemYesThe load-planning problem.

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It discloses that it runs a solver and returns the numeric result verbatim with result_id and content hash, which is useful. However, it does not mention side effects, performance implications, error behavior, or prerequisites, leaving a moderate gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, front-loaded with the action, and contains no redundant or irrelevant information. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the rich schema for the input and no output schema, the description adequately describes the output format (numeric result, result_id, content hash) and provides context (only numeric tool). However, it omits details like failure handling or solver constraints, which could be important. Overall it is fairly complete for a simple tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the schema itself defines the problem structure thoroughly. The tool description adds no specific parameter guidance beyond 'run on a problem', so it meets the baseline of 3 but does not elevate it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (run the CP-SAT load-planning solver) and resource (a problem), and specifically notes 'This is the only tool that produces numbers', which differentiates it from the sibling 'explain'. This meets the standard of a specific verb+resource that distinguishes from siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides clear guidance on when to use this tool (when numeric results are needed) by stating it is the only tool that produces numbers. However, it does not explicitly mention when not to use it or alternative usage scenarios beyond that differentiation, so it falls short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.1.0
    • First observedexplain
    • First observedsolve

TDQS

A4.2/5.0

Scored across 2 tools

Disambiguation5/5

solve and explain have clearly separated responsibilities: solve is the only numeric-producing tool, while explain only consumes a stored result_id and never touches inputs or arithmetic. There is no overlap or plausible misselection.

Naming Consistency5/5

Both tool names are bare imperative verbs following the same style: solve and explain. Although the pattern is not verb_noun, it is entirely consistent across the server.

Tool Count3/5

With only two tools, the server is minimal and borderline thin. Each tool has a distinct role, but the surface feels light for a load-planning MCP.

Completeness4/5

The core solve-then-explain workflow is covered, and solve returns a result_id for later explanation. Gaps appear around stored result management, such as listing or retrieving prior results independently.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Lets AI agents pack shipping containers using the Kubova calculator, with tools for container loading and API key verification.
    4
    12
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Ocean and multimodal freight intelligence suite providing cross-validated rates, total landed cost, transit reliability, customs, risk, emissions, and unified ship decisions through 47 tools.
    47
    15
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Plan optimal container & truck loads: 3D layouts, right-size the container mix, and check utilization, centre of gravity, crush protection and securing across 200+ equipment types.
    16
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Enables AI agents to pack trucks and shipping containers by generating validated 3D load plans with metrics like LDM, linear feet, and pallet positions through the Hansatic packing API.
    3
    19
    MIT