Skip to main content
Glama
jagan-shanmugam

OpenStreetMap MCP Server

find_nearby_places

Search for points of interest and amenities near a geographic location to support location-based recommendations and proximity 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

TableJSON Schema
NameRequiredDescriptionDefault
latitudeYes
longitudeYes
radiusNo
categoriesNo
limitNo

Implementation Reference

  • The primary handler function for the 'find_nearby_places' tool. It orchestrates the search for nearby points of interest using the OSMClient, groups results by category/subcategory, and formats the response.
    @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
        }
  • Supporting utility method in the OSMClient class that queries the Overpass API to retrieve raw points of interest data near the given coordinates, used by the find_nearby_places handler.
    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}")

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jagan-shanmugam/open-streetmap-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server