Skip to main content
Glama
ahonn

Google Search Console MCP Server

by ahonn

enhanced_search_analytics

Analyze Google Search Console data with advanced filtering, regex matching, and quick wins detection to identify optimization opportunities.

Instructions

Enhanced search analytics with up to 25,000 rows, regex filters, and quick wins detection

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
siteUrlYesThe site URL as defined in Search Console. Example: sc-domain:example.com (for domain resources) or http://www.example.com/ (for site prefix resources)
startDateYesStart date in YYYY-MM-DD format
endDateYesEnd date in YYYY-MM-DD format
dimensionsNoComma-separated list of dimensions to break down results by, such as query, page, country, device, searchAppearance
typeNoType of search to filter by, such as web, image, video, news
aggregationTypeNoType of aggregation, such as auto, byNewsShowcasePanel, byProperty, byPage
rowLimitNoMaximum number of rows to return (up to 25,000 for enhanced performance)
pageFilterNoFilter by a specific page URL. Use with filterOperator.
queryFilterNoFilter by a specific query string. Use with filterOperator.
countryFilterNoFilter by a country using ISO 3166-1 alpha-3 code (e.g., USA, CHN).
deviceFilterNoFilter by device type.
filterOperatorNoOperator for page and query filters. Defaults to "equals". Enhanced with regex support.equals
regexFilterNoAdvanced regex filter for intelligent query matching
enableQuickWinsNoEnable automatic quick wins detection
quickWinsThresholdsNoCustom thresholds for quick wins detection

Implementation Reference

  • Main handler function implementing the enhanced search analytics tool logic, supporting up to 25k rows, regex filtering, and quick wins detection.
    async enhancedSearchAnalytics(
      siteUrl: string, 
      requestBody: SearchanalyticsQueryRequest,
      options: {
        regexFilter?: string;
        enableQuickWins?: boolean;
        quickWinsThresholds?: {
          minImpressions?: number;
          maxCtr?: number;
          positionRangeMin?: number;
          positionRangeMax?: number;
        };
      } = {}
    ) {
      // Ensure requestBody is defined
      if (!requestBody) {
        throw new Error('Request body is required');
      }
    
      // Apply regex filter if provided
      if (options.regexFilter && requestBody.dimensions?.includes('query')) {
        requestBody.dimensionFilterGroups = [
          ...(requestBody.dimensionFilterGroups || []),
          {
            groupType: 'and',
            filters: [{
              dimension: 'query',
              operator: 'includingRegex',
              expression: options.regexFilter
            }]
          }
        ];
      }
    
      // Execute enhanced search analytics
      const result = await this.searchAnalytics(siteUrl, requestBody);
      
      // Apply quick wins detection if enabled
      if (options.enableQuickWins && result.data.rows) {
        const quickWins = this.detectQuickWins(result.data.rows, options.quickWinsThresholds);
        return {
          ...result,
          data: {
            ...result.data,
            quickWins: quickWins,
            enhancedFeatures: {
              regexFilterApplied: !!options.regexFilter,
              quickWinsEnabled: true,
              rowLimit: requestBody.rowLimit || 1000
            }
          }
        };
      }
    
      return result;
    }
  • Supporting utility for detecting SEO quick wins based on search analytics data.
    private detectQuickWins(
      rows: any[], 
      thresholds: {
        minImpressions?: number;
        maxCtr?: number;
        positionRangeMin?: number;
        positionRangeMax?: number;
      } = {}
    ) {
      const {
        minImpressions = 50,
        maxCtr = 2.0,
        positionRangeMin = 4,
        positionRangeMax = 10
      } = thresholds;
    
      return rows
        .filter(row => {
          const impressions = row.impressions || 0;
          const ctr = (row.ctr || 0) * 100;
          const position = row.position || 0;
    
          return impressions >= minImpressions &&
                 ctr <= maxCtr &&
                 position >= positionRangeMin &&
                 position <= positionRangeMax;
        })
        .map(row => {
          const impressions = row.impressions || 0;
          const currentClicks = row.clicks || 0;
          const currentCtr = (row.ctr || 0) * 100;
          const position = row.position || 0;
    
          // Calculate potential with 5% target CTR
          const targetCtr = 5.0;
          const potentialClicks = Math.round((impressions * targetCtr) / 100);
          const additionalClicks = Math.max(0, potentialClicks - currentClicks);
    
          return {
            query: row.keys?.[0] || 'N/A',
            page: row.keys?.[1] || 'N/A',
            currentPosition: Number(position.toFixed(1)),
            impressions: impressions,
            currentClicks: currentClicks,
            currentCtr: Number(currentCtr.toFixed(2)),
            potentialClicks: potentialClicks,
            additionalClicks: additionalClicks,
            opportunity: additionalClicks > 0 ? 'High' : 'Low',
            optimizationNote: `Move from position ${position.toFixed(1)} to improve CTR`
          };
        })
        .sort((a, b) => b.additionalClicks - a.additionalClicks);
    }
  • Zod schema defining inputs for the enhanced_search_analytics tool, extending base search analytics with quick wins options.
    export const EnhancedSearchAnalyticsSchema = SearchAnalyticsSchema.extend({
      enableQuickWins: z.boolean().default(false).describe('Enable automatic quick wins detection'),
      quickWinsThresholds: QuickWinsDetectionSchema.pick({
        minImpressions: true,
        maxCtr: true,
        positionRangeMin: true,
        positionRangeMax: true,
      }).optional().describe('Custom thresholds for quick wins detection'),
    });
    
    export type SearchAnalytics = z.infer<typeof SearchAnalyticsSchema>;
    export type EnhancedSearchAnalytics = z.infer<typeof EnhancedSearchAnalyticsSchema>;
  • src/index.ts:54-57 (registration)
    Tool registration in MCP server's listTools handler, specifying name, description, and input schema.
      name: 'enhanced_search_analytics',
      description: 'Enhanced search analytics with up to 25,000 rows, regex filters, and quick wins detection',
      inputSchema: zodToJsonSchema(EnhancedSearchAnalyticsSchema),
    },
  • MCP server handler for calling the enhanced_search_analytics tool, parsing arguments and invoking the service implementation.
    case 'enhanced_search_analytics': {
      const args = EnhancedSearchAnalyticsSchema.parse(request.params.arguments);
      const siteUrl = args.siteUrl;
    
      // Build enhanced request body
      const requestBody: any = {
        startDate: args.startDate,
        endDate: args.endDate,
        dimensions: args.dimensions,
        searchType: args.type,
        aggregationType: args.aggregationType,
        rowLimit: args.rowLimit, // Up to 25,000!
      };
    
      // Build filters (including regex support)
      const filters = [];
      if (args.pageFilter) {
        filters.push({
          dimension: 'page',
          operator: args.filterOperator,
          expression: args.pageFilter,
        });
      }
      if (args.queryFilter) {
        filters.push({
          dimension: 'query',
          operator: args.filterOperator,
          expression: args.queryFilter,
        });
      }
      if (args.countryFilter) {
        filters.push({
          dimension: 'country',
          operator: 'equals',
          expression: args.countryFilter,
        });
      }
      if (args.deviceFilter) {
        filters.push({
          dimension: 'device',
          operator: 'equals',
          expression: args.deviceFilter,
        });
      }
    
      if (filters.length > 0) {
        requestBody.dimensionFilterGroups = [{ groupType: 'and', filters }];
      }
    
      // Call enhanced search analytics
      const enhancedOptions = {
        regexFilter: args.regexFilter,
        enableQuickWins: args.enableQuickWins,
        quickWinsThresholds: args.quickWinsThresholds,
      };
    
      const response = await searchConsole.enhancedSearchAnalytics(siteUrl, requestBody, enhancedOptions);
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(response.data, null, 2),
          },
        ],
      };
    }
