{"_id":"@akson/cortex-api-gsc","_rev":"3-778e59475363ce552c06002f944a2a60","name":"@akson/cortex-api-gsc","dist-tags":{"latest":"0.5.0"},"versions":{"0.5.0":{"name":"@akson/cortex-api-gsc","version":"0.5.0","keywords":["google-search-console","gsc","search-analytics","seo","google-api","mcp","server"],"author":{"name":"MyArmy","email":"contact@myarmy.ch"},"license":"MIT","_id":"@akson/cortex-api-gsc@0.5.0","maintainers":[{"name":"antoineschaller","email":"antoine.schaller@akson.ch"}],"homepage":"https://github.com/antoineschaller/myarmy/tree/main/packages/@akson/cortex-api-gsc","bugs":{"url":"https://github.com/antoineschaller/myarmy/issues"},"bin":{"gsc-mcp-server":"dist/mcp-cli.js"},"dist":{"shasum":"ca7ce43601093d1000ca363efca850d11c8ee6e0","tarball":"https://registry.npmjs.org/@akson/cortex-api-gsc/-/cortex-api-gsc-0.5.0.tgz","fileCount":2,"integrity":"sha512-DkteRJdUqQny2OWgi8tVbFPy71Fa+Zk9uv5TJxglttadu7N5a2xB3d9MWnGa1kZmmPzYpAZ285P5iLJpeSJ4NQ==","signatures":[{"sig":"MEUCIQDI0Qevkw77ai2x+IGQpPp3oGWl+BwU0en+8XkajiH4mwIgTpQKA0k/OAS0dAHFpXIrn/fpbvk6BhvKTMBhGvm70Fk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":23575},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"84153813d2b6cc183f00f23d04cf0e887c512fbe","scripts":{"dev":"tsup --watch","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"antoineschaller","email":"antoine.schaller@akson.ch"},"deprecated":"Package renamed to @akson/cortex-gsc. Please update your dependencies to use the new package name.","repository":{"url":"git+https://github.com/antoineschaller/myarmy.git","type":"git","directory":"packages/@akson/cortex-api-gsc"},"_npmVersion":"11.5.1","description":"Google Search Console API client and MCP server for search analytics and performance data","directories":{},"_nodeVersion":"24.7.0","dependencies":{"zod":"^3.23.8","@akson/cortex-api-shared":"^0.3.0","@modelcontextprotocol/sdk":"^1.17.3"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","typescript":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/cortex-api-gsc_0.5.0_1757683086968_0.7593777468724296","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2025-09-12T13:18:06.865Z","modified":"2025-09-13T10:30:27.129Z","0.5.0":"2025-09-12T13:18:07.149Z"},"bugs":{"url":"https://github.com/antoineschaller/myarmy/issues"},"author":{"name":"MyArmy","email":"contact@myarmy.ch"},"license":"MIT","homepage":"https://github.com/antoineschaller/myarmy/tree/main/packages/@akson/cortex-api-gsc","keywords":["google-search-console","gsc","search-analytics","seo","google-api","mcp","server"],"repository":{"url":"git+https://github.com/antoineschaller/myarmy.git","type":"git","directory":"packages/@akson/cortex-api-gsc"},"description":"Google Search Console API client and MCP server for search analytics and performance data","maintainers":[{"name":"antoineschaller","email":"antoine.schaller@akson.ch"}],"readme":"# @akson/api-gsc\n\nTypeScript client for Google Search Console API, enabling programmatic SEO monitoring, performance analysis, crawl management, and search optimization.\n\n## User Stories\n\n### Site Management Stories\n\n**As an SEO Manager**, I want to programmatically add new sites to Search Console, so that I can scale SEO monitoring across multiple properties.\n\n**As a Technical SEO Specialist**, I want to verify site ownership automatically, so that I can streamline the onboarding process for new domains.\n\n**As a Multi-Site SEO Director**, I want to list all sites in my Search Console account, so that I can audit and manage my property portfolio.\n\n**As a Digital Marketing Manager**, I want to remove outdated sites from monitoring, so that I can keep my Search Console account organized.\n\n### Performance Analysis Stories\n\n**As an SEO Analyst**, I want to retrieve search performance data by query, so that I can identify keyword opportunities and trends.\n\n**As a Content Marketing Manager**, I want to analyze page performance data, so that I can optimize high-potential pages for better rankings.\n\n**As an SEO Director**, I want to track click-through rates and impressions over time, so that I can measure the impact of SEO optimizations.\n\n**As a Digital Marketing Analyst**, I want to segment performance by device and country, so that I can optimize for different user segments and markets.\n\n### Search Analytics Stories\n\n**As a Keyword Research Specialist**, I want to export query performance data, so that I can identify long-tail keyword opportunities.\n\n**As an SEO Consultant**, I want to compare performance across date ranges, so that I can measure the impact of SEO changes.\n\n**As a Content Strategist**, I want to identify top-performing content by search metrics, so that I can replicate successful content patterns.\n\n**As a Technical SEO Manager**, I want to analyze search appearance types (rich snippets, featured snippets), so that I can optimize for SERP features.\n\n### Crawl Management Stories\n\n**As a Technical SEO Specialist**, I want to monitor crawl errors, so that I can quickly identify and fix indexing issues.\n\n**As a Site Reliability Engineer**, I want to track crawl statistics, so that I can optimize server resources for search bot traffic.\n\n**As an SEO Manager**, I want to identify blocked resources, so that I can ensure search bots can access important site assets.\n\n**As a Web Developer**, I want to monitor crawl rate changes, so that I can adjust server capacity during high-traffic periods.\n\n### Sitemap Management Stories\n\n**As a Technical SEO Manager**, I want to submit sitemaps programmatically, so that I can automate sitemap updates for dynamic content sites.\n\n**As an E-commerce SEO Specialist**, I want to monitor sitemap processing status, so that I can ensure product pages are being discovered.\n\n**As a Content Management System Developer**, I want to automatically resubmit sitemaps after content updates, so that I can ensure fresh content gets indexed quickly.\n\n**As an SEO Consultant**, I want to track sitemap errors and warnings, so that I can maintain optimal site crawlability.\n\n### URL Inspection Stories\n\n**As a Technical SEO Analyst**, I want to inspect specific URLs for indexing status, so that I can troubleshoot indexing issues.\n\n**As a Content Manager**, I want to request indexing for new or updated pages, so that I can expedite their appearance in search results.\n\n**As an E-commerce Manager**, I want to check product page indexing status, so that I can ensure new products are discoverable.\n\n**As a Website Owner**, I want to validate page accessibility for search bots, so that I can ensure my content can be properly crawled.\n\n### Mobile Usability Stories\n\n**As a UX Designer**, I want to monitor mobile usability issues, so that I can prioritize mobile optimization efforts.\n\n**As a Technical SEO Specialist**, I want to track mobile-first indexing status, so that I can ensure mobile versions are properly optimized.\n\n**As a Web Developer**, I want to identify pages with mobile usability problems, so that I can fix responsive design issues.\n\n### Security & Spam Stories\n\n**As a Website Security Manager**, I want to monitor manual actions and penalties, so that I can quickly respond to security or quality issues.\n\n**As an SEO Manager**, I want to track spam or hacking notifications, so that I can protect site reputation and rankings.\n\n**As a Brand Protection Specialist**, I want to monitor for security issues across multiple properties, so that I can maintain brand integrity.\n\n### Reporting & Automation Stories\n\n**As an SEO Director**, I want to generate automated SEO performance reports, so that I can provide regular stakeholder updates.\n\n**As a Marketing Operations Manager**, I want to integrate Search Console data with other analytics platforms, so that I can create comprehensive performance dashboards.\n\n**As a Client Services Manager**, I want to automatically alert clients about significant ranking changes, so that I can provide proactive SEO support.\n\n### Competitive Analysis Stories\n\n**As an SEO Consultant**, I want to analyze search visibility trends, so that I can identify competitive opportunities and threats.\n\n**As a Digital Marketing Manager**, I want to track branded search performance, so that I can measure brand awareness and reputation.\n\n**As a Market Research Analyst**, I want to analyze search trends by geography, so that I can identify expansion opportunities.\n\n## Installation\n\n```bash\nnpm install @akson/api-gsc\n```\n\n## Quick Start\n\n```typescript\nimport { GSCClient } from '@akson/api-gsc';\n\nconst client = new GSCClient({\n  config: {\n    siteUrl: 'https://example.com',\n    serviceAccount: {\n      keyFile: 'path/to/service-account.json',\n      email: 'gsc-service@project.iam.gserviceaccount.com'\n    }\n  }\n});\n\n// Authenticate and get sites\nawait client.authenticate();\nconst sites = await client.getSites();\n\n// Get search analytics data\nconst searchData = await client.getSearchAnalytics({\n  startDate: '2025-01-01',\n  endDate: '2025-01-31',\n  dimensions: ['query', 'page'],\n  metrics: ['clicks', 'impressions', 'ctr', 'position']\n});\n\n// Check URL indexing status\nconst urlInspection = await client.inspectUrl('https://example.com/important-page');\n\n// Submit sitemap\nawait client.submitSitemap('https://example.com/sitemap.xml');\n```\n\n## Configuration\n\n### Environment Variables\n\n```bash\nGSC_SITE_URL=https://example.com\nGSC_SERVICE_ACCOUNT_EMAIL=gsc-service@project.iam.gserviceaccount.com\nGSC_SERVICE_ACCOUNT_KEY_FILE=path/to/key.json\n```\n\n### Configuration File\n\nCreate `gsc-config.json`:\n\n```json\n{\n  \"siteUrl\": \"https://example.com\",\n  \"serviceAccount\": {\n    \"email\": \"gsc-service@project.iam.gserviceaccount.com\",\n    \"keyFile\": \"path/to/service-account.json\"\n  }\n}\n```\n\n## API Reference\n\n### Authentication\n\n```typescript\n// Authenticate with service account\nconst authResult = await client.authenticate();\nif (!authResult.success) {\n  throw new Error(authResult.error);\n}\n```\n\n### Site Management\n\n```typescript\n// Get all sites\nconst sitesResult = await client.getSites();\nif (sitesResult.success) {\n  sitesResult.data.forEach(site => {\n    console.log(`Site: ${site.siteUrl}, Permission: ${site.permissionLevel}`);\n  });\n}\n\n// Add new site\nawait client.addSite('https://newsite.com');\n\n// Delete site\nawait client.deleteSite('https://oldsite.com');\n```\n\n### Search Analytics\n\n```typescript\n// Basic search analytics\nconst analyticsResult = await client.getSearchAnalytics({\n  startDate: '2025-01-01',\n  endDate: '2025-01-31',\n  dimensions: ['query'],\n  metrics: ['clicks', 'impressions', 'ctr', 'position'],\n  rowLimit: 1000\n});\n\n// Search analytics with filters\nconst filteredAnalytics = await client.getSearchAnalytics({\n  startDate: '2025-01-01',\n  endDate: '2025-01-31',\n  dimensions: ['query', 'page'],\n  filters: [\n    {\n      dimension: 'country',\n      operator: 'equals',\n      expression: 'USA'\n    },\n    {\n      dimension: 'device',\n      operator: 'equals',\n      expression: 'MOBILE'\n    }\n  ]\n});\n\n// Performance by page\nconst pagePerformance = await client.getPagePerformance({\n  startDate: '2025-01-01',\n  endDate: '2025-01-31',\n  page: '/product-category',\n  metrics: ['clicks', 'impressions', 'ctr', 'position']\n});\n\n// Query performance\nconst queryPerformance = await client.getQueryPerformance({\n  startDate: '2025-01-01',\n  endDate: '2025-01-31',\n  query: 'best running shoes',\n  groupBy: ['page', 'device']\n});\n```\n\n### URL Inspection\n\n```typescript\n// Inspect URL indexing status\nconst inspectionResult = await client.inspectUrl('https://example.com/product/123');\nif (inspectionResult.success) {\n  const status = inspectionResult.data;\n  console.log('Indexing status:', status.indexStatusResult?.verdict);\n  console.log('Coverage state:', status.indexStatusResult?.coverageState);\n  console.log('Last crawl time:', status.indexStatusResult?.lastCrawlTime);\n}\n\n// Request indexing for URL\nconst indexingRequest = await client.requestIndexing('https://example.com/new-product');\nif (indexingRequest.success) {\n  console.log('Indexing requested successfully');\n}\n```\n\n### Sitemap Management\n\n```typescript\n// List sitemaps\nconst sitemapsResult = await client.getSitemaps();\nif (sitemapsResult.success) {\n  sitemapsResult.data.forEach(sitemap => {\n    console.log(`Sitemap: ${sitemap.path}, Status: ${sitemap.type}`);\n    console.log(`Submitted: ${sitemap.submitted}, Indexed: ${sitemap.indexed}`);\n  });\n}\n\n// Submit sitemap\nawait client.submitSitemap('https://example.com/sitemap.xml');\n\n// Delete sitemap\nawait client.deleteSitemap('https://example.com/old-sitemap.xml');\n\n// Get sitemap status\nconst sitemapStatus = await client.getSitemapStatus('https://example.com/sitemap.xml');\n```\n\n### Mobile Usability\n\n```typescript\n// Get mobile usability issues\nconst mobileIssues = await client.getMobileUsabilityIssues();\nif (mobileIssues.success) {\n  mobileIssues.data.forEach(issue => {\n    console.log(`Issue: ${issue.issueType}`);\n    console.log(`Severity: ${issue.severity}`);\n    console.log(`Sample URLs: ${issue.sampleUrls?.length || 0}`);\n  });\n}\n```\n\n### Messages & Manual Actions\n\n```typescript\n// Get messages (manual actions, security issues, etc.)\nconst messagesResult = await client.getMessages();\nif (messagesResult.success) {\n  messagesResult.data.forEach(message => {\n    console.log(`Message: ${message.subject}`);\n    console.log(`Type: ${message.messageType}`);\n    console.log(`Date: ${message.publishTime}`);\n  });\n}\n\n// Mark message as read\nawait client.markMessageAsRead('message-id');\n```\n\n## Error Handling\n\nAll methods return `GSCOperationResult<T>`:\n\n```typescript\nconst result = await client.getSearchAnalytics({\n  startDate: '2025-01-01',\n  endDate: '2025-01-31',\n  dimensions: ['query']\n});\n\nif (result.success) {\n  console.log('Data retrieved:', result.data.length, 'rows');\n  console.log('Total clicks:', result.data.reduce((sum, row) => sum + row.clicks, 0));\n} else {\n  console.error('Error retrieving data:', result.error);\n  if (result.details) {\n    console.error('API Details:', result.details);\n  }\n}\n```\n\n## Advanced Usage\n\n### SEO Performance Monitoring\n\n```typescript\nclass SEOMonitor {\n  constructor(private client: GSCClient) {}\n\n  async dailyPerformanceCheck() {\n    const yesterday = new Date();\n    yesterday.setDate(yesterday.getDate() - 1);\n    const yesterdayStr = yesterday.toISOString().split('T')[0];\n\n    const performance = await this.client.getSearchAnalytics({\n      startDate: yesterdayStr,\n      endDate: yesterdayStr,\n      dimensions: ['query'],\n      metrics: ['clicks', 'impressions', 'ctr', 'position'],\n      rowLimit: 100\n    });\n\n    if (performance.success) {\n      const totalClicks = performance.data.reduce((sum, row) => sum + row.clicks, 0);\n      const avgPosition = performance.data.reduce((sum, row) => sum + row.position, 0) / performance.data.length;\n\n      console.log(`📊 Yesterday's Performance:`);\n      console.log(`- Total clicks: ${totalClicks}`);\n      console.log(`- Average position: ${avgPosition.toFixed(1)}`);\n      \n      // Alert on significant changes\n      if (totalClicks < 100) {\n        console.warn('⚠️ Low traffic alert: Less than 100 clicks yesterday');\n      }\n    }\n  }\n\n  async trackKeywordRankings(keywords: string[]) {\n    const results = [];\n\n    for (const keyword of keywords) {\n      const performance = await this.client.getQueryPerformance({\n        startDate: '2025-01-01',\n        endDate: '2025-01-31',\n        query: keyword\n      });\n\n      if (performance.success && performance.data.length > 0) {\n        results.push({\n          keyword,\n          position: performance.data[0].position,\n          clicks: performance.data[0].clicks,\n          impressions: performance.data[0].impressions\n        });\n      }\n    }\n\n    return results;\n  }\n}\n```\n\n### Automated SEO Reporting\n\n```typescript\nclass SEOReporter {\n  constructor(private client: GSCClient) {}\n\n  async generateMonthlyReport(year: number, month: number) {\n    const startDate = new Date(year, month - 1, 1).toISOString().split('T')[0];\n    const endDate = new Date(year, month, 0).toISOString().split('T')[0];\n\n    // Get overall performance\n    const performance = await this.client.getSearchAnalytics({\n      startDate,\n      endDate,\n      dimensions: ['date'],\n      metrics: ['clicks', 'impressions', 'ctr', 'position']\n    });\n\n    // Get top queries\n    const topQueries = await this.client.getSearchAnalytics({\n      startDate,\n      endDate,\n      dimensions: ['query'],\n      metrics: ['clicks', 'impressions', 'ctr', 'position'],\n      rowLimit: 50\n    });\n\n    // Get top pages\n    const topPages = await this.client.getSearchAnalytics({\n      startDate,\n      endDate,\n      dimensions: ['page'],\n      metrics: ['clicks', 'impressions', 'ctr', 'position'],\n      rowLimit: 50\n    });\n\n    const report = {\n      period: `${year}-${month.toString().padStart(2, '0')}`,\n      summary: {\n        totalClicks: performance.success ? performance.data.reduce((sum, d) => sum + d.clicks, 0) : 0,\n        totalImpressions: performance.success ? performance.data.reduce((sum, d) => sum + d.impressions, 0) : 0,\n        avgCTR: 0,\n        avgPosition: 0\n      },\n      topQueries: topQueries.success ? topQueries.data.slice(0, 20) : [],\n      topPages: topPages.success ? topPages.data.slice(0, 20) : []\n    };\n\n    if (performance.success && performance.data.length > 0) {\n      report.summary.avgCTR = performance.data.reduce((sum, d) => sum + d.ctr, 0) / performance.data.length;\n      report.summary.avgPosition = performance.data.reduce((sum, d) => sum + d.position, 0) / performance.data.length;\n    }\n\n    return report;\n  }\n\n  async comparePerformance(period1: string, period2: string) {\n    const [start1, end1] = period1.split(' to ');\n    const [start2, end2] = period2.split(' to ');\n\n    const performance1 = await this.client.getSummaryData(start1, end1);\n    const performance2 = await this.client.getSummaryData(start2, end2);\n\n    if (performance1.success && performance2.success) {\n      return {\n        clicksChange: ((performance2.data.totalClicks - performance1.data.totalClicks) / performance1.data.totalClicks) * 100,\n        impressionsChange: ((performance2.data.totalImpressions - performance1.data.totalImpressions) / performance1.data.totalImpressions) * 100,\n        ctrChange: performance2.data.averageCTR - performance1.data.averageCTR,\n        positionChange: performance2.data.averagePosition - performance1.data.averagePosition\n      };\n    }\n\n    return null;\n  }\n}\n```\n\n### Content Optimization Insights\n\n```typescript\nclass ContentOptimizer {\n  constructor(private client: GSCClient) {}\n\n  async findOptimizationOpportunities() {\n    // Get pages with high impressions but low CTR\n    const analytics = await this.client.getSearchAnalytics({\n      startDate: '2025-01-01',\n      endDate: '2025-01-31',\n      dimensions: ['page', 'query'],\n      metrics: ['clicks', 'impressions', 'ctr', 'position'],\n      rowLimit: 1000\n    });\n\n    if (!analytics.success) return [];\n\n    const opportunities = analytics.data\n      .filter(row => row.impressions > 100 && row.ctr < 0.02) // High impressions, low CTR\n      .sort((a, b) => b.impressions - a.impressions)\n      .map(row => ({\n        page: row.page,\n        query: row.query,\n        impressions: row.impressions,\n        ctr: (row.ctr * 100).toFixed(2) + '%',\n        position: row.position.toFixed(1),\n        opportunity: 'Low CTR despite high impressions - optimize title/meta description'\n      }));\n\n    return opportunities.slice(0, 20);\n  }\n\n  async findRankingOpportunities() {\n    const analytics = await this.client.getSearchAnalytics({\n      startDate: '2025-01-01',\n      endDate: '2025-01-31',\n      dimensions: ['page', 'query'],\n      metrics: ['clicks', 'impressions', 'ctr', 'position'],\n      rowLimit: 1000\n    });\n\n    if (!analytics.success) return [];\n\n    const opportunities = analytics.data\n      .filter(row => row.position > 10 && row.position <= 20 && row.impressions > 50) // Page 2 rankings\n      .sort((a, b) => b.impressions - a.impressions)\n      .map(row => ({\n        page: row.page,\n        query: row.query,\n        position: row.position.toFixed(1),\n        impressions: row.impressions,\n        opportunity: 'Page 2 ranking with decent impressions - optimize for first page'\n      }));\n\n    return opportunities.slice(0, 20);\n  }\n}\n```\n\n### Site Health Monitoring\n\n```typescript\nclass SiteHealthMonitor {\n  constructor(private client: GSCClient) {}\n\n  async checkSiteHealth() {\n    const health = {\n      indexing: { status: 'unknown', issues: [] },\n      mobile: { status: 'unknown', issues: [] },\n      sitemaps: { status: 'unknown', issues: [] },\n      security: { status: 'unknown', issues: [] }\n    };\n\n    // Check for crawl errors\n    const crawlErrors = await this.client.getCrawlErrorsCount();\n    if (crawlErrors.success) {\n      const totalErrors = Object.values(crawlErrors.data).reduce((sum, count) => sum + count, 0);\n      health.indexing.status = totalErrors === 0 ? 'healthy' : 'issues';\n      if (totalErrors > 0) {\n        health.indexing.issues.push(`${totalErrors} crawl errors found`);\n      }\n    }\n\n    // Check mobile usability\n    const mobileIssues = await this.client.getMobileUsabilityIssues();\n    if (mobileIssues.success) {\n      health.mobile.status = mobileIssues.data.length === 0 ? 'healthy' : 'issues';\n      health.mobile.issues = mobileIssues.data.map(issue => issue.issueType);\n    }\n\n    // Check sitemaps\n    const sitemaps = await this.client.getSitemaps();\n    if (sitemaps.success) {\n      const hasErrors = sitemaps.data.some(sitemap => sitemap.errors && sitemap.errors > 0);\n      health.sitemaps.status = !hasErrors ? 'healthy' : 'issues';\n      if (hasErrors) {\n        health.sitemaps.issues = sitemaps.data\n          .filter(s => s.errors && s.errors > 0)\n          .map(s => `${s.path}: ${s.errors} errors`);\n      }\n    }\n\n    // Check for security issues\n    const messages = await this.client.getMessages();\n    if (messages.success) {\n      const securityMessages = messages.data.filter(m => \n        m.messageType === 'SECURITY_AND_MANUAL_ACTION'\n      );\n      health.security.status = securityMessages.length === 0 ? 'healthy' : 'critical';\n      health.security.issues = securityMessages.map(m => m.subject);\n    }\n\n    return health;\n  }\n}\n```\n\n## Integration Examples\n\n### SEO Dashboard Integration\n\n```typescript\nimport { GSCClient } from '@akson/api-gsc';\nimport { createDashboard } from './dashboard';\n\nasync function updateSEODashboard() {\n  const client = new GSCClient();\n  await client.authenticate();\n\n  const thirtyDaysAgo = new Date();\n  thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);\n\n  const performance = await client.getSearchAnalytics({\n    startDate: thirtyDaysAgo.toISOString().split('T')[0],\n    endDate: new Date().toISOString().split('T')[0],\n    dimensions: ['date'],\n    metrics: ['clicks', 'impressions', 'ctr', 'position']\n  });\n\n  if (performance.success) {\n    await createDashboard({\n      totalClicks: performance.data.reduce((sum, d) => sum + d.clicks, 0),\n      totalImpressions: performance.data.reduce((sum, d) => sum + d.impressions, 0),\n      avgCTR: (performance.data.reduce((sum, d) => sum + d.ctr, 0) / performance.data.length) * 100,\n      avgPosition: performance.data.reduce((sum, d) => sum + d.position, 0) / performance.data.length\n    });\n  }\n}\n```\n\n### Alert System\n\n```typescript\nclass SEOAlertSystem {\n  constructor(private client: GSCClient, private thresholds: any) {}\n\n  async checkAlerts() {\n    const alerts = [];\n\n    // Check for traffic drops\n    const lastWeek = await this.getWeeklyData(-1);\n    const previousWeek = await this.getWeeklyData(-2);\n\n    if (lastWeek && previousWeek) {\n      const trafficChange = ((lastWeek.clicks - previousWeek.clicks) / previousWeek.clicks) * 100;\n      \n      if (trafficChange < this.thresholds.trafficDropPercent) {\n        alerts.push({\n          type: 'traffic_drop',\n          severity: 'high',\n          message: `Traffic dropped by ${Math.abs(trafficChange).toFixed(1)}% last week`,\n          data: { lastWeek: lastWeek.clicks, previousWeek: previousWeek.clicks }\n        });\n      }\n    }\n\n    // Check for ranking drops\n    const rankingIssues = await this.checkRankingDrops();\n    alerts.push(...rankingIssues);\n\n    return alerts;\n  }\n\n  private async getWeeklyData(weeksAgo: number) {\n    const endDate = new Date();\n    endDate.setDate(endDate.getDate() + (weeksAgo * 7));\n    const startDate = new Date(endDate);\n    startDate.setDate(startDate.getDate() - 6);\n\n    const result = await this.client.getSummaryData(\n      startDate.toISOString().split('T')[0],\n      endDate.toISOString().split('T')[0]\n    );\n\n    return result.success ? result.data : null;\n  }\n}\n```\n\n## TypeScript Support\n\nFull TypeScript definitions included:\n\n```typescript\nimport type {\n  GSCSite,\n  GSCSearchAnalyticsResponse,\n  GSCPerformanceData,\n  GSCSitemap,\n  GSCUrlInspectionResponse,\n  GSCMobileUsabilityIssue,\n  GSCMessage,\n  GSCOperationResult,\n  GSCListResult\n} from '@akson/api-gsc';\n```\n\n## Requirements\n\n- Node.js ≥18.0.0\n- Google Search Console API access\n- Service account with Search Console permissions\n- Verified site ownership in Google Search Console\n\n## License\n\nMIT","readmeFilename":"README.md"}