Skip to main content
Glama
Noveum
by Noveum

distance_and_duration_bw_starts_and_stops

Calculate optimal route distance and driving duration between origin and destination coordinates on real road networks, avoiding tolls, highways, or ferries, with customizable start times.

Instructions

Calculate length and driving time of the optimal routes between origin and destination points on the real road network.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
avoid_ferriesNoAvoid ferries
avoid_highwaysNoAvoid highways
avoid_tollsNoAvoid tolls
destinationsNoList of destinations described as semicolon-delimited coordinate pairs with latitudes and longitudes. If not specified, an n x n matrix will be generated using the origins. Maximum 25 pairs per request.
originsYesList of origins described as semicolon-delimited coordinate pairs with latitudes and longitudes. Maximum 25 pairs per request.
start_timeNoTime when travel is expected to start. You can specify the time as an integer in seconds since midnight, January 1, 1970 UTC or you can use 'now' to specify the current time.

Implementation Reference

  • This is the core handler function for executing any MCP tool call, including 'distance_and_duration_bw_starts_and_stops'. It looks up the tool by name or ID, reconstructs the corresponding API endpoint path from the tool ID (e.g., POST-distance_and_duration_bw_starts_and_stops -> /distance/and_duration_bw/starts/and_stops or similar based on transformation), constructs the axios request with parameters as query or body, calls the external API at apiBaseUrl, and returns the response.
    // Handle tool execution 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; } });
  • Parses list of OpenAPI specs from config.openApiSpec (modified_files.txt), loads each spec, iterates over paths and methods, generates toolId from method-path, sets tool.name from op.summary (shortened to 'distance and duration bw starts and stops' -> 'distance_and_duration_bw_starts_and_stops'), builds inputSchema from params and body schema, registers tool in this.tools map.
    private async parseOpenAPISpec(): Promise<void> { const paths = await this.listOfFilePaths() for (const cur_path of paths) { if (!cur_path || cur_path.trim() === '') { console.error('Skipping empty path'); continue; } try { const spec = await this.loadOpenAPISpec(path.resolve(__dirname, cur_path)); // Convert each OpenAPI path to an MCP tool for (const [path, pathItem] of Object.entries(spec.paths)) { if (!pathItem) continue; for (const [method, operation] of Object.entries(pathItem)) { if (method === "parameters" || !operation) continue; const op = operation as OpenAPIV3.OperationObject; // Create a clean tool ID by removing the leading slash and replacing special chars 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); } } } catch (error) { console.error(`Error parsing OpenAPI spec from ${cur_path}:`, error); } } }
  • Hardcoded manual shortening of operation summaries in OpenAPI specs. Maps the original summary 'Calculate distances and durations between a set of origins and destinations.' to 'distance and duration bw starts and stops', which becomes the MCP tool name 'distance_and_duration_bw_starts_and_stops' after replacing spaces with underscores in the server code.
    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"]
  • Dynamically constructs the inputSchema for each tool (including this one) from OpenAPI operation parameters (query/path/header) and requestBody schema (JSON or form), marking required fields.
    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); } } } }

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