Harden MCP transport preset routing
This commit is contained in:
+2
-14
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user