{
    "serverInfo": {
        "name": "SiteGuru",
        "title": "SiteGuru SEO",
        "version": "1.0.0"
    },
    "protocolVersion": "2025-11-25",
    "description": "Access your SiteGuru SEO data: site audits, Google Search Console performance, keywords, competitor analyses and Shopify insights.",
    "documentationUrl": "https://mcp.siteguru.co",
    "transport": {
        "type": "streamable-http",
        "endpoint": "https://mcp.siteguru.co/mcp"
    },
    "capabilities": {
        "tools": {
            "listChanged": false
        }
    },
    "authentication": {
        "type": "oauth2",
        "authorizationServer": "https://mcp.siteguru.co",
        "note": "The MCP endpoint requires an OAuth access token or a SiteGuru access key. Tool discovery is public; every tool call is authenticated."
    },
    "tools": [
        {
            "name": "list_sites",
            "title": "List Sites",
            "description": "List the SiteGuru sites you can access, by domain. Call this first to discover which sites are available; the other tools reference a site by its domain. Each site includes a health_score (the passing percentage of its SEO checks, same as the SiteGuru dashboard — higher is healthier), so you can compare sites and answer \"which site needs the most attention\" (lowest score) without another call; a site with no completed audit yet has health_score null and audit_status \"pending\". Each site also includes available_ranges (the cached Search Console periods, with concrete dates) and default_range — pass one of those range values to the performance tools for an instant cache hit. Each site also has a data_sources object showing which sources are connected — search_console and analytics (Google Analytics 4) — each with `connected` and, when connected, a headline number for the default period (search_console.clicks, analytics.sessions). About half of sites are Search Console only (analytics.connected false), so check this before promising traffic or revenue figures, and don't call the analytics-backed parts for sites without it.",
            "inputSchema": {
                "properties": {
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object"
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_todo_list",
            "title": "Get Todo List",
            "description": "Get the prioritized SEO to-do list for a site — the most important issues to fix, grouped into technical, content and opportunity categories. Pass the site's domain (from list_sites). Each item has a severity and a report_url; include the report_url as a link so the user can open the full report in SiteGuru.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_seo_report",
            "title": "Get Seo Report",
            "description": "Get an SEO report for a site. Reports: indexation, page titles, meta descriptions, headings, image alt texts, structured data, canonical urls, page speed, og tags, orphan pages, hreflangs, broken links, redirected urls, error pages, sitemap, lorem ipsum, backlinks, site-wide checks. Common aliases work (e.g. \"h1\", \"alt tags\", \"title\", \"schema\", \"og\", \"orphans\", \"hreflang\", \"sitemaps\", \"lorem\", \"dummy content\", \"redirects\", \"inbound links\"). Pass the site domain (from list_sites), the report name, and a \"mode\": \"summary\" (default) returns counts + severity; \"issues\" returns per-page rows for the problem pages only — use for \"fix my X\", \"what's broken\"; \"all\" returns per-page rows for EVERY page with its status — use whenever the user asks for \"all my X\", \"every X\", \"list all my X\", \"show me all\", or any audit/inventory intent. If the user says \"all X\" they almost always want mode=\"all\" — do not silently filter to issues. Most page-level reports support all three modes. **site-wide checks** is summary-only — it has no per-page rows. **broken links** and **redirected urls** only check internal links (links between pages on the same site); external links are not validated. **error pages** lists pages that themselves return a 4xx/5xx (or failed to load) — distinct from broken links (internal links pointing at a 404); summary + issues only. **sitemap** supports summary + issues only; its payload is structured (sitemap files list, plus pages_missing_from_sitemap, redirected_urls_in_sitemap, and error_urls_in_sitemap arrays in issues mode) — not the usual `items` shape. **backlinks** is only available on plans that include DataForSEO backlink data; data refreshes weekly (anything > 6 weeks old is reported as missing with a refresh prompt). The **indexation** report's per-page rows include each page's indexability status (indexable / noindex meta tag / noindex header / blocked by robots.txt) and, when GSC is connected, Google's coverage state. Each summary response advertises its `available_modes`. mode=issues/all results are paginated: read total + has_more and call again with the returned next_offset, or narrow a large site with path_contains (e.g. \"/blog/\").",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "report": {
                        "description": "Which report to fetch — one of: \"indexation\", \"page titles\", \"meta descriptions\", \"headings\", \"image alt texts\", \"structured data\", \"canonical urls\", \"page speed\", \"og tags\", \"orphan pages\", \"hreflangs\", \"broken links\", \"redirected urls\", \"error pages\", \"sitemap\", \"lorem ipsum\", \"backlinks\", \"site-wide checks\". Aliases like \"h1\", \"alt tags\", \"title\", \"schema\", \"og\", \"orphans\", \"hreflang\", \"sitemaps\", \"lorem\", \"dummy content\", \"redirects\", \"inbound links\", \"4xx\" are accepted. Note: \"error pages\" lists pages that THEMSELVES return a 4xx/5xx (or fail to load) — different from \"broken links\" (internal links pointing AT a 404) and from the \"sitemap\" report's error URLs (only entries listed in the sitemap).",
                        "type": "string"
                    },
                    "mode": {
                        "description": "What to return — one of \"summary\", \"issues\", \"all\". \"summary\" (default): counts + severity, no pages. Perfect for \"how many\", \"is anything wrong\", a quick overview. \"issues\": per-page rows for ONLY the problem pages, each with url, page title, per-page status and the specifics. Use for \"fix my X\", \"what's broken\", \"what's wrong with my Y\", \"show me the bad ones\". \"all\": per-page rows for EVERY page with its status (passing pages included). Use whenever the user wants the complete inventory — \"all my titles\", \"every page\", \"list all my meta descriptions\", \"show me all\", \"every X regardless of issues\", any audit/inventory intent. If the user says \"all X\" they almost always want mode=\"all\", not \"issues\". Each report's summary response advertises which modes it supports in `available_modes`.",
                        "type": "string"
                    },
                    "limit": {
                        "description": "mode=issues/all only: max pages to return (default 100, max 200).",
                        "type": "integer"
                    },
                    "offset": {
                        "description": "mode=issues/all only: number of pages to skip, for pagination. Use the next_offset from the previous response.",
                        "type": "integer"
                    },
                    "path_contains": {
                        "description": "mode=issues/all only: return only pages whose URL contains this substring (e.g. \"/blog/\"). Useful to focus on a section of a large site.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site",
                    "report"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_page_report",
            "title": "Get Page Report",
            "description": "Get the full SEO audit for a single page on a site. Returns the page's actual content (title, first H1, meta description, canonical URL, page speed score), its indexation status (including Google's coverage state when GSC is connected), and the list of issues (warnings + suggestions) and compliments from every check the audit ran. Use this when the user asks about one specific page — e.g. \"what's wrong with my homepage\", \"audit /about\", \"list everything on this page\". For check-by-check rollups across many pages, use get_seo_report instead. Modes: \"summary\" (default) returns the page block + counts only; \"issues\" adds the warnings + suggestions list; \"all\" also adds the compliments.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "page": {
                        "description": "The page on the site to audit. Accepts a full URL (\"https://example.com/about\"), an absolute path (\"/about\"), or just a path segment (\"about\"). Trailing slashes are ignored. Use \"homepage\" or \"/\" or the bare site URL for the home page.",
                        "type": "string"
                    },
                    "mode": {
                        "description": "What to return — one of \"summary\", \"issues\", \"all\". \"summary\" (default): the page block + warning/suggestion/compliment counts, no per-check details. \"issues\": adds an `issues` array with every warning and suggestion check (title, description, severity). \"all\": adds both the `issues` array AND a `compliments` array, so the agent can also tell the user what's working.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site",
                    "page"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "find_pages",
            "title": "Find Pages",
            "description": "List and search a site's pages by SEO health — to find a specific page or surface the worst-scoring ones. Returns each page's path, title, type and a health_score (the passing percentage of its on-page checks, same as the SiteGuru dashboard — higher is healthier), plus the previous score for a trend. Worst-first by default, so \"find my worst pages\" is one call. Use search to find a specific page by path or title (e.g. \"pricing\") or to scope to a whole section (e.g. \"/seo-academy\"), sort to order by \"worst\" or \"best\", and limit to cap results. Results are paginated: the response carries a `pagination` block with the total match count and a `next_offset` — to read a whole section, keep calling with offset = next_offset until has_more is false. Pass the site domain from list_sites. Follow up on any page with get_page_report.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain (from list_sites), e.g. \"example.com\". With or without www/https:// — matching ignores them.",
                        "type": "string"
                    },
                    "search": {
                        "description": "Optional. Return only pages whose path or title contains this text — use it to locate a specific page like \"pricing\" or \"/about\".",
                        "type": "string"
                    },
                    "sort": {
                        "description": "Order of results: \"worst\" (default) lists the pages with the most issues first; \"best\" lists the healthiest pages first.",
                        "type": "string"
                    },
                    "limit": {
                        "description": "Optional. Page size — maximum number of pages to return in one call (default 75, max 200).",
                        "type": "integer"
                    },
                    "offset": {
                        "description": "Optional. Number of pages to skip before returning results (default 0). To page through a section, pass the `next_offset` from the previous response's `pagination` block; stop when has_more is false.",
                        "type": "integer"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_traffic_overview",
            "title": "Get Traffic Overview",
            "description": "Get a traffic overview for a site and date range from its two data sources, plus its top pages and top search queries. The payload has a `search_console` block (Google Search Console: clicks, impressions, CTR) and an `analytics` block (Google Analytics 4: sessions, users, and revenue for ecommerce sites) — each metric carries the change vs the comparison period, and each block has its own `status`. The `analytics` status is \"ok\" with the metrics, \"not_connected\" if the site has no Google Analytics 4 connection (about half of sites are Search Console only), or \"not_fetched\" if GA data hasn't been pulled for this date range yet — report the Search Console figures regardless. Pass the site domain (from list_sites); omit the dates to use the user's default period. Each top page includes its path so you can follow up with get_page_report.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "range": {
                        "description": "PREFERRED. A named period guaranteed to be cached — typically \"last_7_days\", \"last_30_days\" or \"last_90_days\" (use the exact `range` values listed in this site's available_ranges, from list_sites or a previous response). This always resolves to an instant cache hit, so use it instead of computing start/end yourself whenever the user wants a standard recent window. If you pass range, omit start and end.",
                        "type": "string"
                    },
                    "start": {
                        "description": "Start of a CUSTOM period, as YYYY-MM-DD. Only use start/end for a window not offered in available_ranges; otherwise prefer range. Do not invent dates for \"last 30 days\" — use range, or copy the exact start/end from an available_ranges entry. Omit start, end and range to use the user's default period (also a guaranteed hit).",
                        "type": "string"
                    },
                    "end": {
                        "description": "End of a custom period, as YYYY-MM-DD. Search Console lags a few days, so the most recent usable end is what available_ranges reports — don't assume today. Required whenever start is given.",
                        "type": "string"
                    },
                    "compare_start": {
                        "description": "Optional comparison-period start (YYYY-MM-DD), e.g. for year-over-year. If omitted, the immediately preceding period of the same length is used. Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "compare_end": {
                        "description": "Optional comparison-period end (YYYY-MM-DD). Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_traffic_sources",
            "title": "Get Traffic Sources",
            "description": "Get the site's main traffic sources from Google Analytics 4 for a date range — the channels (e.g. Organic Search, Direct, Referral, Paid Search, Organic Social) sending sessions, largest first. Each source has its sessions and a share_pct (its percentage of total sessions), plus revenue for ecommerce sites; sessions and revenue each carry value, previous and the change vs the comparison period, and the response includes total_sessions. This is Google Analytics data, so analytics_status is \"ok\" with the sources, \"not_connected\" if the site has no Google Analytics 4 connection (about half of sites are Search Console only), or \"not_fetched\" if GA data has not been pulled for this date range yet. Pass the site domain (from list_sites); omit the dates to use the user's default period.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "range": {
                        "description": "PREFERRED. A named period guaranteed to be cached — typically \"last_7_days\", \"last_30_days\" or \"last_90_days\" (use the exact `range` values listed in this site's available_ranges, from list_sites or a previous response). This always resolves to an instant cache hit, so use it instead of computing start/end yourself whenever the user wants a standard recent window. If you pass range, omit start and end.",
                        "type": "string"
                    },
                    "start": {
                        "description": "Start of a CUSTOM period, as YYYY-MM-DD. Only use start/end for a window not offered in available_ranges; otherwise prefer range. Do not invent dates for \"last 30 days\" — use range, or copy the exact start/end from an available_ranges entry. Omit start, end and range to use the user's default period (also a guaranteed hit).",
                        "type": "string"
                    },
                    "end": {
                        "description": "End of a custom period, as YYYY-MM-DD. Search Console lags a few days, so the most recent usable end is what available_ranges reports — don't assume today. Required whenever start is given.",
                        "type": "string"
                    },
                    "compare_start": {
                        "description": "Optional comparison-period start (YYYY-MM-DD), e.g. for year-over-year. If omitted, the immediately preceding period of the same length is used. Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "compare_end": {
                        "description": "Optional comparison-period end (YYYY-MM-DD). Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_top_keywords",
            "title": "Get Top Keywords",
            "description": "Get the site's top 100 Google Search Console keywords for a date range — clicks, impressions, CTR and average position, each with the change vs the comparison period. Pass the site domain (from list_sites); omit the dates to use the user's default period. Note on average position: `avg_position.change` is the absolute change in Google ranking (current minus previous), NOT a percentage. Position 1 is the top of Google, so a NEGATIVE change means the keyword moved UP and improved, and a positive change means it dropped — describe it that way to the user.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "range": {
                        "description": "PREFERRED. A named period guaranteed to be cached — typically \"last_7_days\", \"last_30_days\" or \"last_90_days\" (use the exact `range` values listed in this site's available_ranges, from list_sites or a previous response). This always resolves to an instant cache hit, so use it instead of computing start/end yourself whenever the user wants a standard recent window. If you pass range, omit start and end.",
                        "type": "string"
                    },
                    "start": {
                        "description": "Start of a CUSTOM period, as YYYY-MM-DD. Only use start/end for a window not offered in available_ranges; otherwise prefer range. Do not invent dates for \"last 30 days\" — use range, or copy the exact start/end from an available_ranges entry. Omit start, end and range to use the user's default period (also a guaranteed hit).",
                        "type": "string"
                    },
                    "end": {
                        "description": "End of a custom period, as YYYY-MM-DD. Search Console lags a few days, so the most recent usable end is what available_ranges reports — don't assume today. Required whenever start is given.",
                        "type": "string"
                    },
                    "compare_start": {
                        "description": "Optional comparison-period start (YYYY-MM-DD), e.g. for year-over-year. If omitted, the immediately preceding period of the same length is used. Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "compare_end": {
                        "description": "Optional comparison-period end (YYYY-MM-DD). Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_top_pages",
            "title": "Get Top Pages",
            "description": "Get the site's top pages by Google Search Console clicks for a date range — each with clicks, impressions and average position (each with the change vs the comparison period), plus the single keyword that page wins the most clicks on (its top_keyword, with full metrics). When the site has Google Analytics 4 connected, each page also includes pageviews (GA4, with the change vs the comparison period); GSC-only sites omit it. Returns the top 50 by default; results are paginated, so the response carries a `pagination` block with the total page count and a `next_offset` — to walk deeper than the first page, keep calling with offset = next_offset until has_more is false (use limit up to 200 to fetch more per call). Use it to see which pages drive the most search traffic and what query each one ranks for; follow up on any page with get_page_report. Pass the site domain (from list_sites); omit the dates to use the user's default period. For average position, `avg_position.change` is the absolute move in Google ranking (current minus previous) — a NEGATIVE change means the page or keyword moved UP and improved, not a percentage.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "range": {
                        "description": "PREFERRED. A named period guaranteed to be cached — typically \"last_7_days\", \"last_30_days\" or \"last_90_days\" (use the exact `range` values listed in this site's available_ranges, from list_sites or a previous response). This always resolves to an instant cache hit, so use it instead of computing start/end yourself whenever the user wants a standard recent window. If you pass range, omit start and end.",
                        "type": "string"
                    },
                    "start": {
                        "description": "Start of a CUSTOM period, as YYYY-MM-DD. Only use start/end for a window not offered in available_ranges; otherwise prefer range. Do not invent dates for \"last 30 days\" — use range, or copy the exact start/end from an available_ranges entry. Omit start, end and range to use the user's default period (also a guaranteed hit).",
                        "type": "string"
                    },
                    "end": {
                        "description": "End of a custom period, as YYYY-MM-DD. Search Console lags a few days, so the most recent usable end is what available_ranges reports — don't assume today. Required whenever start is given.",
                        "type": "string"
                    },
                    "compare_start": {
                        "description": "Optional comparison-period start (YYYY-MM-DD), e.g. for year-over-year. If omitted, the immediately preceding period of the same length is used. Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "compare_end": {
                        "description": "Optional comparison-period end (YYYY-MM-DD). Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "limit": {
                        "description": "Optional. Page size — number of pages to return in one call (default 50, max 200).",
                        "type": "integer"
                    },
                    "offset": {
                        "description": "Optional. Number of pages to skip before returning results (default 0). To page deeper, pass the `next_offset` from the previous response's `pagination` block; stop when has_more is false.",
                        "type": "integer"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_focus_keywords",
            "title": "Get Focus Keywords",
            "description": "Get the site's focus keywords with how they are performing for a date range — the keywords the user has chosen to track and rank for. Each focus keyword has a target page plus its clicks, impressions and average Google position (each with the change vs the comparison period). For average position, `avg_position.change` is the absolute move in ranking (current minus previous) — a NEGATIVE change means it moved UP and improved, not a percentage. Pass the site domain (from list_sites); omit the dates to use the user's default period. Set include_actions=true only when the user asks how to improve or what to do for a focus keyword: it adds optimization_actions (a short title + status per on-page area) for each one.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "range": {
                        "description": "PREFERRED. A named period guaranteed to be cached — typically \"last_7_days\", \"last_30_days\" or \"last_90_days\" (use the exact `range` values listed in this site's available_ranges, from list_sites or a previous response). This always resolves to an instant cache hit, so use it instead of computing start/end yourself whenever the user wants a standard recent window. If you pass range, omit start and end.",
                        "type": "string"
                    },
                    "start": {
                        "description": "Start of a CUSTOM period, as YYYY-MM-DD. Only use start/end for a window not offered in available_ranges; otherwise prefer range. Do not invent dates for \"last 30 days\" — use range, or copy the exact start/end from an available_ranges entry. Omit start, end and range to use the user's default period (also a guaranteed hit).",
                        "type": "string"
                    },
                    "end": {
                        "description": "End of a custom period, as YYYY-MM-DD. Search Console lags a few days, so the most recent usable end is what available_ranges reports — don't assume today. Required whenever start is given.",
                        "type": "string"
                    },
                    "compare_start": {
                        "description": "Optional comparison-period start (YYYY-MM-DD), e.g. for year-over-year. If omitted, the immediately preceding period of the same length is used. Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "compare_end": {
                        "description": "Optional comparison-period end (YYYY-MM-DD). Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "include_actions": {
                        "description": "Optional, default false. When true, add optimization_actions — a distilled page-optimization checklist (a short action title plus status per on-page area: title, headings, meta description, links, page health, word count) for each focus keyword. Use it for \"how do I improve my focus keywords / what should I do\"; leave it off for a plain performance read.",
                        "type": "boolean"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_search_topics",
            "title": "Get Search Topics",
            "description": "Get the site's Search Topics (also known as keyword clusters) — each Search Topic groups related keywords the site ranks for. Called WITHOUT a topic, it lists every Search Topic with its combined clicks, impressions, CTR and average Google position (each with the change vs the comparison period), its keyword_count and search volume, and a preview of its top keywords. Pass topic=\"<name>\" (a name from the list) to drill into ONE Search Topic and get its full keyword list, each keyword with the same metrics plus its search volume. For average position, `avg_position.change` is the absolute move in ranking (current minus previous, NEGATIVE = moved up and improved); a position is null when the topic or keyword is not ranking. Search volume is shown only when already known in SiteGuru — it is never fetched on demand, and search_volume.complete tells you whether every keyword had a known volume. Pass the site domain (from list_sites); omit the dates to use the user's default period.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "range": {
                        "description": "PREFERRED. A named period guaranteed to be cached — typically \"last_7_days\", \"last_30_days\" or \"last_90_days\" (use the exact `range` values listed in this site's available_ranges, from list_sites or a previous response). This always resolves to an instant cache hit, so use it instead of computing start/end yourself whenever the user wants a standard recent window. If you pass range, omit start and end.",
                        "type": "string"
                    },
                    "start": {
                        "description": "Start of a CUSTOM period, as YYYY-MM-DD. Only use start/end for a window not offered in available_ranges; otherwise prefer range. Do not invent dates for \"last 30 days\" — use range, or copy the exact start/end from an available_ranges entry. Omit start, end and range to use the user's default period (also a guaranteed hit).",
                        "type": "string"
                    },
                    "end": {
                        "description": "End of a custom period, as YYYY-MM-DD. Search Console lags a few days, so the most recent usable end is what available_ranges reports — don't assume today. Required whenever start is given.",
                        "type": "string"
                    },
                    "compare_start": {
                        "description": "Optional comparison-period start (YYYY-MM-DD), e.g. for year-over-year. If omitted, the immediately preceding period of the same length is used. Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "compare_end": {
                        "description": "Optional comparison-period end (YYYY-MM-DD). Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "topic": {
                        "description": "Optional. The exact name of a Search Topic (from a previous list call) to drill into — returns that one topic with its full keyword list. Omit to list all Search Topics with their combined KPIs and a top-keyword preview.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_keyword_cannibalization",
            "title": "Get Keyword Cannibalization",
            "description": "Find keyword cannibalization for a site and date range — search queries where two or more of the site's own pages compete for the same keyword (also called competing pages). This can split clicks between pages and hold rankings back. Each result is a keyword with its combined clicks, impressions and average Google position (each with the change vs the comparison period) and a competing_pages list — every competing page with its own clicks, impressions and position. Only keywords with real traffic are included (each page must take a meaningful share of the keyword's clicks), and hreflang / localized alternates are not counted as cannibalization. For average position, `avg_position.change` is the absolute move in ranking (current minus previous, NEGATIVE = moved up), not a percentage. Pass the site domain (from list_sites); omit the dates to use the user's default period.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "range": {
                        "description": "PREFERRED. A named period guaranteed to be cached — typically \"last_7_days\", \"last_30_days\" or \"last_90_days\" (use the exact `range` values listed in this site's available_ranges, from list_sites or a previous response). This always resolves to an instant cache hit, so use it instead of computing start/end yourself whenever the user wants a standard recent window. If you pass range, omit start and end.",
                        "type": "string"
                    },
                    "start": {
                        "description": "Start of a CUSTOM period, as YYYY-MM-DD. Only use start/end for a window not offered in available_ranges; otherwise prefer range. Do not invent dates for \"last 30 days\" — use range, or copy the exact start/end from an available_ranges entry. Omit start, end and range to use the user's default period (also a guaranteed hit).",
                        "type": "string"
                    },
                    "end": {
                        "description": "End of a custom period, as YYYY-MM-DD. Search Console lags a few days, so the most recent usable end is what available_ranges reports — don't assume today. Required whenever start is given.",
                        "type": "string"
                    },
                    "compare_start": {
                        "description": "Optional comparison-period start (YYYY-MM-DD), e.g. for year-over-year. If omitted, the immediately preceding period of the same length is used. Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "compare_end": {
                        "description": "Optional comparison-period end (YYYY-MM-DD). Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_new_keywords",
            "title": "Get New Keywords",
            "description": "Get the site's new Google Search Console keywords for a date range — queries it has started ranking for this period (now in roughly the top 30 with real impressions) that it was not ranking for in the comparison period. Each keyword includes its clicks, impressions and current average position; because the keyword is new, its previous position is null. Up to 50 are returned, the ones with the most clicks first. For average position, `avg_position.change` is the absolute move in ranking (negative = up) and is null here since there is no previous ranking. Pass the site domain (from list_sites); omit the dates to use the user's default period.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "range": {
                        "description": "PREFERRED. A named period guaranteed to be cached — typically \"last_7_days\", \"last_30_days\" or \"last_90_days\" (use the exact `range` values listed in this site's available_ranges, from list_sites or a previous response). This always resolves to an instant cache hit, so use it instead of computing start/end yourself whenever the user wants a standard recent window. If you pass range, omit start and end.",
                        "type": "string"
                    },
                    "start": {
                        "description": "Start of a CUSTOM period, as YYYY-MM-DD. Only use start/end for a window not offered in available_ranges; otherwise prefer range. Do not invent dates for \"last 30 days\" — use range, or copy the exact start/end from an available_ranges entry. Omit start, end and range to use the user's default period (also a guaranteed hit).",
                        "type": "string"
                    },
                    "end": {
                        "description": "End of a custom period, as YYYY-MM-DD. Search Console lags a few days, so the most recent usable end is what available_ranges reports — don't assume today. Required whenever start is given.",
                        "type": "string"
                    },
                    "compare_start": {
                        "description": "Optional comparison-period start (YYYY-MM-DD), e.g. for year-over-year. If omitted, the immediately preceding period of the same length is used. Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "compare_end": {
                        "description": "Optional comparison-period end (YYYY-MM-DD). Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_lost_keywords",
            "title": "Get Lost Keywords",
            "description": "Get the site's lost Google Search Console keywords for a date range — queries that used to rank in the top 30 (with more than 5 impressions in the comparison period) but have since fallen out of the top 30 (now ranking beyond position 30, or no longer ranking at all). It is the mirror of get_new_keywords. Each keyword includes its current and previous clicks, impressions and average position; the current position is null when the keyword no longer ranks. For average position, `avg_position.change` is the absolute move in ranking (current minus previous) and is positive because the keyword fell. Up to 50 are returned, the ones with the most prior traffic first. Pass the site domain (from list_sites); omit the dates to use the user's default period.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "range": {
                        "description": "PREFERRED. A named period guaranteed to be cached — typically \"last_7_days\", \"last_30_days\" or \"last_90_days\" (use the exact `range` values listed in this site's available_ranges, from list_sites or a previous response). This always resolves to an instant cache hit, so use it instead of computing start/end yourself whenever the user wants a standard recent window. If you pass range, omit start and end.",
                        "type": "string"
                    },
                    "start": {
                        "description": "Start of a CUSTOM period, as YYYY-MM-DD. Only use start/end for a window not offered in available_ranges; otherwise prefer range. Do not invent dates for \"last 30 days\" — use range, or copy the exact start/end from an available_ranges entry. Omit start, end and range to use the user's default period (also a guaranteed hit).",
                        "type": "string"
                    },
                    "end": {
                        "description": "End of a custom period, as YYYY-MM-DD. Search Console lags a few days, so the most recent usable end is what available_ranges reports — don't assume today. Required whenever start is given.",
                        "type": "string"
                    },
                    "compare_start": {
                        "description": "Optional comparison-period start (YYYY-MM-DD), e.g. for year-over-year. If omitted, the immediately preceding period of the same length is used. Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "compare_end": {
                        "description": "Optional comparison-period end (YYYY-MM-DD). Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_low_hanging_fruit",
            "title": "Get Low Hanging Fruit",
            "description": "Get up to 20 low-hanging-fruit keywords for a site and date range — queries already getting impressions that rank just below the click-winning positions, i.e. the quickest opportunities to improve. Each item includes the page path so you can follow up with get_page_report. Pass the site domain (from list_sites); omit the dates to use the user's default period.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "range": {
                        "description": "PREFERRED. A named period guaranteed to be cached — typically \"last_7_days\", \"last_30_days\" or \"last_90_days\" (use the exact `range` values listed in this site's available_ranges, from list_sites or a previous response). This always resolves to an instant cache hit, so use it instead of computing start/end yourself whenever the user wants a standard recent window. If you pass range, omit start and end.",
                        "type": "string"
                    },
                    "start": {
                        "description": "Start of a CUSTOM period, as YYYY-MM-DD. Only use start/end for a window not offered in available_ranges; otherwise prefer range. Do not invent dates for \"last 30 days\" — use range, or copy the exact start/end from an available_ranges entry. Omit start, end and range to use the user's default period (also a guaranteed hit).",
                        "type": "string"
                    },
                    "end": {
                        "description": "End of a custom period, as YYYY-MM-DD. Search Console lags a few days, so the most recent usable end is what available_ranges reports — don't assume today. Required whenever start is given.",
                        "type": "string"
                    },
                    "compare_start": {
                        "description": "Optional comparison-period start (YYYY-MM-DD), e.g. for year-over-year. If omitted, the immediately preceding period of the same length is used. Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "compare_end": {
                        "description": "Optional comparison-period end (YYYY-MM-DD). Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_top_products",
            "title": "Get Top Products",
            "description": "Get a Shopify store's top products for a date range — products ranked by revenue then Search Console clicks. Each product has its clicks and impressions (Google Search Console) and, when Google Analytics 4 is connected and the product has sales, revenue (with its currency), each carrying the change vs the comparison period. Only Shopify sites have products: for any other site the status is \"not_shopify\". This is the performance angle — to see what to fix on product pages, use get_product_issues. Pass the site domain (from list_sites); omit the dates to use the user's default period.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "range": {
                        "description": "PREFERRED. A named period guaranteed to be cached — typically \"last_7_days\", \"last_30_days\" or \"last_90_days\" (use the exact `range` values listed in this site's available_ranges, from list_sites or a previous response). This always resolves to an instant cache hit, so use it instead of computing start/end yourself whenever the user wants a standard recent window. If you pass range, omit start and end.",
                        "type": "string"
                    },
                    "start": {
                        "description": "Start of a CUSTOM period, as YYYY-MM-DD. Only use start/end for a window not offered in available_ranges; otherwise prefer range. Do not invent dates for \"last 30 days\" — use range, or copy the exact start/end from an available_ranges entry. Omit start, end and range to use the user's default period (also a guaranteed hit).",
                        "type": "string"
                    },
                    "end": {
                        "description": "End of a custom period, as YYYY-MM-DD. Search Console lags a few days, so the most recent usable end is what available_ranges reports — don't assume today. Required whenever start is given.",
                        "type": "string"
                    },
                    "compare_start": {
                        "description": "Optional comparison-period start (YYYY-MM-DD), e.g. for year-over-year. If omitted, the immediately preceding period of the same length is used. Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "compare_end": {
                        "description": "Optional comparison-period end (YYYY-MM-DD). Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_product_issues",
            "title": "Get Product Issues",
            "description": "Find the Shopify products that need SEO work — products ranked by health score, lowest (most issues) first, each with its failing checks (title, meta description, H1, thin content, internal links and more). Pass a product handle (from the list) to drill into that one product's full SEO checklist. Only Shopify sites have products: for any other site the status is \"not_shopify\". This is the \"what to fix\" angle — to rank products by traffic or sales, use get_top_products. Pass the site domain (from list_sites).",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain (exactly as returned by list_sites).",
                        "type": "string"
                    },
                    "product": {
                        "description": "Optional. A product handle (from the list) to drill into its full SEO checklist. Omit to list the products with the most issues.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_top_collections",
            "title": "Get Top Collections",
            "description": "Get a Shopify store's top collections (category pages) for a date range — collections ranked by revenue then Search Console clicks. Each collection has its clicks and impressions (Google Search Console, for the collection page) and, when Google Analytics 4 is connected, revenue — the combined sales of every product in the collection — each carrying the change vs the comparison period. Only Shopify sites have collections: for any other site the status is \"not_shopify\". This is the performance angle — to see what to fix on collection pages, use get_collection_issues. Pass the site domain (from list_sites); omit the dates to use the user's default period.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain — with or without www or https:// (matching ignores them). Use a value from list_sites, e.g. \"example.com\".",
                        "type": "string"
                    },
                    "range": {
                        "description": "PREFERRED. A named period guaranteed to be cached — typically \"last_7_days\", \"last_30_days\" or \"last_90_days\" (use the exact `range` values listed in this site's available_ranges, from list_sites or a previous response). This always resolves to an instant cache hit, so use it instead of computing start/end yourself whenever the user wants a standard recent window. If you pass range, omit start and end.",
                        "type": "string"
                    },
                    "start": {
                        "description": "Start of a CUSTOM period, as YYYY-MM-DD. Only use start/end for a window not offered in available_ranges; otherwise prefer range. Do not invent dates for \"last 30 days\" — use range, or copy the exact start/end from an available_ranges entry. Omit start, end and range to use the user's default period (also a guaranteed hit).",
                        "type": "string"
                    },
                    "end": {
                        "description": "End of a custom period, as YYYY-MM-DD. Search Console lags a few days, so the most recent usable end is what available_ranges reports — don't assume today. Required whenever start is given.",
                        "type": "string"
                    },
                    "compare_start": {
                        "description": "Optional comparison-period start (YYYY-MM-DD), e.g. for year-over-year. If omitted, the immediately preceding period of the same length is used. Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "compare_end": {
                        "description": "Optional comparison-period end (YYYY-MM-DD). Provide both compare_start and compare_end, or neither.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_collection_issues",
            "title": "Get Collection Issues",
            "description": "Find the Shopify collections (category pages) that need SEO work — collections ranked by health score, lowest (most issues) first, each with its failing checks (title, meta description, H1, thin content, description relevance, internal links and more). Pass a collection handle (from the list) to drill into that one collection's full SEO checklist. Only Shopify sites have collections: for any other site the status is \"not_shopify\". This is the \"what to fix\" angle — to rank collections by traffic or sales, use get_top_collections. Pass the site domain (from list_sites).",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain (exactly as returned by list_sites).",
                        "type": "string"
                    },
                    "collection": {
                        "description": "Optional. A collection handle (from the list) to drill into its full SEO checklist. Omit to list the collections with the most issues.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_backlink_opportunities",
            "title": "Get Backlink Opportunities",
            "description": "Find backlink opportunities for a site — referring domains that link to the site's competitors (so they may be willing to link to this site too), from SiteGuru's stored competitor backlink-intersection analysis. This reads existing analysis only and NEVER triggers a new (paid) fetch. If there is no fresh analysis, the response has status \"no_data\" and an opportunities_url: tell the user to open it, choose the competitors to compare against and run the analysis there, then ask again — do not try to fetch it yourself. When data exists, the response lists the competitors analysed and the opportunity domains, sorted by rank (highest first; obvious spam and fully-broken ones already removed). Each has a `rank` (DataForSeo domain authority, 0-1000, higher is stronger — the main quality signal, the same one the in-app report sorts by), how many of the competitors it links to, referring_domains, backlinks (note: a high backlink count from few referring_domains is weak — prefer rank), spam_score (0-100, lower is better) and when it was first seen. Review them and recommend the most authoritative, relevant, low-spam domains worth pursuing for outreach. Pass the site domain (from list_sites).",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain (from list_sites), e.g. \"example.com\". With or without www or https://.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "list_competitor_analyses",
            "title": "List Competitor Analyses",
            "description": "List the completed competitor analyses for a site — one entry per competitor (the most recent, non-deleted analysis for each). Use this to discover which competitors the user has analysed before calling get_competitor_analysis for the full comparison. This reads existing analyses only and NEVER starts a new (paid) analysis. If the site has none, the response has status \"no_data\" and an analyses_url: tell the user to open it and run an analysis in the app, then ask again — do not try to run it yourself. Each entry has the competitor_domain, the market it was run in (location_code / language_code are DataForSEO codes, e.g. 2840=US, 2528=NL), keyword gap_count and overlap_count, the month compared (period), when it was analysed (analyzed_on) and is_outdated (true when older than 30 days — still usable, but recommend refreshing it in the app). Pass the site domain (from list_sites).",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain (from list_sites), e.g. \"example.com\". With or without www or https://.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_competitor_analysis",
            "title": "Get Competitor Analysis",
            "description": "Get the full competitor analysis for one competitor of a site: the keyword GAP (keywords the competitor ranks page-1 for where this site is absent or weak — what content to create), the keyword OVERLAP (keywords both rank for, with who leads), the AI summary + recommendations, and a head-to-head KPI comparison (estimated organic traffic, keyword counts, domain rank, referring domains, backlinks) of the competitor vs. this site. Find the competitor domain with list_competitor_analyses first. This reads the most recent stored analysis and NEVER starts a new (paid) one: if there is none for that competitor the response has status \"no_data\" and an analyses_url — tell the user to run it in the app, then ask again. If is_outdated is true the analysis is over 30 days old — still usable, but tell the user and point them to refresh_url to refresh it in the app. Most fields are DataForSEO estimates; only our own ranking positions come from Search Console. Pass the site domain and the competitor domain.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain (from list_sites), e.g. \"example.com\". With or without www or https://.",
                        "type": "string"
                    },
                    "competitor": {
                        "description": "The competitor domain to get the analysis for, as shown by list_competitor_analyses, e.g. \"competitor.com\".",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site",
                    "competitor"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_declining_content",
            "title": "Get Declining Content",
            "description": "Find declining content (content decay) for a site — pages whose Google Search Console clicks have meaningfully dropped over the last 6 complete months, biggest decline first. This is a long-term monthly trend, NOT a date-range view: it reveals gradual decay a 30-day comparison would miss, so it takes no dates. The response lists the 6 `months` (oldest first); each page has a `clicks` map of month (YYYY-MM) to clicks (a null value = that month has not been fetched yet, distinct from 0 clicks; a trailing not-yet-fetched month is omitted entirely), plus net_change and percent_change (negative = declined) and its oldest/newest month clicks. Refresh these pages to reclaim lost traffic. Pass the site domain (from list_sites). If the 6-month history is still being gathered, data_status is \"not_collected\".",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain (from list_sites), e.g. \"example.com\". With or without www or https:// — matching ignores them.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_growing_content",
            "title": "Get Growing Content",
            "description": "Find growing content (rising content) for a site — pages whose Google Search Console clicks have meaningfully grown over the last 6 complete months, biggest growth first. This is a long-term monthly trend, NOT a date-range view, so it takes no dates. The response lists the 6 `months` (oldest first); each page has a `clicks` map of month (YYYY-MM) to clicks (a null value = that month has not been fetched yet, distinct from 0 clicks; a trailing not-yet-fetched month is omitted entirely), plus net_change and percent_change (positive = grew) and its oldest/newest month clicks. These are pages with momentum worth doubling down on. Pass the site domain (from list_sites). If the 6-month history is still being gathered, data_status is \"not_collected\".",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain (from list_sites), e.g. \"example.com\". With or without www or https:// — matching ignores them.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        },
        {
            "name": "get_page_content_trend",
            "title": "Get Page Content Trend",
            "description": "Drill into ONE page's keyword trends over the last 6 complete months — the \"why\" behind a declining or growing page from get_declining_content / get_growing_content. Pass the site domain and the page `page` (a path like \"/blog/post\", e.g. copied from those tools). Returns the 6 `months` (oldest first) and the page's keywords, biggest movers first; each keyword has a `clicks` map of month (YYYY-MM) to clicks plus net_change and percent_change (negative = declined, positive = grew). Note: keyword totals can be lower than the page total — Google hides rare queries from the per-keyword breakdown. This keyword data is gathered on demand: if it has not been fetched for this page yet, the response has keywords_status \"fetching\" — tell the user it is being gathered (usually a few seconds) and to ask again shortly; do not retry in a loop.",
            "inputSchema": {
                "properties": {
                    "site": {
                        "description": "The site domain (from list_sites), e.g. \"example.com\".",
                        "type": "string"
                    },
                    "page": {
                        "description": "The page path to drill into, e.g. \"/blog/post\" — copy it from a get_declining_content / get_growing_content result. A full URL is also accepted.",
                        "type": "string"
                    },
                    "context": {
                        "description": "Briefly, in your own words, why you are calling this tool — the question or task the user is trying to answer. Helps SiteGuru understand usage and improve.",
                        "type": "string"
                    }
                },
                "type": "object",
                "required": [
                    "site",
                    "page"
                ]
            },
            "annotations": {
                "readOnlyHint": true,
                "destructiveHint": false,
                "openWorldHint": false
            },
            "outputSchema": {
                "type": "object",
                "description": "This tool's structured data. The exact keys depend on the tool; the same data is also returned as a human-readable summary in the text content.",
                "additionalProperties": true
            }
        }
    ]
}