Skip to main content
Glama
jonfreeland

MongoDB MCP Server

by jonfreeland

geo_query

Execute geospatial queries on MongoDB collections to find locations near points, within polygons or circles, and calculate distances between coordinates.

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:

  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]] ] } }

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
databaseNoDatabase name (optional if default database is configured)
collectionYesCollection name
operationYesGeospatial operation to perform
locationFieldNoField containing geospatial data (default: "location")
pointNoPoint coordinates [longitude, latitude] for near/nearSphere queries
maxDistanceNoMaximum distance in meters for near/nearSphere queries
minDistanceNoMinimum distance in meters for near/nearSphere queries
geometryNoGeoJSON geometry for geoWithin/geoIntersects queries
distanceFieldNoField to store calculated distances (for near/nearSphere queries)
sphericalNoCalculate distances on a sphere (Earth) rather than flat plane
limitNoMaximum number of results to return
additionalFilterNoAdditional MongoDB query criteria to combine with geospatial query

Implementation Reference

  • src/index.ts:843-944 (registration)
    Registration of the 'geo_query' tool including its detailed description and complete input schema definition in the ListToolsRequestSchema handler.
    { 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'], }, },
  • Input schema definition for the 'geo_query' tool, specifying parameters for various geospatial operations.
    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'], },
  • Implementation of the 'geo_query' tool handler within the CallToolRequestSchema switch statement. Handles near/nearSphere using $geoNear aggregation and geoWithin/geoIntersects using find with GeoJSON geometry. Includes input validation, geospatial index checks, 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}` ); } }
  • Supporting helper method generateVisualizationHint used by geo_query (and others) that detects geospatial data and suggests map-based visualizations.
    private generateVisualizationHint(data: any[]): string { if (!Array.isArray(data) || data.length === 0) return ''; // Check if the data looks like time series const hasDateFields = Object.keys(data[0]).some(key => data[0][key] instanceof Date || (typeof data[0][key] === 'string' && !isNaN(Date.parse(data[0][key]))) ); // Check if the data has numeric fields const numericFields = Object.keys(data[0]).filter(key => typeof data[0][key] === 'number' ); // Check if the data has categorical fields const categoricalFields = Object.keys(data[0]).filter(key => typeof data[0][key] === 'string' && data.every(item => typeof item[key] === 'string') ); // Check if the data has geospatial fields 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'); } if (data.length > 1000) { hints.push('Large Dataset Considerations:\n- Consider sampling for initial visualization\n- Use aggregation for summary views\n- Implement pagination or infinite scroll\n- Consider server-side rendering'); } return hints.join('\n\n'); }

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/jonfreeland/mongodb-mcp'

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