check_service_status
Verify if an API service or platform is currently experiencing operational issues or downtime.
Instructions
Check if an API service or platform is experiencing issues.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| service | Yes | ||
| reasoning | Yes |
Implementation Reference
- src/searxng_mcp/server.py:1461-1499 (handler)The @mcp.tool()-decorated async function that serves as the MCP tool handler for 'check_service_status'. It invokes ServiceHealthChecker.check_service(service), formats the result as JSON, handles errors, and tracks usage.async def check_service_status( service: Annotated[str, "Service name (e.g., stripe, aws, github, openai)"], reasoning: Annotated[str, "Why you're checking service status"], ) -> str: """Check if an API service or platform is experiencing issues.""" import json start_time = time.time() success = False error_msg = None result = "" try: # Check service health status = await service_health_checker.check_service(service) result = json.dumps(status, indent=2, ensure_ascii=False) result = clamp_text(result, MAX_RESPONSE_CHARS) success = "error" not in status if not success: error_msg = status.get("error") except Exception as exc: error_msg = str(exc) result = f"Service health check failed for {service}: {exc}" finally: response_time = (time.time() - start_time) * 1000 tracker.track_usage( tool_name="check_service_status", reasoning=reasoning, parameters={"service": service}, response_time_ms=response_time, success=success, error_message=error_msg, response_size=len(result.encode("utf-8")), ) return result
- The core implementation logic in ServiceHealthChecker.check_service() method, which finds the service's status page URL, attempts API fetch/parse, falls back to HTML parsing or accessibility check, and formats the ServiceStatus response.async def check_service(self, service: str) -> dict: """Check service health status.""" # Find status page status_url = self.detector.find_status_page(service) if not status_url: return { "service": service, "status": "unknown", "status_emoji": "❓", "error": "Could not find status page for this service", "suggestion": f"Try checking {service}.com/status or searching for '{service} status page'", } # Strategy 1: Try Statuspage.io API (many services use this - it's more reliable) api_data = await self._fetch_statuspage_api(status_url) if api_data: status = self._parse_statuspage_api_response(api_data, service) status.status_page_url = status_url status.checked_at = datetime.utcnow().isoformat() + "Z" return self._format_status_response(status) # Strategy 2: Try crawling the page try: html = await self.crawler.fetch_raw(status_url, max_chars=200000) if html and len(html.strip()) > 100: # Parse status from HTML status = self.parser.parse_status_page(html, service) status.status_page_url = status_url status.checked_at = datetime.utcnow().isoformat() + "Z" return self._format_status_response(status) except Exception: pass # Fall through to HTTP check # Strategy 3: Fallback - just check if URL is accessible accessible, http_code = await self._check_url_accessible(status_url) if accessible: # Page is up but we couldn't parse it (likely JS-rendered) return { "service": service, "status": "unknown", "status_emoji": "❓", "status_page_url": status_url, "checked_at": datetime.utcnow().isoformat() + "Z", "message": "Status page is accessible but requires JavaScript to render. Please check manually.", "note": f"Visit {status_url} to see current status", } else: return { "service": service, "status": "unknown", "status_emoji": "❓", "status_page_url": status_url, "error": f"Status page returned HTTP {http_code}" if http_code else "Status page unreachable", } def _format_status_response(self, status: ServiceStatus) -> dict: """Format status object as response dict.""" response = { "service": status.service, "status": status.status, "status_emoji": self.parser.get_status_emoji(status.status), "status_page_url": status.status_page_url, "checked_at": status.checked_at, } if status.current_incidents: response["current_incidents"] = status.current_incidents else: response["current_incidents"] = [] response["message"] = "No active incidents reported" if status.components: response["components"] = [ {"name": comp.name, "status": comp.status} for comp in status.components[:10] ] return response
- ServiceStatus dataclass defining the structured output format returned by the health check, including status, incidents, components, etc.class ServiceStatus: """Overall service health status.""" service: str status: str status_page_url: str | None = None checked_at: str | None = None current_incidents: list[str] = field(default_factory=list) components: list[ServiceComponent] = field(default_factory=list)
- StatusPageDetector class that resolves service names to status page URLs using predefined mappings (KNOWN_STATUS_PAGES), aliases, and fallback patterns.class StatusPageDetector: """Detect and find status pages for services.""" # Known service → status page mappings KNOWN_STATUS_PAGES = { # Payment & Finance "stripe": "https://status.stripe.com", "paypal": "https://www.paypal-status.com", "plaid": "https://status.plaid.com", # Code & DevOps "github": "https://www.githubstatus.com", "gitlab": "https://status.gitlab.com", "bitbucket": "https://bitbucket.status.atlassian.com", "vercel": "https://www.vercel-status.com", "netlify": "https://www.netlifystatus.com", "heroku": "https://status.heroku.com", "docker": "https://status.docker.com", "dockerhub": "https://status.docker.com", "npm": "https://status.npmjs.org", "pypi": "https://status.python.org", "circleci": "https://status.circleci.com", # AI & ML Services "openai": "https://status.openai.com", "anthropic": "https://status.anthropic.com", "claude": "https://status.anthropic.com", "claudeapi": "https://status.anthropic.com", "anthropicclaudeapi": "https://status.anthropic.com", "gemini": "https://status.cloud.google.com", "googlegemini": "https://status.cloud.google.com", "googlegeminiapi": "https://status.cloud.google.com", "vertexai": "https://status.cloud.google.com", "googlecloudvertexai": "https://status.cloud.google.com", "googlecloud": "https://status.cloud.google.com", "replicate": "https://replicate.statuspage.io", "huggingface": "https://status.huggingface.co", "hf": "https://status.huggingface.co", "cohere": "https://status.cohere.com", "mistral": "https://status.mistral.ai", "mistralai": "https://status.mistral.ai", "together": "https://status.together.ai", "togetherai": "https://status.together.ai", "groq": "https://status.groq.com", "perplexity": "https://status.perplexity.ai", "perplexityai": "https://status.perplexity.ai", # Image/Video AI "fal": "https://fal.statuspage.io", "falai": "https://fal.statuspage.io", "midjourney": "https://status.midjourney.com", "stability": "https://status.stability.ai", "stabilityai": "https://status.stability.ai", "runway": "https://status.runwayml.com", "runwayml": "https://status.runwayml.com", "leonardo": "https://status.leonardo.ai", "leonardoai": "https://status.leonardo.ai", "ideogram": "https://status.ideogram.ai", "flux": "https://status.bfl.ml", "bfl": "https://status.bfl.ml", "blackforestlabs": "https://status.bfl.ml", "blackforestlabsbflfluxapi": "https://status.bfl.ml", "bflblackforestlabsfluxapi": "https://status.bfl.ml", # Voice/Audio AI "elevenlabs": "https://status.elevenlabs.io", "11labs": "https://status.elevenlabs.io", "resemble": "https://status.resemble.ai", "assemblyai": "https://status.assemblyai.com", "deepgram": "https://status.deepgram.com", # Video AI "heygen": "https://status.heygen.com", "descript": "https://status.descript.com", "luma": "https://status.lumalabs.ai", "lumalabs": "https://status.lumalabs.ai", "pika": "https://status.pika.art", "sync": "https://status.sync.so", "syncso": "https://status.sync.so", "synclabs": "https://status.sync.so", # Cloud Providers "aws": "https://health.aws.amazon.com/health/status", "amazon": "https://health.aws.amazon.com/health/status", "gcp": "https://status.cloud.google.com", "googlecloudplatform": "https://status.cloud.google.com", "azure": "https://status.azure.com", "microsoft": "https://status.azure.com", "digitalocean": "https://status.digitalocean.com", "linode": "https://status.linode.com", "vultr": "https://status.vultr.com", "render": "https://status.render.com", "railway": "https://railway.instatus.com", "fly": "https://status.fly.io", "flyio": "https://status.fly.io", # Databases "mongodb": "https://status.mongodb.com", "supabase": "https://status.supabase.com", "planetscale": "https://www.planetscalestatus.com", "neon": "https://neonstatus.com", "fauna": "https://status.fauna.com", "redis": "https://status.redis.com", "upstash": "https://status.upstash.com", "cockroachdb": "https://status.cockroachlabs.cloud", # Communication "twilio": "https://status.twilio.com", "sendgrid": "https://status.sendgrid.com", "mailgun": "https://status.mailgun.com", "postmark": "https://status.postmarkapp.com", "slack": "https://status.slack.com", "discord": "https://discordstatus.com", "zoom": "https://status.zoom.us", "intercom": "https://www.intercomstatus.com", # CDN & DNS "cloudflare": "https://www.cloudflarestatus.com", "fastly": "https://status.fastly.com", "akamai": "https://cloudharmony.com/status-for-akamai", # Auth & Identity "auth0": "https://status.auth0.com", "okta": "https://status.okta.com", "clerk": "https://status.clerk.com", # Analytics & Monitoring "datadog": "https://status.datadoghq.com", "newrelic": "https://status.newrelic.com", "sentry": "https://status.sentry.io", "mixpanel": "https://status.mixpanel.com", "amplitude": "https://status.amplitude.com", "segment": "https://status.segment.com", "posthog": "https://status.posthog.com", # Other "notion": "https://status.notion.so", "airtable": "https://status.airtable.com", "figma": "https://status.figma.com", "linear": "https://linearstatus.com", "jira": "https://jira-software.status.atlassian.com", "confluence": "https://confluence.status.atlassian.com", "atlassian": "https://status.atlassian.com", "shopify": "https://www.shopifystatus.com", "algolia": "https://status.algolia.com", "pinecone": "https://status.pinecone.io", "weaviate": "https://status.weaviate.io", "qdrant": "https://status.qdrant.io", "milvus": "https://status.milvus.io", } # Service name aliases - map variations to canonical names SERVICE_ALIASES = { # Anthropic/Claude variations "anthropic claude": "anthropic", "anthropic claude api": "anthropic", "claude api": "anthropic", "claude": "anthropic", # Google variations "google cloud": "gcp", "google cloud platform": "gcp", "google cloud vertex ai": "vertexai", "vertex ai": "vertexai", "google gemini": "gemini", "google gemini api": "gemini", "gemini api": "gemini", # Fal variations "fal.ai": "fal", "fal ai": "fal", "fal.ai api": "fal", # BFL/Flux variations "black forest labs": "bfl", "black forest labs flux": "bfl", "bfl flux": "bfl", "flux api": "bfl", "black forest labs bfl flux api": "bfl", "bfl black forest labs flux api": "bfl", # Sync variations "sync.so": "sync", "sync labs": "sync", # Other common variations "eleven labs": "elevenlabs", "stability ai": "stability", "runway ml": "runway", "leonardo ai": "leonardo", "hugging face": "huggingface", "together ai": "together", "mistral ai": "mistral", "perplexity ai": "perplexity", "luma labs": "luma", "fly.io": "fly", } # Common patterns to try STATUS_PAGE_PATTERNS = [ "https://status.{service}.com", "https://status.{service}.io", "https://status.{service}.ai", "https://{service}.statuspage.io", "https://{service}.instatus.com", "https://{service}status.com", "https://www.{service}status.com", "https://{service}.com/status", ] def normalize_service_name(self, service: str) -> str: """Normalize service name using aliases.""" # Clean up the input service_lower = service.lower().strip() # Check aliases first if service_lower in self.SERVICE_ALIASES: return self.SERVICE_ALIASES[service_lower] # Try partial matching for aliases for alias, canonical in self.SERVICE_ALIASES.items(): if alias in service_lower or service_lower in alias: return canonical # Remove common suffixes and clean up cleaned = service_lower for suffix in [" api", " status", " service"]: if cleaned.endswith(suffix): cleaned = cleaned[: -len(suffix)].strip() # Remove spaces, dots, dashes for lookup cleaned = cleaned.replace(" ", "").replace(".", "").replace("-", "") return cleaned def find_status_page(self, service: str) -> str | None: """Find status page URL for a service.""" # Normalize the service name normalized = self.normalize_service_name(service) # Check known mappings first if normalized in self.KNOWN_STATUS_PAGES: return self.KNOWN_STATUS_PAGES[normalized] # Also try the raw cleaned name (no alias resolution) raw_cleaned = service.lower().replace(" ", "").replace(".", "").replace("-", "") if raw_cleaned in self.KNOWN_STATUS_PAGES: return self.KNOWN_STATUS_PAGES[raw_cleaned] # Try common patterns with normalized name for pattern in self.STATUS_PAGE_PATTERNS: url = pattern.format(service=normalized) return url # Return first pattern to try return None
- src/searxng_mcp/server.py:43-43 (registration)Instantiation of ServiceHealthChecker with crawler_client, imported from .service_health, used by the tool handler.service_health_checker = ServiceHealthChecker(crawler_client)