Behavior2/5

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

With no annotations provided, the description carries full burden but offers minimal behavioral insight. It mentions 'enhanced performance' and 'quick wins detection' but doesn't clarify what 'enhanced' means operationally, whether this is a read-only or mutating operation, potential rate limits, or what 'quick wins' entails in practice. The description lacks critical behavioral context for a complex tool.

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 extremely concise—a single sentence that efficiently lists key features without redundancy. It's front-loaded with the core purpose and doesn't waste words, making it easy for an agent to parse quickly.

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

Completeness2/5

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

For a complex tool with 15 parameters, no annotations, and no output schema, the description is inadequate. It doesn't explain what the tool returns, how 'quick wins' are presented, or the operational implications of 'enhanced' features. The agent lacks sufficient context to understand the tool's full behavior and output.

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?

Schema description coverage is 100%, so parameters are well-documented in the schema itself. The description adds marginal value by hinting at 'regex filters' and 'quick wins detection' which correspond to specific parameters, but doesn't provide additional semantic context beyond what's already in the schema descriptions. Baseline 3 is appropriate given high schema coverage.

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

Purpose4/5

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

The description clearly states the tool performs 'enhanced search analytics' with specific capabilities (up to 25,000 rows, regex filters, quick wins detection). It distinguishes from the simpler 'search_analytics' sibling by highlighting enhanced features, though it doesn't explicitly contrast with 'detect_quick_wins' which might overlap in functionality.

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

Usage Guidelines2/5

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

No guidance is provided on when to use this tool versus alternatives like 'search_analytics' or 'detect_quick_wins'. The description mentions capabilities but doesn't specify use cases, prerequisites, or exclusions that would help an agent choose appropriately among siblings.

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/ahonn/mcp-server-gsc'

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