Skip to main content
Glama

simple_highs_solver

Solve linear programming problems using a simplified HiGHs interface without complex model structures. Input objective coefficients, constraints, and variables to get optimization solutions.

Instructions

A simplified interface for solving HiGHs linear programming problems.

This tool provides a more straightforward interface for HiGHs problems,
without requiring the full HiGHSProblem model structure.

Args:
    sense: Optimization sense, either "minimize" or "maximize"
    objective_coeffs: List of objective function coefficients
    variables: List of variable definitions with optional bounds and types
    constraint_matrix: 2D list representing the constraint matrix (dense format)
    constraint_senses: List of constraint directions ("<=", ">=", "=")
    rhs_values: List of right-hand side values for constraints
    options: Optional solver options dictionary
    description: Optional description of the problem

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

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
senseYes
objective_coeffsYes
variablesYes
constraint_matrixYes
constraint_sensesYes
rhs_valuesYes
optionsNo
descriptionNo

Implementation Reference

  • MCP tool handler: validates parameters, builds HiGHSProblem from simple inputs using Pydantic models, solves with imported solve_highs_problem, formats result as TextContent.
    @app.tool("simple_highs_solver")
    async def simple_highs_solver(
        sense: str,
        objective_coeffs: list[float],
        variables: list[dict[str, Any]],
        constraint_matrix: list[list[float]],
        constraint_senses: list[str],
        rhs_values: list[float],
        options: dict[str, Any] | None = None,
        description: str = "",
    ) -> list[TextContent]:
        """A simplified interface for solving HiGHs linear programming problems.
    
        This tool provides a more straightforward interface for HiGHs problems,
        without requiring the full HiGHSProblem model structure.
    
        Args:
            sense: Optimization sense, either "minimize" or "maximize"
            objective_coeffs: List of objective function coefficients
            variables: List of variable definitions with optional bounds and types
            constraint_matrix: 2D list representing the constraint matrix (dense format)
            constraint_senses: List of constraint directions ("<=", ">=", "=")
            rhs_values: List of right-hand side values for constraints
            options: Optional solver options dictionary
            description: Optional description of the problem
    
        Returns:
            A list of TextContent containing the solution or an error message
        """
        try:
            # Validate sense
            try:
                problem_sense = HiGHSSense(sense)
            except ValueError:
                return [
                    TextContent(
                        type="text",
                        text=(
                            f"Invalid sense: {sense}. "
                            f"Must be one of: {', '.join([s.value for s in HiGHSSense])}"
                        ),
                    )
                ]
    
            # Create objective
            objective = HiGHSObjective(linear=objective_coeffs)
    
            # Create variables
            problem_variables = []
            for i, var in enumerate(variables):
                var_name = var.get("name", f"x{i+1}")
                var_lb = var.get("lb", 0.0)
                var_ub = var.get("ub", None)
                var_type_str = var.get("type", "cont")
    
                try:
                    var_type = HiGHSVariableType(var_type_str)
                except ValueError:
                    return [
                        TextContent(
                            type="text",
                            text=(
                                f"Invalid variable type: {var_type_str}. "
                                f"Must be one of: {', '.join([t.value for t in HiGHSVariableType])}"
                            ),
                        )
                    ]
    
                problem_variables.append(
                    HiGHSVariable(name=var_name, lb=var_lb, ub=var_ub, type=var_type)
                )
    
            # Create constraints
            constraint_sense_enums = []
            for sense_str in constraint_senses:
                try:
                    constraint_sense_enums.append(HiGHSConstraintSense(sense_str))
                except ValueError:
                    return [
                        TextContent(
                            type="text",
                            text=(
                                f"Invalid constraint sense: {sense_str}. "
                                f"Must be one of: {', '.join([s.value for s in HiGHSConstraintSense])}"
                            ),
                        )
                    ]
    
            constraints = HiGHSConstraints(
                dense=constraint_matrix,
                sparse=None,
                sense=constraint_sense_enums,
                rhs=rhs_values,
            )
    
            # Create problem specification
            problem_spec = HiGHSProblemSpec(
                sense=problem_sense,
                objective=objective,
                variables=problem_variables,
                constraints=constraints,
            )
    
            # Create options if provided
            highs_options = None
            if options:
                highs_options = HiGHSOptions(**options)
    
            # Create full problem
            problem = HiGHSProblem(problem=problem_spec, options=highs_options)
    
            # Solve the problem
            result = solve_highs_problem(problem)
    
            match result:
                case Success(solution):
                    return [
                        TextContent(
                            type="text",
                            text=json.dumps(
                                {
                                    "status": solution.status.value,
                                    "objective_value": solution.objective_value,
                                    "solution": solution.solution,
                                    "dual_solution": solution.dual_solution,
                                    "variable_duals": solution.variable_duals,
                                }
                            ),
                        )
                    ]
                case Failure(error):
                    return [
                        TextContent(type="text", text=f"Error solving problem: {error}")
                    ]
                case _:
                    return [
                        TextContent(
                            type="text",
                            text="Unexpected error in simple_highs_solver",
                        )
                    ]
        except Exception as e:
            return [TextContent(type="text", text=f"Error in simple_highs_solver: {e!s}")]
  • FastMCP tool registration decorator for simple_highs_solver.
    @app.tool("simple_highs_solver")
  • Core solver logic (imported as solve_highs_problem): interfaces with highspy.Highs to set up LP/MIP, apply options, solve, and return HiGHSOutput.
    def solve_problem(problem: HiGHSProblem) -> Result[HiGHSOutput, str]:
        """Solve a HiGHs optimization problem."""
        try:
            # Create HiGHs instance
            h = highspy.Highs()
    
            # Always disable output for MCP server compatibility
            h.setOptionValue("output_flag", False)
            h.setOptionValue("log_to_console", False)
    
            # Apply options
            options_result = _apply_options(h, problem.options)
            if isinstance(options_result, Failure):
                return options_result
    
            problem_spec = problem.problem
            num_vars = len(problem_spec.variables)
            num_constraints = len(problem_spec.constraints.sense)
    
            # Set up objective
            obj_coeffs = np.array(problem_spec.objective.linear, dtype=float)
            if len(obj_coeffs) != num_vars:
                return Failure(
                    f"Objective coefficients length ({len(obj_coeffs)}) doesn't match number of variables ({num_vars})"
                )
    
            # Set up variable bounds
            var_lower = np.zeros(num_vars, dtype=float)
            var_upper = np.full(num_vars, highspy.kHighsInf, dtype=float)
    
            for i, var_spec in enumerate(problem_spec.variables):
                lb, ub = _get_variable_bounds(var_spec, i)
                var_lower[i] = lb
                var_upper[i] = ub
    
            # Build constraint matrix
            matrix_result = _build_constraint_matrix(problem_spec)
            if isinstance(matrix_result, Failure):
                return matrix_result
    
            rows, cols, values = matrix_result.unwrap()
    
            # Set up constraint bounds
            constraint_lower = np.zeros(num_constraints, dtype=float)
            constraint_upper = np.zeros(num_constraints, dtype=float)
    
            for i, (sense, rhs) in enumerate(
                zip(
                    problem_spec.constraints.sense,
                    problem_spec.constraints.rhs,
                    strict=False,
                )
            ):
                lb, ub = _convert_constraint_sense(sense, rhs)
                constraint_lower[i] = lb
                constraint_upper[i] = ub
    
            # Add variables
            h.addCols(
                num_vars,
                obj_coeffs,
                var_lower,
                var_upper,
                0,
                np.array([]),
                np.array([]),
                np.array([]),
            )
    
            # Set variable integrality constraints
            integrality = np.zeros(num_vars, dtype=int)  # 0 = continuous
            for i, var_spec in enumerate(problem_spec.variables):
                if var_spec.type == HiGHSVariableType.BINARY:
                    integrality[i] = 1  # 1 = integer (binary is integer with bounds 0-1)
                elif var_spec.type == HiGHSVariableType.INTEGER:
                    integrality[i] = 1  # 1 = integer
                # else: continuous (already 0)
    
            # Apply integrality constraints if any variables are integer/binary
            if np.any(integrality > 0):
                h.changeColsIntegrality(num_vars, np.arange(num_vars), integrality)
    
            # Add constraints using sparse format
            if len(rows) > 0:
                # Convert to row-wise sparse format for HiGHs
                # Group by rows and create start array
                unique_rows = np.unique(rows)
                start_array = np.zeros(num_constraints + 1, dtype=int)
    
                for row in unique_rows:
                    start_array[row] = np.sum(rows < row)
    
                start_array[-1] = len(rows)  # Final start
    
                h.addRows(
                    num_constraints,
                    constraint_lower,
                    constraint_upper,
                    len(values),
                    start_array,
                    cols,
                    values,
                )
            else:
                # No constraints case
                h.addRows(
                    num_constraints,
                    constraint_lower,
                    constraint_upper,
                    0,
                    np.array([0]),
                    np.array([]),
                    np.array([]),
                )
    
            # Set objective sense
            if problem_spec.sense == HiGHSSense.MAXIMIZE:
                h.changeObjectiveSense(highspy.ObjSense.kMaximize)
            else:
                h.changeObjectiveSense(highspy.ObjSense.kMinimize)
    
            # Solve the problem
            h.run()
    
            # Get results
            model_status = h.getModelStatus()
            solution = h.getSolution()
            info = h.getInfo()
    
            # Convert status
            status = _convert_status(model_status)
    
            # Extract solution values
            solution_values = solution.col_value if hasattr(solution, "col_value") else []
            dual_values = solution.row_dual if hasattr(solution, "row_dual") else []
            reduced_costs = solution.col_dual if hasattr(solution, "col_dual") else []
    
            # Get objective value
            objective_value = (
                info.objective_function_value
                if hasattr(info, "objective_function_value")
                else 0.0
            )
    
            return Success(
                HiGHSOutput(
                    status=status,
                    objective_value=objective_value,
                    solution=list(solution_values),
                    dual_solution=list(dual_values),
                    variable_duals=list(reduced_costs),
                )
            )
    
        except Exception as e:
            return Failure(f"Error solving HiGHs problem: {e}")
  • Pydantic models defining structure for HiGHS problems used internally by the handler (HiGHSProblem, HiGHSProblemSpec, HiGHSObjective, HiGHSVariable, HiGHSConstraints, etc.).
    class HiGHSProblem(BaseModel):
        """Complete HiGHS optimization problem."""
    
        problem: HiGHSProblemSpec = Field(..., description="Problem specification")
        options: HiGHSOptions | None = Field(None, description="Solver options")
  • Simplified highs solver wrapper (duplicated logic in MCP handler, used directly in example scripts).
    def simple_highs_solver(
        sense: str,
        objective_coeffs: list[float],
        variables: list[dict],
        constraint_matrix: list[list[float]],
        constraint_senses: list[str],
        rhs_values: list[float],
        options: dict | None = None,
        description: str = "",
    ) -> Result[HiGHSOutput, str]:
        """A simplified interface for solving HiGHs linear programming problems.
    
        This function provides a more straightforward interface for HiGHs problems,
        without requiring the full HiGHSProblem model structure.
    
        Args:
            sense: Optimization sense, either "minimize" or "maximize"
            objective_coeffs: List of objective function coefficients
            variables: List of variable definitions with optional bounds and types
            constraint_matrix: 2D list representing the constraint matrix (dense format)
            constraint_senses: List of constraint directions ("<=", ">=", "=")
            rhs_values: List of right-hand side values for constraints
            options: Optional solver options dictionary
            description: Optional description of the problem
    
        Returns:
            Result containing HiGHSOutput or error message
        """
        try:
            # Validate sense
            try:
                problem_sense = HiGHSSense(sense)
            except ValueError:
                return Failure(
                    f"Invalid sense: {sense}. "
                    f"Must be one of: {', '.join([s.value for s in HiGHSSense])}"
                )
    
            # Create objective
            objective = HiGHSObjective(linear=objective_coeffs)
    
            # Create variables
            problem_variables = []
            for i, var in enumerate(variables):
                var_name = var.get("name", f"x{i+1}")
                var_lb = var.get("lb", 0.0)
                var_ub = var.get("ub", None)
                var_type_str = var.get("type", "cont")
    
                try:
                    var_type = HiGHSVariableType(var_type_str)
                except ValueError:
                    return Failure(
                        f"Invalid variable type: {var_type_str}. "
                        f"Must be one of: {', '.join([t.value for t in HiGHSVariableType])}"
                    )
    
                problem_variables.append(
                    HiGHSVariable(name=var_name, lb=var_lb, ub=var_ub, type=var_type)
                )
    
            # Create constraints
            constraint_sense_enums = []
            for sense_str in constraint_senses:
                try:
                    constraint_sense_enums.append(HiGHSConstraintSense(sense_str))
                except ValueError:
                    return Failure(
                        f"Invalid constraint sense: {sense_str}. "
                        f"Must be one of: {', '.join([s.value for s in HiGHSConstraintSense])}"
                    )
    
            constraints = HiGHSConstraints(
                dense=constraint_matrix,
                sparse=None,
                sense=constraint_sense_enums,
                rhs=rhs_values,
            )
    
            # Create problem specification
            problem_spec = HiGHSProblemSpec(
                sense=problem_sense,
                objective=objective,
                variables=problem_variables,
                constraints=constraints,
            )
    
            # Create options if provided
            highs_options = None
            if options:
                highs_options = HiGHSOptions(**options)
    
            # Create full problem
            problem = HiGHSProblem(problem=problem_spec, options=highs_options)
    
            # Solve the problem
            return solve_problem(problem)
    
        except Exception as e:
            return Failure(f"Error in simple_highs_solver: {e!s}")
