geo_query
Perform geospatial queries on MongoDB collections to find points near a location, within shapes, or calculate distances. Supports GeoJSON, legacy formats, and requires a geospatial index.
Instructions
Execute geospatial queries on a MongoDB collection.
Supports:
Finding points near a location
Finding documents within a polygon, circle, or box
Calculating distances between points
GeoJSON and legacy coordinate pair formats
Requirements:
Collection must have a geospatial index (2dsphere recommended)
Coordinates should follow MongoDB conventions (longitude first, then latitude)
Examples:
Find locations near a point (2 miles radius): use_mcp_tool with server_name: "mongodb", tool_name: "geo_query", arguments: { "collection": "restaurants", "operation": "near", "point": [-73.9667, 40.78], "maxDistance": 3218.69, // 2 miles in meters "distanceField": "distance" }
Find locations within a polygon: use_mcp_tool with server_name: "mongodb", tool_name: "geo_query", arguments: { "collection": "properties", "operation": "geoWithin", "geometry": { "type": "Polygon", "coordinates": [ [[-73.958, 40.8], [-73.94, 40.79], [-73.95, 40.76], [-73.97, 40.76], [-73.958, 40.8]] ] } }
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| additionalFilter | No | Additional MongoDB query criteria to combine with geospatial query | |
| collection | Yes | Collection name | |
| database | No | Database name (optional if default database is configured) | |
| distanceField | No | Field to store calculated distances (for near/nearSphere queries) | |
| geometry | No | GeoJSON geometry for geoWithin/geoIntersects queries | |
| limit | No | Maximum number of results to return | |
| locationField | No | Field containing geospatial data (default: "location") | |
| maxDistance | No | Maximum distance in meters for near/nearSphere queries | |
| minDistance | No | Minimum distance in meters for near/nearSphere queries | |
| operation | Yes | Geospatial operation to perform | |
| point | No | Point coordinates [longitude, latitude] for near/nearSphere queries | |
| spherical | No | Calculate distances on a sphere (Earth) rather than flat plane |
Implementation Reference
- src/index.ts:1428-1589 (handler)Main execution handler for the geo_query tool. Handles geospatial operations including near/nearSphere using $geoNear aggregation, and geoWithin/geoIntersects using find queries. Includes input validation, coordinate checks, error handling for missing indexes, and visualization hints.case 'geo_query': { const { database, collection, operation, locationField = 'location', point, maxDistance, minDistance, geometry, distanceField, spherical = true, limit = 100, additionalFilter = {} } = request.params.arguments as { database?: string; collection: string; operation: 'near' | 'geoWithin' | 'geoIntersects' | 'nearSphere'; locationField?: string; point?: number[]; maxDistance?: number; minDistance?: number; geometry?: any; distanceField?: string; spherical?: boolean; limit?: number; additionalFilter?: object; }; const dbName = database || this.defaultDatabase; if (!dbName) { throw new McpError( ErrorCode.InvalidRequest, 'Database name is required when no default database is configured' ); } const db = client.db(dbName); switch (operation) { case 'near': case 'nearSphere': { if (!Array.isArray(point) || point.length !== 2) { throw new McpError( ErrorCode.InvalidRequest, 'Point coordinates [longitude, latitude] are required for near/nearSphere queries' ); } // Validate coordinates const [longitude, latitude] = point; if (longitude < -180 || longitude > 180) { throw new McpError( ErrorCode.InvalidRequest, 'Invalid longitude: must be between -180 and 180' ); } if (latitude < -90 || latitude > 90) { throw new McpError( ErrorCode.InvalidRequest, 'Invalid latitude: must be between -90 and 90' ); } const geoNearOptions: any = { near: { type: 'Point', coordinates: point }, distanceField: distanceField || 'distance', spherical: operation === 'nearSphere' || spherical, query: additionalFilter }; if (maxDistance !== undefined) geoNearOptions.maxDistance = maxDistance; if (minDistance !== undefined) geoNearOptions.minDistance = minDistance; if (limit) geoNearOptions.limit = limit; try { // Use aggregation for geoNear const results = await db.collection(collection).aggregate([ { $geoNear: geoNearOptions } ]).toArray(); const vizHint = this.generateVisualizationHint(results); return { content: [ { type: 'text', text: JSON.stringify(results, null, 2) + (vizHint ? `\n\nVisualization Hint:\n${vizHint}` : ''), }, ], }; } catch (error) { // Check if error is due to missing geospatial index if (error instanceof Error && (error.message.includes('2dsphere') || error.message.includes('geo'))) { throw new McpError( ErrorCode.InvalidRequest, `Geospatial index required. Create one using: db.${collection}.createIndex({ "${locationField}": "2dsphere" })` ); } throw error; } } case 'geoWithin': case 'geoIntersects': { if (!geometry || !geometry.type || !geometry.coordinates) { throw new McpError( ErrorCode.InvalidRequest, 'Valid GeoJSON geometry is required for geoWithin/geoIntersects queries' ); } const operator = operation === 'geoWithin' ? '$geoWithin' : '$geoIntersects'; const geoQuery = { [locationField]: { [operator]: { $geometry: geometry } }, ...additionalFilter }; try { const results = await db.collection(collection) .find(geoQuery) .limit(limit) .toArray(); const vizHint = this.generateVisualizationHint(results); return { content: [ { type: 'text', text: JSON.stringify(results, null, 2) + (vizHint ? `\n\nVisualization Hint:\n${vizHint}` : ''), }, ], }; } catch (error) { // Check if error is due to missing geospatial index if (error instanceof Error && (error.message.includes('2dsphere') || error.message.includes('geo'))) { throw new McpError( ErrorCode.InvalidRequest, `Geospatial index required. Create one using: db.${collection}.createIndex({ "${locationField}": "2dsphere" })` ); } throw error; } } default: throw new McpError( ErrorCode.InvalidRequest, `Unsupported geospatial operation: ${operation}` ); } }
- src/index.ts:884-943 (schema)Input schema defining parameters for geo_query tool, including operation type, coordinates, distances, geometry, and filters.inputSchema: { type: 'object', properties: { database: { type: 'string', description: 'Database name (optional if default database is configured)', }, collection: { type: 'string', description: 'Collection name', }, operation: { type: 'string', description: 'Geospatial operation to perform', enum: ['near', 'geoWithin', 'geoIntersects', 'nearSphere'], }, locationField: { type: 'string', description: 'Field containing geospatial data (default: "location")', }, point: { type: 'array', description: 'Point coordinates [longitude, latitude] for near/nearSphere queries', items: { type: 'number', }, }, maxDistance: { type: 'number', description: 'Maximum distance in meters for near/nearSphere queries', }, minDistance: { type: 'number', description: 'Minimum distance in meters for near/nearSphere queries', }, geometry: { type: 'object', description: 'GeoJSON geometry for geoWithin/geoIntersects queries', }, distanceField: { type: 'string', description: 'Field to store calculated distances (for near/nearSphere queries)', }, spherical: { type: 'boolean', description: 'Calculate distances on a sphere (Earth) rather than flat plane', }, limit: { type: 'number', description: 'Maximum number of results to return', minimum: 1, maximum: 1000, }, additionalFilter: { type: 'object', description: 'Additional MongoDB query criteria to combine with geospatial query', }, }, required: ['collection', 'operation'], },
- src/index.ts:843-944 (registration)Tool registration in the ListTools response, defining name, description, and referencing the input schema for geo_query.{ name: 'geo_query', description: `Execute geospatial queries on a MongoDB collection. Supports: - Finding points near a location - Finding documents within a polygon, circle, or box - Calculating distances between points - GeoJSON and legacy coordinate pair formats Requirements: - Collection must have a geospatial index (2dsphere recommended) - Coordinates should follow MongoDB conventions (longitude first, then latitude) Examples: 1. Find locations near a point (2 miles radius): use_mcp_tool with server_name: "mongodb", tool_name: "geo_query", arguments: { "collection": "restaurants", "operation": "near", "point": [-73.9667, 40.78], "maxDistance": 3218.69, // 2 miles in meters "distanceField": "distance" } 2. Find locations within a polygon: use_mcp_tool with server_name: "mongodb", tool_name: "geo_query", arguments: { "collection": "properties", "operation": "geoWithin", "geometry": { "type": "Polygon", "coordinates": [ [[-73.958, 40.8], [-73.94, 40.79], [-73.95, 40.76], [-73.97, 40.76], [-73.958, 40.8]] ] } }`, inputSchema: { type: 'object', properties: { database: { type: 'string', description: 'Database name (optional if default database is configured)', }, collection: { type: 'string', description: 'Collection name', }, operation: { type: 'string', description: 'Geospatial operation to perform', enum: ['near', 'geoWithin', 'geoIntersects', 'nearSphere'], }, locationField: { type: 'string', description: 'Field containing geospatial data (default: "location")', }, point: { type: 'array', description: 'Point coordinates [longitude, latitude] for near/nearSphere queries', items: { type: 'number', }, }, maxDistance: { type: 'number', description: 'Maximum distance in meters for near/nearSphere queries', }, minDistance: { type: 'number', description: 'Minimum distance in meters for near/nearSphere queries', }, geometry: { type: 'object', description: 'GeoJSON geometry for geoWithin/geoIntersects queries', }, distanceField: { type: 'string', description: 'Field to store calculated distances (for near/nearSphere queries)', }, spherical: { type: 'boolean', description: 'Calculate distances on a sphere (Earth) rather than flat plane', }, limit: { type: 'number', description: 'Maximum number of results to return', minimum: 1, maximum: 1000, }, additionalFilter: { type: 'object', description: 'Additional MongoDB query criteria to combine with geospatial query', }, }, required: ['collection', 'operation'], }, },
- src/index.ts:215-239 (helper)Helper function generateVisualizationHint includes specific logic to detect geospatial data and suggest map-based visualizations, used by geo_query handler.const hasGeoData = Object.keys(data[0]).some(key => { const value = data[0][key]; return value && typeof value === 'object' && (('type' in value && value.type === 'Point' && 'coordinates' in value) || (Array.isArray(value) && value.length === 2 && typeof value[0] === 'number' && typeof value[1] === 'number')); }); let hints = []; if (hasDateFields && numericFields.length > 0) { hints.push('Time Series Visualization:\n- Consider line charts for temporal trends\n- Time-based heat maps for density patterns\n- Area charts for cumulative values over time'); } if (categoricalFields.length > 0 && numericFields.length > 0) { hints.push('Categorical Analysis:\n- Bar charts for comparing categories\n- Box plots for distribution analysis\n- Heat maps for category correlations\n- Treemaps for hierarchical data'); } if (numericFields.length >= 2) { hints.push('Numerical Analysis:\n- Scatter plots for correlation analysis\n- Bubble charts if three numeric dimensions\n- Correlation matrices for multiple variables\n- Histograms for distribution analysis'); } if (hasGeoData) { hints.push('Geospatial Visualization:\n- Map overlays for location data\n- Choropleth maps for regional analysis\n- Heat maps for density visualization\n- Cluster maps for point concentration'); }