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,
    )
Behavior5/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively describes key behavioral traits: it solves problems defined with variables, constraints, and optional objectives; returns a solution if one exists; and includes critical usage constraints (e.g., 'Each constraint expression must be a single evaluable Python statement' and restrictions on control flow). This provides essential context beyond basic functionality.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately front-loaded with the core purpose, but it includes a lengthy example (over 40 lines) that, while helpful, could be considered excessive. The structure is logical, but the example dominates the text, potentially reducing conciseness for quick scanning.

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

Completeness4/5

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

Given the complexity of the tool (1 parameter with nested objects, no annotations, no output schema), the description is largely complete. It explains the tool's purpose, usage constraints, and parameter semantics with an example. However, it doesn't detail the return format beyond 'A list of TextContent containing the solution or an error message,' which could be more specific given the lack of output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It adds significant meaning beyond the input schema by explaining that the 'problem' parameter includes 'variables, constraints, and an optional objective,' and provides a detailed example showing how to structure these components. This clarifies the semantics of the nested objects in the schema.

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 the tool's purpose: 'Solve a constraint programming problem using Google OR-Tools.' It specifies the verb ('solve'), resource ('constraint programming problem'), and technology ('Google OR-Tools'), distinguishing it from sibling tools like 'solve_cvxpy_problem' or 'solve_z3' which use different solvers.

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

Usage Guidelines4/5

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

The description provides clear context for when to use this tool by specifying it's for 'constraint programming problems' and mentioning it uses 'Google OR-Tools.' However, it doesn't explicitly state when not to use it or name specific alternatives among the sibling tools, though the technology distinction implies usage guidance.

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

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