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');
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure and does so effectively. It explains prerequisites (geospatial index requirement), coordinate format conventions, and provides concrete examples showing how to structure queries. The examples demonstrate practical usage patterns including parameter combinations and distance calculations, though it doesn't mention rate limits, authentication needs, or error conditions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured and efficiently organized with clear sections (overview, supports, requirements, examples). Every sentence earns its place by providing essential information without redundancy. The front-loaded purpose statement is followed by progressively detailed information, making it easy to scan while maintaining comprehensive coverage.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex tool with 12 parameters, no annotations, and no output schema, the description provides substantial context through examples, requirements, and operation explanations. It covers the main use cases and parameter combinations effectively. However, without an output schema, it doesn't describe what the tool returns (document format, distance calculations in results), which is a notable gap for a query tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 100%, so the baseline is 3. The description adds some value by mentioning coordinate format conventions and providing examples that show how parameters like 'point', 'maxDistance', and 'geometry' are used in practice, but it doesn't add significant semantic information beyond what's already documented in the comprehensive schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose as 'Execute geospatial queries on a MongoDB collection' with specific verbs and resources. It distinguishes itself from sibling tools like 'query' or 'text_search' by focusing exclusively on geospatial operations, listing supported query types (near, within polygons/circles/boxes, distance calculations) and supported formats (GeoJSON, legacy coordinate pairs).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for when to use this tool through its 'Requirements' section, specifying that the collection must have a geospatial index and coordinates should follow MongoDB conventions. However, it doesn't explicitly state when NOT to use this tool or name specific alternatives among the sibling tools for non-geospatial queries, which prevents a perfect score.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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