Skip to main content
Glama

solve_ortools_problem

Solve constraint programming problems using Google OR-Tools by defining variables, constraints, and objectives to find optimal solutions.

Instructions

Solve a constraint programming problem using Google OR-Tools.

This tool takes a constraint programming problem defined with variables,
constraints, and an optional objective, and returns a solution if one exists.

Important Note:
    Each constraint expression must be a single evaluable Python statement.
    You cannot use Python control flow (loops, if statements) in the expressions.
    Instead, you need to generate separate constraints for each case.

Example:
    Nurse Scheduling Problem:
    ```python
    # Schedule 4 nurses across 3 shifts over 3 days
    shifts_var = Variable(
        name="shifts_var",
        type=VariableType.BOOLEAN,
        shape=[4, 3, 3],  # [nurses, days, shifts]
        description="Binary variable indicating if a nurse works a shift",
    )

    constraints = []

    # INCORRECT - This will fail:
    # Constraint(
    #     expression=(
    #         "for d in range(3): for s in range(3): "
    #         "model.add(sum([shifts_var[n][d][s] for n in range(4)]) == 1)"
    #     )
    # )

    # CORRECT - Add each constraint separately:
    # Each shift must have exactly one nurse
    for d in range(3):
        for s in range(3):
            constraints.append(
                Constraint(
                    expression=f"model.add(sum([shifts_var[n][{d}][{s}] for n in range(4)]) == 1)",
                    description=f"One nurse for day {d}, shift {s}",
                )
            )

    # Each nurse works at most one shift per day
    for n in range(4):
        for d in range(3):
            constraints.append(
                Constraint(
                    expression=f"model.add(sum([shifts_var[{n}][{d}][s] for s in range(3)]) <= 1)",
                    description=f"Max one shift for nurse {n} on day {d}",
                )
            )

    # Each nurse works 2-3 shifts total
    for n in range(4):
        constraints.append(
            Constraint(
                expression=f"model.add(sum([shifts_var[{n}][d][s] for d in range(3) for s in range(3)]) >= 2)",
                description=f"Min shifts for nurse {n}",
            )
        )

    problem = Problem(
        variables=[shifts_var],
        constraints=constraints,
        description="Hospital nurse scheduling problem",
    )
    ```

Args:
    problem: The problem definition with variables, constraints, and optional objective

Returns:
    A list of TextContent containing the solution or an error message

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
problemYes

