Skip to main content
Glama

solve_mixed_integer_program

Define variables, constraints, and objective to solve mixed-integer programming problems with support for multiple solvers like SCIP, CBC, GUROBI, and CPLEX.

Instructions

Solve Mixed-Integer Programming (MIP) problems with integer, binary, and continuous variables.

    Args:
        variables: List of variable definitions with bounds and types
        constraints: List of constraint definitions with coefficients and bounds
        objective: Objective function definition with coefficients and direction
        solver_name: Solver to use ("SCIP", "CBC", "GUROBI", "CPLEX")
        time_limit_seconds: Maximum solving time in seconds (default: 30.0)

    Returns:
        Optimization result with optimal variable values and objective
    

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
variablesYes
constraintsYes
objectiveYes
solver_nameNoSCIP
time_limit_secondsNo

Implementation Reference

  • MCP tool handler for solve_mixed_integer_program. Decorated with @mcp.tool(), it accepts variables, constraints, objective, solver_name, and time_limit_seconds, builds input_data dict, and delegates to solve_integer_program().
    @mcp.tool()
    def solve_mixed_integer_program(
        variables: list[dict[str, Any]],
        constraints: list[dict[str, Any]],
        objective: dict[str, Any],
        solver_name: str = "SCIP",
        time_limit_seconds: float = 30.0,
    ) -> dict[str, Any]:
        """Solve Mixed-Integer Programming (MIP) problems with integer, binary, and continuous variables.
    
        Args:
            variables: List of variable definitions with bounds and types
            constraints: List of constraint definitions with coefficients and bounds
            objective: Objective function definition with coefficients and direction
            solver_name: Solver to use ("SCIP", "CBC", "GUROBI", "CPLEX")
            time_limit_seconds: Maximum solving time in seconds (default: 30.0)
    
        Returns:
            Optimization result with optimal variable values and objective
        """
        input_data = {
            "variables": variables,
            "constraints": constraints,
            "objective": objective,
            "solver_name": solver_name,
            "time_limit_seconds": time_limit_seconds,
        }
    
        result = solve_integer_program(input_data)
        result_dict: dict[str, Any] = result.model_dump()
        return result_dict
  • Wrapper function solve_mixed_integer_program that takes a dict input_data and delegates to solve_integer_program, returning a dict result.
    def solve_mixed_integer_program(input_data: dict[str, Any]) -> dict[str, Any]:
        """Solve Mixed-Integer Programming Problem (alias for solve_integer_program).
    
        Args:
            input_data: Mixed-integer programming problem specification
    
        Returns:
            Dictionary with optimization result
        """
        result = solve_integer_program(input_data)
        result_dict: dict[str, Any] = result.model_dump()
        return result_dict
  • Pydantic input schemas used by solve_integer_program (the core solver). IntegerVariable, IntegerConstraint, IntegerObjective, IntegerProgramInput define the input format.
    class IntegerVariable(BaseModel):
        """Integer variable definition."""
    
        name: str
        type: str = Field(pattern="^(integer|binary|continuous)$")
        lower: float | None = None
        upper: float | None = None
    
        @field_validator("upper")
        @classmethod
        def validate_upper(cls, v: float | None, info: ValidationInfo) -> float | None:
            if v is not None and info.data and "lower" in info.data and v < info.data["lower"]:
                raise ValueError("upper bound must be >= lower bound")
            return v
    
    
    class IntegerConstraint(BaseModel):
        """Integer programming constraint."""
    
        name: str | None = None
        expression: dict[str, float]  # variable_name -> coefficient
        operator: str = Field(pattern="^(<=|>=|==)$")
        rhs: float
    
    
    class IntegerObjective(BaseModel):
        """Integer programming objective."""
    
        sense: str = Field(pattern="^(minimize|maximize)$")
        coefficients: dict[str, float]  # variable_name -> coefficient
    
        @field_validator("coefficients")
        @classmethod
        def validate_coefficients(cls, v: dict[str, float]) -> dict[str, float]:
            if not v:
                raise ValueError("Objective must have at least one coefficient")
            return v
  • Registration in mcp_server.py: imports and calls register_integer_programming_tools(mcp) to register the tool with the MCP server.
    from mcp_optimizer.tools.integer_programming import register_integer_programming_tools
    from mcp_optimizer.tools.knapsack import register_knapsack_tools
    from mcp_optimizer.tools.linear_programming import register_linear_programming_tools
    from mcp_optimizer.tools.production import register_production_tools
    from mcp_optimizer.tools.routing import register_routing_tools
    from mcp_optimizer.tools.scheduling import register_scheduling_tools
    from mcp_optimizer.tools.validation import register_validation_tools
    from mcp_optimizer.utils.resource_monitor import (
        get_resource_status,
        reset_resource_stats,
        resource_monitor,
    )
    
    logger = logging.getLogger(__name__)
    
    # Server start time for uptime calculation
    _server_start_time = time.time()
    
    
    def get_health() -> dict[str, Any]:
        """Get server health status and resource information."""
        try:
            resource_status = get_resource_status()
    
            status = "healthy"
            messages = []
    
            # Check memory usage
            current_memory = resource_status.get("current_memory_mb", 0)
            max_memory = resource_status.get("max_memory_mb", 1024)
            memory_usage_pct = (current_memory / max_memory) * 100 if max_memory > 0 else 0
    
            if memory_usage_pct > 90:
                status = "critical"
                messages.append(f"High memory usage: {memory_usage_pct:.1f}%")
            elif memory_usage_pct > 75:
                status = "warning"
                messages.append(f"Elevated memory usage: {memory_usage_pct:.1f}%")
    
            # Check active requests
            active_requests = resource_status.get("active_requests", 0)
            max_requests = resource_status.get("max_concurrent_requests", 10)
    
            if active_requests >= max_requests:
                status = "warning"
                messages.append("At maximum concurrent request limit")
    
            health_info = {
                "status": status,
                "version": get_version("mcp-optimizer"),
                "uptime": time.time() - _server_start_time,
                "requests_processed": resource_monitor.total_requests,
                "resource_stats": resource_monitor.get_stats(),
            }
    
            return {
                "status": status,
                "version": get_version("mcp-optimizer"),
                "uptime": time.time() - _server_start_time,
                "message": "; ".join(messages),
                "resource_status": resource_status,
                "health_info": health_info,
            }
        except ImportError as e:
            return {
                "status": "error",
                "message": f"Health check failed: {e}",
                "resource_status": {},
            }
    
    
    def get_resource_stats() -> dict[str, Any]:
        """Get detailed resource usage statistics."""
        return get_resource_status()
    
    
    def reset_resource_statistics() -> dict[str, str]:
        """Reset resource monitoring statistics."""
        reset_resource_stats()
        return {"status": "reset", "message": "Resource statistics have been reset"}
    
    
    def get_server_info() -> dict[str, Any]:
        """Get comprehensive server information."""
        return {
            "name": "MCP Optimizer",
            "version": get_version("mcp-optimizer"),
            "description": "Mathematical optimization server with multiple solvers",
            "uptime": time.time() - _server_start_time,
            "capabilities": {
                "linear_programming": True,
                "integer_programming": True,
                "mixed_integer_programming": True,
                "assignment_problems": True,
                "transportation_problems": True,
                "knapsack_problems": True,
                "routing_problems": True,
                "scheduling_problems": True,
                "portfolio_optimization": True,
                "production_planning": True,
                "input_validation": True,
            },
            "solvers": {
                "pulp": "Linear/Integer Programming",
                "ortools": "Routing, Scheduling, Assignment",
                "native": "Portfolio, Production Planning",
            },
            "configuration": {
                "max_solve_time": settings.max_solve_time,
                "max_memory_mb": settings.max_memory_mb,
                "max_concurrent_requests": settings.max_concurrent_requests,
                "log_level": settings.log_level.value,
                "debug": settings.debug,
            },
        }
    
    
    def create_mcp_server() -> FastMCP[dict[str, str]]:
        """Create and configure the MCP server with optimization tools."""
    
        # Create MCP server
        mcp: FastMCP[dict[str, str]] = FastMCP("MCP Optimizer")
    
        # Register all optimization tools
        register_linear_programming_tools(mcp)
        register_integer_programming_tools(mcp)
        register_assignment_tools(mcp)
        register_knapsack_tools(mcp)
        register_routing_tools(mcp)
        register_scheduling_tools(mcp)
        register_financial_tools(mcp)
        register_production_tools(mcp)
        register_validation_tools(mcp)
  • Core solver function using OR-Tools (pywraplp). Handles SCIP/CBC/GUROBI/CPLEX solvers, creates variables (integer/binary/continuous), constraints, objective, solves, and returns OptimizationResult.
    @with_resource_limits(timeout_seconds=90.0, estimated_memory_mb=150.0)
    def solve_integer_program(input_data: dict[str, Any]) -> OptimizationResult:
        """Solve Integer Programming Problem using OR-Tools.
    
        Args:
            input_data: Integer programming problem specification
    
        Returns:
            OptimizationResult with optimal solution
        """
        if not ORTOOLS_AVAILABLE:
            return OptimizationResult(
                status=OptimizationStatus.ERROR,
                objective_value=None,
                variables={},
                execution_time=0.0,
                error_message="OR-Tools is not available. Please install it with 'pip install ortools'",
            )
    
        start_time = time.time()
    
        try:
            # Parse and validate input
            ip_input = IntegerProgramInput(**input_data)
    
            # Create solver
            solver_name = ip_input.solver
            if solver_name == "SCIP":
                solver = pywraplp.Solver.CreateSolver("SCIP")
            elif solver_name == "CBC":
                solver = pywraplp.Solver.CreateSolver("CBC")
            elif solver_name == "GUROBI":
                solver = pywraplp.Solver.CreateSolver("GUROBI_MIXED_INTEGER_PROGRAMMING")
            elif solver_name == "CPLEX":
                solver = pywraplp.Solver.CreateSolver("CPLEX_MIXED_INTEGER_PROGRAMMING")
            else:
                return OptimizationResult(
                    status=OptimizationStatus.ERROR,
                    error_message=f"Unsupported solver: {solver_name}",
                    execution_time=time.time() - start_time,
                )
    
            if not solver:
                return OptimizationResult(
                    status=OptimizationStatus.ERROR,
                    error_message=f"Could not create {solver_name} solver",
                    execution_time=time.time() - start_time,
                )
    
            # Set time limit
            if ip_input.time_limit_seconds:
                solver.SetTimeLimit(int(ip_input.time_limit_seconds * 1000))  # milliseconds
    
            # Create variables
            variables = {}
            for var_name, var_def in ip_input.variables.items():
                lower = var_def.lower if var_def.lower is not None else -solver.infinity()
                upper = var_def.upper if var_def.upper is not None else solver.infinity()
    
                if var_def.type == "continuous":
                    var = solver.NumVar(lower, upper, var_name)
                elif var_def.type == "integer":
                    var = solver.IntVar(
                        int(lower) if lower != -solver.infinity() else -2147483648,
                        int(upper) if upper != solver.infinity() else 2147483647,
                        var_name,
                    )
                elif var_def.type == "binary":
                    var = solver.BoolVar(var_name)
                else:
                    return OptimizationResult(
                        status=OptimizationStatus.ERROR,
                        error_message=f"Unknown variable type: {var_def.type}",
                        execution_time=time.time() - start_time,
                    )
    
                variables[var_name] = var
    
            # Add constraints
            constraints = []
            for i, constraint_def in enumerate(ip_input.constraints):
                constraint_name = constraint_def.name or f"constraint_{i}"
    
                # Determine bounds based on operator
                if constraint_def.operator == "<=":
                    lower_bound = -solver.infinity()
                    upper_bound = constraint_def.rhs
                elif constraint_def.operator == ">=":
                    lower_bound = constraint_def.rhs
                    upper_bound = solver.infinity()
                elif constraint_def.operator == "==":
                    lower_bound = constraint_def.rhs
                    upper_bound = constraint_def.rhs
                else:
                    return OptimizationResult(
                        status=OptimizationStatus.ERROR,
                        error_message=f"Unknown constraint operator '{constraint_def.operator}' in constraint '{constraint_name}'",
                        execution_time=time.time() - start_time,
                    )
    
                # Build constraint with proper bounds
                constraint = solver.Constraint(lower_bound, upper_bound, constraint_name)
    
                for var_name, coeff in constraint_def.expression.items():
                    if var_name in variables:
                        constraint.SetCoefficient(variables[var_name], coeff)
                    else:
                        return OptimizationResult(
                            status=OptimizationStatus.ERROR,
                            error_message=f"Unknown variable '{var_name}' in constraint '{constraint_name}'",
                            execution_time=time.time() - start_time,
                        )
    
                constraints.append(constraint)
    
            # Set objective
            objective = solver.Objective()
            for var_name, coeff in ip_input.objective.coefficients.items():
                if var_name in variables:
                    objective.SetCoefficient(variables[var_name], coeff)
                else:
                    return OptimizationResult(
                        status=OptimizationStatus.ERROR,
                        error_message=f"Unknown variable '{var_name}' in objective",
                        execution_time=time.time() - start_time,
                    )
    
            if ip_input.objective.sense == "maximize":
                objective.SetMaximization()
            else:
                objective.SetMinimization()
    
            # Set gap tolerance if specified
            if ip_input.gap_tolerance is not None:
                solver.SetSolverSpecificParametersAsString(f"limits/gap={ip_input.gap_tolerance}")
    
            # Solve
            status = solver.Solve()
    
            # Process results
            if status == pywraplp.Solver.OPTIMAL:
                solution_status = OptimizationStatus.OPTIMAL
            elif status == pywraplp.Solver.FEASIBLE:
                solution_status = OptimizationStatus.FEASIBLE
            elif status == pywraplp.Solver.INFEASIBLE:
                solution_status = OptimizationStatus.INFEASIBLE
            elif status == pywraplp.Solver.UNBOUNDED:
                solution_status = OptimizationStatus.UNBOUNDED
            else:
                solution_status = OptimizationStatus.ERROR
    
            execution_time = time.time() - start_time
    
            if status in [pywraplp.Solver.OPTIMAL, pywraplp.Solver.FEASIBLE]:
                # Extract solution
                solution_variables = {}
                for var_name, var in variables.items():
                    solution_variables[var_name] = var.solution_value()
    
                # Calculate constraint violations (for debugging)
                constraint_info = []
                for i, (_constraint, constraint_def) in enumerate(
                    zip(constraints, ip_input.constraints, strict=False)
                ):
                    lhs_value = sum(
                        coeff * variables[var_name].solution_value()
                        for var_name, coeff in constraint_def.expression.items()
                    )
    
                    constraint_info.append(
                        {
                            "name": constraint_def.name or f"constraint_{i}",
                            "lhs_value": lhs_value,
                            "operator": constraint_def.operator,
                            "rhs_value": constraint_def.rhs,
                            "slack": constraint_def.rhs - lhs_value
                            if constraint_def.operator == "<="
                            else lhs_value - constraint_def.rhs,
                        }
                    )
    
                return OptimizationResult(
                    status=solution_status,
                    objective_value=solver.Objective().Value(),
                    variables=solution_variables,
                    execution_time=execution_time,
                    solver_info={
                        "solver_name": solver_name,
                        "iterations": solver.iterations() if hasattr(solver, "iterations") else None,
                        "nodes": solver.nodes() if hasattr(solver, "nodes") else None,
                        "gap": (solver.Objective().BestBound() - solver.Objective().Value())
                        / abs(solver.Objective().Value())
                        if solver.Objective().Value() != 0 and hasattr(solver.Objective(), "BestBound")
                        else 0,
                        "constraint_info": constraint_info,
                    },
                )
            else:
                error_messages = {
                    pywraplp.Solver.INFEASIBLE: "Problem is infeasible",
                    pywraplp.Solver.UNBOUNDED: "Problem is unbounded",
                    pywraplp.Solver.ABNORMAL: "Solver encountered an error",
                    pywraplp.Solver.NOT_SOLVED: "Problem not solved",
                }
    
                return OptimizationResult(
                    status=solution_status,
                    error_message=error_messages.get(status, f"Unknown solver status: {status}"),
                    execution_time=execution_time,
                    solver_info={"solver_name": solver_name},
                )
    
        except Exception as e:
            return OptimizationResult(
                status=OptimizationStatus.ERROR,
                error_message=f"Integer programming error: {str(e)}",
                execution_time=time.time() - start_time,
            )
Behavior2/5

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

With no annotations, the description fails to disclose behavioral traits such as potential solver unavailability, performance constraints, or side effects. Only a simple functional description is given.

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 concise with a clear purpose statement and structured Args list. Every sentence adds value, no redundancy.

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

Completeness2/5

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

The description lacks details about the return structure, error handling, and validation. Given the complexity and absent output schema, it is incomplete for an agent to use confidently.

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?

Despite 0% schema coverage, the description adds meaning for all five parameters: explains variable definitions, constraints, objective components, solver options, and default time limit. However, it could provide more structure details for nested objects.

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 it solves Mixed-Integer Programming problems and specifies the variable types (integer, binary, continuous). This distinguishes it from siblings like solve_linear_program_tool or solve_integer_program_tool.

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

Usage Guidelines2/5

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

No guidance is provided on when to use this tool versus alternatives like solve_integer_program_tool or solve_linear_program_tool. There is no mention of contexts or prerequisites.

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

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dmitryanchikov/mcp-optimizer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server