Files
RollCalcPython/rollcalc_mcp_tools.py
T

201 lines
6.7 KiB
Python

"""Thin MCP-facing adapters over the shared RollCalc domain services."""
from __future__ import annotations
from typing import Any
from machine_constraints import (
calculate_machine_max_product_length,
check_production_feasibility,
get_machine,
)
from roll_calculation import (
ArticleRepository,
calculate_material_weight,
calculate_product_length,
calculate_roll,
get_article,
transport_roll_inputs_from_roll_calculation,
)
from transport_calculation import analyze_transport
def get_article_result(
article_number: str | None = None,
article_name_hint: str | None = None,
) -> dict[str, Any]:
"""Delegate article resolution to the shared article repository."""
return get_article(article_number, article_name_hint)
def search_articles_result(query: str) -> dict[str, Any]:
"""Delegate article discovery to the shared article repository."""
return ArticleRepository.load().search(query)
def get_machine_result(machine: str | None = None) -> dict[str, Any]:
"""Delegate configured-machine resolution to the shared repository."""
return get_machine(machine)
def check_production_feasibility_result(
*,
machine: str,
roll_weight_kg: float | None = None,
average_diameter_mm: float | None = None,
maximum_diameter_mm: float | None = None,
core_diameter_mm: float | None = None,
product_width_m: float | None = None,
line_speed_m_per_min: float | None = None,
) -> dict[str, Any]:
"""Delegate configured production feasibility to the shared domain."""
return check_production_feasibility({
"machine": machine,
"roll_weight_kg": roll_weight_kg,
"average_diameter_mm": average_diameter_mm,
"maximum_diameter_mm": maximum_diameter_mm,
"core_diameter_mm": core_diameter_mm,
"product_width_m": product_width_m,
"line_speed_m_per_min": line_speed_m_per_min,
})
def calculate_machine_max_product_length_result(
*,
machine: str,
core_diameter_mm: float,
thickness_mm: float,
thickness_stddev_mm: float | None = None,
product_width_m: float | None = None,
area_weight_g_m2: float | None = None,
) -> dict[str, Any]:
"""Return the compact MCP view of the machine-aware domain calculation."""
result = calculate_machine_max_product_length(
machine=machine,
core_diameter_mm=core_diameter_mm,
thickness_mm=thickness_mm,
thickness_stddev_mm=thickness_stddev_mm,
product_width_m=product_width_m,
area_weight_g_m2=area_weight_g_m2,
)
return project_machine_max_product_length_result(result)
def project_machine_max_product_length_result(result: dict[str, Any]) -> dict[str, Any]:
"""Project successful maximum-length results for MCP without diagnostics.
The shared domain result deliberately contains detailed verification reports.
They remain available to Flask and other consumers, but duplicate machine
metadata and checks are unnecessary in the MCP response.
"""
if result.get("status") != "success":
return result
machine = result["machine"]
constraints = machine["constraints"]
return {
"status": result["status"],
"machine": {"id": machine["id"], "name": machine["name"]},
"weight_scope": result["weight_scope"],
"machine_limits": {
"maximum_roll_diameter_mm": constraints["roll_diameter_mm"]["maximum"]
if constraints["roll_diameter_mm"] is not None else None,
"maximum_material_roll_weight_kg": constraints["roll_weight_kg"]["maximum"]
if constraints["roll_weight_kg"] is not None else None,
},
"production_maximums": result["production_maximums"],
"material_weights_kg": result["material_weights_kg"],
"warnings": result["warnings"],
}
def calculate_material_weight_result(
*,
roll_length_m: float,
width_m: float,
area_weight_g_m2: float,
) -> dict[str, Any]:
"""Delegate material-only weight calculation to the shared domain service."""
return calculate_material_weight(
roll_length_m=roll_length_m,
width_m=width_m,
area_weight_g_m2=area_weight_g_m2,
)
def calculate_product_length_result(
*,
target_roll_diameter_mm: float,
core_diameter_mm: float,
thickness_mm: float,
thickness_stddev_mm: float | None = None,
width_m: float | None = None,
area_weight_g_m2: float | None = None,
) -> dict[str, Any]:
"""Delegate target-diameter product-length calculation to the domain."""
return calculate_product_length(
target_roll_diameter_mm=target_roll_diameter_mm,
core_diameter_mm=core_diameter_mm,
thickness_mm=thickness_mm,
thickness_stddev_mm=thickness_stddev_mm,
width_m=width_m,
area_weight_g_m2=area_weight_g_m2,
)
def calculate_roll_diameter_result(
*,
article_number: str | None = None,
article_name_hint: str | None = None,
roll_length_m: float | None = None,
width_m: float | None = None,
thickness_mm: float | None = None,
thickness_stddev_mm: float | None = None,
area_weight_g_m2: float | None = None,
core_diameter_mm: float | None = None,
core_type: str | None = None,
category: str | None = None,
production_site: str | None = None,
) -> dict[str, Any]:
"""Map MCP inputs to the existing calculation, always requesting weight."""
result = calculate_roll({
"article_number": article_number,
"article_name_hint": article_name_hint,
"roll_length_m": roll_length_m,
"width_m": width_m,
"thickness_mm": thickness_mm,
"thickness_stddev_mm": thickness_stddev_mm,
"area_weight_g_m2": area_weight_g_m2,
"core_diameter_mm": core_diameter_mm,
"core_type": core_type,
"category": category,
"production_site": production_site,
"include_roll_weight": True,
})
if result.get("status") == "success":
result = dict(result)
result["transport_roll_inputs"] = (
transport_roll_inputs_from_roll_calculation(result)
)
return result
def analyze_transport_capacity_result(
*,
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 the preset-only MCP contract to the shared transport domain."""
return analyze_transport({
"transport_preset": transport_preset,
"roll_diameter_mm": roll_diameter_mm,
"core_diameter_mm": core_diameter_mm,
"roll_width_m": roll_width_m,
"roll_weight_kg": roll_weight_kg,
"product_length_m": product_length_m,
})