Implementation Reference

  • MCP tool handler: solve_ortools_problem_tool - registers and executes the tool, calls the solver, handles Success/Failure and formats output as TextContent list
    @app.tool("solve_ortools_problem")
    async def solve_ortools_problem_tool(
        problem: ORToolsProblem,
    ) -> list[TextContent]:
        """Solve a constraint programming problem using Google OR-Tools.
    
        This tool takes a constraint programming problem defined with variables,
        constraints, and an optional objective, and returns a solution if one exists.
    
        Important Note:
            Each constraint expression must be a single evaluable Python statement.
            You cannot use Python control flow (loops, if statements) in the expressions.
            Instead, you need to generate separate constraints for each case.
    
        Example:
            Nurse Scheduling Problem:
            ```python
            # Schedule 4 nurses across 3 shifts over 3 days
            shifts_var = Variable(
                name="shifts_var",
                type=VariableType.BOOLEAN,
                shape=[4, 3, 3],  # [nurses, days, shifts]
                description="Binary variable indicating if a nurse works a shift",
            )
    
            constraints = []
    
            # INCORRECT - This will fail:
            # Constraint(
            #     expression=(
            #         "for d in range(3): for s in range(3): "
            #         "model.add(sum([shifts_var[n][d][s] for n in range(4)]) == 1)"
            #     )
            # )
    
            # CORRECT - Add each constraint separately:
            # Each shift must have exactly one nurse
            for d in range(3):
                for s in range(3):
                    constraints.append(
                        Constraint(
                            expression=f"model.add(sum([shifts_var[n][{d}][{s}] for n in range(4)]) == 1)",
                            description=f"One nurse for day {d}, shift {s}",
                        )
                    )
    
            # Each nurse works at most one shift per day
            for n in range(4):
                for d in range(3):
                    constraints.append(
                        Constraint(
                            expression=f"model.add(sum([shifts_var[{n}][{d}][s] for s in range(3)]) <= 1)",
                            description=f"Max one shift for nurse {n} on day {d}",
                        )
                    )
    
            # Each nurse works 2-3 shifts total
            for n in range(4):
                constraints.append(
                    Constraint(
                        expression=f"model.add(sum([shifts_var[{n}][d][s] for d in range(3) for s in range(3)]) >= 2)",
                        description=f"Min shifts for nurse {n}",
                    )
                )
    
            problem = Problem(
                variables=[shifts_var],
                constraints=constraints,
                description="Hospital nurse scheduling problem",
            )
            ```
    
        Args:
            problem: The problem definition with variables, constraints, and optional objective
    
        Returns:
            A list of TextContent containing the solution or an error message
        """
        result = solve_ortools_problem(problem)
    
        match result:
            case Success(solution):
                return [
                    TextContent(
                        type="text",
                        text=json.dumps(
                            {
                                "values": solution.values,
                                "is_feasible": solution.is_feasible,
                                "status": solution.status,
                                "objective_value": solution.objective_value,
                                "statistics": solution.statistics,
                            }
                        ),
                    )
                ]
            case Failure(error):
                return [TextContent(type="text", text=f"Error solving problem: {error}")]
            case _:
                return [
                    TextContent(
                        type="text",
                        text="Unexpected error in solve_ortools_problem_tool",
                    )
                ]
  • Core solver logic: solve_problem - builds CpModel, creates variables/constraints/objective, solves with CpSolver, extracts solution and returns Result[Solution, str]
    def solve_problem(problem: Problem) -> Result[Solution, str]:
        """Solve an OR-Tools problem and return the solution.
    
        Args:
            problem: The problem definition
    
        Returns:
            Result containing a Solution or an error message
        """
        try:
            # Create model
            model = cp_model.CpModel()
    
            # Create variables
            vars_result = create_variables(model, problem.variables)
            if isinstance(vars_result, Failure):
                return vars_result
    
            variables_dict, globals_dict = vars_result.unwrap()
    
            # Create constraints
            constraints_result = create_constraints(
                model, problem.constraints, variables_dict, globals_dict
            )
            if isinstance(constraints_result, Failure):
                return constraints_result
    
            # Add objective
            objective_result = add_objective(
                model, problem.objective, variables_dict, globals_dict
            )
            if isinstance(objective_result, Failure):
                return objective_result
    
            # Create solver
            solver = cp_model.CpSolver()
    
            # Add parameters if provided
            if problem.parameters:
                for key, value in problem.parameters.items():
                    if hasattr(solver.parameters, key):
                        setattr(solver.parameters, key, value)
    
            # Solve the problem
            status = solver.Solve(model)
    
            # Extract solution
            is_feasible = status in [
                cp_model.OPTIMAL,
                cp_model.FEASIBLE,
            ]
            values = (
                extract_solution(solver, variables_dict, problem.variables)
                if is_feasible
                else {}
            )
    
            return Success(
                Solution(
                    values=values,
                    is_feasible=is_feasible,
                    status=solver.StatusName(status),
                    objective_value=(
                        solver.ObjectiveValue()
                        if is_feasible and problem.objective
                        else None
                    ),
                    statistics={
                        "num_conflicts": solver.NumConflicts(),
                        "num_branches": solver.NumBranches(),
                        "wall_time": solver.WallTime(),
                    },
                )
            )
        except Exception as e:
            return Failure(f"Error solving problem: {e!s}")
  • Input schema: Problem Pydantic model defining the structure for ORToolsProblem (variables, constraints, objective, parameters)
    class Problem(BaseModel):
        """Model representing a complete OR-Tools constraint programming problem."""
    
        variables: list[Variable]
        constraints: list[Constraint]
        objective: Objective | None = None
        parameters: dict[str, Any] = Field(default_factory=dict)
        description: str = ""
  • Output schema: Solution Pydantic model for solver results (values, status, etc.)
    class Solution(BaseModel):
        """Model representing a solution to an OR-Tools problem."""
    
        values: dict[str, Any]
        is_feasible: bool
        status: str
        objective_value: float | None = None
        statistics: dict[str, Any] = Field(default_factory=dict)
  • Import and alias of the solver function as solve_ortools_problem for use in the tool handler
    from usolver_mcp.solvers.ortools_solver import (
        solve_problem as solve_ortools_problem,
    )

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/sdiehl/usolver'

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