Skip to main content
Glama
Noveum

API-Market MCP Server

by Noveum

best_route_for_multiple_stops

Plan the most efficient driving route for up to 25 stops, avoiding tolls, highways, or ferries as needed. Optimize stop order while keeping the first and last stops fixed for better trip planning.

Instructions

Finding optimal route to visit several locations.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
avoid_ferriesNoAvoid ferries
avoid_highwaysNoAvoid highways
avoid_tollsNoAvoid tolls
optimizeNoInstructs the API to reorder stops to find the optimized route. The route first stop and last stop order is not changed, their position is considered fixed.
stopsYesList of locations described as semicolon-delimited coordinate pairs with latitudes and longitudes. Maximum 25 pairs per request.

Implementation Reference

  • Executes the tool 'best_route_for_multiple_stops' by resolving its toolId, constructing the corresponding API URL from the toolId (e.g., GET-/path becomes GET /path), preparing request params/body based on method, and proxying the HTTP request via axios to the configured apiBaseUrl.
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { id, name, arguments: params } = request.params; console.error("Received request:", request.params); console.error("Using parameters from arguments:", params); // Find tool by ID or name let tool: Tool | undefined; let toolId: string | undefined; if (id) { toolId = id.trim(); tool = this.tools.get(toolId); } else if (name) { // Search for tool by name for (const [tid, t] of this.tools.entries()) { if (t.name === name) { tool = t; toolId = tid; break; } } } if (!tool || !toolId) { console.error( `Available tools: ${Array.from(this.tools.entries()) .map(([id, t]) => `${id} (${t.name})`) .join(", ")}`, ); throw new Error(`Tool not found: ${id || name}`); } console.error(`Executing tool: ${toolId} (${tool.name})`); try { // Extract method and path from tool ID const [method, ...pathParts] = toolId.split("-"); let path = "/" + pathParts.join("/") .replace(/-/g, "/") .replaceAll('!', "-"); // Ensure base URL ends with slash for proper joining const baseUrl = this.config.apiBaseUrl.endsWith("/") ? this.config.apiBaseUrl : `${this.config.apiBaseUrl}/`; // Remove leading slash from path to avoid double slashes const cleanPath = path.startsWith("/") ? path.slice(1) : path; let url; try { // Validate that the path results in a valid URL // Construct the full URL url = new URL(cleanPath, baseUrl).toString(); } catch (error) { throw new Error(`Invalid path generated from tool ID ${toolId}: ${error.message}`); } // Prepare request configuration this.config.headers = this.config.headers || {}; const contentType = this.headers.get(toolId); if (contentType) { this.config.headers['Content-Type'] = contentType; } const config: any = { method: method.toLowerCase(), url: url, headers: this.config.headers, }; // Handle different parameter types based on HTTP method if (method.toLowerCase() === "get") { // For GET requests, ensure parameters are properly structured if (params && typeof params === "object") { // Handle array parameters properly const queryParams: Record<string, string> = {}; for (const [key, value] of Object.entries(params)) { if (Array.isArray(value)) { // Join array values with commas for query params queryParams[key] = value.join(","); } else if (value !== undefined && value !== null) { // Convert other values to strings queryParams[key] = String(value); } } config.params = queryParams; } } else { // For POST, PUT, PATCH - send as body config.data = params; } console.error("Final request config:", config); try { const response = await axios(config); return { content: [{ type: "text", text: JSON.stringify(response.data, null, 2) }] }; } catch (error) { if (axios.isAxiosError(error)) { console.error("Request failed:", { status: error.response?.status, statusText: error.response?.statusText, data: error.response?.data, headers: error.response?.headers, }); throw new Error( `API request failed: ${error.message} - ${JSON.stringify(error.response?.data)}`, ); } throw error; } } catch (error) { if (axios.isAxiosError(error)) { throw new Error(`API request failed: ${error.message}`); } throw error; } });
  • Registers the tool by generating toolId from path/method, sets tool.name from op.operationId or summary (spaces replaced by underscores, yielding 'best_route_for_multiple_stops'), builds inputSchema from parameters and requestBody, and stores in this.tools Map.
    const cleanPath = path.replace(/^\//, ""); const toolId = `${method.toUpperCase()}-${cleanPath}`.replace( /[^a-zA-Z0-9-_]/g, "-", ); const tool: Tool = { name: (op.operationId || op.summary || `${method.toUpperCase()} ${path}`).replace(/\s+/g, "_"), description: op.description || `Make a ${method.toUpperCase()} request to ${path}`, inputSchema: { type: "object", properties: {}, // Add any additional properties from OpenAPI spec }, }; // Add parameters from operation if (op.parameters) { for (const param of op.parameters) { if ("name" in param && "in" in param) { const paramSchema = param.schema as OpenAPIV3.SchemaObject; tool.inputSchema.properties[param.name] = { type: paramSchema.type || "string", description: param.description || `${param.name} parameter`, }; if (param.required) { tool.inputSchema.required = tool.inputSchema.required || []; tool.inputSchema.required.push(param.name); } } } } // Add request body if present (for POST, PUT, etc.) if (op.requestBody) { const requestBody = op.requestBody as OpenAPIV3.RequestBodyObject; const content = requestBody.content; // Usually we'd look for application/json content type if (content?.['application/json']) { this.headers.set(toolId, 'application/json'); const jsonSchema = content['application/json'].schema as OpenAPIV3.SchemaObject; // If it's a reference, we'd need to resolve it // For simplicity, assuming it's an inline schema if (jsonSchema.properties) { // Add all properties from the request body schema for (const [propName, propSchema] of Object.entries(jsonSchema.properties)) { tool.inputSchema.properties[propName] = propSchema; } // Add required properties if defined if (jsonSchema.required) { tool.inputSchema.required = tool.inputSchema.required || []; tool.inputSchema.required.push(...jsonSchema.required); } } } else if (content?.['application/x-www-form-urlencoded']) { this.headers.set(toolId, 'application/x-www-form-urlencoded'); const urlencodedSchema = content['application/x-www-form-urlencoded'].schema as OpenAPIV3.SchemaObject; if (urlencodedSchema.properties) { for (const [propName, propSchema] of Object.entries(urlencodedSchema.properties)) { tool.inputSchema.properties[propName] = propSchema; } if (urlencodedSchema.required) { tool.inputSchema.required = tool.inputSchema.required || []; tool.inputSchema.required.push(...urlencodedSchema.required); } } } } this.tools.set(toolId, tool);
  • Registers the list tools handler which returns all registered tools including 'best_route_for_multiple_stops' with their schemas.
    this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: Array.from(this.tools.values()), }; });
  • Helper script that shortens OpenAPI operation summaries; maps long summary to 'best route for multiple stops', which becomes the tool name 'best_route_for_multiple_stops'.
    summaries = ["A fast text-to-image model that makes high-quality images in 4 steps", "Finding the best route and get multiple stops driving directions", "Finding the best route between an origin and a destination", "Calculate distances and durations between a set of origins and destinations."] shortened_summaries = ["text-to-image", "best route for multiple stops", "best route bw start and stop", "distance and duration bw starts and stops"]

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/Noveum/api-market-mcp-server'

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