list_areas
Retrieve distinct neighborhood names for any city to use as valid area filters.
Instructions
List distinct area/neighborhood names for a given city. Use this to discover valid area filters.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| city | No | City slug (defaults to tokyo) |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| city | Yes | ||
| areas | Yes |
Implementation Reference
- src/services/helpers.ts:38-75 (handler)Core handler for list_areas: queries the venues table for a given city, deduplicates area names from city_en/city/city_ja columns, and returns sorted unique areas.
export async function listAreas( supabase: SupabaseClient, config: AppConfig, citySlug?: string, ): Promise<{ city: string; areas: string[] }> { const slug = citySlug?.trim().toLowerCase() || config.defaultCity; const cityCtx = await getCityContext(supabase, slug, config.defaultCountryCode); if (!cityCtx) { throw new NightlifeError("INVALID_REQUEST", `City not found: ${slug}`); } const { data, error } = await supabase .from("venues") .select("city_en,city,city_ja") .eq("city_id", cityCtx.id); if (error) { throw new NightlifeError("INTERNAL_ERROR", `Failed to fetch areas: ${error.message}`); } const seen = new Set<string>(); const areas: string[] = []; for (const row of (data ?? []) as VenueAreaRow[]) { const raw = row.city_en || row.city || row.city_ja; if (!raw) continue; const normalized = raw.trim().toLowerCase(); if (normalized && !seen.has(normalized)) { seen.add(normalized); areas.push(raw.trim()); } } areas.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); return { city: cityCtx.slug, areas }; } - src/tools/helpers.ts:77-80 (schema)Zod output schema for list_areas: an object with 'city' (string) and 'areas' (string array).
const listAreasOutputSchema = z.object({ city: z.string(), areas: z.array(z.string()), }); - src/tools/helpers.ts:113-130 (registration)MCP tool registration for 'list_areas' via registerHelperTools(), defining description, input schema (optional city slug), output schema, and handler that calls listAreas().
server.registerTool( "list_areas", { description: "List distinct area/neighborhood names for a given city. Use this to discover valid area filters.", inputSchema: { city: z .string() .optional() .describe("City slug (defaults to tokyo)"), }, outputSchema: listAreasOutputSchema, }, async ({ city }) => runTool("list_areas", listAreasOutputSchema, async () => listAreas(deps.supabase, deps.config, city), ), ); - src/server.ts:36-36 (registration)Top-level registration: registerHelperTools(server, {config, supabase}) called in createNightlifeServer().
registerHelperTools(server, { config, supabase }); - src/rest.ts:223-230 (helper)REST endpoint GET /api/v1/areas that also calls listAreas(), providing the same functionality via HTTP.
// GET /api/v1/areas router.get("/areas", async (req, res) => { try { const result = await listAreas(supabase, config, str(req.query.city)); res.json(result); } catch (error) { sendError(res, error); }