Skip to main content
Glama
firetix

MCP Vulnerability Checker Server

by firetix

get_vulnerability_timeline

Retrieve vulnerability timeline and patch status information for a CVE ID to understand disclosure history, patch availability, and remediation guidance for effective security management.

Instructions

Get vulnerability timeline and patch status information

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
cve_idYesGet comprehensive timeline and patch status information for a vulnerability including publication dates, disclosure timeline, patch availability, vendor advisories, and remediation guidance. Provide a CVE ID in the format CVE-YYYY-NNNN (e.g., CVE-2021-44228). Essential for understanding vulnerability lifecycle and planning patch management strategies.

Implementation Reference

  • Core handler function that implements the tool's logic: validates input, fetches vulnerability data from NVD and MITRE, analyzes timeline and patch status, computes risk metrics, and returns a comprehensive formatted report.
    async def get_vulnerability_timeline(
        cve_id: str,
    ) -> List[types.TextContent | types.ImageContent | types.EmbeddedResource]:
        """
        Get vulnerability timeline including patch status and remediation information.
    
        Args:
            cve_id: CVE identifier in format CVE-YYYY-NNNN
    
        Returns:
            List of content containing timeline information or error messages
        """
        # Clean up CVE ID format
        cve_id = cve_id.upper().strip()
        if not cve_id.startswith("CVE-"):
            cve_id = f"CVE-{cve_id}"
    
        # Validate CVE ID format (CVE-YYYY-NNNN)
        if not re.match(r"^CVE-\d{4}-\d{4,}$", cve_id):
            return [
                types.TextContent(
                    type="text",
                    text=f"Error: Invalid CVE ID format. Expected format: CVE-YYYY-NNNN (e.g., CVE-2021-44228). Got: {cve_id}",
                )
            ]
    
        headers = {
            "User-Agent": "MCP Vulnerability Timeline Tool v1.0",
            "Accept": "application/json",
        }
    
        timeline_data = {}
    
        try:
            timeout = httpx.Timeout(20.0, connect=10.0)
            async with httpx.AsyncClient(
                follow_redirects=True, headers=headers, timeout=timeout
            ) as client:
                # Get comprehensive CVE data from NVD
                nvd_url = f"https://services.nvd.nist.gov/rest/json/cves/2.0?cveId={cve_id}"
                try:
                    nvd_response = await client.get(nvd_url)
                    if nvd_response.status_code == 200:
                        nvd_data = nvd_response.json()
                        if nvd_data.get("totalResults", 0) > 0:
                            cve_item = nvd_data["vulnerabilities"][0]["cve"]
    
                            # Extract key timeline dates
                            published_date = parse_date(cve_item.get("published", ""))
                            modified_date = parse_date(cve_item.get("lastModified", ""))
    
                            timeline_data["nvd_info"] = {
                                "published": published_date,
                                "last_modified": modified_date,
                                "descriptions": cve_item.get("descriptions", []),
                                "references": cve_item.get("references", []),
                                "cvss_scores": cve_item.get("metrics", {}),
                            }
    
                            # Analyze references for patch and vendor information
                            patch_refs = []
                            vendor_refs = []
                            advisory_refs = []
    
                            for ref in cve_item.get("references", []):
                                url = ref.get("url", "").lower()
                                tags = [tag.lower() for tag in ref.get("tags", [])]
    
                                # Categorize references
                                if any(tag in ["patch", "vendor advisory"] for tag in tags):
                                    if "patch" in tags:
                                        patch_refs.append(ref)
                                    if "vendor advisory" in tags:
                                        vendor_refs.append(ref)
                                elif any(
                                    keyword in url
                                    for keyword in [
                                        "security",
                                        "advisory",
                                        "bulletin",
                                        "patch",
                                        "update",
                                    ]
                                ):
                                    advisory_refs.append(ref)
    
                            timeline_data["patches"] = patch_refs
                            timeline_data["vendor_advisories"] = vendor_refs
                            timeline_data["security_advisories"] = advisory_refs
    
                except Exception as e:
                    timeline_data["nvd_error"] = str(e)
    
                # Try to get additional timeline info from MITRE
                try:
                    mitre_url = f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve_id}"
                    mitre_response = await client.get(mitre_url)
                    if mitre_response.status_code == 200:
                        content = mitre_response.text
    
                        # Look for timeline indicators in MITRE page
                        timeline_indicators = []
                        if "reserved" in content.lower():
                            timeline_indicators.append("CVE ID was reserved")
                        if "disputed" in content.lower():
                            timeline_indicators.append("Vulnerability is disputed")
                        if "rejected" in content.lower():
                            timeline_indicators.append("CVE was rejected")
    
                        timeline_data["mitre_indicators"] = timeline_indicators
    
                except Exception as e:
                    timeline_data["mitre_error"] = str(e)
    
        except Exception as e:
            return [
                types.TextContent(
                    type="text",
                    text=f"Error: Failed to fetch vulnerability timeline: {str(e)}",
                )
            ]
    
        # Generate timeline report
        if not timeline_data.get("nvd_info"):
            return [
                types.TextContent(
                    type="text",
                    text=f"No timeline information found for {cve_id}. The CVE might not exist or be publicly available yet.",
                )
            ]
    
        nvd_info = timeline_data["nvd_info"]
        published_date = nvd_info["published"]
        modified_date = nvd_info["last_modified"]
    
        # Calculate timeline metrics
        current_date = datetime.now()
        age_days = 0
        update_days = 0
    
        if published_date:
            age_days = (current_date - published_date).days
        if modified_date and published_date:
            update_days = (modified_date - published_date).days
    
        # Format the response
        result = f"⏰ **Vulnerability Timeline Report: {cve_id}**\n\n"
    
        # Timeline Overview
        result += "πŸ“Š **Timeline Overview:**\n"
        if published_date:
            result += f"   β€’ **Published:** {published_date.strftime('%Y-%m-%d')} ({age_days} days ago)\n"
        if modified_date:
            result += f"   β€’ **Last Modified:** {modified_date.strftime('%Y-%m-%d')} ({update_days} days after publication)\n"
        result += f"   β€’ **Age:** {age_days} days\n"
    
        # Age-based risk assessment
        if age_days > 365:
            age_risk = "🟒 STABLE"
            age_desc = "Well-established vulnerability with likely available patches"
        elif age_days > 90:
            age_risk = "🟑 MATURING"
            age_desc = "Patches likely available, check vendor advisories"
        elif age_days > 30:
            age_risk = "🟠 RECENT"
            age_desc = "Recent vulnerability, patches may be in development"
        else:
            age_risk = "πŸ”΄ NEW"
            age_desc = "Very recent vulnerability, patches may not be available yet"
    
        result += f"   β€’ **Maturity:** {age_risk} - {age_desc}\n\n"
    
        # Patch Information
        patches = timeline_data.get("patches", [])
        if patches:
            result += (
                f"πŸ”§ **Patch Information:** βœ… {len(patches)} patch reference(s) found\n"
            )
            for i, patch in enumerate(patches[:3], 1):  # Show first 3
                result += f"   {i}. {patch.get('url', 'N/A')}\n"
                if patch.get("source"):
                    result += f"      Source: {patch.get('source')}\n"
            if len(patches) > 3:
                result += f"   ... and {len(patches) - 3} more patch references\n"
        else:
            result += (
                "πŸ”§ **Patch Information:** ⚠️ No explicit patch references found in NVD\n"
            )
        result += "\n"
    
        # Vendor Advisories
        vendor_advisories = timeline_data.get("vendor_advisories", [])
        if vendor_advisories:
            result += f"🏭 **Vendor Advisories:** βœ… {len(vendor_advisories)} advisory(ies) found\n"
            for i, advisory in enumerate(vendor_advisories[:3], 1):
                result += f"   {i}. {advisory.get('url', 'N/A')}\n"
                if advisory.get("source"):
                    result += f"      Source: {advisory.get('source')}\n"
        else:
            result += (
                "🏭 **Vendor Advisories:** βšͺ No vendor advisories in NVD references\n"
            )
        result += "\n"
    
        # Security Advisories
        security_advisories = timeline_data.get("security_advisories", [])
        if security_advisories:
            result += f"πŸ“’ **Security Advisories:** βœ… {len(security_advisories)} security-related reference(s)\n"
            for i, advisory in enumerate(security_advisories[:3], 1):
                result += f"   {i}. {advisory.get('url', 'N/A')}\n"
        else:
            result += "πŸ“’ **Security Advisories:** βšͺ No security advisories detected\n"
        result += "\n"
    
        # MITRE Status Indicators
        mitre_indicators = timeline_data.get("mitre_indicators", [])
        if mitre_indicators:
            result += "πŸ›οΈ **MITRE Status:** ⚠️ Special status detected\n"
            for indicator in mitre_indicators:
                result += f"   β€’ {indicator}\n"
        else:
            result += "πŸ›οΈ **MITRE Status:** βœ… Standard active CVE\n"
        result += "\n"
    
        # Remediation Timeline Estimate
        result += "πŸ› οΈ **Remediation Timeline Guidance:**\n"
    
        if patches:
            result += "   β€’ **Patch Status:** βœ… Patches appear to be available\n"
            result += "   β€’ **Action:** Verify and apply vendor patches immediately\n"
            result += "   β€’ **Timeline:** Patch within 24-72 hours for critical systems\n"
        elif vendor_advisories:
            result += "   β€’ **Patch Status:** 🟑 Vendor acknowledgment found\n"
            result += "   β€’ **Action:** Monitor vendor channels for patch release\n"
            result += "   β€’ **Timeline:** Patches typically released within 30-90 days\n"
        elif age_days > 90:
            result += "   β€’ **Patch Status:** ⚠️ Patches may be available from vendors\n"
            result += "   β€’ **Action:** Check vendor security pages directly\n"
            result += "   β€’ **Timeline:** Manual verification required\n"
        else:
            result += "   β€’ **Patch Status:** πŸ”΄ Patches likely not available yet\n"
            result += "   β€’ **Action:** Implement workarounds and monitor for updates\n"
            result += "   β€’ **Timeline:** Monitor for next 30-60 days\n"
    
        result += "\nπŸ“‹ **Next Steps:**\n"
        result += "   1. πŸ” **Verify Impact:** Check if your systems are affected\n"
        result += "   2. πŸ›‘οΈ **Implement Workarounds:** If patches unavailable\n"
        result += "   3. πŸ“Š **Monitor Updates:** Set up alerts for patch releases\n"
        result += "   4. 🎯 **Apply Patches:** Test and deploy when available\n"
        result += "   5. πŸ”’ **Validate Fix:** Confirm vulnerability is resolved\n\n"
    
        result += "πŸ”— **Useful Resources:**\n"
        result += f"   β€’ NVD CVE Page: https://nvd.nist.gov/vuln/detail/{cve_id}\n"
        result += f"   β€’ MITRE CVE Page: https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve_id}\n"
        result += "   β€’ Vendor Security Pages: Check affected product vendors\n\n"
    
        result += f"πŸ“Š **Report Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
    
        return [types.TextContent(type="text", text=result)]
  • Registers the tool with the MCP server in list_tools(), defining name, description, and input schema requiring 'cve_id'.
    types.Tool(
        name="get_vulnerability_timeline",
        description="Get vulnerability timeline and patch status information",
        inputSchema={
            "type": "object",
            "required": ["cve_id"],
            "properties": {
                "cve_id": {
                    "type": "string",
                    "description": timeline_description,
                }
            },
        },
    ),
  • Defines the input schema for the tool: object with required 'cve_id' string parameter.
    inputSchema={
        "type": "object",
        "required": ["cve_id"],
        "properties": {
            "cve_id": {
                "type": "string",
                "description": timeline_description,
            }
        },
    },
  • Dispatch handler in @app.call_tool() that validates arguments and calls the tool implementation.
    elif name == "get_vulnerability_timeline":
        if "cve_id" not in arguments:
            return [
                types.TextContent(
                    type="text", text="Error: Missing required argument 'cve_id'"
                )
            ]
        return await get_vulnerability_timeline(arguments["cve_id"])
  • Helper function to parse various date formats from vulnerability data sources.
    def parse_date(date_str: str) -> Optional[datetime]:
        """Parse various date formats commonly found in vulnerability data."""
        if not date_str:
            return None
    
        # Remove timezone info for simplicity
        date_str = date_str.replace("Z", "").replace("+00:00", "")
    
        try:
            # Try ISO format first
            if "T" in date_str:
                return datetime.fromisoformat(date_str.split("T")[0])
            else:
                return datetime.fromisoformat(date_str)
        except (ValueError, TypeError):
            # Try other common formats
            for fmt in ["%Y-%m-%d", "%Y/%m/%d", "%m/%d/%Y", "%d/%m/%Y"]:
                try:
                    return datetime.strptime(date_str, fmt)
                except (ValueError, TypeError):
                    continue
        return None

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/firetix/vulnerability-intelligence-mcp-server'

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