search_category
Find specific types of places like restaurants, schools, or parks within a defined geographic area using OpenStreetMap data.
Instructions
Search for specific types of places within a defined geographic area.
This tool allows targeted searches for places matching specific categories within a rectangular geographic region. It's particularly useful for filtering places by type (restaurants, schools, parks, etc.) within a neighborhood or city district. Results include complete location details and metadata about each matching place.
Args: category: Main OSM category to search for (e.g., "amenity", "shop", "tourism", "building") min_latitude: Southern boundary of search area (decimal degrees) min_longitude: Western boundary of search area (decimal degrees) max_latitude: Northern boundary of search area (decimal degrees) max_longitude: Eastern boundary of search area (decimal degrees) subcategories: Optional list of specific subcategories to filter by (e.g., ["restaurant", "cafe"])
Returns: Structured results including: - Query parameters - Count of matching places - List of matching places with coordinates, names, and metadata
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| category | Yes | ||
| min_latitude | Yes | ||
| min_longitude | Yes | ||
| max_latitude | Yes | ||
| max_longitude | Yes | ||
| subcategories | No |
Implementation Reference
- src/osm_mcp_server/server.py:442-525 (handler)The main execution logic for the 'search_category' MCP tool. It constructs a bounding box from input coordinates, calls the helper method to query OSM features, processes the results to extract coordinates and metadata, and returns a structured response with query details, results list, and count.async def search_category( category: str, min_latitude: float, min_longitude: float, max_latitude: float, max_longitude: float, ctx: Context, subcategories: List[str] = None ) -> Dict[str, Any]: """ Search for specific types of places within a defined geographic area. This tool allows targeted searches for places matching specific categories within a rectangular geographic region. It's particularly useful for filtering places by type (restaurants, schools, parks, etc.) within a neighborhood or city district. Results include complete location details and metadata about each matching place. Args: category: Main OSM category to search for (e.g., "amenity", "shop", "tourism", "building") min_latitude: Southern boundary of search area (decimal degrees) min_longitude: Western boundary of search area (decimal degrees) max_latitude: Northern boundary of search area (decimal degrees) max_longitude: Eastern boundary of search area (decimal degrees) subcategories: Optional list of specific subcategories to filter by (e.g., ["restaurant", "cafe"]) Returns: Structured results including: - Query parameters - Count of matching places - List of matching places with coordinates, names, and metadata """ osm_client = ctx.request_context.lifespan_context.osm_client bbox = (min_longitude, min_latitude, max_longitude, max_latitude) ctx.info(f"Searching for {category} in bounding box") features = await osm_client.search_features_by_category(bbox, category, subcategories) # Process results results = [] for feature in features: tags = feature.get("tags", {}) # Get coordinates based on feature type coords = {} if feature.get("type") == "node": coords = { "latitude": feature.get("lat"), "longitude": feature.get("lon") } # For ways and relations, use center coordinates if available elif "center" in feature: coords = { "latitude": feature.get("center", {}).get("lat"), "longitude": feature.get("center", {}).get("lon") } # Only include features with valid coordinates if coords: results.append({ "id": feature.get("id"), "type": feature.get("type"), "name": tags.get("name", "Unnamed"), "coordinates": coords, "category": category, "subcategory": tags.get(category), "tags": tags }) return { "query": { "category": category, "subcategories": subcategories, "bbox": { "min_latitude": min_latitude, "min_longitude": min_longitude, "max_latitude": max_latitude, "max_longitude": max_longitude } }, "results": results, "count": len(results) }
- src/osm_mcp_server/server.py:144-177 (helper)Supporting method in the OSMClient class that constructs and executes an Overpass API query to fetch OSM nodes, ways, and relations matching the specified category (and optional subcategories) within the given bounding box.async def search_features_by_category(self, bbox: Tuple[float, float, float, float], category: str, subcategories: List[str] = None) -> List[Dict]: """Search for OSM features by category and subcategories""" if not self.session: raise RuntimeError("OSM client not connected") overpass_url = "https://overpass-api.de/api/interpreter" # Build query for specified category and subcategories if subcategories: subcategory_filters = " or ".join([f'"{category}"="{sub}"' for sub in subcategories]) query_filter = f'({subcategory_filters})' else: query_filter = f'"{category}"' query = f""" [out:json]; ( node[{query_filter}]({bbox[1]},{bbox[0]},{bbox[3]},{bbox[2]}); way[{query_filter}]({bbox[1]},{bbox[0]},{bbox[3]},{bbox[2]}); relation[{query_filter}]({bbox[1]},{bbox[0]},{bbox[3]},{bbox[2]}); ); out body; """ async with self.session.post(overpass_url, data={"data": query}) as response: if response.status == 200: data = await response.json() return data.get("elements", []) else: raise Exception(f"Failed to search features by category: {response.status}")
- src/osm_mcp_server/server.py:442-442 (registration)The @mcp.tool() decorator registers the search_category function as an MCP tool.async def search_category(