Behavior2/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 states the tool 'returns a list of TextContent containing the solution or an error message,' which provides some output behavior. However, it doesn't disclose critical behavioral traits like whether this is a read-only or mutating operation, computational complexity, potential side effects, authentication requirements, rate limits, or error handling specifics beyond mentioning error messages.

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

Conciseness4/5

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

The description is well-structured with a clear introductory sentence followed by organized parameter documentation. It's appropriately sized for an 8-parameter tool with no schema descriptions. However, the opening paragraph could be more front-loaded with the core purpose, and some phrasing ('more straightforward interface') is slightly redundant with 'simplified.'

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

Completeness3/5

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

Given the complexity of an 8-parameter linear programming solver with no annotations and no output schema, the description provides good parameter semantics and basic return format information. However, it lacks sufficient behavioral context for a computational tool (no performance characteristics, no mention of what 'solving' entails, no error conditions beyond 'error message'). The absence of output schema means the description should ideally provide more detail about solution structure.

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?

With 0% schema description coverage for 8 parameters, the description fully compensates by providing detailed semantic explanations for every parameter in the Args section. Each parameter (sense, objective_coeffs, variables, constraint_matrix, constraint_senses, rhs_values, options, description) receives clear explanations of what they represent, including format details (e.g., 'dense format' for constraint_matrix, 'list of TextContent' for returns) and optional status.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool 'solves HiGHs linear programming problems' and specifies it's a 'simplified interface' that 'doesn't require the full HiGHSProblem model structure.' This provides a specific verb (solve) and resource (HiGHs linear programming problems) with clear differentiation from the more complex alternative. However, it doesn't explicitly distinguish from all sibling tools like 'solve_highs_problem' beyond the 'simplified' aspect.

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

Usage Guidelines3/5

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

The description implies usage context by stating this is 'simplified' and 'without requiring the full HiGHSProblem model structure,' suggesting it should be used when a simpler interface is needed. However, it doesn't provide explicit guidance on when to use this versus the 'solve_highs_problem' sibling tool or other alternatives, nor does it mention any prerequisites or exclusions.

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