Skip to main content
Glama

solve_traveling_salesman_problem

Find the shortest route to visit all specified locations, optimizing travel distance for logistics and planning.

Instructions

Solve Traveling Salesman Problem (TSP) to find the shortest route visiting all locations.

Args: locations: List of location dictionaries with name and coordinates distance_matrix: Optional pre-calculated distance matrix start_location: Index of starting location (default: 0) return_to_start: Whether to return to starting location (default: True) time_limit_seconds: Maximum solving time in seconds (default: 30.0) Returns: Optimization result with route and total distance

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
locationsYes
distance_matrixNo
start_locationNo
return_to_startNo
time_limit_secondsNo

Implementation Reference

  • The MCP tool handler function 'solve_traveling_salesman_problem' that wraps input data into TSPInput schema, calls the core solver, and returns the result as dict.
    @mcp.tool() def solve_traveling_salesman_problem( locations: list[dict[str, Any]], distance_matrix: list[list[float]] | None = None, start_location: int = 0, return_to_start: bool = True, time_limit_seconds: float = 30.0, ) -> dict[str, Any]: """Solve Traveling Salesman Problem (TSP) to find the shortest route visiting all locations. Args: locations: List of location dictionaries with name and coordinates distance_matrix: Optional pre-calculated distance matrix start_location: Index of starting location (default: 0) return_to_start: Whether to return to starting location (default: True) time_limit_seconds: Maximum solving time in seconds (default: 30.0) Returns: Optimization result with route and total distance """ input_data = { "locations": locations, "distance_matrix": distance_matrix, "start_location": start_location, "return_to_start": return_to_start, "time_limit_seconds": time_limit_seconds, } result = solve_traveling_salesman(input_data) result_dict: dict[str, Any] = result.model_dump() return result_dict
  • Core helper function implementing the TSP algorithm using OR-Tools constraint solver, handling distance matrix, routing model, and solution extraction.
    @with_resource_limits(timeout_seconds=120.0, estimated_memory_mb=150.0) def solve_traveling_salesman(input_data: dict[str, Any]) -> OptimizationResult: """Solve Traveling Salesman Problem using OR-Tools. Args: input_data: TSP problem specification Returns: OptimizationResult with route and total distance """ if not ORTOOLS_AVAILABLE: return OptimizationResult( status=OptimizationStatus.ERROR, objective_value=None, variables={}, execution_time=0.0, error_message="OR-Tools is not available. Please install it with 'pip install ortools'", ) start_time = time.time() try: # Parse and validate input tsp_input = TSPInput(**input_data) locations = tsp_input.locations n_locations = len(locations) if n_locations < 2: return OptimizationResult( status=OptimizationStatus.ERROR, error_message="At least 2 locations required for TSP", execution_time=time.time() - start_time, ) # Get or calculate distance matrix if tsp_input.distance_matrix: distance_matrix = tsp_input.distance_matrix if len(distance_matrix) != n_locations or any( len(row) != n_locations for row in distance_matrix ): return OptimizationResult( status=OptimizationStatus.ERROR, error_message="Distance matrix dimensions don't match number of locations", execution_time=time.time() - start_time, ) else: distance_matrix = calculate_distance_matrix(locations) # type: ignore # Create routing model manager = pywrapcp.RoutingIndexManager(n_locations, 1, tsp_input.start_location) routing = pywrapcp.RoutingModel(manager) # Create distance callback def distance_callback(from_index: int, to_index: int) -> int: from_node = manager.IndexToNode(from_index) to_node = manager.IndexToNode(to_index) return int(distance_matrix[from_node][to_node] * 1000) # Scale for integer transit_callback_index = routing.RegisterTransitCallback(distance_callback) routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index) # Set search parameters search_parameters = pywrapcp.DefaultRoutingSearchParameters() search_parameters.first_solution_strategy = ( routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC ) search_parameters.time_limit.seconds = int(tsp_input.time_limit_seconds) # Solve solution = routing.SolveWithParameters(search_parameters) if solution: # Extract route route = [] total_distance = 0.0 index = routing.Start(0) while not routing.IsEnd(index): node = manager.IndexToNode(index) route.append( { "location": locations[node].name, "index": node, "coordinates": { "lat": locations[node].lat, "lng": locations[node].lng, "x": locations[node].x, "y": locations[node].y, }, } ) previous_index = index index = solution.Value(routing.NextVar(index)) if not routing.IsEnd(index): from_node = manager.IndexToNode(previous_index) to_node = manager.IndexToNode(index) total_distance += distance_matrix[from_node][to_node] # Add final location if returning to start if tsp_input.return_to_start: final_node = manager.IndexToNode(index) route.append( { "location": locations[final_node].name, "index": final_node, "coordinates": { "lat": locations[final_node].lat, "lng": locations[final_node].lng, "x": locations[final_node].x, "y": locations[final_node].y, }, } ) # Add distance back to start if len(route) > 1: last_node = route[-2]["index"] start_node = route[-1]["index"] total_distance += distance_matrix[last_node][start_node] execution_time = time.time() - start_time return OptimizationResult( status=OptimizationStatus.OPTIMAL, objective_value=total_distance, variables={ "route": route, "total_distance": total_distance, "num_locations": len(route), }, execution_time=execution_time, solver_info={ "solver_name": "OR-Tools Routing", "search_strategy": "PATH_CHEAPEST_ARC", }, ) else: return OptimizationResult( status=OptimizationStatus.INFEASIBLE, error_message="No solution found within time limit", execution_time=time.time() - start_time, ) except Exception as e: return OptimizationResult( status=OptimizationStatus.ERROR, error_message=f"TSP solving error: {str(e)}", execution_time=time.time() - start_time, )
  • Pydantic model defining the input schema for TSP, including locations list, optional distance matrix, start location, etc., with validation.
    class TSPInput(BaseModel): """Input schema for Traveling Salesman Problem.""" locations: list[Location] distance_matrix: list[list[float]] | None = None start_location: int = Field(default=0, ge=0) return_to_start: bool = True time_limit_seconds: float = Field(default=30.0, ge=0) @field_validator("start_location") @classmethod def validate_start_location(cls, v: int, info: ValidationInfo) -> int: if info.data and "locations" in info.data and v >= len(info.data["locations"]): raise ValueError("start_location must be a valid location index") return v
  • Pydantic model for individual locations used in TSP/VRP inputs, supporting lat/lng or x/y coordinates.
    class Location(BaseModel): """Location with coordinates and optional properties.""" name: str lat: float | None = None lng: float | None = None x: float | None = None y: float | None = None demand: float = Field(default=0.0, ge=0) time_window: tuple[int, int] | None = None service_time: int = Field(default=0, ge=0) @field_validator("time_window") @classmethod def validate_time_window(cls, v: tuple[int, int] | None) -> tuple[int, int] | None: if v is not None and v[0] >= v[1]: raise ValueError("Time window start must be before end") return v
  • Registration call in the main MCP server setup that adds the solve_traveling_salesman_problem tool via register_routing_tools.
    register_routing_tools(mcp)

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/dmitryanchikov/mcp-optimizer'

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