solve_constraint_programming
Solve combinatorial optimization problems with logical and numerical constraints using OR-Tools for scheduling, assignment, and constraint satisfaction.
Instructions
Solve constraint programming problems using OR-Tools.
This tool is ideal for combinatorial optimization problems, scheduling,
assignment problems, and constraint satisfaction with discrete variables.
Args:
variables: List of variable definitions with 'name', 'type', and optional 'domain'/'shape'
constraints: List of constraint expressions as strings
objective: Optional objective definition with 'type' and 'expression'
parameters: Dictionary of solver parameters
description: Optional problem description
Returns:
Solution results including variable values and feasibility status
Example:
variables = [
{"name": "x", "type": "integer", "domain": [0, 10]},
{"name": "y", "type": "boolean"}
]
constraints = [
"x + y >= 5",
"x - y <= 3"
]
objective = {"type": "minimize", "expression": "x + y"}
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| constraints | Yes | ||
| description | No | ||
| objective | No | ||
| parameters | No | ||
| variables | Yes |
Implementation Reference
- MCP tool handler: validates inputs, constructs ORToolsProblem from dicts, calls solve_ortools_problem, serializes result to JSON text.@app.tool("solve_constraint_programming") async def solve_constraint_programming( variables: List[dict[str, Any]], constraints: List[str], objective: dict[str, Any] = None, parameters: dict[str, Any] = None, description: str = "", ) -> List[TextContent]: """ Solve constraint programming problems using OR-Tools. This tool is ideal for combinatorial optimization problems, scheduling, assignment problems, and constraint satisfaction with discrete variables. Args: variables: List of variable definitions with 'name', 'type', and optional 'domain'/'shape' constraints: List of constraint expressions as strings objective: Optional objective definition with 'type' and 'expression' parameters: Dictionary of solver parameters description: Optional problem description Returns: Solution results including variable values and feasibility status Example: variables = [ {"name": "x", "type": "integer", "domain": [0, 10]}, {"name": "y", "type": "boolean"} ] constraints = [ "x + y >= 5", "x - y <= 3" ] objective = {"type": "minimize", "expression": "x + y"} """ try: # Convert to OR-Tools problem problem_variables = [] for var in variables: if "name" not in var or "type" not in var: return [ TextContent( type="text", text="Each variable must have 'name' and 'type' fields", ) ] try: from ..models.ortools_models import ORToolsVariableType var_type = ORToolsVariableType(var["type"]) except ValueError: return [ TextContent( type="text", text=( f"Invalid variable type: {var['type']}. " f"Must be one of: boolean, integer, interval" ), ) ] problem_variables.append(ORToolsVariable(**var)) problem_constraints = [ORToolsConstraint(expression=expr) for expr in constraints] # Create objective if provided problem_objective = None if objective: try: from ..models.ortools_models import ORToolsObjectiveType obj_type = ORToolsObjectiveType(objective["type"]) problem_objective = ORToolsObjective( type=obj_type, expression=objective.get("expression"), description=objective.get("description", "") ) except ValueError: return [ TextContent( type="text", text=( f"Invalid objective type: {objective['type']}. " f"Must be one of: minimize, maximize, feasibility" ), ) ] problem = ORToolsProblem( variables=problem_variables, constraints=problem_constraints, objective=problem_objective, parameters=parameters or {}, description=description, ) # Solve the problem 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, "solve_time": solution.solve_time, "statistics": solution.statistics, } ), ) ] case Failure(error): return [TextContent(type="text", text=f"Error solving problem: {error}")] except Exception as e: return [TextContent(type="text", text=f"Error in solve_constraint_programming: {e!s}")]
- constrained_opt_mcp/server/main.py:418-418 (registration)FastMCP tool registration decorator binding the handler function to the tool name 'solve_constraint_programming'.@app.tool("solve_constraint_programming")
- Core solver implementation using OR-Tools CP-SAT: builds CpModel, creates variables/constraints/objective, solves, extracts solution and statistics.def solve_ortools_problem(problem: ORToolsProblem) -> Result[ORToolsSolution, 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: # Validate problem if not problem.validate(): return Failure("Invalid problem definition") start_time = time.time() # 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) # Set time limit if specified if problem.time_limit: solver.parameters.max_time_in_seconds = problem.time_limit # Solve the problem status = solver.Solve(model) solve_time = time.time() - start_time # Extract solution is_feasible = status in [ cp_model.OPTIMAL, cp_model.FEASIBLE, ] values = ( extract_solution(solver, variables_dict, problem.variables) if is_feasible else {} ) # Create solver statistics statistics = { "num_conflicts": solver.NumConflicts(), "num_branches": solver.NumBranches(), "wall_time": solver.WallTime(), "solve_time": solve_time, } return Success( ORToolsSolution( values=values, is_feasible=is_feasible, status=solver.StatusName(status), objective_value=( solver.ObjectiveValue() if is_feasible and problem.objective else None ), solve_time=solve_time, statistics=statistics, ) ) except Exception as e: return Failure(f"Error solving problem: {e!s}")
- Pydantic model defining input schema for OR-Tools problems, used by the handler and solver for validation and type safety.class ORToolsProblem(BaseProblem): """Model representing a complete OR-Tools constraint programming problem.""" variables: List[ORToolsVariable] = Field(..., description="Problem variables") constraints: List[ORToolsConstraint] = Field(..., description="Problem constraints") objective: Optional[ORToolsObjective] = Field(default=None, description="Problem objective") parameters: Dict[str, Any] = Field(default_factory=dict, description="Problem parameters") description: str = Field(default="", description="Problem description") # Base problem properties problem_type: ProblemType = Field(default=ProblemType.CONSTRAINT_PROGRAMMING, description="Problem type") sense: OptimizationSense = Field(..., description="Optimization sense") # OR-Tools specific properties solver_type: str = Field(default="cp", description="Solver type (cp, sat, etc.)") time_limit: Optional[float] = Field(default=None, description="Time limit in seconds") search_strategy: Optional[str] = Field(default=None, description="Search strategy") log_search_progress: bool = Field(default=False, description="Log search progress") def get_variables(self) -> List[BaseVariable]: """Get all variables in the problem.""" return self.variables def get_constraints(self) -> List[BaseConstraint]: """Get all constraints in the problem.""" return self.constraints def validate(self) -> bool: """Validate the problem definition.""" if not self.variables: return False # Check that all variable names are unique var_names = [var.name for var in self.variables] if len(var_names) != len(set(var_names)): return False return True