Skip to main content
Glama
Sharmarajnish

Constrained Optimization MCP Server

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
NameRequiredDescriptionDefault
constraintsYes
descriptionNo
objectiveNo
parametersNo
variablesYes

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}")]
  • 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

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/Sharmarajnish/MCP-Constrained-Optimization'

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