All files / src/tools locations.ts

100% Statements 14/14
85.71% Branches 12/14
100% Functions 4/4
100% Lines 13/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65            2x 3x                 3x     2x 11x                                   5x 5x             4x   5x 2x     3x   2x         1x          
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import * as z from "zod/v4";
import type { AnwbClient } from "../anwb-client.js";
import type { LocationResult } from "../types.js";
import { toTextResult, toErrorResult } from "../tool-result.js";
 
const formatLocation = (loc: LocationResult): string => {
  const parts = [
    loc.poi ? `POI: ${loc.poi.name}` : null,
    `Address: ${loc.address}`,
    loc.city ? `City: ${loc.city}` : null,
    loc.countrySubdivision ? `Province: ${loc.countrySubdivision}` : null,
    loc.country ? `Country: ${loc.country}` : null,
    `Coordinates: ${loc.latitude}, ${loc.longitude}`,
    `Type: ${loc.type}`,
  ].filter(Boolean);
  return parts.join("\n  ");
};
 
export const registerLocationTools = (server: McpServer, client: AnwbClient): void => {
  server.registerTool(
    "find_locations",
    {
      title: "Search ANWB Locations",
      description:
        "Search for locations, addresses, streets, cities, and points of interest in the Netherlands and surrounding countries. " +
        "Returns matching locations with coordinates, useful for route planning. " +
        "Use this to find coordinates for the calculate_route tool.",
      annotations: { readOnlyHint: true, openWorldHint: true },
 
      inputSchema: z.object({
        query: z.string().describe("Free-text search query (e.g. city name, address, POI name)."),
        limit: z.number().int().min(1).max(20).default(7).describe("Maximum number of results to return (1-20)."),
        area: z.enum(["ALL", "NLD"]).default("ALL").describe("Geographic area to search in. ALL includes the Netherlands and surrounding countries, NLD limits to the Netherlands only."),
        indexSets: z.array(z.enum(["PAD", "Str", "Geo", "Addr", "POI"])).optional().describe("Index sets to search: PAD (addresses), Str (streets), Geo (geographic areas), Addr (address ranges), POI (points of interest). Default is all."),
      }),
    },
    async ({ query, limit, area, indexSets }) => {
      try {
        const response = await client.findLocations({
          freetext: query,
          limit,
          area,
          idxSet: indexSets,
        });
 
        const results = response.data ?? [];
 
        if (results.length === 0) {
          return toTextResult("No locations found matching the search query.");
        }
 
        const lines = results.map((loc, i) => `${i + 1}. ${formatLocation(loc)}`);
 
        return toTextResult(
          [`Found ${results.length} location${results.length !== 1 ? "s" : ""}:`, "", ...lines].join("\n"),
          { locations: results } as Record<string, unknown>,
        );
      } catch (error) {
        return toErrorResult(error);
      }
    },
  );
};