find_nearby_places
Search for points of interest, amenities, and establishments near a specific geographic location. Results are categorized and include details like names, coordinates, and tags, aiding in location-based decisions, recommendations, and analysis.
Instructions
Discover points of interest and amenities near a specific location.
This tool performs a comprehensive search around a geographic point to identify nearby establishments, amenities, and points of interest. Results are organized by category and subcategory, making it easy to find specific types of places. Essential for location-based recommendations, neighborhood analysis, and proximity-based decision making.
Args: latitude: Center point latitude (decimal degrees) longitude: Center point longitude (decimal degrees) radius: Search radius in meters (defaults to 1000m/1km) categories: List of OSM categories to search for (e.g., ["amenity", "shop", "tourism"]). If omitted, searches common categories. limit: Maximum number of total results to return
Returns: Structured dictionary containing: - Original query parameters - Total count of places found - Results grouped by category and subcategory - Each place includes name, coordinates, and associated tags
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| categories | No | ||
| latitude | Yes | ||
| limit | No | ||
| longitude | Yes | ||
| radius | No |
Implementation Reference
- src/osm_mcp_server/server.py:263-347 (handler)The primary handler function for the 'find_nearby_places' tool, decorated with @mcp.tool(). It fetches nearby POIs using OSMClient, groups them by category/subcategory, and returns structured results. Includes input schema in args and detailed docstring.@mcp.tool() async def find_nearby_places( latitude: float, longitude: float, ctx: Context, radius: float = 1000, # meters categories: List[str] = None, limit: int = 20 ) -> Dict[str, Any]: """ Discover points of interest and amenities near a specific location. This tool performs a comprehensive search around a geographic point to identify nearby establishments, amenities, and points of interest. Results are organized by category and subcategory, making it easy to find specific types of places. Essential for location-based recommendations, neighborhood analysis, and proximity-based decision making. Args: latitude: Center point latitude (decimal degrees) longitude: Center point longitude (decimal degrees) radius: Search radius in meters (defaults to 1000m/1km) categories: List of OSM categories to search for (e.g., ["amenity", "shop", "tourism"]). If omitted, searches common categories. limit: Maximum number of total results to return Returns: Structured dictionary containing: - Original query parameters - Total count of places found - Results grouped by category and subcategory - Each place includes name, coordinates, and associated tags """ osm_client = ctx.request_context.lifespan_context.osm_client # Set default categories if not provided if not categories: categories = ["amenity", "shop", "tourism", "leisure"] ctx.info(f"Searching for places within {radius}m of ({latitude}, {longitude})") places = await osm_client.get_nearby_pois(latitude, longitude, radius, categories) # Group results by category results_by_category = {} for place in places[:limit]: tags = place.get("tags", {}) # Find the matching category for category in categories: if category in tags: subcategory = tags[category] if category not in results_by_category: results_by_category[category] = {} if subcategory not in results_by_category[category]: results_by_category[category][subcategory] = [] # Add place to appropriate category and subcategory place_info = { "id": place.get("id"), "name": tags.get("name", "Unnamed"), "latitude": place.get("lat"), "longitude": place.get("lon"), "tags": tags } results_by_category[category][subcategory].append(place_info) # Calculate total count total_count = sum( len(places) for category_data in results_by_category.values() for places in category_data.values() ) return { "query": { "latitude": latitude, "longitude": longitude, "radius": radius }, "categories": results_by_category, "total_count": total_count }
- src/osm_mcp_server/server.py:93-142 (helper)Supporting helper method in OSMClient class that queries the Overpass API for nearby points of interest within a bounding box derived from lat/lon/radius and specified categories.async def get_nearby_pois(self, lat: float, lon: float, radius: float = 1000, categories: List[str] = None) -> List[Dict]: """Get points of interest near a location""" if not self.session: raise RuntimeError("OSM client not connected") # Convert radius to bounding box (approximate) # 1 degree latitude ~= 111km # 1 degree longitude ~= 111km * cos(latitude) lat_delta = radius / 111000 lon_delta = radius / (111000 * math.cos(math.radians(lat))) bbox = ( lon - lon_delta, lat - lat_delta, lon + lon_delta, lat + lat_delta ) # Build Overpass query overpass_url = "https://overpass-api.de/api/interpreter" # Default to common POI types if none specified if not categories: categories = ["amenity", "shop", "tourism", "leisure"] # Build tag filters tag_filters = [] for category in categories: tag_filters.append(f'node["{category}"]({{bbox}});') query = f""" [out:json]; ( {" ".join(tag_filters)} ); out body; """ query = query.replace("{bbox}", f"{bbox[1]},{bbox[0]},{bbox[3]},{bbox[2]}") 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 get nearby POIs: {response.status}")