We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/saurabhsharma2u/search-console-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
import * as sites from './sites.js';
import * as sitemaps from './sitemaps.js';
import * as analytics from './analytics.js';
import { limitConcurrency } from '../../common/concurrency.js';
/**
* Health status for a single site property.
*/
export interface SiteHealthReport {
/** The site URL. */
siteUrl: string;
/** Overall status: healthy, warning, or critical. */
status: 'healthy' | 'warning' | 'critical';
/** Permission level in Search Console. */
permissionLevel: string;
/** Performance comparison: this week vs last week. */
performance: {
current: analytics.PerformanceSummary;
previous: analytics.PerformanceSummary;
changes: {
clicks: number;
clicksPercent: number;
impressions: number;
impressionsPercent: number;
ctr: number;
ctrPercent: number;
position: number;
positionPercent: number;
};
};
/** Sitemap health summary. */
sitemaps: {
total: number;
withErrors: number;
withWarnings: number;
details: Array<{
path: string;
type: string | undefined;
isPending: boolean;
hasErrors: boolean;
lastDownloaded: string | undefined;
}>;
};
/** Detected anomalies in the last 14 days. */
anomalies: analytics.AnomalyItem[];
/** Human-readable list of issues found. */
issues: string[];
}
/**
* Run a health check on a single site, returning a structured report.
*
* @param siteUrl - The site URL to check.
* @param existingSiteInfo - Optional. Pre-fetched site info to avoid redundant calls.
* @returns A health report for the site.
*/
async function checkSite(siteUrl: string, existingSiteInfo?: Awaited<ReturnType<typeof sites.getSite>>): Promise<SiteHealthReport> {
const issues: string[] = [];
// Run all checks in parallel
const [siteInfo, sitemapList, comparison, anomalies] = await Promise.all([
existingSiteInfo ? Promise.resolve(existingSiteInfo) : sites.getSite(siteUrl).catch(() => null),
sitemaps.listSitemaps(siteUrl).catch(() => []),
getWeekOverWeekComparison(siteUrl),
analytics.detectAnomalies(siteUrl, { days: 14, threshold: 2.5 }).catch(() => []),
]);
const permissionLevel = siteInfo?.permissionLevel ?? 'unknown';
// --- Analyze performance ---
const { period1, period2, changes } = comparison;
if (changes.clicksPercent <= -30) {
issues.push(`Critical traffic drop: clicks down ${Math.abs(changes.clicksPercent).toFixed(1)}% week-over-week`);
} else if (changes.clicksPercent <= -15) {
issues.push(`Traffic declining: clicks down ${Math.abs(changes.clicksPercent).toFixed(1)}% week-over-week`);
}
if (changes.impressionsPercent <= -30) {
issues.push(`Critical visibility drop: impressions down ${Math.abs(changes.impressionsPercent).toFixed(1)}% week-over-week`);
} else if (changes.impressionsPercent <= -15) {
issues.push(`Visibility declining: impressions down ${Math.abs(changes.impressionsPercent).toFixed(1)}% week-over-week`);
}
if (changes.position > 3) {
issues.push(`Average position worsened by ${changes.position.toFixed(1)} positions`);
}
if (period1.clicks === 0 && period1.impressions === 0) {
issues.push('No traffic data for the current period — site may not be receiving any search traffic');
}
// --- Analyze sitemaps ---
let sitemapsWithErrors = 0;
let sitemapsWithWarnings = 0;
const sitemapDetails = sitemapList.map(sm => {
const hasErrors = (Number(sm.errors) || 0) > 0;
const isPending = sm.isPending ?? false;
if (hasErrors) sitemapsWithErrors++;
if ((Number(sm.warnings) || 0) > 0) sitemapsWithWarnings++;
return {
path: sm.path ?? 'unknown',
type: sm.type ?? undefined,
isPending,
hasErrors,
lastDownloaded: sm.lastDownloaded ?? undefined,
};
});
if (sitemapList.length === 0) {
issues.push('No sitemaps submitted — consider submitting a sitemap for better crawling');
}
if (sitemapsWithErrors > 0) {
issues.push(`${sitemapsWithErrors} sitemap(s) have errors`);
}
if (sitemapsWithWarnings > 0) {
issues.push(`${sitemapsWithWarnings} sitemap(s) have warnings`);
}
// --- Analyze anomalies ---
const drops = anomalies.filter(a => a.type === 'drop');
if (drops.length > 0) {
issues.push(`${drops.length} traffic anomaly drop(s) detected in the last 14 days`);
}
// --- Determine overall status ---
let status: SiteHealthReport['status'] = 'healthy';
if (issues.some(i => i.startsWith('Critical') || i.includes('No traffic data'))) {
status = 'critical';
} else if (issues.length > 0) {
status = 'warning';
}
return {
siteUrl,
status,
permissionLevel,
performance: {
current: period1,
previous: period2,
changes,
},
sitemaps: {
total: sitemapList.length,
withErrors: sitemapsWithErrors,
withWarnings: sitemapsWithWarnings,
details: sitemapDetails,
},
anomalies,
issues,
};
}
/**
* Get a week-over-week performance comparison for a site.
*/
async function getWeekOverWeekComparison(siteUrl: string): Promise<analytics.PeriodComparison> {
const DATA_DELAY_DAYS = 3;
const now = new Date();
const period1End = new Date(now);
period1End.setDate(period1End.getDate() - DATA_DELAY_DAYS);
const period1Start = new Date(period1End);
period1Start.setDate(period1Start.getDate() - 7);
const period2End = new Date(period1Start);
period2End.setDate(period2End.getDate() - 1);
const period2Start = new Date(period2End);
period2Start.setDate(period2Start.getDate() - 7);
const fmt = (d: Date) => d.toISOString().split('T')[0];
return analytics.comparePeriods(
siteUrl,
fmt(period1Start),
fmt(period1End),
fmt(period2Start),
fmt(period2End),
);
}
/**
* Run a health check on one or all verified sites.
*
* @param siteUrl - Optional. If provided, check only this site. If omitted, check all verified sites.
* @returns An array of health reports.
*/
export async function healthCheck(siteUrl?: string): Promise<SiteHealthReport[]> {
if (siteUrl) {
const report = await checkSite(siteUrl);
return [report];
}
const allSites = await sites.listSites();
if (allSites.length === 0) {
return [];
}
const reports = await limitConcurrency(allSites, 5, site => checkSite(site.siteUrl!, site));
// Sort: critical first, then warning, then healthy
const order = { critical: 0, warning: 1, healthy: 2 };
return reports.sort((a, b) => order[a.status] - order[b.status]);
}