Harden MCP transport preset routing

This commit is contained in:
2026-09-25 12:04:01 +02:00
parent 78b00fedf8
commit 6f224a1831
3 changed files with 55 additions and 61 deletions
+2 -14
View File
@@ -57,28 +57,16 @@ def create_server() -> Any:
@server.tool()
def analyze_transport_capacity(
transport_preset: TransportPresetKey,
roll_diameter_mm: float,
core_diameter_mm: float,
roll_width_m: float,
roll_weight_kg: float,
product_length_m: float,
transport_preset: TransportPresetKey | None = None,
length_m: float | None = None,
width_m: float | None = None,
height_m: float | None = None,
max_weight_kg: float | None = None,
margin_side_m: float | None = None,
margin_ceiling_m: float | None = None,
) -> dict[str, Any]:
"""Analyze transport capacity for calculated rolls. For a chained request, first call get_article and calculate_roll_diameter, then pass every field from its transport_roll_inputs bundle unchanged: roll_diameter_mm, core_diameter_mm, roll_width_m, roll_weight_kg, and product_length_m. Do not choose among minimum, average, or maximum diameter; the bundle contains the browser-equivalent nominal transport diameter. product_length_m is used for reported m², not placement geometry. transport_preset must be one of the listed canonical identifiers: lkw_sattelzug means LKW Sattelzug / semi-trailer and lkw_tandem means LKW Tandem. Alternatively, supply all custom transport dimensions in m and max_weight_kg. This stateless tool does not recalculate a roll; roll_weight_kg is consumed exactly as supplied."""
"""Use this tool when the user asks how many rolls fit on a known RollCalc transport type. A named transport type is already fully defined by its RollCalc preset: do not ask the user for vehicle dimensions or payload, and do not estimate or invent vehicle data. Use these canonical mappings: LKW-Sattelzug -> lkw_sattelzug; LKW-Tandem -> lkw_tandem; 20ft Container -> container_20ft; 40ft Container -> container_40ft; 40ft High Cube -> container_40ft_hc. If the user asks how many rolls fit on an LKW-Sattelzug, call this tool with transport_preset=\"lkw_sattelzug\". For a chained request, first call get_article and calculate_roll_diameter, then pass every field from its transport_roll_inputs bundle unchanged: roll_diameter_mm, core_diameter_mm, roll_width_m, roll_weight_kg, and product_length_m. Do not choose among minimum, average, or maximum diameter; the bundle contains the browser-equivalent nominal transport diameter. product_length_m is used for reported m², not placement geometry. This stateless tool does not recalculate a roll; roll_weight_kg is consumed exactly as supplied."""
return analyze_transport_capacity_result(
transport_preset=transport_preset,
length_m=length_m,
width_m=width_m,
height_m=height_m,
max_weight_kg=max_weight_kg,
margin_side_m=margin_side_m,
margin_ceiling_m=margin_ceiling_m,
roll_diameter_mm=roll_diameter_mm,
core_diameter_mm=core_diameter_mm,
roll_width_m=roll_width_m,
+7 -19
View File
@@ -59,28 +59,16 @@ def calculate_roll_diameter_result(
def analyze_transport_capacity_result(
*,
transport_preset: str | None = None,
length_m: float | None = None,
width_m: float | None = None,
height_m: float | None = None,
max_weight_kg: float | None = None,
margin_side_m: float | None = None,
margin_ceiling_m: float | None = None,
roll_diameter_mm: float | None = None,
core_diameter_mm: float | None = None,
roll_width_m: float | None = None,
roll_weight_kg: float | None = None,
product_length_m: float | None = None,
transport_preset: str,
roll_diameter_mm: float,
core_diameter_mm: float,
roll_width_m: float,
roll_weight_kg: float,
product_length_m: float,
) -> dict[str, Any]:
"""Map MCP arguments to the shared transport-analysis input shape."""
"""Map the preset-only MCP contract to the shared transport domain."""
return analyze_transport({
"transport_preset": transport_preset,
"length_m": length_m,
"width_m": width_m,
"height_m": height_m,
"max_weight_kg": max_weight_kg,
"margin_side_m": margin_side_m,
"margin_ceiling_m": margin_ceiling_m,
"roll_diameter_mm": roll_diameter_mm,
"core_diameter_mm": core_diameter_mm,
"roll_width_m": roll_width_m,
+46 -28
View File
@@ -1,3 +1,4 @@
import inspect
import sys
import types
import unittest
@@ -90,33 +91,25 @@ class McpAdapterTests(unittest.TestCase):
helper.assert_called_once()
self.assertEqual(result["transport_roll_inputs"], expected_inputs)
def test_transport_tool_delegates_for_preset_custom_and_invalid_cases(self):
for arguments in (
{
"transport_preset": "container_20ft",
"roll_diameter_mm": 1000.0,
"core_diameter_mm": 150.0,
"roll_width_m": 2.0,
"roll_weight_kg": 1000.0,
"product_length_m": 50.0,
},
{
"length_m": 10.0,
"width_m": 5.0,
"height_m": 5.0,
"max_weight_kg": 10000.0,
"roll_diameter_mm": 1000.0,
"core_diameter_mm": 150.0,
"roll_width_m": 1.0,
"roll_weight_kg": 1000.0,
},
{"length_m": 0.0},
):
with self.subTest(arguments=arguments):
self.assertEqual(
analyze_transport_capacity_result(**arguments),
analyze_transport(arguments),
)
def test_transport_tool_delegates_to_domain_with_lkw_sattelzug_preset(self):
arguments = {
"transport_preset": "lkw_sattelzug",
"roll_diameter_mm": 1000.0,
"core_diameter_mm": 150.0,
"roll_width_m": 2.0,
"roll_weight_kg": 1000.0,
"product_length_m": 50.0,
}
self.assertEqual(
analyze_transport_capacity_result(**arguments),
analyze_transport(arguments),
)
result = analyze_transport_capacity_result(**arguments)
self.assertEqual(result["status"], "success")
self.assertEqual(result["analysis"]["transport_preset"], "lkw_sattelzug")
self.assertEqual(result["analysis"]["final_rolls"], 24)
self.assertEqual(result["analysis"]["limiting"]["name"], "Weight")
def test_server_registers_exactly_the_three_stdio_poc_tools(self):
mcp = types.ModuleType("mcp")
@@ -150,12 +143,13 @@ class McpAdapterTests(unittest.TestCase):
self.assertNotIn("include_roll_weight", roll_schema["properties"])
preset_schema = transport_schema["properties"]["transport_preset"]
self.assertEqual(
preset_schema["anyOf"][0]["enum"],
preset_schema["enum"],
[preset.key for preset in transport_presets()],
)
self.assertEqual(
transport_schema["required"],
[
"transport_preset",
"roll_diameter_mm",
"core_diameter_mm",
"roll_width_m",
@@ -164,15 +158,39 @@ class McpAdapterTests(unittest.TestCase):
],
)
for field in transport_schema["required"]:
if field == "transport_preset":
continue
self.assertEqual(
transport_schema["properties"][field]["type"], "number"
)
for field in (
"length_m",
"width_m",
"height_m",
"max_weight_kg",
"margin_side_m",
"margin_ceiling_m",
):
self.assertNotIn(field, transport_schema["properties"])
self.assertNotIn(
field,
inspect.signature(analyze_transport_capacity_result).parameters,
)
description = transport_tool.description
for text in (
"Use this tool when the user asks how many rolls fit on a known RollCalc transport type",
"do not ask the user for vehicle dimensions or payload",
"LKW-Sattelzug -> lkw_sattelzug",
"LKW-Tandem -> lkw_tandem",
"20ft Container -> container_20ft",
"40ft Container -> container_40ft",
"40ft High Cube -> container_40ft_hc",
'transport_preset=\"lkw_sattelzug\"',
"first call get_article and calculate_roll_diameter",
"transport_roll_inputs bundle unchanged",
"Do not choose among minimum, average, or maximum diameter",
"product_length_m",
"do not estimate or invent",
"stateless tool does not recalculate a roll",
):
self.assertIn(text, description)