Search Localities
searchLocalitiesSearch for Romanian localities by name. Supports diacritics, punctuation, and reversed county-city queries for accurate address lookup.
Instructions
Search for localities by country and name. Supports diacritics, punctuation, and reversed county-city queries. Parameters: country_code (2 letters, required), search (min 2 chars, required), per_page (15|50|100|200)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| country_code | Yes | The country code - must be 'RO' (Romania) | |
| search | Yes | The search term for locality names (minimum 2 characters) | |
| per_page | No | Number of results per page - must be 15, 50, 100, or 200 (default: 15) |
Implementation Reference
- Main handler function that registers the searchLocalities tool with the MCP server. Validates inputs (country_code, search, per_page), calls the API client, and formats the response with locality details.
export function registerSearchLocalitiesTool(server: McpServer): void { // Create API client instance // Register searchLocalities tool server.registerTool( "searchLocalities", { title: "Search Localities", description: "Search for localities by country and name. Supports diacritics, punctuation, and reversed county-city queries. Parameters: country_code (2 letters, required), search (min 2 chars, required), per_page (15|50|100|200)", inputSchema: { country_code: z .enum(["RO"]) .describe("The country code - must be 'RO' (Romania)"), search: z .string() .min(2) .describe( "The search term for locality names (minimum 2 characters)", ), per_page: z .union([z.literal(15), z.literal(50), z.literal(100), z.literal(200)]) .optional() .describe( "Number of results per page - must be 15, 50, 100, or 200 (default: 15)", ), }, }, async (args: any) => { // Get API key from async context const apiKey = apiKeyStorage.getStore(); if (!apiKey) { return { content: [ { type: "text", text: "Error: X-API-KEY header is required", }, ], }; } // Create API client with customer's API key const client = new EuroparcelApiClient(apiKey); try { // Validate required parameters if ( !args.country_code || typeof args.country_code !== "string" || args.country_code.length !== 2 ) { return { content: [ { type: "text", text: "Error: country_code is required and must be exactly 2 letters (e.g., 'RO', 'HU')", }, ], }; } if ( !args.search || typeof args.search !== "string" || args.search.length < 2 ) { return { content: [ { type: "text", text: "Error: search parameter is required and must be at least 2 characters", }, ], }; } const perPage = args.per_page || 15; const allowedPerPage = [15, 50, 100, 200]; if (!allowedPerPage.includes(perPage)) { return { content: [ { type: "text", text: "Error: per_page must be one of: 15, 50, 100, 200", }, ], }; } logger.info("Searching localities", { country_code: args.country_code.toUpperCase(), search: args.search, per_page: perPage, }); const response = await client.searchLocalities( args.country_code.toUpperCase(), args.search, perPage as 15 | 50 | 100 | 200, ); logger.info(`Found ${response.data.length} localities`); let formattedResponse = `Found ${response.meta.count} localities matching "${response.meta.search_term}" in ${response.meta.country_code}:\n\n`; if (response.data.length === 0) { formattedResponse += "No localities found."; } else { response.data.forEach((locality) => { formattedResponse += `📍 ${locality.name_and_county}\n`; formattedResponse += ` ID: ${locality.id}\n`; formattedResponse += ` County: ${locality.county} (${locality.county_code})\n\n`; }); if (response.meta.count > response.meta.per_page) { formattedResponse += `\nShowing first ${response.meta.per_page} results. Use per_page parameter to see more.`; } } return { content: [ { type: "text", text: formattedResponse, }, ], }; } catch (error: any) { logger.error("Failed to search localities", error); return { content: [ { type: "text", text: `Error searching localities: ${error.message || "Unknown error"}`, }, ], }; } }, ); logger.info("searchLocalities tool registered successfully"); } - Input schema for searchLocalities using Zod: country_code (enum RO), search (string min 2), per_page (optional union of 15|50|100|200).
{ title: "Search Localities", description: "Search for localities by country and name. Supports diacritics, punctuation, and reversed county-city queries. Parameters: country_code (2 letters, required), search (min 2 chars, required), per_page (15|50|100|200)", inputSchema: { country_code: z .enum(["RO"]) .describe("The country code - must be 'RO' (Romania)"), search: z .string() .min(2) .describe( "The search term for locality names (minimum 2 characters)", ), per_page: z .union([z.literal(15), z.literal(50), z.literal(100), z.literal(200)]) .optional() .describe( "Number of results per page - must be 15, 50, 100, or 200 (default: 15)", ), }, - src/tools/search/index.ts:7-16 (registration)Registration entry point: registerSearchTools() calls registerSearchLocalitiesTool(server) to register the tool.
export function registerSearchTools(server: McpServer): void { logger.info("Registering search tools..."); // Register all search-related tools registerSearchLocalitiesTool(server); registerSearchStreetsTool(server); registerPostalCodeReverseTool(server); logger.info("All search tools registered successfully"); } - src/api/client.ts:284-298 (helper)API client method searchLocalities() that sends a GET request to /search/localities/{countryCode}/{search} with optional per_page param.
async searchLocalities( countryCode: string, search: string, perPage?: 15 | 50 | 100 | 200, ): Promise<LocalitySearchResponse> { const response = await this.client.get<LocalitySearchResponse>( `/search/localities/${countryCode}/${search}`, { params: { per_page: perPage, }, }, ); return response.data; } - src/types/index.ts:214-222 (schema)TypeScript interface LocalitySearchResponse and LocalitySearchResult defining the response shape from the API.
export interface LocalitySearchResponse { data: LocalitySearchResult[]; meta: { count: number; per_page: number; country_code: string; search_term: string; }; }