Skip to main content
Glama
elad12390

Web Research Assistant

by elad12390

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
NameRequiredDescriptionDefault
serviceYes
reasoningYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • 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
  • Instantiation of ServiceHealthChecker with crawler_client, imported from .service_health, used by the tool handler.
    service_health_checker = ServiceHealthChecker(crawler_client)
Behavior2/5

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

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool checks for issues but doesn't explain how it determines status (e.g., via API calls, external monitoring), what the output includes, or any constraints like rate limits or authentication needs. This leaves significant gaps for a tool that likely interacts with external services.

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 a single, clear sentence that directly states the tool's purpose without unnecessary words. It's front-loaded and efficient, making it easy to parse quickly, which is ideal for conciseness.

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

Completeness3/5

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

Given the tool has an output schema (which should cover return values), the description's job is reduced. However, with no annotations and 0% schema coverage, it lacks details on behavior and parameters. For a simple status-check tool, the description is minimally adequate but incomplete, as it doesn't address how the check is performed or parameter usage.

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

Parameters2/5

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

Schema description coverage is 0%, so the schema provides no parameter details. The description doesn't add any meaning beyond the parameter names; it doesn't explain what 'service' should be (e.g., a service name, URL), what 'reasoning' is for, or provide examples. This fails to compensate for the low 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's purpose with a specific verb ('check') and resource ('API service or platform'), explaining it determines if there are issues. However, it doesn't differentiate from sibling tools like 'api_docs' or 'web_search', which might also provide status-related information in some contexts, so it misses full sibling distinction.

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?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention when-not scenarios, prerequisites, or compare to siblings like 'api_docs' for documentation or 'web_search' for broader status checks, leaving the agent without contextual usage cues.

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/elad12390/web-research-assistant'

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