[
  {
    "name": "microsoft_ads_get_account_summary",
    "title": "Get Microsoft Ads Account Summary",
    "description": "THE tool for an account-level PERFORMANCE SUMMARY — one rolled-up total for a whole Microsoft ",
    "cli_command": "hopkin microsoft account-summary get",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising AccountId to report on.",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Microsoft Advertising CustomerId (agency/manager). Required when accessing accounts managed under a customer hierarchy.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--date-range",
        "description": "Custom date range. Mutually exclusive with date_preset. If neither is provided, defaults to the last 30 days.",
        "type": "object",
        "required": false
      },
      {
        "flag": "--date-preset",
        "description": "Predefined date range. Mutually exclusive with date_range. If neither is provided, defaults to the last 30 days.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--include-all-conversions",
        "description": "When true, includes an additional all_conversions block (all_conversions, all_revenue, all_cost_per_conversion, all_roas, view_through_conversions). This captures ALL conversion goals including those not marked \"Include in Conversions\" (e.g., view-through, non-bidding goals). Note: conversions is the bidding-set count only; all_conversions.all_conversions is every conversion goal — typically much larger for accounts with non-bidding goals.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--breakdown-by-goal",
        "description": "When true (default), runs a parallel GoalsAndFunnelsReport and returns goal_breakdown keyed by account_id. Each entry is an array of per-goal rows with all_conversions, all_revenue, view_through_conversions, and assists. Pass false to skip the parallel report when latency matters more than goal context. goal_breakdown_granularity will be \"account\" for this tool.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "account-summary",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "description": "Microsoft Advertising AccountId to report on."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Microsoft Advertising CustomerId (agency/manager). Required when accessing accounts managed under a customer hierarchy."
        },
        "date_range": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "Start date (inclusive), e.g. \"2026-01-01\""
            },
            "end": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "End date (inclusive), e.g. \"2026-01-31\""
            }
          },
          "required": [
            "start",
            "end"
          ],
          "additionalProperties": false,
          "description": "Custom date range. Mutually exclusive with date_preset. If neither is provided, defaults to the last 30 days."
        },
        "date_preset": {
          "type": "string",
          "enum": [
            "Today",
            "Yesterday",
            "LastSevenDays",
            "ThisWeek",
            "LastWeek",
            "LastFourWeeks",
            "ThisMonth",
            "LastMonth",
            "LastThreeMonths",
            "LastSixMonths",
            "ThisYear",
            "LastYear"
          ],
          "description": "Predefined date range. Mutually exclusive with date_range. If neither is provided, defaults to the last 30 days."
        },
        "include_all_conversions": {
          "type": "boolean",
          "default": false,
          "description": "When true, includes an additional all_conversions block (all_conversions, all_revenue, all_cost_per_conversion, all_roas, view_through_conversions). This captures ALL conversion goals including those not marked \"Include in Conversions\" (e.g., view-through, non-bidding goals). Note: conversions is the bidding-set count only; all_conversions.all_conversions is every conversion goal — typically much larger for accounts with non-bidding goals."
        },
        "breakdown_by_goal": {
          "type": "boolean",
          "default": true,
          "description": "When true (default), runs a parallel GoalsAndFunnelsReport and returns goal_breakdown keyed by account_id. Each entry is an array of per-goal rows with all_conversions, all_revenue, view_through_conversions, and assists. Pass false to skip the parallel report when latency matters more than goal context. goal_breakdown_granularity will be \"account\" for this tool."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising AccountId to report on.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Microsoft Advertising CustomerId (agency/manager). Required when accessing accounts managed under a customer hierarchy.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "date_range",
        "type": "object",
        "required": false,
        "description": "Custom date range. Mutually exclusive with date_preset. If neither is provided, defaults to the last 30 days."
      },
      {
        "name": "date_preset",
        "type": "string",
        "required": false,
        "description": "Predefined date range. Mutually exclusive with date_range. If neither is provided, defaults to the last 30 days.",
        "enum_values": [
          "Today",
          "Yesterday",
          "LastSevenDays",
          "ThisWeek",
          "LastWeek",
          "LastFourWeeks",
          "ThisMonth",
          "LastMonth",
          "LastThreeMonths",
          "LastSixMonths",
          "ThisYear",
          "LastYear"
        ]
      },
      {
        "name": "include_all_conversions",
        "type": "boolean",
        "required": false,
        "description": "When true, includes an additional all_conversions block (all_conversions, all_revenue, all_cost_per_conversion, all_roas, view_through_conversions). This captures ALL conversion goals including those not marked \"Include in Conversions\" (e.g., view-through, non-bidding goals). Note: conversions is the bidding-set count only; all_conversions.all_conversions is every conversion goal — typically much larger for accounts with non-bidding goals.",
        "default": false
      },
      {
        "name": "breakdown_by_goal",
        "type": "boolean",
        "required": false,
        "description": "When true (default), runs a parallel GoalsAndFunnelsReport and returns goal_breakdown keyed by account_id. Each entry is an array of per-goal rows with all_conversions, all_revenue, view_through_conversions, and assists. Pass false to skip the parallel report when latency matters more than goal context. goal_breakdown_granularity will be \"account\" for this tool.",
        "default": true
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_get_account_summary",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_get_account_summary",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_get_account_summary",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_get_account_summary",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_get_activities",
    "title": "Get Microsoft Ads Change History",
    "description": "",
    "cli_command": "hopkin microsoft activities get",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising Account ID (numeric string).",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer (manager) ID. Optional: only used to check access to an agency-managed account.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--start-date",
        "description": "Start of the window, YYYY-MM-DD (UTC). Defaults to 7 days before end_date.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--end-date",
        "description": "End of the window, YYYY-MM-DD (UTC), inclusive. Defaults to today.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--item-types",
        "description": "Only these kinds of change. Keyword includes negative keyword lists and product groups; Extension includes sitelinks, callouts and other ad extensions.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--entity-ids",
        "description": "Only changes to these entity IDs (campaign, ad group, ad, keyword…).",
        "type": "array",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Changes per page (1-200, default 50).",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "nextCursor from the previous response. Pass it with otherwise identical parameters for the next page.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--refresh",
        "description": "Re-read the account from Microsoft before answering. Normally not needed: it is re-read when the last read is over an hour old.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "activities",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": true,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Microsoft Advertising Account ID (numeric string)."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Customer (manager) ID. Optional: only used to check access to an agency-managed account."
        },
        "start_date": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
          "description": "Start of the window, YYYY-MM-DD (UTC). Defaults to 7 days before end_date."
        },
        "end_date": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
          "description": "End of the window, YYYY-MM-DD (UTC), inclusive. Defaults to today."
        },
        "item_types": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Account",
              "Campaign",
              "AdGroup",
              "Ad",
              "Keyword",
              "Extension",
              "Other"
            ]
          },
          "minItems": 1,
          "description": "Only these kinds of change. Keyword includes negative keyword lists and product groups; Extension includes sitelinks, callouts and other ad extensions."
        },
        "entity_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20
          },
          "minItems": 1,
          "maxItems": 100,
          "description": "Only changes to these entity IDs (campaign, ad group, ad, keyword…)."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50,
          "description": "Changes per page (1-200, default 50)."
        },
        "cursor": {
          "type": "string",
          "description": "nextCursor from the previous response. Pass it with otherwise identical parameters for the next page."
        },
        "refresh": {
          "type": "boolean",
          "default": false,
          "description": "Re-read the account from Microsoft before answering. Normally not needed: it is re-read when the last read is over an hour old."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising Account ID (numeric string).",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer (manager) ID. Optional: only used to check access to an agency-managed account.",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "start_date",
        "type": "string",
        "required": false,
        "description": "Start of the window, YYYY-MM-DD (UTC). Defaults to 7 days before end_date.",
        "constraints": {
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
        }
      },
      {
        "name": "end_date",
        "type": "string",
        "required": false,
        "description": "End of the window, YYYY-MM-DD (UTC), inclusive. Defaults to today.",
        "constraints": {
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
        }
      },
      {
        "name": "item_types",
        "type": "array",
        "required": false,
        "description": "Only these kinds of change. Keyword includes negative keyword lists and product groups; Extension includes sitelinks, callouts and other ad extensions."
      },
      {
        "name": "entity_ids",
        "type": "array",
        "required": false,
        "description": "Only changes to these entity IDs (campaign, ad group, ad, keyword…)."
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Changes per page (1-200, default 50).",
        "default": 50,
        "constraints": {
          "min": 1,
          "max": 200
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "nextCursor from the previous response. Pass it with otherwise identical parameters for the next page."
      },
      {
        "name": "refresh",
        "type": "boolean",
        "required": false,
        "description": "Re-read the account from Microsoft before answering. Normally not needed: it is re-read when the last read is over an hour old.",
        "default": false
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "Recent changes",
        "mcp_params": {
          "account_id": "123456789",
          "reason": "What changed in the account this week"
        },
        "cli_command": "hopkin microsoft activities get --account 123456789",
        "natural_language": "Recent changes"
      },
      {
        "label": "Budget audit, last 30 days",
        "mcp_params": {
          "account_id": "123456789",
          "start_date": "2026-09-01",
          "end_date": "2026-09-30",
          "item_types": [
            "Campaign"
          ],
          "reason": "Did anyone change budgets before the dip?"
        },
        "cli_command": "hopkin microsoft activities get --account 123456789 --start-date 2026-09-01 --end-date 2026-09-30 --item-types Campaign",
        "natural_language": "Budget audit, last 30 days"
      },
      {
        "label": "One campaign",
        "mcp_params": {
          "account_id": "123456789",
          "entity_ids": [
            "487745984"
          ],
          "reason": "Who paused this campaign?"
        },
        "cli_command": "hopkin microsoft activities get --account 123456789 --entity-ids 487745984",
        "natural_language": "One campaign"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_get_activities",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_get_activities",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_get_activities",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_get_activities",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_get_activities",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_list_accounts",
    "title": "List Microsoft Ads Accounts",
    "description": "List Microsoft Advertising accounts accessible to the authenticated user, or look up one ",
    "cli_command": "hopkin microsoft accounts list",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Look up a single account by its Microsoft Advertising AccountId. When provided, other filters and pagination are ignored and the tool returns exactly one account. This is the single-account lookup pattern — there is no separate get_account tool.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--customer-id",
        "description": "Microsoft Advertising CustomerId of the agency/manager account. Required when accessing accounts managed under a customer hierarchy. Equivalent to Google Ads login_customer_id.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--status",
        "description": "Filter accounts by status. Accepts a JSON array or repeated values.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--search",
        "description": "Case-insensitive partial match on account name or account_id.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Number of accounts per page (default: 20, max: 100).",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--refresh",
        "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "accounts",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "description": "Look up a single account by its Microsoft Advertising AccountId. When provided, other filters and pagination are ignored and the tool returns exactly one account. This is the single-account lookup pattern — there is no separate get_account tool."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Microsoft Advertising CustomerId of the agency/manager account. Required when accessing accounts managed under a customer hierarchy. Equivalent to Google Ads login_customer_id."
        },
        "status": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Active",
              "Inactive",
              "Pause",
              "Pending",
              "Suspended"
            ]
          },
          "minItems": 1,
          "description": "Filter accounts by status. Accepts a JSON array or repeated values."
        },
        "search": {
          "type": "string",
          "minLength": 1,
          "description": "Case-insensitive partial match on account name or account_id."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "description": "Number of accounts per page (default: 20, max: 100)."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page."
        },
        "refresh": {
          "type": "boolean",
          "default": false,
          "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": false,
        "description": "Look up a single account by its Microsoft Advertising AccountId. When provided, other filters and pagination are ignored and the tool returns exactly one account. This is the single-account lookup pattern — there is no separate get_account tool.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Microsoft Advertising CustomerId of the agency/manager account. Required when accessing accounts managed under a customer hierarchy. Equivalent to Google Ads login_customer_id.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "status",
        "type": "array",
        "required": false,
        "description": "Filter accounts by status. Accepts a JSON array or repeated values."
      },
      {
        "name": "search",
        "type": "string",
        "required": false,
        "description": "Case-insensitive partial match on account name or account_id.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Number of accounts per page (default: 20, max: 100).",
        "constraints": {
          "min": 1,
          "max": 100
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "refresh",
        "type": "boolean",
        "required": false,
        "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false.",
        "default": false
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "List all accounts",
        "mcp_params": {
          "reason": "Show me all connected Microsoft Ads accounts"
        },
        "cli_command": "hopkin microsoft accounts list",
        "natural_language": "List all accounts"
      },
      {
        "label": "Look up a single account",
        "mcp_params": {
          "account_id": "1234567",
          "reason": "Get details for account 1234567"
        },
        "cli_command": "hopkin microsoft accounts list --account 1234567",
        "natural_language": "Look up a single account"
      },
      {
        "label": "Filter by status",
        "mcp_params": {
          "status": [
            "Active"
          ],
          "reason": "Show only active accounts"
        },
        "cli_command": "hopkin microsoft accounts list --status Active",
        "natural_language": "Filter by status"
      },
      {
        "label": "Search by name",
        "mcp_params": {
          "search": "marketing",
          "reason": "Find accounts with marketing in the name"
        },
        "cli_command": "hopkin microsoft accounts list --search marketing",
        "natural_language": "Search by name"
      },
      {
        "label": "Force refresh",
        "mcp_params": {
          "refresh": true,
          "reason": "Get the latest account data from the API"
        },
        "cli_command": "hopkin microsoft accounts list --refresh true",
        "natural_language": "Force refresh"
      },
      {
        "label": "Agency: accounts under a customer",
        "mcp_params": {
          "customer_id": "9876543",
          "reason": "List accounts managed under customer 9876543"
        },
        "cli_command": "hopkin microsoft accounts list --customer-id 9876543",
        "natural_language": "Agency: accounts under a customer"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "google_ads_list_accounts",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_list_customers",
    "title": "List Microsoft Ads Customers",
    "description": "List all Microsoft Advertising customers (manager/agency accounts) accessible to the authenticated user. ",
    "cli_command": "hopkin microsoft customers list",
    "cli_flags": [
      {
        "flag": "--search",
        "description": "Case-insensitive partial match on customer name or customer_id.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--status",
        "description": "Filter customers by status.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Number of customers per page (default: 20, max: 100).",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "customers",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": true,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "search": {
          "type": "string",
          "minLength": 1,
          "description": "Case-insensitive partial match on customer name or customer_id."
        },
        "status": {
          "type": "string",
          "enum": [
            "Active",
            "Inactive"
          ],
          "description": "Filter customers by status."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "description": "Number of customers per page (default: 20, max: 100)."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "search",
        "type": "string",
        "required": false,
        "description": "Case-insensitive partial match on customer name or customer_id.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "status",
        "type": "string",
        "required": false,
        "description": "Filter customers by status.",
        "enum_values": [
          "Active",
          "Inactive"
        ]
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Number of customers per page (default: 20, max: 100).",
        "constraints": {
          "min": 1,
          "max": 100
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "List all customers",
        "mcp_params": {
          "reason": "Show me all accessible Microsoft Advertising customers"
        },
        "cli_command": "hopkin microsoft customers list",
        "natural_language": "List all customers"
      },
      {
        "label": "Filter active customers",
        "mcp_params": {
          "status": "Active",
          "reason": "Show only active customer accounts"
        },
        "cli_command": "hopkin microsoft customers list --status Active",
        "natural_language": "Filter active customers"
      },
      {
        "label": "Search by name",
        "mcp_params": {
          "search": "Contoso",
          "reason": "Find the Contoso customer account"
        },
        "cli_command": "hopkin microsoft customers list --search Contoso",
        "natural_language": "Search by name"
      },
      {
        "label": "Paginate",
        "mcp_params": {
          "limit": 10,
          "cursor": "eyJvZmZzZXQiOjEwfQ",
          "reason": "Get next page of customers"
        },
        "cli_command": "hopkin microsoft customers list --limit 10 --cursor eyJvZmZzZXQiOjEwfQ",
        "natural_language": "Paginate"
      }
    ],
    "response_format": "markdown",
    "related_tools": [],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_list_ad_extensions",
    "title": "List Microsoft Ads Ad Extensions",
    "description": "",
    "cli_command": "hopkin microsoft ad-extensions list",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (AccountId).",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--campaign-ids",
        "description": "Only these campaigns and their ad groups. Account-level extensions are still listed (they serve on every campaign) unless association_levels leaves Account out. Default: every campaign.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--association-levels",
        "description": "Which attachments to list: Account, Campaign, AdGroup. Default: all three — leave it out to see everything that serves on a campaign, since account-level extensions serve on every campaign and ad-group ones override the campaign's. Narrow it only when the user asks about one level.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--extension-types",
        "description": "Only these extension types, e.g. [\"Sitelink\", \"Callout\", \"StructuredSnippet\"]. Default: every type.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--editorial-status",
        "description": "Only associations in these editorial (policy) states: Disapproved (failed review, not serving), ActiveLimited (approved in some markets only), Inactive (under review), Active (approved). Applied to the whole scan before paging, so nothing is missed. Pass [\"Disapproved\", \"ActiveLimited\", \"Inactive\"] for \"what is not approved\". Default: every state.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--include-raw",
        "description": "Default false. When true, each row also carries the full extension as returned by Microsoft (every field, e.g. scheduling, device preference, all URLs). Much larger output.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Rows per page (1-500, default 50).",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "ad-extensions",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Microsoft Advertising account ID (AccountId)."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts."
        },
        "campaign_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20,
            "pattern": "^\\d+$"
          },
          "minItems": 1,
          "maxItems": 300,
          "description": "Only these campaigns and their ad groups. Account-level extensions are still listed (they serve on every campaign) unless association_levels leaves Account out. Default: every campaign."
        },
        "association_levels": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Account",
              "Campaign",
              "AdGroup"
            ]
          },
          "minItems": 1,
          "description": "Which attachments to list: Account, Campaign, AdGroup. Default: all three — leave it out to see everything that serves on a campaign, since account-level extensions serve on every campaign and ad-group ones override the campaign's. Narrow it only when the user asks about one level."
        },
        "extension_types": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Action",
              "App",
              "Call",
              "Callout",
              "Disclaimer",
              "FilterLink",
              "Flyer",
              "Image",
              "Location",
              "Logo",
              "Price",
              "Promotion",
              "Review",
              "Sitelink",
              "StructuredSnippet",
              "Video"
            ]
          },
          "minItems": 1,
          "description": "Only these extension types, e.g. [\"Sitelink\", \"Callout\", \"StructuredSnippet\"]. Default: every type."
        },
        "editorial_status": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Active",
              "ActiveLimited",
              "Disapproved",
              "Inactive"
            ]
          },
          "minItems": 1,
          "description": "Only associations in these editorial (policy) states: Disapproved (failed review, not serving), ActiveLimited (approved in some markets only), Inactive (under review), Active (approved). Applied to the whole scan before paging, so nothing is missed. Pass [\"Disapproved\", \"ActiveLimited\", \"Inactive\"] for \"what is not approved\". Default: every state."
        },
        "include_raw": {
          "type": "boolean",
          "description": "Default false. When true, each row also carries the full extension as returned by Microsoft (every field, e.g. scheduling, device preference, all URLs). Much larger output."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 500,
          "description": "Rows per page (1-500, default 50)."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (AccountId).",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts.",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "campaign_ids",
        "type": "array",
        "required": false,
        "description": "Only these campaigns and their ad groups. Account-level extensions are still listed (they serve on every campaign) unless association_levels leaves Account out. Default: every campaign."
      },
      {
        "name": "association_levels",
        "type": "array",
        "required": false,
        "description": "Which attachments to list: Account, Campaign, AdGroup. Default: all three — leave it out to see everything that serves on a campaign, since account-level extensions serve on every campaign and ad-group ones override the campaign's. Narrow it only when the user asks about one level."
      },
      {
        "name": "extension_types",
        "type": "array",
        "required": false,
        "description": "Only these extension types, e.g. [\"Sitelink\", \"Callout\", \"StructuredSnippet\"]. Default: every type."
      },
      {
        "name": "editorial_status",
        "type": "array",
        "required": false,
        "description": "Only associations in these editorial (policy) states: Disapproved (failed review, not serving), ActiveLimited (approved in some markets only), Inactive (under review), Active (approved). Applied to the whole scan before paging, so nothing is missed. Pass [\"Disapproved\", \"ActiveLimited\", \"Inactive\"] for \"what is not approved\". Default: every state."
      },
      {
        "name": "include_raw",
        "type": "boolean",
        "required": false,
        "description": "Default false. When true, each row also carries the full extension as returned by Microsoft (every field, e.g. scheduling, device preference, all URLs). Much larger output."
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Rows per page (1-500, default 50).",
        "constraints": {
          "min": 1,
          "max": 500
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [],
    "response_format": "markdown",
    "related_tools": [],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_list_ad_groups",
    "title": "List Microsoft Ads Ad Groups",
    "description": "List ad groups across one or more Microsoft Ads campaigns. ",
    "cli_command": "hopkin microsoft ad-groups list",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (numeric string).",
        "type": "string",
        "required": true
      },
      {
        "flag": "--campaign-ids",
        "description": "Required. Parent campaign IDs to list ad groups for (1–50). Ad groups are scoped to a campaign in Bing — supply one or more campaign IDs whether listing or looking up by ID. Accepts JSON array string or native array.",
        "type": "array",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID. Required for managed accounts.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--ad-group-id",
        "description": "Look up a single ad group by ID. Mutually exclusive with ad_group_ids. Throws if not found. Filters and pagination are ignored when set.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--ad-group-ids",
        "description": "Look up multiple ad groups by ID (max 100). Mutually exclusive with ad_group_id. Filters and pagination are ignored when set.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--status",
        "description": "Filter by ad group status. One or more of: Active, Paused, Deleted, Expired.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--search",
        "description": "Filter ad groups by name (case-insensitive substring match).",
        "type": "string",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Number of ad groups per page (default: 20, max: 100).",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--refresh",
        "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "ad-groups",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "description": "Microsoft Advertising account ID (numeric string)."
        },
        "campaign_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 50,
          "description": "Required. Parent campaign IDs to list ad groups for (1–50). Ad groups are scoped to a campaign in Bing — supply one or more campaign IDs whether listing or looking up by ID. Accepts JSON array string or native array."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Customer/manager ID. Required for managed accounts."
        },
        "ad_group_id": {
          "type": "string",
          "minLength": 1,
          "description": "Look up a single ad group by ID. Mutually exclusive with ad_group_ids. Throws if not found. Filters and pagination are ignored when set."
        },
        "ad_group_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 100,
          "description": "Look up multiple ad groups by ID (max 100). Mutually exclusive with ad_group_id. Filters and pagination are ignored when set."
        },
        "status": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Active",
              "Paused",
              "Deleted",
              "Expired"
            ]
          },
          "description": "Filter by ad group status. One or more of: Active, Paused, Deleted, Expired."
        },
        "search": {
          "type": "string",
          "minLength": 1,
          "description": "Filter ad groups by name (case-insensitive substring match)."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "description": "Number of ad groups per page (default: 20, max: 100)."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page."
        },
        "refresh": {
          "type": "boolean",
          "default": false,
          "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "campaign_ids",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (numeric string).",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "campaign_ids",
        "type": "array",
        "required": true,
        "description": "Required. Parent campaign IDs to list ad groups for (1–50). Ad groups are scoped to a campaign in Bing — supply one or more campaign IDs whether listing or looking up by ID. Accepts JSON array string or native array."
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID. Required for managed accounts.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "ad_group_id",
        "type": "string",
        "required": false,
        "description": "Look up a single ad group by ID. Mutually exclusive with ad_group_ids. Throws if not found. Filters and pagination are ignored when set.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "ad_group_ids",
        "type": "array",
        "required": false,
        "description": "Look up multiple ad groups by ID (max 100). Mutually exclusive with ad_group_id. Filters and pagination are ignored when set."
      },
      {
        "name": "status",
        "type": "array",
        "required": false,
        "description": "Filter by ad group status. One or more of: Active, Paused, Deleted, Expired."
      },
      {
        "name": "search",
        "type": "string",
        "required": false,
        "description": "Filter ad groups by name (case-insensitive substring match).",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Number of ad groups per page (default: 20, max: 100).",
        "constraints": {
          "min": 1,
          "max": 100
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "refresh",
        "type": "boolean",
        "required": false,
        "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false.",
        "default": false
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "List all ad groups in a campaign",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_ids": [
            "987654321"
          ],
          "reason": "Show all ad groups for this campaign"
        },
        "cli_command": "hopkin microsoft ad-groups list --account 123456789 --campaign-ids 987654321",
        "natural_language": "List all ad groups in a campaign"
      },
      {
        "label": "List ad groups across multiple campaigns",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_ids": [
            "987654321",
            "987654322"
          ],
          "reason": "Compare ad groups across two campaigns"
        },
        "cli_command": "hopkin microsoft ad-groups list --account 123456789 --campaign-ids 987654321,987654322",
        "natural_language": "List ad groups across multiple campaigns"
      },
      {
        "label": "Filter by active status",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_ids": [
            "987654321"
          ],
          "status": [
            "Active"
          ],
          "reason": "List only active ad groups"
        },
        "cli_command": "hopkin microsoft ad-groups list --account 123456789 --campaign-ids 987654321 --status Active",
        "natural_language": "Filter by active status"
      },
      {
        "label": "Search by name",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_ids": [
            "987654321"
          ],
          "search": "brand",
          "reason": "Find brand-related ad groups"
        },
        "cli_command": "hopkin microsoft ad-groups list --account 123456789 --campaign-ids 987654321 --search brand",
        "natural_language": "Search by name"
      },
      {
        "label": "Get by ID",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_ids": [
            "987654321"
          ],
          "ad_group_id": "555666777",
          "reason": "Get a specific ad group"
        },
        "cli_command": "hopkin microsoft ad-groups list --account 123456789 --campaign-ids 987654321 --ad-group-id 555666777",
        "natural_language": "Get by ID"
      },
      {
        "label": "Get multiple by ID",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_ids": [
            "987654321"
          ],
          "ad_group_ids": [
            "555666777",
            "888999111"
          ],
          "reason": "Compare two ad groups"
        },
        "cli_command": "hopkin microsoft ad-groups list --account 123456789 --campaign-ids 987654321 --ad-group-ids 555666777,888999111",
        "natural_language": "Get multiple by ID"
      },
      {
        "label": "Via manager account",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_ids": [
            "987654321"
          ],
          "customer_id": "999888777",
          "reason": "List ad groups via agency manager"
        },
        "cli_command": "hopkin microsoft ad-groups list --account 123456789 --campaign-ids 987654321 --customer-id 999888777",
        "natural_language": "Via manager account"
      },
      {
        "label": "Force refresh",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_ids": [
            "987654321"
          ],
          "refresh": true,
          "reason": "Get latest ad group data from API"
        },
        "cli_command": "hopkin microsoft ad-groups list --account 123456789 --campaign-ids 987654321 --refresh true",
        "natural_language": "Force refresh"
      },
      {
        "label": "Paginated",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_ids": [
            "987654321"
          ],
          "limit": 10,
          "cursor": "eyJvZmZzZXQiOjEwfQ==",
          "reason": "Get second page of ad groups"
        },
        "cli_command": "hopkin microsoft ad-groups list --account 123456789 --campaign-ids 987654321 --limit 10 --cursor eyJvZmZzZXQiOjEwfQ==",
        "natural_language": "Paginated"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "google_ads_list_ad_groups",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_list_ad_groups",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_list_ad_groups",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_list_ad_groups",
        "relationship": "cross-platform"
      },
      {
        "name": "microsoft_ads_list_ads",
        "relationship": "hierarchy"
      },
      {
        "name": "microsoft_ads_list_campaigns",
        "relationship": "hierarchy"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_list_ads",
    "title": "List Microsoft Ads",
    "description": "",
    "cli_command": "hopkin microsoft ads list",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (AccountId)",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts through an agency/manager relationship.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--ad-group-ids",
        "description": "Required. Parent ad group IDs to list ads for (1–50). Ads are scoped to an ad group in Bing — supply one or more ad_group_ids whether listing or looking up by ID. Accepts JSON array string or native array.",
        "type": "array",
        "required": true
      },
      {
        "flag": "--ad",
        "description": "Look up a single ad by ID. Mutually exclusive with ad_ids. Throws if not found. Filters and pagination are ignored when set.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--ad-ids",
        "description": "Look up multiple ads by ID (1–50). Mutually exclusive with ad_id. Partial misses are silently dropped. Filters and pagination are ignored when set.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--ad-types",
        "description": "Filter by ad type: ResponsiveSearch, ExpandedText, Responsive, DynamicSearch, Product (Shopping). Defaults to all five. Ignored when ad_id/ad_ids is set.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--status",
        "description": "Filter by ad status: Active, Paused, Deleted. Ignored when ad_id/ad_ids is set.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--editorial-status",
        "description": "Filter by editorial (policy) review status: Disapproved (failed review), ActiveLimited (approved in some markets only), Inactive (under review), Active (approved). Always answered from a live API call, never from cache. Ignored when ad_id/ad_ids is set.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--include-policy-issues",
        "description": "Default false. When true, attaches the editorial reasons and appeal status for every returned ad that is NOT editorially Active — what Microsoft objected to and whether it can be appealed. Costs one extra API call and always fetches live data.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Maximum number of ads to return (1-100, default 20)",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--refresh",
        "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "ads",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "description": "Microsoft Advertising account ID (AccountId)"
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Customer/manager ID (CustomerId). Required when accessing accounts through an agency/manager relationship."
        },
        "ad_group_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 50,
          "description": "Required. Parent ad group IDs to list ads for (1–50). Ads are scoped to an ad group in Bing — supply one or more ad_group_ids whether listing or looking up by ID. Accepts JSON array string or native array."
        },
        "ad_id": {
          "type": "string",
          "minLength": 1,
          "description": "Look up a single ad by ID. Mutually exclusive with ad_ids. Throws if not found. Filters and pagination are ignored when set."
        },
        "ad_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 50,
          "description": "Look up multiple ads by ID (1–50). Mutually exclusive with ad_id. Partial misses are silently dropped. Filters and pagination are ignored when set."
        },
        "ad_types": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "ResponsiveSearch",
              "ExpandedText",
              "Responsive",
              "DynamicSearch",
              "Product"
            ]
          },
          "minItems": 1,
          "description": "Filter by ad type: ResponsiveSearch, ExpandedText, Responsive, DynamicSearch, Product (Shopping). Defaults to all five. Ignored when ad_id/ad_ids is set."
        },
        "status": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Active",
              "Paused",
              "Deleted"
            ]
          },
          "description": "Filter by ad status: Active, Paused, Deleted. Ignored when ad_id/ad_ids is set."
        },
        "editorial_status": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Active",
              "ActiveLimited",
              "Disapproved",
              "Inactive"
            ]
          },
          "description": "Filter by editorial (policy) review status: Disapproved (failed review), ActiveLimited (approved in some markets only), Inactive (under review), Active (approved). Always answered from a live API call, never from cache. Ignored when ad_id/ad_ids is set."
        },
        "include_policy_issues": {
          "type": "boolean",
          "description": "Default false. When true, attaches the editorial reasons and appeal status for every returned ad that is NOT editorially Active — what Microsoft objected to and whether it can be appealed. Costs one extra API call and always fetches live data."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "description": "Maximum number of ads to return (1-100, default 20)"
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page."
        },
        "refresh": {
          "type": "boolean",
          "default": false,
          "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "ad_group_ids",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (AccountId)",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts through an agency/manager relationship.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "ad_group_ids",
        "type": "array",
        "required": true,
        "description": "Required. Parent ad group IDs to list ads for (1–50). Ads are scoped to an ad group in Bing — supply one or more ad_group_ids whether listing or looking up by ID. Accepts JSON array string or native array."
      },
      {
        "name": "ad_id",
        "type": "string",
        "required": false,
        "description": "Look up a single ad by ID. Mutually exclusive with ad_ids. Throws if not found. Filters and pagination are ignored when set.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "ad_ids",
        "type": "array",
        "required": false,
        "description": "Look up multiple ads by ID (1–50). Mutually exclusive with ad_id. Partial misses are silently dropped. Filters and pagination are ignored when set."
      },
      {
        "name": "ad_types",
        "type": "array",
        "required": false,
        "description": "Filter by ad type: ResponsiveSearch, ExpandedText, Responsive, DynamicSearch, Product (Shopping). Defaults to all five. Ignored when ad_id/ad_ids is set."
      },
      {
        "name": "status",
        "type": "array",
        "required": false,
        "description": "Filter by ad status: Active, Paused, Deleted. Ignored when ad_id/ad_ids is set."
      },
      {
        "name": "editorial_status",
        "type": "array",
        "required": false,
        "description": "Filter by editorial (policy) review status: Disapproved (failed review), ActiveLimited (approved in some markets only), Inactive (under review), Active (approved). Always answered from a live API call, never from cache. Ignored when ad_id/ad_ids is set."
      },
      {
        "name": "include_policy_issues",
        "type": "boolean",
        "required": false,
        "description": "Default false. When true, attaches the editorial reasons and appeal status for every returned ad that is NOT editorially Active — what Microsoft objected to and whether it can be appealed. Costs one extra API call and always fetches live data."
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Maximum number of ads to return (1-100, default 20)",
        "constraints": {
          "min": 1,
          "max": 100
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "refresh",
        "type": "boolean",
        "required": false,
        "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false.",
        "default": false
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "List all ads in an ad group",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_ids": [
            "987654321"
          ],
          "reason": "List all ads for review"
        },
        "cli_command": "hopkin microsoft ads list --account 123456789 --ad-group-ids 987654321",
        "natural_language": "List all ads in an ad group"
      },
      {
        "label": "List ads across multiple ad groups",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_ids": [
            "987654321",
            "987654322"
          ],
          "reason": "Compare ads across two ad groups"
        },
        "cli_command": "hopkin microsoft ads list --account 123456789 --ad-group-ids 987654321,987654322",
        "natural_language": "List ads across multiple ad groups"
      },
      {
        "label": "Filter by RSA type only",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_ids": [
            "987654321"
          ],
          "ad_types": [
            "ResponsiveSearch"
          ],
          "reason": "List only responsive search ads"
        },
        "cli_command": "hopkin microsoft ads list --account 123456789 --ad-group-ids 987654321 --ad-types ResponsiveSearch",
        "natural_language": "Filter by RSA type only"
      },
      {
        "label": "Filter by active status",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_ids": [
            "987654321"
          ],
          "status": [
            "Active"
          ],
          "reason": "List active ads"
        },
        "cli_command": "hopkin microsoft ads list --account 123456789 --ad-group-ids 987654321 --status Active",
        "natural_language": "Filter by active status"
      },
      {
        "label": "Get a single ad by ID",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_ids": [
            "987654321"
          ],
          "ad_id": "111222333",
          "reason": "Get full creative for this ad"
        },
        "cli_command": "hopkin microsoft ads list --account 123456789 --ad-group-ids 987654321 --ad 111222333",
        "natural_language": "Get a single ad by ID"
      },
      {
        "label": "Get multiple ads by ID",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_ids": [
            "987654321"
          ],
          "ad_ids": [
            "111222333",
            "444555666"
          ],
          "reason": "Compare two ads side-by-side"
        },
        "cli_command": "hopkin microsoft ads list --account 123456789 --ad-group-ids 987654321 --ad-ids 111222333,444555666",
        "natural_language": "Get multiple ads by ID"
      },
      {
        "label": "Disapproved ads only",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_ids": [
            "987654321"
          ],
          "editorial_status": [
            "Disapproved"
          ],
          "reason": "Find ads blocked by editorial review in this ad group"
        },
        "cli_command": "hopkin microsoft ads list --account 123456789 --ad-group-ids 987654321 --editorial-status Disapproved",
        "natural_language": "Disapproved ads only"
      },
      {
        "label": "Disapproved ads with reasons",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_ids": [
            "987654321"
          ],
          "editorial_status": [
            "Disapproved"
          ],
          "include_policy_issues": true,
          "reason": "Find out why these ads were disapproved and whether we can appeal"
        },
        "cli_command": "hopkin microsoft ads list --account 123456789 --ad-group-ids 987654321 --editorial-status Disapproved --include-policy-issues true",
        "natural_language": "Disapproved ads with reasons"
      },
      {
        "label": "Force refresh",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_ids": [
            "987654321"
          ],
          "refresh": true,
          "reason": "Get latest ad data"
        },
        "cli_command": "hopkin microsoft ads list --account 123456789 --ad-group-ids 987654321 --refresh true",
        "natural_language": "Force refresh"
      },
      {
        "label": "Paginate",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_ids": [
            "987654321"
          ],
          "limit": 10,
          "cursor": "eyJvZmZzZXQiOjEwfQ",
          "reason": "Get next page of ads"
        },
        "cli_command": "hopkin microsoft ads list --account 123456789 --ad-group-ids 987654321 --limit 10 --cursor eyJvZmZzZXQiOjEwfQ",
        "natural_language": "Paginate"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_list_ads",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_list_ads",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_list_ads",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_list_ads",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_list_ads",
        "relationship": "cross-platform"
      },
      {
        "name": "microsoft_ads_list_ad_groups",
        "relationship": "hierarchy"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_get_audience_report",
    "title": "Get Microsoft Ads Audience Report",
    "description": "",
    "cli_command": "hopkin microsoft audience-report get",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (AccountId). Required.",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts under a manager hierarchy.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--date-range",
        "description": "Custom date range. Provide either date_range or date_preset.",
        "type": "object",
        "required": false
      },
      {
        "flag": "--date-preset",
        "description": "Predefined date range: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--breakdowns",
        "description": "One or more breakdown dimensions to include as columns. Microsoft Ads serves these across three different report types, so each call must pick breakdowns from one group only:\n  • demographics: AgeGroup, Gender (AgeGenderAudienceReport)\n  • geo: Location (GeographicPerformanceReport)\n  • campaign: Device, DayOfWeek, HourOfDay (CampaignPerformanceReport)\nCross-group combinations (e.g. AgeGroup + Device) are rejected — split them into separate calls. DayOfWeek and HourOfDay are mutually-exclusive aggregations and cannot be combined. When breakdowns include DayOfWeek or HourOfDay, the aggregation parameter must be Summary (the breakdown itself becomes the aggregation). Default: [\"AgeGroup\"].",
        "type": "array",
        "required": false
      },
      {
        "flag": "--campaign-ids",
        "description": "Filter to specific campaign IDs (up to 100). Accepts JSON array string or actual array.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--campaign-type",
        "description": "Return only rows from campaigns of these types. Options: Search, Shopping, PerformanceMax, Audience. Applied to the report rows before order_by and limit, so the limit counts matching rows only; goal_breakdown covers the matching campaigns. Video is rejected with an error: Microsoft Audience Network video runs in Audience campaigns, so use Audience.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--aggregation",
        "description": "Report aggregation period. Summary (default — collapses all dates), Daily, Weekly, Monthly.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Page size: rows per page (1–10000, default 1000; the geo/Location route defaults to 50 instead when limit is not explicitly set). count is the total across all pages. When more rows remain the response carries nextCursor; pass it back as cursor to get the next page.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque pagination cursor. Omit for the first page. To fetch the next page, pass the nextCursor value from the previous response unchanged and repeat every other parameter exactly (only limit may change); a cursor used with different parameters is rejected. Later pages re-read the same report, so a cursor expires with it (about 24 hours).",
        "type": "string",
        "required": false
      },
      {
        "flag": "--include-all-conversions",
        "description": "When true, adds an all_conversions sub-object to each row containing AllConversionsQualified (all goals, including non-bidding), AllRevenue, ViewThroughConversionsQualified, and derived all_cost_per_conversion / all_roas. Mirrors the google-ads-mcp include_all_conversions opt-in. Default: false. The conversions field always contains bidding-set conversions (ConversionsQualified) only; all_conversions.all_conversions captures every conversion goal including those not marked \"Include in Conversions\" — typically 2-13× larger.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--breakdown-by-goal",
        "description": "When true (default), runs a parallel GoalsAndFunnelsReport and attaches goal_breakdown keyed by campaign_id (campaign-scope when campaign_ids filter present, account_id otherwise) to the response. Granularity is campaign-level (Bing does not support demographic/geo-level goal joins). Set to false to skip the parallel call when latency matters more than goal split.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--order-by",
        "description": "Sort rows by this metric descending before applying the limit. Default: spend. Options: spend, impressions, clicks, conversions, revenue, ctr. For geo (Location) breakdowns this is especially important: Bing returns rows ordered by location_id, not spend, so without sorting the top-N rows are arbitrary city IDs.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "audience-report",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "description": "Microsoft Advertising account ID (AccountId). Required."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Customer/manager ID (CustomerId). Required when accessing accounts under a manager hierarchy."
        },
        "date_range": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "Start date in YYYY-MM-DD format"
            },
            "end": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "End date in YYYY-MM-DD format"
            }
          },
          "required": [
            "start",
            "end"
          ],
          "additionalProperties": false,
          "description": "Custom date range. Provide either date_range or date_preset."
        },
        "date_preset": {
          "type": "string",
          "enum": [
            "Today",
            "Yesterday",
            "LastSevenDays",
            "ThisWeek",
            "LastWeek",
            "LastFourWeeks",
            "ThisMonth",
            "LastMonth",
            "LastThreeMonths",
            "LastSixMonths",
            "ThisYear",
            "LastYear"
          ],
          "description": "Predefined date range: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear."
        },
        "breakdowns": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "AgeGroup",
              "Gender",
              "Location",
              "Device",
              "DayOfWeek",
              "HourOfDay"
            ]
          },
          "minItems": 1,
          "maxItems": 6,
          "default": [
            "AgeGroup"
          ],
          "description": "One or more breakdown dimensions to include as columns. Microsoft Ads serves these across three different report types, so each call must pick breakdowns from one group only:\n  • demographics: AgeGroup, Gender (AgeGenderAudienceReport)\n  • geo: Location (GeographicPerformanceReport)\n  • campaign: Device, DayOfWeek, HourOfDay (CampaignPerformanceReport)\nCross-group combinations (e.g. AgeGroup + Device) are rejected — split them into separate calls. DayOfWeek and HourOfDay are mutually-exclusive aggregations and cannot be combined. When breakdowns include DayOfWeek or HourOfDay, the aggregation parameter must be Summary (the breakdown itself becomes the aggregation). Default: [\"AgeGroup\"]."
        },
        "campaign_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 100,
          "description": "Filter to specific campaign IDs (up to 100). Accepts JSON array string or actual array."
        },
        "campaign_type": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Search",
              "Shopping",
              "PerformanceMax",
              "Audience",
              "Video"
            ]
          },
          "description": "Return only rows from campaigns of these types. Options: Search, Shopping, PerformanceMax, Audience. Applied to the report rows before order_by and limit, so the limit counts matching rows only; goal_breakdown covers the matching campaigns. Video is rejected with an error: Microsoft Audience Network video runs in Audience campaigns, so use Audience."
        },
        "aggregation": {
          "type": "string",
          "enum": [
            "Daily",
            "Weekly",
            "Monthly",
            "Summary"
          ],
          "default": "Summary",
          "description": "Report aggregation period. Summary (default — collapses all dates), Daily, Weekly, Monthly."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10000,
          "default": 1000,
          "description": "Page size: rows per page (1–10000, default 1000; the geo/Location route defaults to 50 instead when limit is not explicitly set). count is the total across all pages. When more rows remain the response carries nextCursor; pass it back as cursor to get the next page."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque pagination cursor. Omit for the first page. To fetch the next page, pass the nextCursor value from the previous response unchanged and repeat every other parameter exactly (only limit may change); a cursor used with different parameters is rejected. Later pages re-read the same report, so a cursor expires with it (about 24 hours)."
        },
        "include_all_conversions": {
          "type": "boolean",
          "default": false,
          "description": "When true, adds an all_conversions sub-object to each row containing AllConversionsQualified (all goals, including non-bidding), AllRevenue, ViewThroughConversionsQualified, and derived all_cost_per_conversion / all_roas. Mirrors the google-ads-mcp include_all_conversions opt-in. Default: false. The conversions field always contains bidding-set conversions (ConversionsQualified) only; all_conversions.all_conversions captures every conversion goal including those not marked \"Include in Conversions\" — typically 2-13× larger."
        },
        "breakdown_by_goal": {
          "type": "boolean",
          "default": true,
          "description": "When true (default), runs a parallel GoalsAndFunnelsReport and attaches goal_breakdown keyed by campaign_id (campaign-scope when campaign_ids filter present, account_id otherwise) to the response. Granularity is campaign-level (Bing does not support demographic/geo-level goal joins). Set to false to skip the parallel call when latency matters more than goal split."
        },
        "order_by": {
          "type": "string",
          "enum": [
            "spend",
            "impressions",
            "clicks",
            "conversions",
            "revenue",
            "ctr"
          ],
          "default": "spend",
          "description": "Sort rows by this metric descending before applying the limit. Default: spend. Options: spend, impressions, clicks, conversions, revenue, ctr. For geo (Location) breakdowns this is especially important: Bing returns rows ordered by location_id, not spend, so without sorting the top-N rows are arbitrary city IDs."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (AccountId). Required.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts under a manager hierarchy.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "date_range",
        "type": "object",
        "required": false,
        "description": "Custom date range. Provide either date_range or date_preset."
      },
      {
        "name": "date_preset",
        "type": "string",
        "required": false,
        "description": "Predefined date range: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear.",
        "enum_values": [
          "Today",
          "Yesterday",
          "LastSevenDays",
          "ThisWeek",
          "LastWeek",
          "LastFourWeeks",
          "ThisMonth",
          "LastMonth",
          "LastThreeMonths",
          "LastSixMonths",
          "ThisYear",
          "LastYear"
        ]
      },
      {
        "name": "breakdowns",
        "type": "array",
        "required": false,
        "description": "One or more breakdown dimensions to include as columns. Microsoft Ads serves these across three different report types, so each call must pick breakdowns from one group only:\n  • demographics: AgeGroup, Gender (AgeGenderAudienceReport)\n  • geo: Location (GeographicPerformanceReport)\n  • campaign: Device, DayOfWeek, HourOfDay (CampaignPerformanceReport)\nCross-group combinations (e.g. AgeGroup + Device) are rejected — split them into separate calls. DayOfWeek and HourOfDay are mutually-exclusive aggregations and cannot be combined. When breakdowns include DayOfWeek or HourOfDay, the aggregation parameter must be Summary (the breakdown itself becomes the aggregation). Default: [\"AgeGroup\"].",
        "default": [
          "AgeGroup"
        ]
      },
      {
        "name": "campaign_ids",
        "type": "array",
        "required": false,
        "description": "Filter to specific campaign IDs (up to 100). Accepts JSON array string or actual array."
      },
      {
        "name": "campaign_type",
        "type": "array",
        "required": false,
        "description": "Return only rows from campaigns of these types. Options: Search, Shopping, PerformanceMax, Audience. Applied to the report rows before order_by and limit, so the limit counts matching rows only; goal_breakdown covers the matching campaigns. Video is rejected with an error: Microsoft Audience Network video runs in Audience campaigns, so use Audience."
      },
      {
        "name": "aggregation",
        "type": "string",
        "required": false,
        "description": "Report aggregation period. Summary (default — collapses all dates), Daily, Weekly, Monthly.",
        "enum_values": [
          "Daily",
          "Weekly",
          "Monthly",
          "Summary"
        ],
        "default": "Summary"
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Page size: rows per page (1–10000, default 1000; the geo/Location route defaults to 50 instead when limit is not explicitly set). count is the total across all pages. When more rows remain the response carries nextCursor; pass it back as cursor to get the next page.",
        "default": 1000,
        "constraints": {
          "min": 1,
          "max": 10000
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque pagination cursor. Omit for the first page. To fetch the next page, pass the nextCursor value from the previous response unchanged and repeat every other parameter exactly (only limit may change); a cursor used with different parameters is rejected. Later pages re-read the same report, so a cursor expires with it (about 24 hours).",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "include_all_conversions",
        "type": "boolean",
        "required": false,
        "description": "When true, adds an all_conversions sub-object to each row containing AllConversionsQualified (all goals, including non-bidding), AllRevenue, ViewThroughConversionsQualified, and derived all_cost_per_conversion / all_roas. Mirrors the google-ads-mcp include_all_conversions opt-in. Default: false. The conversions field always contains bidding-set conversions (ConversionsQualified) only; all_conversions.all_conversions captures every conversion goal including those not marked \"Include in Conversions\" — typically 2-13× larger.",
        "default": false
      },
      {
        "name": "breakdown_by_goal",
        "type": "boolean",
        "required": false,
        "description": "When true (default), runs a parallel GoalsAndFunnelsReport and attaches goal_breakdown keyed by campaign_id (campaign-scope when campaign_ids filter present, account_id otherwise) to the response. Granularity is campaign-level (Bing does not support demographic/geo-level goal joins). Set to false to skip the parallel call when latency matters more than goal split.",
        "default": true
      },
      {
        "name": "order_by",
        "type": "string",
        "required": false,
        "description": "Sort rows by this metric descending before applying the limit. Default: spend. Options: spend, impressions, clicks, conversions, revenue, ctr. For geo (Location) breakdowns this is especially important: Bing returns rows ordered by location_id, not spend, so without sorting the top-N rows are arbitrary city IDs.",
        "enum_values": [
          "spend",
          "impressions",
          "clicks",
          "conversions",
          "revenue",
          "ctr"
        ],
        "default": "spend"
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "google_ads_get_audience_report",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_get_audience_segment_report",
    "title": "Get Microsoft Ads Audience Segment Report",
    "description": "",
    "cli_command": "hopkin microsoft audience-segment-report get",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (AccountId). Required.",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts under a manager hierarchy.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--date-range",
        "description": "Custom date range. Provide either date_range or date_preset.",
        "type": "object",
        "required": false
      },
      {
        "flag": "--date-preset",
        "description": "Predefined date range: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--campaign-ids",
        "description": "Filter to specific campaign IDs (up to 100). Accepts JSON-string or actual array.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--ad-group-ids",
        "description": "Narrow the report to these ad group IDs (up to 100). Each ad group's parent campaign is read from the local cache, or looked up under campaign_ids when given. With campaign_ids, every ad group must belong to one of those campaigns, and the report covers only the listed ad groups. An ad group that cannot be resolved is rejected with an error, never silently widened.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--account-status",
        "description": "Filter by account status: Active, Paused, Deleted.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--campaign-status",
        "description": "Filter by campaign status: Active, Paused, Deleted.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--ad-group-status",
        "description": "Filter by ad group status: Active, Paused, Deleted.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--aggregation",
        "description": "Report aggregation period. Summary (default — collapses dates), Daily, Weekly, Monthly. TimePeriod column is added automatically when aggregation is not Summary.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--include-all-conversions",
        "description": "When true, adds an all_conversions sub-object to each row containing AllConversionsQualified (all goals, including non-bidding), AllRevenue, ViewThroughConversionsQualified, and derived all_cost_per_conversion / all_roas. Mirrors the google-ads-mcp include_all_conversions opt-in. Default: false.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--breakdown-by-goal",
        "description": "When true (default), runs a parallel GoalsAndFunnelsReport and attaches goal_breakdown keyed by campaign_id to the response. Granularity is campaign-level (segment-level goal join is not supported by Bing). Set to false to skip the parallel call when latency matters more than goal split.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--include-video-metrics",
        "description": "Include VideoViews, ViewThroughRate, AverageCPV, VideoCompletionRate, CompletedVideoViews (Audience Network / video placements only).",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--order-by",
        "description": "Sort rows server-side (descending) before applying limit. Options: spend, impressions, clicks, conversions, ctr, bid_adjustment. Default: spend.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Page size: rows per page (1–10000, default 1000). count is the total across all pages. When more rows remain the response carries nextCursor; pass it back as cursor to get the next page.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque pagination cursor. Omit for the first page. To fetch the next page, pass the nextCursor value from the previous response unchanged and repeat every other parameter exactly (only limit may change); a cursor used with different parameters is rejected. Later pages re-read the same report, so a cursor expires with it (about 24 hours).",
        "type": "string",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "audience-segment-report",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "description": "Microsoft Advertising account ID (AccountId). Required."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Customer/manager ID (CustomerId). Required when accessing accounts under a manager hierarchy."
        },
        "date_range": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "Start date in YYYY-MM-DD format"
            },
            "end": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "End date in YYYY-MM-DD format"
            }
          },
          "required": [
            "start",
            "end"
          ],
          "additionalProperties": false,
          "description": "Custom date range. Provide either date_range or date_preset."
        },
        "date_preset": {
          "type": "string",
          "enum": [
            "Today",
            "Yesterday",
            "LastSevenDays",
            "ThisWeek",
            "LastWeek",
            "LastFourWeeks",
            "ThisMonth",
            "LastMonth",
            "LastThreeMonths",
            "LastSixMonths",
            "ThisYear",
            "LastYear"
          ],
          "description": "Predefined date range: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear."
        },
        "campaign_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 100,
          "description": "Filter to specific campaign IDs (up to 100). Accepts JSON-string or actual array."
        },
        "ad_group_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 100,
          "description": "Narrow the report to these ad group IDs (up to 100). Each ad group's parent campaign is read from the local cache, or looked up under campaign_ids when given. With campaign_ids, every ad group must belong to one of those campaigns, and the report covers only the listed ad groups. An ad group that cannot be resolved is rejected with an error, never silently widened."
        },
        "account_status": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Active",
              "Paused",
              "Deleted"
            ]
          },
          "description": "Filter by account status: Active, Paused, Deleted."
        },
        "campaign_status": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Active",
              "Paused",
              "Deleted"
            ]
          },
          "description": "Filter by campaign status: Active, Paused, Deleted."
        },
        "ad_group_status": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Active",
              "Paused",
              "Deleted"
            ]
          },
          "description": "Filter by ad group status: Active, Paused, Deleted."
        },
        "aggregation": {
          "type": "string",
          "enum": [
            "Summary",
            "Daily",
            "Weekly",
            "Monthly"
          ],
          "default": "Summary",
          "description": "Report aggregation period. Summary (default — collapses dates), Daily, Weekly, Monthly. TimePeriod column is added automatically when aggregation is not Summary."
        },
        "include_all_conversions": {
          "type": "boolean",
          "default": false,
          "description": "When true, adds an all_conversions sub-object to each row containing AllConversionsQualified (all goals, including non-bidding), AllRevenue, ViewThroughConversionsQualified, and derived all_cost_per_conversion / all_roas. Mirrors the google-ads-mcp include_all_conversions opt-in. Default: false."
        },
        "breakdown_by_goal": {
          "type": "boolean",
          "default": true,
          "description": "When true (default), runs a parallel GoalsAndFunnelsReport and attaches goal_breakdown keyed by campaign_id to the response. Granularity is campaign-level (segment-level goal join is not supported by Bing). Set to false to skip the parallel call when latency matters more than goal split."
        },
        "include_video_metrics": {
          "type": "boolean",
          "default": false,
          "description": "Include VideoViews, ViewThroughRate, AverageCPV, VideoCompletionRate, CompletedVideoViews (Audience Network / video placements only)."
        },
        "order_by": {
          "type": "string",
          "enum": [
            "spend",
            "impressions",
            "clicks",
            "conversions",
            "ctr",
            "bid_adjustment"
          ],
          "default": "spend",
          "description": "Sort rows server-side (descending) before applying limit. Options: spend, impressions, clicks, conversions, ctr, bid_adjustment. Default: spend."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10000,
          "default": 1000,
          "description": "Page size: rows per page (1–10000, default 1000). count is the total across all pages. When more rows remain the response carries nextCursor; pass it back as cursor to get the next page."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque pagination cursor. Omit for the first page. To fetch the next page, pass the nextCursor value from the previous response unchanged and repeat every other parameter exactly (only limit may change); a cursor used with different parameters is rejected. Later pages re-read the same report, so a cursor expires with it (about 24 hours)."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (AccountId). Required.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts under a manager hierarchy.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "date_range",
        "type": "object",
        "required": false,
        "description": "Custom date range. Provide either date_range or date_preset."
      },
      {
        "name": "date_preset",
        "type": "string",
        "required": false,
        "description": "Predefined date range: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear.",
        "enum_values": [
          "Today",
          "Yesterday",
          "LastSevenDays",
          "ThisWeek",
          "LastWeek",
          "LastFourWeeks",
          "ThisMonth",
          "LastMonth",
          "LastThreeMonths",
          "LastSixMonths",
          "ThisYear",
          "LastYear"
        ]
      },
      {
        "name": "campaign_ids",
        "type": "array",
        "required": false,
        "description": "Filter to specific campaign IDs (up to 100). Accepts JSON-string or actual array."
      },
      {
        "name": "ad_group_ids",
        "type": "array",
        "required": false,
        "description": "Narrow the report to these ad group IDs (up to 100). Each ad group's parent campaign is read from the local cache, or looked up under campaign_ids when given. With campaign_ids, every ad group must belong to one of those campaigns, and the report covers only the listed ad groups. An ad group that cannot be resolved is rejected with an error, never silently widened."
      },
      {
        "name": "account_status",
        "type": "array",
        "required": false,
        "description": "Filter by account status: Active, Paused, Deleted."
      },
      {
        "name": "campaign_status",
        "type": "array",
        "required": false,
        "description": "Filter by campaign status: Active, Paused, Deleted."
      },
      {
        "name": "ad_group_status",
        "type": "array",
        "required": false,
        "description": "Filter by ad group status: Active, Paused, Deleted."
      },
      {
        "name": "aggregation",
        "type": "string",
        "required": false,
        "description": "Report aggregation period. Summary (default — collapses dates), Daily, Weekly, Monthly. TimePeriod column is added automatically when aggregation is not Summary.",
        "enum_values": [
          "Summary",
          "Daily",
          "Weekly",
          "Monthly"
        ],
        "default": "Summary"
      },
      {
        "name": "include_all_conversions",
        "type": "boolean",
        "required": false,
        "description": "When true, adds an all_conversions sub-object to each row containing AllConversionsQualified (all goals, including non-bidding), AllRevenue, ViewThroughConversionsQualified, and derived all_cost_per_conversion / all_roas. Mirrors the google-ads-mcp include_all_conversions opt-in. Default: false.",
        "default": false
      },
      {
        "name": "breakdown_by_goal",
        "type": "boolean",
        "required": false,
        "description": "When true (default), runs a parallel GoalsAndFunnelsReport and attaches goal_breakdown keyed by campaign_id to the response. Granularity is campaign-level (segment-level goal join is not supported by Bing). Set to false to skip the parallel call when latency matters more than goal split.",
        "default": true
      },
      {
        "name": "include_video_metrics",
        "type": "boolean",
        "required": false,
        "description": "Include VideoViews, ViewThroughRate, AverageCPV, VideoCompletionRate, CompletedVideoViews (Audience Network / video placements only).",
        "default": false
      },
      {
        "name": "order_by",
        "type": "string",
        "required": false,
        "description": "Sort rows server-side (descending) before applying limit. Options: spend, impressions, clicks, conversions, ctr, bid_adjustment. Default: spend.",
        "enum_values": [
          "spend",
          "impressions",
          "clicks",
          "conversions",
          "ctr",
          "bid_adjustment"
        ],
        "default": "spend"
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Page size: rows per page (1–10000, default 1000). count is the total across all pages. When more rows remain the response carries nextCursor; pass it back as cursor to get the next page.",
        "default": 1000,
        "constraints": {
          "min": 1,
          "max": 10000
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque pagination cursor. Omit for the first page. To fetch the next page, pass the nextCursor value from the previous response unchanged and repeat every other parameter exactly (only limit may change); a cursor used with different parameters is rejected. Later pages re-read the same report, so a cursor expires with it (about 24 hours).",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [],
    "response_format": "markdown",
    "related_tools": [],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_list_audiences",
    "title": "List Microsoft Ads Audiences",
    "description": "",
    "cli_command": "hopkin microsoft audiences list",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (AccountId).",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--audience-type",
        "description": "Filter by audience type. One or more of: RemarketingList, SimilarRemarketingList, CustomerList, CombinedList. Default: all types. Note: in-market / custom / product audiences cannot be enumerated via this tool — they are only visible in audience-performance reports.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Maximum number of audiences to return (1-1000, default 200).",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "audiences",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "description": "Microsoft Advertising account ID (AccountId)."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts."
        },
        "audience_type": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "RemarketingList",
              "SimilarRemarketingList",
              "CustomerList",
              "CombinedList"
            ]
          },
          "description": "Filter by audience type. One or more of: RemarketingList, SimilarRemarketingList, CustomerList, CombinedList. Default: all types. Note: in-market / custom / product audiences cannot be enumerated via this tool — they are only visible in audience-performance reports."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 1000,
          "description": "Maximum number of audiences to return (1-1000, default 200)."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (AccountId).",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "audience_type",
        "type": "array",
        "required": false,
        "description": "Filter by audience type. One or more of: RemarketingList, SimilarRemarketingList, CustomerList, CombinedList. Default: all types. Note: in-market / custom / product audiences cannot be enumerated via this tool — they are only visible in audience-performance reports."
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Maximum number of audiences to return (1-1000, default 200).",
        "constraints": {
          "min": 1,
          "max": 1000
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "google_ads_list_audiences",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_list_audiences",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_check_auth_status",
    "title": "Check Microsoft Ads Auth Status",
    "description": "THE tool for questions about Microsoft Ads authentication and connection status. Returns:\n- Whether the user has a connected Microsoft Ads OAuth token\n- Whether MICROSOFT_ADS_DEVELOPER_TOKEN is configured on the server (a server-side requirement that no data tool can report)\n- Microsoft Ads user info (name, login) when authenticated\n- List of all accessible ad accounts when authenticated\n\nCall this tool directly whenever the user's question IS about auth/connection/token status — e.g. \"Am I authenticated?\", \"Is my Microsoft Ads account connected?\", \"Check my connection\", \"Diagnose my auth / MISSING_DEVELOPER_TOKEN error\". It is the authoritative connection check and reports the developer-token configuration, which microsoft_ads_list_accounts cannot. Do NOT substitute a data tool like list_accounts to \"prove\" a connection when the user explicitly asked about auth status.\n\nDo NOT call it proactively before a data request the user actually made: if the user asks for their accounts/campaigns/etc., call that data tool directly, and only fall back to this tool reactively if the data tool fails with an auth or permission error (codes 105 InvalidCredentials, 106 UserIsNotAuthorized, 109 AuthenticationTokenExpired). Code 117 is rate limiting, not auth — wait and retry instead.",
    "cli_command": "hopkin microsoft auth check",
    "cli_flags": [
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "auth",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": true,
      "openWorld": false
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "After an auth error",
        "mcp_params": {
          "reason": "microsoft_ads_list_campaigns returned an authentication error; checking connection status"
        },
        "cli_command": "hopkin microsoft auth check",
        "natural_language": "After an auth error"
      },
      {
        "label": "After a permission error",
        "mcp_params": {
          "reason": "Got error code 106 (UserIsNotAuthorized); checking if Microsoft Ads account is connected"
        },
        "cli_command": "hopkin microsoft auth check",
        "natural_language": "After a permission error"
      },
      {
        "label": "Check developer token",
        "mcp_params": {
          "reason": "Received MISSING_DEVELOPER_TOKEN error; verifying server environment configuration"
        },
        "cli_command": "hopkin microsoft auth check",
        "natural_language": "Check developer token"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_check_auth_status",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_check_auth_status",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_check_auth_status",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_check_auth_status",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_check_auth_status",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_check_auth_status",
        "relationship": "cross-platform"
      },
      {
        "name": "microsoft_ads_ping",
        "relationship": "sibling"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_get_billing_status",
    "title": "Billing status",
    "description": "Why a call was refused with a billing error (402) and how to get unblocked. Reports whether this organization is entitled, its subscription status, tier, trial end, credits used and left this period, the price table and a link to the billing page. With checkout_tier, the organization owner also gets a Stripe Checkout link for that plan; a member gets the owner's contact and a message to forward. Call it after any 402. While waiting for a human to pay, poll it every 10 seconds for the first minute, every 30 seconds until 5 minutes, then every 60 seconds until 15 minutes, then stop and tell the human. Once it returns entitled: true, retry the blocked call; if the retry still returns 402, wait 15 seconds and retry once. A Checkout link is a payment link: give it to the organization owner only.",
    "cli_command": "hopkin microsoft billing-status get",
    "cli_flags": [
      {
        "flag": "--checkout-tier",
        "description": "Organization owner only: also create a Stripe Checkout link for this plan. A member gets the owner's contact and a message to forward instead.",
        "type": "string",
        "required": false
      }
    ],
    "category": "billing-status",
    "annotations": {
      "readOnly": false,
      "destructive": false,
      "idempotent": true,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "checkout_tier": {
          "type": "string",
          "enum": [
            "starter",
            "pro",
            "enterprise"
          ],
          "description": "Organization owner only: also create a Stripe Checkout link for this plan. A member gets the owner's contact and a message to forward instead."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "checkout_tier",
        "type": "string",
        "required": false,
        "description": "Organization owner only: also create a Stripe Checkout link for this plan. A member gets the owner's contact and a message to forward instead.",
        "enum_values": [
          "starter",
          "pro",
          "enterprise"
        ]
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "After a billing error",
        "mcp_params": {
          "reason": "A tool call was refused with a billing error (402)"
        },
        "cli_command": "hopkin microsoft billing-status get",
        "natural_language": "After a billing error"
      },
      {
        "label": "Owner: Checkout link",
        "mcp_params": {
          "checkout_tier": "pro",
          "reason": "The organization owner wants to pay for Pro now"
        },
        "cli_command": "hopkin microsoft billing-status get --checkout-tier pro",
        "natural_language": "Owner: Checkout link"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_get_billing_status",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_get_billing_status",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_get_billing_status",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_get_billing_status",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_get_billing_status",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_get_billing_status",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_list_campaigns",
    "title": "List Microsoft Ads Campaigns",
    "description": "List campaigns for a Microsoft Advertising account. ",
    "cli_command": "hopkin microsoft campaigns list",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising Account ID (numeric string)",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts via an agency manager. Equivalent to Google's login_customer_id.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--campaign",
        "description": "Look up a single campaign by ID. Mutually exclusive with campaign_ids. Filters and pagination are ignored when set.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--campaign-ids",
        "description": "Look up multiple campaigns by ID (max 50). Mutually exclusive with campaign_id. Filters and pagination are ignored when set. Accepts JSON array string or native array.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--status",
        "description": "Filter by campaign status. One or more of: Active, Paused, BudgetPaused, Deleted.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--campaign-type",
        "description": "Filter by campaign type. Allowed: Search, Shopping, PerformanceMax, Audience.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--search",
        "description": "Filter campaigns by name (case-insensitive substring match).",
        "type": "string",
        "required": false
      },
      {
        "flag": "--include-policy-issues",
        "description": "Default false. When true, also checks the ads and keywords inside each campaign on this page for editorial (policy) problems — Disapproved, ActiveLimited (approved in some markets only) and Inactive (under review) — and attaches them as policyIssues with Microsoft's reasons and appeal status. This is the account-wide disapproval check: combine with status:[\"Active\"] to cover only running campaigns. Live API scan of every ad group on the page — adds roughly 10-30s. Performance Max asset groups are not covered.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--include-url-tracking",
        "description": "Default false. When true, runs the account-wide URL tracking audit (UTM and click-parameter checks): returns the account-level tracking template, final URL suffix, MSCLKID and UTM auto-tagging settings as urlTrackingScan.account, and attaches urlTracking to each campaign on this page covering every ad group, ad and keyword that sets its OWN tracking template, custom parameters, final URL suffix or (keywords) final URL — anything not listed inherits from its parent. urlTracking.patterns groups those overrides by shape with counts (a parameter value shows as null where it varies between entities), up to 15 patterns across the page; urlTracking.overrides lists the first 15 across the page with exact values. A campaign whose patterns were cut by that limit reports patternsOmitted > 0 — narrow with campaign_ids to see them. Each campaign row already carries its own values. Combine with status:[\"Active\"] to audit only running campaigns. Live API scan of every ad group on the page — adds roughly 10-30s; can be combined with include_policy_issues at no extra scan cost. Performance Max asset groups are not covered.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--include-criteria",
        "description": "Campaign targeting detail, attached to each campaign on this page as settings: location targets and exclusions (with bid modifiers), location intent, radius targets, device bid adjustments, ad schedules, age and gender targets. Default \"none\" (unlike Google): it costs one extra API call per campaign on the page, so it is opt-in. \"summary\" caps the location and radius lists at 25 entries per campaign and reports the full size as locationTargetsTotalCount / locationExclusionsTotalCount / radiusTargetsTotalCount when it cut one; ad schedules are never capped; use \"summary\" for a daily settings check across the account (page through with nextCursor; the default page is up to 10 campaigns here, sized for the client, and fewer when campaigns carry many ad schedules — so a short page with a nextCursor is not the end; narrow with campaign_ids or a smaller limit if a page is still too large). \"full\" returns every entry uncapped and requires campaign_id or campaign_ids (max 10). For a campaign whose languages is null (its ad groups carry the language), settings.adGroupLanguages lists them, at one more call for that campaign. Bid strategy targets and languages are on every row regardless. Campaign-level targeting only: ad-group-level criteria, which can narrow a campaign's targeting, are not included.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Number of campaigns per page (default: 20, max: 100; default up to 10 with include_criteria, fewer when campaigns carry many ad schedules; an explicit limit is honoured as given). Each row carries its own tracking template and custom parameters, so on accounts with long campaign-level parameters (e.g. managed through Search Ads 360) a 100-campaign page can exceed MCP client output limits — keep the default page size and follow nextCursor instead.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--refresh",
        "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "campaigns",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Microsoft Advertising Account ID (numeric string)"
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Customer/manager ID (CustomerId). Required when accessing accounts via an agency manager. Equivalent to Google's login_customer_id."
        },
        "campaign_id": {
          "type": "string",
          "minLength": 1,
          "description": "Look up a single campaign by ID. Mutually exclusive with campaign_ids. Filters and pagination are ignored when set."
        },
        "campaign_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 50,
          "description": "Look up multiple campaigns by ID (max 50). Mutually exclusive with campaign_id. Filters and pagination are ignored when set. Accepts JSON array string or native array."
        },
        "status": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Active",
              "Paused",
              "BudgetPaused",
              "Deleted"
            ]
          },
          "description": "Filter by campaign status. One or more of: Active, Paused, BudgetPaused, Deleted."
        },
        "campaign_type": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Search",
              "Shopping",
              "PerformanceMax",
              "Audience"
            ]
          },
          "description": "Filter by campaign type. Allowed: Search, Shopping, PerformanceMax, Audience."
        },
        "search": {
          "type": "string",
          "minLength": 1,
          "description": "Filter campaigns by name (case-insensitive substring match)."
        },
        "include_policy_issues": {
          "type": "boolean",
          "description": "Default false. When true, also checks the ads and keywords inside each campaign on this page for editorial (policy) problems — Disapproved, ActiveLimited (approved in some markets only) and Inactive (under review) — and attaches them as policyIssues with Microsoft's reasons and appeal status. This is the account-wide disapproval check: combine with status:[\"Active\"] to cover only running campaigns. Live API scan of every ad group on the page — adds roughly 10-30s. Performance Max asset groups are not covered."
        },
        "include_url_tracking": {
          "type": "boolean",
          "description": "Default false. When true, runs the account-wide URL tracking audit (UTM and click-parameter checks): returns the account-level tracking template, final URL suffix, MSCLKID and UTM auto-tagging settings as urlTrackingScan.account, and attaches urlTracking to each campaign on this page covering every ad group, ad and keyword that sets its OWN tracking template, custom parameters, final URL suffix or (keywords) final URL — anything not listed inherits from its parent. urlTracking.patterns groups those overrides by shape with counts (a parameter value shows as null where it varies between entities), up to 15 patterns across the page; urlTracking.overrides lists the first 15 across the page with exact values. A campaign whose patterns were cut by that limit reports patternsOmitted > 0 — narrow with campaign_ids to see them. Each campaign row already carries its own values. Combine with status:[\"Active\"] to audit only running campaigns. Live API scan of every ad group on the page — adds roughly 10-30s; can be combined with include_policy_issues at no extra scan cost. Performance Max asset groups are not covered."
        },
        "include_criteria": {
          "type": "string",
          "enum": [
            "none",
            "summary",
            "full"
          ],
          "description": "Campaign targeting detail, attached to each campaign on this page as settings: location targets and exclusions (with bid modifiers), location intent, radius targets, device bid adjustments, ad schedules, age and gender targets. Default \"none\" (unlike Google): it costs one extra API call per campaign on the page, so it is opt-in. \"summary\" caps the location and radius lists at 25 entries per campaign and reports the full size as locationTargetsTotalCount / locationExclusionsTotalCount / radiusTargetsTotalCount when it cut one; ad schedules are never capped; use \"summary\" for a daily settings check across the account (page through with nextCursor; the default page is up to 10 campaigns here, sized for the client, and fewer when campaigns carry many ad schedules — so a short page with a nextCursor is not the end; narrow with campaign_ids or a smaller limit if a page is still too large). \"full\" returns every entry uncapped and requires campaign_id or campaign_ids (max 10). For a campaign whose languages is null (its ad groups carry the language), settings.adGroupLanguages lists them, at one more call for that campaign. Bid strategy targets and languages are on every row regardless. Campaign-level targeting only: ad-group-level criteria, which can narrow a campaign's targeting, are not included."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "description": "Number of campaigns per page (default: 20, max: 100; default up to 10 with include_criteria, fewer when campaigns carry many ad schedules; an explicit limit is honoured as given). Each row carries its own tracking template and custom parameters, so on accounts with long campaign-level parameters (e.g. managed through Search Ads 360) a 100-campaign page can exceed MCP client output limits — keep the default page size and follow nextCursor instead."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page."
        },
        "refresh": {
          "type": "boolean",
          "default": false,
          "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising Account ID (numeric string)",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts via an agency manager. Equivalent to Google's login_customer_id.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "campaign_id",
        "type": "string",
        "required": false,
        "description": "Look up a single campaign by ID. Mutually exclusive with campaign_ids. Filters and pagination are ignored when set.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "campaign_ids",
        "type": "array",
        "required": false,
        "description": "Look up multiple campaigns by ID (max 50). Mutually exclusive with campaign_id. Filters and pagination are ignored when set. Accepts JSON array string or native array."
      },
      {
        "name": "status",
        "type": "array",
        "required": false,
        "description": "Filter by campaign status. One or more of: Active, Paused, BudgetPaused, Deleted."
      },
      {
        "name": "campaign_type",
        "type": "array",
        "required": false,
        "description": "Filter by campaign type. Allowed: Search, Shopping, PerformanceMax, Audience."
      },
      {
        "name": "search",
        "type": "string",
        "required": false,
        "description": "Filter campaigns by name (case-insensitive substring match).",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "include_policy_issues",
        "type": "boolean",
        "required": false,
        "description": "Default false. When true, also checks the ads and keywords inside each campaign on this page for editorial (policy) problems — Disapproved, ActiveLimited (approved in some markets only) and Inactive (under review) — and attaches them as policyIssues with Microsoft's reasons and appeal status. This is the account-wide disapproval check: combine with status:[\"Active\"] to cover only running campaigns. Live API scan of every ad group on the page — adds roughly 10-30s. Performance Max asset groups are not covered."
      },
      {
        "name": "include_url_tracking",
        "type": "boolean",
        "required": false,
        "description": "Default false. When true, runs the account-wide URL tracking audit (UTM and click-parameter checks): returns the account-level tracking template, final URL suffix, MSCLKID and UTM auto-tagging settings as urlTrackingScan.account, and attaches urlTracking to each campaign on this page covering every ad group, ad and keyword that sets its OWN tracking template, custom parameters, final URL suffix or (keywords) final URL — anything not listed inherits from its parent. urlTracking.patterns groups those overrides by shape with counts (a parameter value shows as null where it varies between entities), up to 15 patterns across the page; urlTracking.overrides lists the first 15 across the page with exact values. A campaign whose patterns were cut by that limit reports patternsOmitted > 0 — narrow with campaign_ids to see them. Each campaign row already carries its own values. Combine with status:[\"Active\"] to audit only running campaigns. Live API scan of every ad group on the page — adds roughly 10-30s; can be combined with include_policy_issues at no extra scan cost. Performance Max asset groups are not covered."
      },
      {
        "name": "include_criteria",
        "type": "string",
        "required": false,
        "description": "Campaign targeting detail, attached to each campaign on this page as settings: location targets and exclusions (with bid modifiers), location intent, radius targets, device bid adjustments, ad schedules, age and gender targets. Default \"none\" (unlike Google): it costs one extra API call per campaign on the page, so it is opt-in. \"summary\" caps the location and radius lists at 25 entries per campaign and reports the full size as locationTargetsTotalCount / locationExclusionsTotalCount / radiusTargetsTotalCount when it cut one; ad schedules are never capped; use \"summary\" for a daily settings check across the account (page through with nextCursor; the default page is up to 10 campaigns here, sized for the client, and fewer when campaigns carry many ad schedules — so a short page with a nextCursor is not the end; narrow with campaign_ids or a smaller limit if a page is still too large). \"full\" returns every entry uncapped and requires campaign_id or campaign_ids (max 10). For a campaign whose languages is null (its ad groups carry the language), settings.adGroupLanguages lists them, at one more call for that campaign. Bid strategy targets and languages are on every row regardless. Campaign-level targeting only: ad-group-level criteria, which can narrow a campaign's targeting, are not included.",
        "enum_values": [
          "none",
          "summary",
          "full"
        ]
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Number of campaigns per page (default: 20, max: 100; default up to 10 with include_criteria, fewer when campaigns carry many ad schedules; an explicit limit is honoured as given). Each row carries its own tracking template and custom parameters, so on accounts with long campaign-level parameters (e.g. managed through Search Ads 360) a 100-campaign page can exceed MCP client output limits — keep the default page size and follow nextCursor instead.",
        "constraints": {
          "min": 1,
          "max": 100
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "refresh",
        "type": "boolean",
        "required": false,
        "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false.",
        "default": false
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "List all campaigns",
        "mcp_params": {
          "account_id": "123456789",
          "reason": "Show all campaigns for this account"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789",
        "natural_language": "List all campaigns"
      },
      {
        "label": "Filter by status",
        "mcp_params": {
          "account_id": "123456789",
          "status": [
            "Active"
          ],
          "reason": "List only active campaigns"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789 --status Active",
        "natural_language": "Filter by status"
      },
      {
        "label": "Filter by type",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_type": [
            "Search",
            "PerformanceMax"
          ],
          "reason": "List Search and PMax campaigns"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789 --campaign-type Search,PerformanceMax",
        "natural_language": "Filter by type"
      },
      {
        "label": "Search by name",
        "mcp_params": {
          "account_id": "123456789",
          "search": "brand",
          "reason": "Find brand campaigns"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789 --search brand",
        "natural_language": "Search by name"
      },
      {
        "label": "Get by ID",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_id": "987654321",
          "reason": "Get specific campaign details"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789 --campaign 987654321",
        "natural_language": "Get by ID"
      },
      {
        "label": "Get multiple by ID",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_ids": [
            "111",
            "222",
            "333"
          ],
          "reason": "Compare specific campaigns"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789 --campaign-ids 111,222,333",
        "natural_language": "Get multiple by ID"
      },
      {
        "label": "Via manager account",
        "mcp_params": {
          "account_id": "123456789",
          "customer_id": "999888777",
          "reason": "List campaigns via agency manager"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789 --customer-id 999888777",
        "natural_language": "Via manager account"
      },
      {
        "label": "Account-wide policy check (active campaigns)",
        "mcp_params": {
          "account_id": "123456789",
          "status": [
            "Active"
          ],
          "include_policy_issues": true,
          "limit": 100,
          "reason": "Daily check for disapproved ads and keywords on running campaigns"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789 --status Active --include-policy-issues true --limit 100",
        "natural_language": "Account-wide policy check (active campaigns)"
      },
      {
        "label": "Policy issues for one campaign",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_id": "987654321",
          "include_policy_issues": true,
          "reason": "Investigate why this campaign is underdelivering"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789 --campaign 987654321 --include-policy-issues true",
        "natural_language": "Policy issues for one campaign"
      },
      {
        "label": "Daily settings check (bid targets, locations, languages)",
        "mcp_params": {
          "account_id": "123456789",
          "include_criteria": "summary",
          "reason": "Verify no target CPA, location or language changed since yesterday"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789 --include-criteria summary",
        "natural_language": "Daily settings check (bid targets, locations, languages)"
      },
      {
        "label": "Every location target of named campaigns",
        "mcp_params": {
          "account_id": "123456789",
          "campaign_ids": [
            "111",
            "222"
          ],
          "include_criteria": "full",
          "reason": "List every ZIP code these campaigns target"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789 --campaign-ids 111,222 --include-criteria full",
        "natural_language": "Every location target of named campaigns"
      },
      {
        "label": "Force refresh",
        "mcp_params": {
          "account_id": "123456789",
          "refresh": true,
          "reason": "Get latest campaign data from API"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789 --refresh true",
        "natural_language": "Force refresh"
      },
      {
        "label": "Paginated",
        "mcp_params": {
          "account_id": "123456789",
          "limit": 10,
          "cursor": "eyJvZmZzZXQiOjEwfQ==",
          "reason": "Get second page of campaigns"
        },
        "cli_command": "hopkin microsoft campaigns list --account 123456789 --limit 10 --cursor eyJvZmZzZXQiOjEwfQ==",
        "natural_language": "Paginated"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_list_campaigns",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_list_campaigns",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_list_campaigns",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_list_campaigns",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_list_campaigns",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_list_campaigns",
        "relationship": "cross-platform"
      },
      {
        "name": "microsoft_ads_list_ad_groups",
        "relationship": "hierarchy"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_render_chart",
    "title": "Render Chart",
    "description": "Interactive UI for rendering Microsoft Ads data visualization charts",
    "cli_command": "",
    "cli_flags": [],
    "category": "chart",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": true,
      "openWorld": false
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Brief explanation of why you are rendering this chart"
        },
        "chart": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "const": "bar"
                },
                "data": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "label": {
                        "type": "string"
                      },
                      "values": {
                        "type": "object",
                        "additionalProperties": {
                          "type": [
                            "number",
                            "null"
                          ]
                        }
                      }
                    },
                    "required": [
                      "label",
                      "values"
                    ],
                    "additionalProperties": false
                  }
                },
                "metric": {
                  "anyOf": [
                    {
                      "type": "object",
                      "properties": {
                        "field": {
                          "type": "string"
                        },
                        "label": {
                          "type": "string"
                        },
                        "format": {
                          "type": "string"
                        },
                        "tickFormat": {
                          "type": "string"
                        },
                        "labelFields": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "field": {
                                "type": "string"
                              },
                              "format": {
                                "type": "string"
                              },
                              "suffix": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "field"
                            ],
                            "additionalProperties": false
                          }
                        }
                      },
                      "required": [
                        "field",
                        "label"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "format": {
                            "type": "string"
                          },
                          "tickFormat": {
                            "type": "string"
                          },
                          "labelFields": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "field": {
                                  "type": "string"
                                },
                                "format": {
                                  "type": "string"
                                },
                                "suffix": {
                                  "type": "string"
                                }
                              },
                              "required": [
                                "field"
                              ],
                              "additionalProperties": false
                            }
                          }
                        },
                        "required": [
                          "field",
                          "label"
                        ],
                        "additionalProperties": false
                      }
                    }
                  ]
                },
                "orientation": {
                  "type": "string",
                  "enum": [
                    "horizontal",
                    "vertical"
                  ],
                  "default": "horizontal"
                },
                "sort": {
                  "type": "string",
                  "enum": [
                    "asc",
                    "desc",
                    "none"
                  ],
                  "default": "desc"
                },
                "colorBy": {
                  "anyOf": [
                    {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "const": "fixed"
                        }
                      },
                      "required": [
                        "type"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "const": "threshold"
                        },
                        "field": {
                          "type": "string"
                        },
                        "threshold": {
                          "type": "number"
                        },
                        "sentiment": {
                          "type": "string",
                          "enum": [
                            "above-positive",
                            "above-negative"
                          ],
                          "default": "above-positive"
                        }
                      },
                      "required": [
                        "type",
                        "field",
                        "threshold"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "const": "continuous"
                        },
                        "field": {
                          "type": "string"
                        },
                        "scheme": {
                          "type": "string"
                        },
                        "reverse": {
                          "type": "boolean",
                          "default": false
                        },
                        "muteZero": {
                          "type": "boolean",
                          "default": false
                        },
                        "label": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "type",
                        "field",
                        "scheme"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "const": "categorical"
                        }
                      },
                      "required": [
                        "type"
                      ],
                      "additionalProperties": false
                    }
                  ]
                },
                "referenceLines": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "value": {
                        "type": "number"
                      },
                      "label": {
                        "type": "string"
                      },
                      "dash": {
                        "type": "string",
                        "enum": [
                          "solid",
                          "dashed"
                        ],
                        "default": "dashed"
                      }
                    },
                    "required": [
                      "value"
                    ],
                    "additionalProperties": false
                  }
                },
                "table": {
                  "type": "object",
                  "properties": {
                    "columns": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "format": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "field",
                          "label"
                        ],
                        "additionalProperties": false
                      }
                    }
                  },
                  "required": [
                    "columns"
                  ],
                  "additionalProperties": false
                },
                "panelWidth": {
                  "type": "number"
                },
                "width": {
                  "type": "number",
                  "default": 700
                },
                "height": {
                  "type": "number",
                  "default": 400
                }
              },
              "required": [
                "type",
                "data",
                "metric"
              ],
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "const": "scatter"
                },
                "data": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "label": {
                        "type": "string"
                      },
                      "values": {
                        "type": "object",
                        "additionalProperties": {
                          "type": "number"
                        }
                      }
                    },
                    "required": [
                      "label",
                      "values"
                    ],
                    "additionalProperties": false
                  }
                },
                "x": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "scale": {
                      "type": "string",
                      "enum": [
                        "linear",
                        "log"
                      ],
                      "default": "linear"
                    },
                    "format": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "field",
                    "label"
                  ],
                  "additionalProperties": false
                },
                "y": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "scale": {
                      "type": "string",
                      "enum": [
                        "linear",
                        "log"
                      ],
                      "default": "linear"
                    },
                    "format": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "field",
                    "label"
                  ],
                  "additionalProperties": false
                },
                "size": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "range": {
                      "type": "array",
                      "items": {
                        "type": "number"
                      },
                      "minItems": 2,
                      "maxItems": 2,
                      "default": [
                        3,
                        20
                      ]
                    }
                  },
                  "required": [
                    "field"
                  ],
                  "additionalProperties": false
                },
                "color": {
                  "anyOf": [
                    {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "const": "fixed"
                        }
                      },
                      "required": [
                        "type"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "const": "continuous"
                        },
                        "field": {
                          "type": "string"
                        },
                        "scheme": {
                          "type": "string"
                        },
                        "reverse": {
                          "type": "boolean",
                          "default": false
                        }
                      },
                      "required": [
                        "type",
                        "field",
                        "scheme"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "const": "threshold"
                        },
                        "field": {
                          "type": "string"
                        },
                        "threshold": {
                          "type": "number"
                        },
                        "sentiment": {
                          "type": "string",
                          "enum": [
                            "above-positive",
                            "above-negative"
                          ],
                          "default": "above-positive"
                        }
                      },
                      "required": [
                        "type",
                        "field",
                        "threshold"
                      ],
                      "additionalProperties": false
                    }
                  ]
                },
                "referenceLines": {
                  "type": "object",
                  "properties": {
                    "medianX": {
                      "type": "boolean",
                      "default": false
                    },
                    "medianY": {
                      "type": "boolean",
                      "default": false
                    },
                    "targets": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "slope": {
                            "type": "number"
                          },
                          "label": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "slope",
                          "label"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "regression": {
                      "type": "boolean",
                      "default": false
                    }
                  },
                  "additionalProperties": false
                },
                "table": {
                  "type": "object",
                  "properties": {
                    "columns": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "format": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "field",
                          "label"
                        ],
                        "additionalProperties": false
                      }
                    }
                  },
                  "required": [
                    "columns"
                  ],
                  "additionalProperties": false
                },
                "width": {
                  "type": "number",
                  "default": 700
                },
                "height": {
                  "type": "number",
                  "default": 450
                }
              },
              "required": [
                "type",
                "data",
                "x",
                "y"
              ],
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "const": "timeseries"
                },
                "data": {
                  "type": "object",
                  "properties": {
                    "current": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string"
                          },
                          "values": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "number"
                            }
                          }
                        },
                        "required": [
                          "date",
                          "values"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "previous": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string"
                          },
                          "values": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "number"
                            }
                          }
                        },
                        "required": [
                          "date",
                          "values"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "projection": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string"
                          },
                          "values": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "number"
                            }
                          }
                        },
                        "required": [
                          "date",
                          "values"
                        ],
                        "additionalProperties": false
                      }
                    }
                  },
                  "required": [
                    "current"
                  ],
                  "additionalProperties": false
                },
                "primaryAxis": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "mark": {
                      "type": "string",
                      "enum": [
                        "bar",
                        "area",
                        "line"
                      ]
                    },
                    "groupBars": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "field",
                    "label",
                    "mark"
                  ],
                  "additionalProperties": false
                },
                "secondaryAxis": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "dash": {
                      "type": "string",
                      "enum": [
                        "solid",
                        "dashed"
                      ],
                      "default": "solid"
                    }
                  },
                  "required": [
                    "field",
                    "label"
                  ],
                  "additionalProperties": false
                },
                "width": {
                  "type": "number",
                  "default": 700
                },
                "height": {
                  "type": "number",
                  "default": 400
                }
              },
              "required": [
                "type",
                "data",
                "primaryAxis"
              ],
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "const": "funnel"
                },
                "data": {
                  "type": "object",
                  "properties": {
                    "stages": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "volume": {
                      "type": "array",
                      "items": {
                        "type": "number"
                      }
                    },
                    "costPer": {
                      "type": "array",
                      "items": {
                        "type": "number"
                      }
                    }
                  },
                  "required": [
                    "stages",
                    "volume"
                  ],
                  "additionalProperties": false
                },
                "showConversionRates": {
                  "type": "boolean",
                  "default": true
                },
                "showCostPer": {
                  "type": "boolean",
                  "default": true
                },
                "colorScheme": {
                  "type": "string",
                  "default": "Blues"
                },
                "width": {
                  "type": "number",
                  "default": 700
                },
                "height": {
                  "type": "number",
                  "default": 350
                }
              },
              "required": [
                "type",
                "data"
              ],
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "const": "waterfall"
                },
                "data": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "label": {
                        "type": "string"
                      },
                      "value": {
                        "type": "number"
                      },
                      "colorValue": {
                        "type": "number"
                      }
                    },
                    "required": [
                      "label",
                      "value",
                      "colorValue"
                    ],
                    "additionalProperties": false
                  }
                },
                "colorScheme": {
                  "type": "string",
                  "default": "RdYlGn"
                },
                "colorLabel": {
                  "type": "string",
                  "default": "CPA ($)"
                },
                "valueLabel": {
                  "type": "string",
                  "default": "Cumulative Spend ($)"
                },
                "width": {
                  "type": "number",
                  "default": 700
                },
                "height": {
                  "type": "number"
                }
              },
              "required": [
                "type",
                "data"
              ],
              "additionalProperties": false
            },
            {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "const": "choropleth"
                },
                "data": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "state": {
                        "type": "string"
                      },
                      "value": {
                        "type": "number"
                      },
                      "meta": {
                        "type": "object",
                        "additionalProperties": {
                          "type": [
                            "string",
                            "number",
                            "null"
                          ]
                        }
                      }
                    },
                    "required": [
                      "state",
                      "value"
                    ],
                    "additionalProperties": false
                  }
                },
                "valueLabel": {
                  "type": "string"
                },
                "colorScheme": {
                  "type": "string",
                  "default": "YlOrRd"
                },
                "colorType": {
                  "type": "string",
                  "enum": [
                    "quantize",
                    "linear",
                    "threshold"
                  ],
                  "default": "quantize"
                },
                "reverse": {
                  "type": "boolean",
                  "default": false
                },
                "tip": {
                  "type": "object",
                  "properties": {
                    "fields": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "format": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "field",
                          "label"
                        ],
                        "additionalProperties": false
                      }
                    }
                  },
                  "required": [
                    "fields"
                  ],
                  "additionalProperties": false
                },
                "width": {
                  "type": "number",
                  "default": 700
                },
                "height": {
                  "type": "number",
                  "default": 450
                }
              },
              "required": [
                "type",
                "data",
                "valueLabel"
              ],
              "additionalProperties": false
            }
          ]
        }
      },
      "required": [
        "reason",
        "chart"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Brief explanation of why you are rendering this chart",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      },
      {
        "name": "chart",
        "type": "object",
        "required": true,
        "description": "Chart configuration. Supported types: bar, scatter, timeseries, funnel, waterfall, choropleth."
      }
    ],
    "examples": [],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_render_chart",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_render_chart",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_render_chart",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_render_chart",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "app",
    "chart_types": [
      "bar",
      "scatter",
      "timeseries",
      "funnel",
      "waterfall",
      "choropleth"
    ]
  },
  {
    "name": "microsoft_ads_list_connections",
    "title": "List Microsoft Connections",
    "description": "List the Microsoft Ads connections available to you — both ones you own and ones shared with you via an organization. Use this to discover connection IDs for set_default / share / rename / revoke.",
    "cli_command": "hopkin microsoft connections list",
    "cli_flags": [],
    "category": "connections",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": true,
      "openWorld": false
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "List connections",
        "mcp_params": {
          "reason": "User asked which Microsoft accounts they have connected"
        },
        "cli_command": "hopkin microsoft connections list",
        "natural_language": "List connections"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_list_connections",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_list_connections",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_list_connections",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_list_connections",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_list_connections",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_list_connections",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_set_default_connection",
    "title": "Set Default Microsoft Connection",
    "description": "Set the Microsoft connection that should be used by default for subsequent Microsoft Ads tool calls. The default is scoped to the calling actor (your user account, or the API key being used).",
    "cli_command": "hopkin microsoft set-default-connection",
    "cli_flags": [
      {
        "flag": "--connection-id",
        "description": "UUID of the connection to mark as the actor's default Microsoft connection.",
        "type": "string",
        "required": true
      }
    ],
    "category": "set-default-connection",
    "annotations": {
      "readOnly": false,
      "destructive": false,
      "idempotent": true,
      "openWorld": false
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "UUID of the connection to mark as the actor's default Microsoft connection."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "connection_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "connection_id",
        "type": "string",
        "required": true,
        "description": "UUID of the connection to mark as the actor's default Microsoft connection."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "Set default",
        "mcp_params": {
          "connection_id": "00000000-0000-0000-0000-000000000001",
          "reason": "User picked their primary Microsoft workspace"
        },
        "cli_command": "hopkin microsoft set-default-connection --connection-id 00000000-0000-0000-0000-000000000001",
        "natural_language": "Set default"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_set_default_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_set_default_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_set_default_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_set_default_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_set_default_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_set_default_connection",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_share_connection",
    "title": "Share Microsoft Connection With Organization",
    "description": "Share an owned Microsoft connection with all members of your organization, so teammates can use it without having to reconnect Microsoft themselves. You must be the owner of the connection.",
    "cli_command": "hopkin microsoft share-connection",
    "cli_flags": [
      {
        "flag": "--connection-id",
        "description": "UUID of the connection to share with your organization. You must be the owner.",
        "type": "string",
        "required": true
      }
    ],
    "category": "share-connection",
    "annotations": {
      "readOnly": false,
      "destructive": false,
      "idempotent": true,
      "openWorld": false
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "UUID of the connection to share with your organization. You must be the owner."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "connection_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "connection_id",
        "type": "string",
        "required": true,
        "description": "UUID of the connection to share with your organization. You must be the owner."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "Share with org",
        "mcp_params": {
          "connection_id": "00000000-0000-0000-0000-000000000001",
          "reason": "User asked to share this Microsoft connection with their teammates"
        },
        "cli_command": "hopkin microsoft share-connection --connection-id 00000000-0000-0000-0000-000000000001",
        "natural_language": "Share with org"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_share_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_share_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_share_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_share_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_share_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_share_connection",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_unshare_connection",
    "title": "Unshare Microsoft Connection From Organization",
    "description": "Stop sharing an owned Microsoft connection with your organization. Teammates lose access immediately. You must be the owner.",
    "cli_command": "hopkin microsoft unshare-connection",
    "cli_flags": [
      {
        "flag": "--connection-id",
        "description": "UUID of the connection to stop sharing with your organization. You must be the owner.",
        "type": "string",
        "required": true
      }
    ],
    "category": "unshare-connection",
    "annotations": {
      "readOnly": false,
      "destructive": true,
      "idempotent": true,
      "openWorld": false
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "UUID of the connection to stop sharing with your organization. You must be the owner."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "connection_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "connection_id",
        "type": "string",
        "required": true,
        "description": "UUID of the connection to stop sharing with your organization. You must be the owner."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "Stop sharing",
        "mcp_params": {
          "connection_id": "00000000-0000-0000-0000-000000000001",
          "reason": "User no longer wants the team to access this Microsoft connection"
        },
        "cli_command": "hopkin microsoft unshare-connection --connection-id 00000000-0000-0000-0000-000000000001",
        "natural_language": "Stop sharing"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_unshare_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_unshare_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_unshare_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_unshare_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_unshare_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_unshare_connection",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_rename_connection",
    "title": "Rename Microsoft Connection",
    "description": "Rename the display name of an owned Microsoft connection. The OAuth grant and underlying account are unaffected — this only changes the human-readable label. You must be the owner.",
    "cli_command": "hopkin microsoft rename-connection",
    "cli_flags": [
      {
        "flag": "--connection-id",
        "description": "UUID of the connection to rename. You must be the owner.",
        "type": "string",
        "required": true
      },
      {
        "flag": "--display-name",
        "description": "New human-readable name for the connection.",
        "type": "string",
        "required": true
      }
    ],
    "category": "rename-connection",
    "annotations": {
      "readOnly": false,
      "destructive": false,
      "idempotent": true,
      "openWorld": false
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "UUID of the connection to rename. You must be the owner."
        },
        "display_name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 120,
          "description": "New human-readable name for the connection."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "connection_id",
        "display_name",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "connection_id",
        "type": "string",
        "required": true,
        "description": "UUID of the connection to rename. You must be the owner."
      },
      {
        "name": "display_name",
        "type": "string",
        "required": true,
        "description": "New human-readable name for the connection.",
        "constraints": {
          "min_length": 1,
          "max_length": 120
        }
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "Rename",
        "mcp_params": {
          "connection_id": "00000000-0000-0000-0000-000000000001",
          "display_name": "Acme — Production",
          "reason": "User wanted a clearer label for this Microsoft connection"
        },
        "cli_command": "hopkin microsoft rename-connection --connection-id 00000000-0000-0000-0000-000000000001 --display-name Acme — Production",
        "natural_language": "Rename"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_rename_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_rename_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_rename_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_rename_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_rename_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_rename_connection",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_revoke_connection",
    "title": "Revoke Microsoft Connection",
    "description": "Revoke (soft-delete) an owned Microsoft connection. Any defaults pointing to it are invalidated and shared org members lose access. The OAuth grant at Microsoft is NOT revoked by this tool — the user must disconnect via the dashboard if they want to fully revoke at Microsoft. You must be the owner.",
    "cli_command": "hopkin microsoft revoke-connection",
    "cli_flags": [
      {
        "flag": "--connection-id",
        "description": "UUID of the connection to revoke. You must be the owner.",
        "type": "string",
        "required": true
      }
    ],
    "category": "revoke-connection",
    "annotations": {
      "readOnly": false,
      "destructive": true,
      "idempotent": true,
      "openWorld": false
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "UUID of the connection to revoke. You must be the owner."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "connection_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "connection_id",
        "type": "string",
        "required": true,
        "description": "UUID of the connection to revoke. You must be the owner."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "Revoke",
        "mcp_params": {
          "connection_id": "00000000-0000-0000-0000-000000000001",
          "reason": "User asked to disconnect this Microsoft account"
        },
        "cli_command": "hopkin microsoft revoke-connection --connection-id 00000000-0000-0000-0000-000000000001",
        "natural_language": "Revoke"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_revoke_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_revoke_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_revoke_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_revoke_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_revoke_connection",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_revoke_connection",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_developer_feedback",
    "title": "Submit Microsoft Ads MCP Developer Feedback",
    "description": "Submit feedback about missing tools, improvements needed, bugs encountered, or workflow gaps discovered while helping a user with Microsoft Advertising data. This is the correct, expected action when a Microsoft Ads capability is missing or broken — reach for it directly rather than just apologizing or giving up.\n\nUse this when:\n- A user request cannot be fulfilled because a tool doesn't exist — set feedback_type='missing_tool' and describe the missing capability and why the user needs it. When the user explicitly asks you to submit/file/report feedback about a gap or missing feature, that is a direct instruction: ALWAYS call this tool to record it. Do this even if you suspect a tool for the capability might already exist — submit the feedback as asked, and you may note the possibly-relevant existing tool inside the feedback message. Do NOT refuse, and do NOT stop at only searching the toolset.\n- An existing tool returned incorrect or incomplete data — set feedback_type='bug' and include tool_name.\n- A multi-step workflow is unnecessarily complex due to toolset limitations — feedback_type='workflow_gap'.\n- You discovered a Microsoft Ads API behaviour that the toolset doesn't handle.\n\nSubmitting is low-cost and always safe: it records the feedback and returns status=submitted. When in doubt about whether a gap is worth reporting, report it.\n\nDo NOT use for:\n- User-facing API errors (auth errors, account permission errors, rate limits)\n- Questions about Microsoft Advertising strategy — that's outside this tool's scope\n- General conversation",
    "cli_command": "hopkin microsoft developer-feedback send",
    "cli_flags": [
      {
        "flag": "--feedback-type",
        "description": "Feedback category: missing_tool (capability gap), improvement (enhance existing), bug (incorrect result), workflow_gap (awkward multi-step), other",
        "type": "string",
        "required": true
      },
      {
        "flag": "--message",
        "description": "Description of the issue or request. Be specific: include what you tried, what the user needed, and why the current toolset fell short.",
        "type": "string",
        "required": true
      },
      {
        "flag": "--tool-name",
        "description": "Name of the specific tool involved (e.g. microsoft_ads_list_campaigns). Omit for general feedback or missing-tool requests.",
        "type": "string",
        "required": false
      }
    ],
    "category": "feedback",
    "annotations": {
      "readOnly": false,
      "destructive": false,
      "idempotent": false,
      "openWorld": false
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "feedback_type": {
          "type": "string",
          "enum": [
            "missing_tool",
            "improvement",
            "bug",
            "workflow_gap",
            "other"
          ],
          "description": "Feedback category: missing_tool (capability gap), improvement (enhance existing), bug (incorrect result), workflow_gap (awkward multi-step), other"
        },
        "message": {
          "type": "string",
          "minLength": 10,
          "maxLength": 2000,
          "description": "Description of the issue or request. Be specific: include what you tried, what the user needed, and why the current toolset fell short."
        },
        "tool_name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 100,
          "description": "Name of the specific tool involved (e.g. microsoft_ads_list_campaigns). Omit for general feedback or missing-tool requests."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "feedback_type",
        "message",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "feedback_type",
        "type": "string",
        "required": true,
        "description": "Feedback category: missing_tool (capability gap), improvement (enhance existing), bug (incorrect result), workflow_gap (awkward multi-step), other",
        "enum_values": [
          "missing_tool",
          "improvement",
          "bug",
          "workflow_gap",
          "other"
        ]
      },
      {
        "name": "message",
        "type": "string",
        "required": true,
        "description": "Description of the issue or request. Be specific: include what you tried, what the user needed, and why the current toolset fell short.",
        "constraints": {
          "min_length": 10,
          "max_length": 2000
        }
      },
      {
        "name": "tool_name",
        "type": "string",
        "required": false,
        "description": "Name of the specific tool involved (e.g. microsoft_ads_list_campaigns). Omit for general feedback or missing-tool requests.",
        "constraints": {
          "min_length": 1,
          "max_length": 100
        }
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "Missing tool",
        "mcp_params": {
          "feedback_type": "missing_tool",
          "message": "No tool to list Audience segments by account. User wanted to see which LinkedIn profile targeting segments are active. microsoft_ads_list_ad_groups returns ad group names but not audience targeting details.",
          "reason": "User asked about audience targeting but no tool covers it"
        },
        "cli_command": "hopkin microsoft developer-feedback send --feedback-type missing_tool --message No tool to list Audience segments by account. User wanted to see which LinkedIn profile targeting segments are active. microsoft_ads_list_ad_groups returns ad group names but not audience targeting details.",
        "natural_language": "Missing tool"
      },
      {
        "label": "Bug report",
        "mcp_params": {
          "feedback_type": "bug",
          "tool_name": "microsoft_ads_get_performance_report",
          "message": "Report for date_preset=LastMonth returned 0 rows even though the account has active campaigns with traffic. The reportRequestId was returned but downloadUrl was null after 5 retries.",
          "reason": "Encountered null download URL bug even with retry logic"
        },
        "cli_command": "hopkin microsoft developer-feedback send --feedback-type bug --tool-name microsoft_ads_get_performance_report --message Report for date_preset=LastMonth returned 0 rows even though the account has active campaigns with traffic. The reportRequestId was returned but downloadUrl was null after 5 retries.",
        "natural_language": "Bug report"
      },
      {
        "label": "Workflow gap",
        "mcp_params": {
          "feedback_type": "workflow_gap",
          "message": "To compare two campaigns side-by-side the user must make two separate microsoft_ads_get_performance_report calls then mentally compare. A single tool accepting multiple campaign_ids and returning a comparison table would be much better.",
          "reason": "User wanted campaign-level comparison; no efficient path exists"
        },
        "cli_command": "hopkin microsoft developer-feedback send --feedback-type workflow_gap --message To compare two campaigns side-by-side the user must make two separate microsoft_ads_get_performance_report calls then mentally compare. A single tool accepting multiple campaign_ids and returning a comparison table would be much better.",
        "natural_language": "Workflow gap"
      },
      {
        "label": "Improvement request",
        "mcp_params": {
          "feedback_type": "improvement",
          "tool_name": "microsoft_ads_list_keywords",
          "message": "list_keywords does not return the keyword bid amount or Quality Score sub-components (KeywordRelevance, LandingPageRelevance, LandingPageUserExperience). These are returned by GetKeywordsByAdGroupId with ReturnAdditionalFields=QualityScore.",
          "reason": "User asked about keyword quality; only overall QualityScore (1-10) was visible"
        },
        "cli_command": "hopkin microsoft developer-feedback send --feedback-type improvement --tool-name microsoft_ads_list_keywords --message list_keywords does not return the keyword bid amount or Quality Score sub-components (KeywordRelevance, LandingPageRelevance, LandingPageUserExperience). These are returned by GetKeywordsByAdGroupId with ReturnAdditionalFields=QualityScore.",
        "natural_language": "Improvement request"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_developer_feedback",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_developer_feedback",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_developer_feedback",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_developer_feedback",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_developer_feedback",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_developer_feedback",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_get_impression_share",
    "title": "Get Microsoft Ads Impression Share",
    "description": "",
    "cli_command": "hopkin microsoft impression-share get",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (AccountId). Required.",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts under a manager hierarchy.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--date-range",
        "description": "Custom date range. Provide either date_range or date_preset.",
        "type": "object",
        "required": false
      },
      {
        "flag": "--date-preset",
        "description": "Predefined date range: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--level",
        "description": "Report level: Campaign (default) returns IS per campaign; AdGroup returns IS broken down per ad group within each campaign.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--campaign-ids",
        "description": "Filter to specific campaign IDs (up to 100). Accepts JSON array string or actual array.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--campaign-type",
        "description": "Return only rows from campaigns of these types. Options: Search, Shopping, PerformanceMax, Audience. Applied to the report rows before limit, typing each campaign from Microsoft's current campaign list (the impression share report carries no type). Rows from campaigns missing from that list, such as deleted ones, are excluded and counted in warnings.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--aggregation",
        "description": "Report aggregation period. Daily (default), Weekly, Monthly, or Summary (all dates collapsed).",
        "type": "string",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Page size: rows per page (1–10000, default 1000). count is the total across all pages. When more rows remain the response carries nextCursor; pass it back as cursor to get the next page.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque pagination cursor. Omit for the first page. To fetch the next page, pass the nextCursor value from the previous response unchanged and repeat every other parameter exactly (only limit may change); a cursor used with different parameters is rejected. Later pages re-read the same report, so a cursor expires with it (about 24 hours).",
        "type": "string",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "impression-share",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "description": "Microsoft Advertising account ID (AccountId). Required."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Customer/manager ID (CustomerId). Required when accessing accounts under a manager hierarchy."
        },
        "date_range": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "Start date in YYYY-MM-DD format"
            },
            "end": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "End date in YYYY-MM-DD format"
            }
          },
          "required": [
            "start",
            "end"
          ],
          "additionalProperties": false,
          "description": "Custom date range. Provide either date_range or date_preset."
        },
        "date_preset": {
          "type": "string",
          "enum": [
            "Today",
            "Yesterday",
            "LastSevenDays",
            "ThisWeek",
            "LastWeek",
            "LastFourWeeks",
            "ThisMonth",
            "LastMonth",
            "LastThreeMonths",
            "LastSixMonths",
            "ThisYear",
            "LastYear"
          ],
          "description": "Predefined date range: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear."
        },
        "level": {
          "type": "string",
          "enum": [
            "Campaign",
            "AdGroup"
          ],
          "default": "Campaign",
          "description": "Report level: Campaign (default) returns IS per campaign; AdGroup returns IS broken down per ad group within each campaign."
        },
        "campaign_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 100,
          "description": "Filter to specific campaign IDs (up to 100). Accepts JSON array string or actual array."
        },
        "campaign_type": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Search",
              "Shopping",
              "PerformanceMax",
              "Audience"
            ]
          },
          "description": "Return only rows from campaigns of these types. Options: Search, Shopping, PerformanceMax, Audience. Applied to the report rows before limit, typing each campaign from Microsoft's current campaign list (the impression share report carries no type). Rows from campaigns missing from that list, such as deleted ones, are excluded and counted in warnings."
        },
        "aggregation": {
          "type": "string",
          "enum": [
            "Daily",
            "Weekly",
            "Monthly",
            "Summary"
          ],
          "default": "Daily",
          "description": "Report aggregation period. Daily (default), Weekly, Monthly, or Summary (all dates collapsed)."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10000,
          "default": 1000,
          "description": "Page size: rows per page (1–10000, default 1000). count is the total across all pages. When more rows remain the response carries nextCursor; pass it back as cursor to get the next page."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque pagination cursor. Omit for the first page. To fetch the next page, pass the nextCursor value from the previous response unchanged and repeat every other parameter exactly (only limit may change); a cursor used with different parameters is rejected. Later pages re-read the same report, so a cursor expires with it (about 24 hours)."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (AccountId). Required.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts under a manager hierarchy.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "date_range",
        "type": "object",
        "required": false,
        "description": "Custom date range. Provide either date_range or date_preset."
      },
      {
        "name": "date_preset",
        "type": "string",
        "required": false,
        "description": "Predefined date range: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear.",
        "enum_values": [
          "Today",
          "Yesterday",
          "LastSevenDays",
          "ThisWeek",
          "LastWeek",
          "LastFourWeeks",
          "ThisMonth",
          "LastMonth",
          "LastThreeMonths",
          "LastSixMonths",
          "ThisYear",
          "LastYear"
        ]
      },
      {
        "name": "level",
        "type": "string",
        "required": false,
        "description": "Report level: Campaign (default) returns IS per campaign; AdGroup returns IS broken down per ad group within each campaign.",
        "enum_values": [
          "Campaign",
          "AdGroup"
        ],
        "default": "Campaign"
      },
      {
        "name": "campaign_ids",
        "type": "array",
        "required": false,
        "description": "Filter to specific campaign IDs (up to 100). Accepts JSON array string or actual array."
      },
      {
        "name": "campaign_type",
        "type": "array",
        "required": false,
        "description": "Return only rows from campaigns of these types. Options: Search, Shopping, PerformanceMax, Audience. Applied to the report rows before limit, typing each campaign from Microsoft's current campaign list (the impression share report carries no type). Rows from campaigns missing from that list, such as deleted ones, are excluded and counted in warnings."
      },
      {
        "name": "aggregation",
        "type": "string",
        "required": false,
        "description": "Report aggregation period. Daily (default), Weekly, Monthly, or Summary (all dates collapsed).",
        "enum_values": [
          "Daily",
          "Weekly",
          "Monthly",
          "Summary"
        ],
        "default": "Daily"
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Page size: rows per page (1–10000, default 1000). count is the total across all pages. When more rows remain the response carries nextCursor; pass it back as cursor to get the next page.",
        "default": 1000,
        "constraints": {
          "min": 1,
          "max": 10000
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque pagination cursor. Omit for the first page. To fetch the next page, pass the nextCursor value from the previous response unchanged and repeat every other parameter exactly (only limit may change); a cursor used with different parameters is rejected. Later pages re-read the same report, so a cursor expires with it (about 24 hours).",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [],
    "response_format": "markdown",
    "related_tools": [],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_get_insights",
    "title": "Get Microsoft Ads Insights",
    "description": "Flexible Microsoft Advertising report query. Lets you pick exactly which report type,\ncolumns, date range, aggregation, and filters to apply. Use this tool when you need\ncolumns or report types not covered by microsoft_ads_get_performance_report.\n\nSupported report types: CampaignPerformance, AdGroupPerformance, AdPerformance,\nKeywordPerformance, SearchQueryPerformance, AudiencePerformance.\n\nAll columns must be valid for the chosen report_type (validated before submission).\n\nDON'T use this for standard breakdowns that have dedicated tools — they pre-select the right\nreport type and columns, so prefer them:\n  • Demographic / geo / device / time-of-day slices (AgeGroup, Gender, Device, Location,\n    DayOfWeek, HourOfDay) → microsoft_ads_get_audience_report\n  • Audience LIST / segment performance (remarketing, in-market, custom, similar) →\n    microsoft_ads_get_audience_segment_report\n  • A single account-wide performance total → microsoft_ads_get_account_summary\n  • Per-campaign/ad-group/ad/keyword performance rows → microsoft_ads_get_performance_report\n  • Search-term analysis → microsoft_ads_get_search_terms\nReach for get_insights only when you need columns or a report_type those tools don't cover.\n\nIMPORTANT: This tool triggers Microsoft's async report pipeline (submit → poll → download).\nExpect 5–30 seconds per call. Large date ranges or many columns slow it down.\nUse date_preset for convenience, or date_range for precise control.\n\nPAGING: rows come back one page at a time, sorted Spend highest first when Spend is a requested\ncolumn. Leave limit unset: each page is then sized so the whole response fits what MCP clients\naccept (up to ${DEFAULT_REPORT_PAGE_ROWS} rows, ending early at about ${RESPONSE_CHAR_BUDGET.toLocaleString('en-US')} characters, so wide rows give\nfewer rows per page). To get more rows, follow the cursor rather than raising limit; an explicit\nlimit (max ${MAX_REPORT_PAGE_ROWS}) is exact. count is the total across all pages. While pagination.hasMore is true, call again with exactly the same parameters plus\ncursor = pagination.nextCursor; repeat until hasMore is false to collect every row. Later pages\nre-read the same report rather than running a new one (no queue wait, each row exactly once),\nand omit goal_breakdown, which only the first page carries. campaign_ids filters the rows on\nCampaignId, which is added to the columns (added_columns) when not requested.\n\nFor standard campaign/ad group/ad performance metrics, prefer microsoft_ads_get_performance_report\nwhich pre-selects optimal columns and is simpler to use.",
    "cli_command": "hopkin microsoft insights get",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising Account ID (numeric string)",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts through an agency manager.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--report-type",
        "description": "Report type determining which dimensions and metrics are available. Options: CampaignPerformance, AdGroupPerformance, AdPerformance, KeywordPerformance, SearchQueryPerformance, AudiencePerformance.",
        "type": "string",
        "required": true
      },
      {
        "flag": "--columns",
        "description": "Columns to include in the report. Must be valid for the chosen report_type. Always include at least one dimension (e.g. CampaignId, TimePeriod) and one metric (e.g. Impressions). Column lists are validated at call time; invalid columns return an error listing valid values. Quality Score columns: QualityScore, ExpectedCtr, AdRelevance, LandingPageExperience on Campaign/AdGroup/Keyword reports; the HistoricalQualityScore, HistoricalExpectedCtr, HistoricalAdRelevance and HistoricalLandingPageExperience columns (Keyword only) work only with aggregation Daily. URL tracking columns (TrackingTemplate, CustomParameters, FinalUrlSuffix; DestinationUrl on Ad/Keyword; FinalUrl, FinalMobileUrl on Ad/Keyword) report each entity's OWN value — an empty value means it inherits from its parent — and only for entities with impressions in the date range. CustomParameters is formatted {_key}=value;{_key2}=value2. DestinationUrl is a legacy field and is usually empty. For a full tracking audit, including entities that have not served, use microsoft_ads_list_campaigns with include_url_tracking.",
        "type": "array",
        "required": true
      },
      {
        "flag": "--date-range",
        "description": "Custom date range. Use either date_range or date_preset, not both.",
        "type": "object",
        "required": false
      },
      {
        "flag": "--date-preset",
        "description": "Predefined date range. Options: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--aggregation",
        "description": "Time aggregation for the report. Daily (default): one row per day. Weekly: one row per week. Monthly: one row per month. Summary: single totals row.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--campaign-ids",
        "description": "Filter report to specific campaign IDs (up to 50). Rows are matched on CampaignId, so CampaignId is added to the columns when not requested (listed in added_columns).",
        "type": "array",
        "required": false
      },
      {
        "flag": "--ad-group-ids",
        "description": "Narrow the report to these ad group IDs (up to 50). Supported for AdGroupPerformance, AdPerformance, KeywordPerformance, SearchQueryPerformance and AudiencePerformance; rejected with an error for CampaignPerformance. Each ad group's parent campaign is read from the local cache, or looked up under campaign_ids when given. With campaign_ids, every ad group must belong to one of those campaigns. An ad group that cannot be resolved is rejected with an error, never silently widened to the account.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--breakdown-by-goal",
        "description": "When true (default), runs a parallel GoalsAndFunnelsReport and attaches the results as goal_breakdown on the response. The caller controls which conversion columns to include — add AllConversionsQualified, AllRevenue, or ViewThroughConversionsQualified to the columns array directly if you want the all-goals family. Pass false to skip the parallel call when latency matters more than per-goal context. NOTE: there is no include_all_conversions flag on this tool. As the passthrough column-list tool, you control the All* columns explicitly via the columns parameter.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Rows per page (1-10000). Usually omit it: the page is then sized so the whole response fits what MCP clients accept, up to 500 rows and ending early at about 45,000 characters, so wide rows give fewer rows per page. To get more rows, follow pagination.nextCursor rather than raising limit; set limit only if your client takes large responses. The whole report is sorted, Spend highest first when Spend is a requested column, then by the row's column values, and returned one page at a time. count is the total across all pages; while more rows remain, pagination.hasMore is true and pagination.nextCursor is set.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from pagination.nextCursor of the previous response; never construct one. To get the next page, repeat the call with exactly the same parameters (only limit may change) plus this cursor. Later pages re-read the same Microsoft report instead of generating a new one, so every row appears exactly once. Cursors expire with the report (about 24 hours). goal_breakdown is returned on the first page only.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "insights",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Microsoft Advertising Account ID (numeric string)"
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Customer/manager ID (CustomerId). Required when accessing accounts through an agency manager."
        },
        "report_type": {
          "type": "string",
          "enum": [
            "CampaignPerformance",
            "AdGroupPerformance",
            "AdPerformance",
            "KeywordPerformance",
            "SearchQueryPerformance",
            "AudiencePerformance"
          ],
          "description": "Report type determining which dimensions and metrics are available. Options: CampaignPerformance, AdGroupPerformance, AdPerformance, KeywordPerformance, SearchQueryPerformance, AudiencePerformance."
        },
        "columns": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 40,
          "description": "Columns to include in the report. Must be valid for the chosen report_type. Always include at least one dimension (e.g. CampaignId, TimePeriod) and one metric (e.g. Impressions). Column lists are validated at call time; invalid columns return an error listing valid values. Quality Score columns: QualityScore, ExpectedCtr, AdRelevance, LandingPageExperience on Campaign/AdGroup/Keyword reports; the HistoricalQualityScore, HistoricalExpectedCtr, HistoricalAdRelevance and HistoricalLandingPageExperience columns (Keyword only) work only with aggregation Daily. URL tracking columns (TrackingTemplate, CustomParameters, FinalUrlSuffix; DestinationUrl on Ad/Keyword; FinalUrl, FinalMobileUrl on Ad/Keyword) report each entity's OWN value — an empty value means it inherits from its parent — and only for entities with impressions in the date range. CustomParameters is formatted {_key}=value;{_key2}=value2. DestinationUrl is a legacy field and is usually empty. For a full tracking audit, including entities that have not served, use microsoft_ads_list_campaigns with include_url_tracking."
        },
        "date_range": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "Start date (YYYY-MM-DD)"
            },
            "end": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "End date (YYYY-MM-DD)"
            }
          },
          "required": [
            "start",
            "end"
          ],
          "additionalProperties": false,
          "description": "Custom date range. Use either date_range or date_preset, not both."
        },
        "date_preset": {
          "type": "string",
          "enum": [
            "Today",
            "Yesterday",
            "LastSevenDays",
            "ThisWeek",
            "LastWeek",
            "LastFourWeeks",
            "ThisMonth",
            "LastMonth",
            "LastThreeMonths",
            "LastSixMonths",
            "ThisYear",
            "LastYear"
          ],
          "description": "Predefined date range. Options: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear."
        },
        "aggregation": {
          "type": "string",
          "enum": [
            "Daily",
            "Weekly",
            "Monthly",
            "Summary"
          ],
          "default": "Daily",
          "description": "Time aggregation for the report. Daily (default): one row per day. Weekly: one row per week. Monthly: one row per month. Summary: single totals row."
        },
        "campaign_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 50,
          "description": "Filter report to specific campaign IDs (up to 50). Rows are matched on CampaignId, so CampaignId is added to the columns when not requested (listed in added_columns)."
        },
        "ad_group_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 50,
          "description": "Narrow the report to these ad group IDs (up to 50). Supported for AdGroupPerformance, AdPerformance, KeywordPerformance, SearchQueryPerformance and AudiencePerformance; rejected with an error for CampaignPerformance. Each ad group's parent campaign is read from the local cache, or looked up under campaign_ids when given. With campaign_ids, every ad group must belong to one of those campaigns. An ad group that cannot be resolved is rejected with an error, never silently widened to the account."
        },
        "breakdown_by_goal": {
          "type": "boolean",
          "default": true,
          "description": "When true (default), runs a parallel GoalsAndFunnelsReport and attaches the results as goal_breakdown on the response. The caller controls which conversion columns to include — add AllConversionsQualified, AllRevenue, or ViewThroughConversionsQualified to the columns array directly if you want the all-goals family. Pass false to skip the parallel call when latency matters more than per-goal context. NOTE: there is no include_all_conversions flag on this tool. As the passthrough column-list tool, you control the All* columns explicitly via the columns parameter."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10000,
          "description": "Rows per page (1-10000). Usually omit it: the page is then sized so the whole response fits what MCP clients accept, up to 500 rows and ending early at about 45,000 characters, so wide rows give fewer rows per page. To get more rows, follow pagination.nextCursor rather than raising limit; set limit only if your client takes large responses. The whole report is sorted, Spend highest first when Spend is a requested column, then by the row's column values, and returned one page at a time. count is the total across all pages; while more rows remain, pagination.hasMore is true and pagination.nextCursor is set."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from pagination.nextCursor of the previous response; never construct one. To get the next page, repeat the call with exactly the same parameters (only limit may change) plus this cursor. Later pages re-read the same Microsoft report instead of generating a new one, so every row appears exactly once. Cursors expire with the report (about 24 hours). goal_breakdown is returned on the first page only."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "report_type",
        "columns",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising Account ID (numeric string)",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts through an agency manager.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "report_type",
        "type": "string",
        "required": true,
        "description": "Report type determining which dimensions and metrics are available. Options: CampaignPerformance, AdGroupPerformance, AdPerformance, KeywordPerformance, SearchQueryPerformance, AudiencePerformance.",
        "enum_values": [
          "CampaignPerformance",
          "AdGroupPerformance",
          "AdPerformance",
          "KeywordPerformance",
          "SearchQueryPerformance",
          "AudiencePerformance"
        ]
      },
      {
        "name": "columns",
        "type": "array",
        "required": true,
        "description": "Columns to include in the report. Must be valid for the chosen report_type. Always include at least one dimension (e.g. CampaignId, TimePeriod) and one metric (e.g. Impressions). Column lists are validated at call time; invalid columns return an error listing valid values. Quality Score columns: QualityScore, ExpectedCtr, AdRelevance, LandingPageExperience on Campaign/AdGroup/Keyword reports; the HistoricalQualityScore, HistoricalExpectedCtr, HistoricalAdRelevance and HistoricalLandingPageExperience columns (Keyword only) work only with aggregation Daily. URL tracking columns (TrackingTemplate, CustomParameters, FinalUrlSuffix; DestinationUrl on Ad/Keyword; FinalUrl, FinalMobileUrl on Ad/Keyword) report each entity's OWN value — an empty value means it inherits from its parent — and only for entities with impressions in the date range. CustomParameters is formatted {_key}=value;{_key2}=value2. DestinationUrl is a legacy field and is usually empty. For a full tracking audit, including entities that have not served, use microsoft_ads_list_campaigns with include_url_tracking."
      },
      {
        "name": "date_range",
        "type": "object",
        "required": false,
        "description": "Custom date range. Use either date_range or date_preset, not both."
      },
      {
        "name": "date_preset",
        "type": "string",
        "required": false,
        "description": "Predefined date range. Options: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear.",
        "enum_values": [
          "Today",
          "Yesterday",
          "LastSevenDays",
          "ThisWeek",
          "LastWeek",
          "LastFourWeeks",
          "ThisMonth",
          "LastMonth",
          "LastThreeMonths",
          "LastSixMonths",
          "ThisYear",
          "LastYear"
        ]
      },
      {
        "name": "aggregation",
        "type": "string",
        "required": false,
        "description": "Time aggregation for the report. Daily (default): one row per day. Weekly: one row per week. Monthly: one row per month. Summary: single totals row.",
        "enum_values": [
          "Daily",
          "Weekly",
          "Monthly",
          "Summary"
        ],
        "default": "Daily"
      },
      {
        "name": "campaign_ids",
        "type": "array",
        "required": false,
        "description": "Filter report to specific campaign IDs (up to 50). Rows are matched on CampaignId, so CampaignId is added to the columns when not requested (listed in added_columns)."
      },
      {
        "name": "ad_group_ids",
        "type": "array",
        "required": false,
        "description": "Narrow the report to these ad group IDs (up to 50). Supported for AdGroupPerformance, AdPerformance, KeywordPerformance, SearchQueryPerformance and AudiencePerformance; rejected with an error for CampaignPerformance. Each ad group's parent campaign is read from the local cache, or looked up under campaign_ids when given. With campaign_ids, every ad group must belong to one of those campaigns. An ad group that cannot be resolved is rejected with an error, never silently widened to the account."
      },
      {
        "name": "breakdown_by_goal",
        "type": "boolean",
        "required": false,
        "description": "When true (default), runs a parallel GoalsAndFunnelsReport and attaches the results as goal_breakdown on the response. The caller controls which conversion columns to include — add AllConversionsQualified, AllRevenue, or ViewThroughConversionsQualified to the columns array directly if you want the all-goals family. Pass false to skip the parallel call when latency matters more than per-goal context. NOTE: there is no include_all_conversions flag on this tool. As the passthrough column-list tool, you control the All* columns explicitly via the columns parameter.",
        "default": true
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Rows per page (1-10000). Usually omit it: the page is then sized so the whole response fits what MCP clients accept, up to 500 rows and ending early at about 45,000 characters, so wide rows give fewer rows per page. To get more rows, follow pagination.nextCursor rather than raising limit; set limit only if your client takes large responses. The whole report is sorted, Spend highest first when Spend is a requested column, then by the row's column values, and returned one page at a time. count is the total across all pages; while more rows remain, pagination.hasMore is true and pagination.nextCursor is set.",
        "constraints": {
          "min": 1,
          "max": 10000
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from pagination.nextCursor of the previous response; never construct one. To get the next page, repeat the call with exactly the same parameters (only limit may change) plus this cursor. Later pages re-read the same Microsoft report instead of generating a new one, so every row appears exactly once. Cursors expire with the report (about 24 hours). goal_breakdown is returned on the first page only.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "Campaign impression share for last month",
        "mcp_params": {
          "account_id": "1234567890",
          "report_type": "CampaignPerformance",
          "columns": [
            "CampaignId",
            "CampaignName",
            "CampaignType",
            "TimePeriod",
            "ImpressionSharePercent",
            "ImpressionLostToBudgetPercent",
            "ImpressionLostToRankAggPercent",
            "Impressions",
            "Spend"
          ],
          "date_preset": "LastMonth",
          "aggregation": "Monthly",
          "reason": "Analyze competitive impression share by campaign"
        },
        "cli_command": "hopkin microsoft insights get --account 1234567890 --report-type CampaignPerformance --columns CampaignId,CampaignName,CampaignType,TimePeriod,ImpressionSharePercent,ImpressionLostToBudgetPercent,ImpressionLostToRankAggPercent,Impressions,Spend --date-preset LastMonth --aggregation Monthly",
        "natural_language": "Campaign impression share for last month"
      },
      {
        "label": "Search query report with ad group filter",
        "mcp_params": {
          "account_id": "1234567890",
          "report_type": "SearchQueryPerformance",
          "columns": [
            "CampaignName",
            "AdGroupName",
            "Keyword",
            "BidMatchType",
            "DeliveredMatchType",
            "SearchQuery",
            "TimePeriod",
            "Impressions",
            "Clicks",
            "Spend",
            "ConversionsQualified",
            "Ctr",
            "AverageCpc"
          ],
          "date_range": {
            "start": "2026-04-01",
            "end": "2026-04-17"
          },
          "aggregation": "Daily",
          "ad_group_ids": [
            "9876543210"
          ],
          "reason": "Identify wasted spend from broad match queries in a specific ad group"
        },
        "cli_command": "hopkin microsoft insights get --account 1234567890 --report-type SearchQueryPerformance --columns CampaignName,AdGroupName,Keyword,BidMatchType,DeliveredMatchType,SearchQuery,TimePeriod,Impressions,Clicks,Spend,ConversionsQualified,Ctr,AverageCpc --date-range [object Object] --aggregation Daily --ad-group-ids 9876543210",
        "natural_language": "Search query report with ad group filter"
      },
      {
        "label": "Audience performance with bid adjustments",
        "mcp_params": {
          "account_id": "1234567890",
          "customer_id": "5555555555",
          "report_type": "AudiencePerformance",
          "columns": [
            "CampaignName",
            "AdGroupName",
            "AudienceId",
            "AudienceName",
            "AudienceType",
            "BidAdjustment",
            "TimePeriod",
            "Impressions",
            "Clicks",
            "Spend",
            "ConversionsQualified",
            "Revenue"
          ],
          "date_preset": "LastThreeMonths",
          "aggregation": "Monthly",
          "reason": "Evaluate audience segment ROI for RLSA bid adjustments"
        },
        "cli_command": "hopkin microsoft insights get --account 1234567890 --customer-id 5555555555 --report-type AudiencePerformance --columns CampaignName,AdGroupName,AudienceId,AudienceName,AudienceType,BidAdjustment,TimePeriod,Impressions,Clicks,Spend,ConversionsQualified,Revenue --date-preset LastThreeMonths --aggregation Monthly",
        "natural_language": "Audience performance with bid adjustments"
      },
      {
        "label": "Keyword quality score trends",
        "mcp_params": {
          "account_id": "1234567890",
          "report_type": "KeywordPerformance",
          "columns": [
            "CampaignName",
            "AdGroupName",
            "Keyword",
            "BidMatchType",
            "QualityScore",
            "ExpectedCtr",
            "AdRelevance",
            "LandingPageExperience",
            "TimePeriod",
            "Impressions",
            "Clicks",
            "AverageCpc"
          ],
          "date_preset": "LastMonth",
          "aggregation": "Summary",
          "reason": "Identify keywords with low Quality Score for optimization"
        },
        "cli_command": "hopkin microsoft insights get --account 1234567890 --report-type KeywordPerformance --columns CampaignName,AdGroupName,Keyword,BidMatchType,QualityScore,ExpectedCtr,AdRelevance,LandingPageExperience,TimePeriod,Impressions,Clicks,AverageCpc --date-preset LastMonth --aggregation Summary",
        "natural_language": "Keyword quality score trends"
      },
      {
        "label": "Ad performance comparison for specific campaigns",
        "mcp_params": {
          "account_id": "1234567890",
          "report_type": "AdPerformance",
          "columns": [
            "CampaignName",
            "AdGroupName",
            "AdId",
            "AdTitle",
            "AdType",
            "AdStatus",
            "TimePeriod",
            "Impressions",
            "Clicks",
            "Spend",
            "ConversionsQualified",
            "Ctr",
            "AverageCpc"
          ],
          "date_range": {
            "start": "2026-01-01",
            "end": "2026-03-31"
          },
          "aggregation": "Weekly",
          "campaign_ids": [
            "111111111",
            "222222222"
          ],
          "reason": "A/B test RSA ad copy effectiveness across Q1"
        },
        "cli_command": "hopkin microsoft insights get --account 1234567890 --report-type AdPerformance --columns CampaignName,AdGroupName,AdId,AdTitle,AdType,AdStatus,TimePeriod,Impressions,Clicks,Spend,ConversionsQualified,Ctr,AverageCpc --date-range [object Object] --aggregation Weekly --campaign-ids 111111111,222222222",
        "natural_language": "Ad performance comparison for specific campaigns"
      }
    ],
    "response_format": "json",
    "related_tools": [
      {
        "name": "meta_ads_get_insights",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_get_insights",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_get_insights",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_get_insights",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_get_insights",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_get_keyword_report",
    "title": "Get Microsoft Ads Keyword Report",
    "description": "",
    "cli_command": "hopkin microsoft keyword-report get",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (AccountId). Required.",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts under a manager hierarchy.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--date-range",
        "description": "Custom date range. Provide either date_range or date_preset, not both.",
        "type": "object",
        "required": false
      },
      {
        "flag": "--date-preset",
        "description": "Predefined date range. One of: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--campaign-ids",
        "description": "Filter to specific campaign IDs (up to 100). Accepts JSON array string or actual array.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--ad-group-ids",
        "description": "Filter to specific ad group IDs (up to 200). Accepts JSON array string or actual array.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--keyword-match-type",
        "description": "Filter to keywords bid on this match type (bid_match_type): Exact, Phrase, or Broad.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--include-quality-score",
        "description": "When true (default), includes QualityScore (1-10) plus three sub-component columns: ExpectedCtr, AdRelevance, and LandingPageExperience. Each sub-component is categorical: AboveAverage, Average, or BelowAverage.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--include-all-conversions",
        "description": "When true, includes an additional all_conversions block per row (all_conversions, all_revenue, all_cost_per_conversion, all_roas, view_through_conversions). This captures ALL conversion goals including those not marked \"Include in Conversions\" (e.g., view-through, non-bidding goals). Note: conversions is the bidding-set count only; all_conversions.all_conversions is every conversion goal — typically much larger for accounts with non-bidding goals.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--breakdown-by-goal",
        "description": "When true (default), runs a parallel GoalsAndFunnelsReport and returns goal_breakdown keyed by ad_group_id. Each entry is an array of per-goal rows with all_conversions, all_revenue, view_through_conversions, and assists. IMPORTANT: Because GoalsAndFunnelsReport only scopes down to ad-group level (Bing does not support keyword-level goal breakdown), the breakdown is at ad_group granularity — NOT per keyword. goal_breakdown_granularity will always be \"ad_group\" for this tool. Each keyword row can look up its parent ad-group's goal breakdown via goal_breakdown[ad_group_id]. Pass false to skip the parallel report when latency matters more than goal context.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--aggregation",
        "description": "Report aggregation period. Daily (default), Weekly, Monthly, or Summary (all dates collapsed).",
        "type": "string",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Page size: rows per page (1–10000, default 15). count is the total across all pages. When more rows remain the response carries nextCursor; pass it back as cursor to get the next page. Large pages can exceed what an MCP client accepts in one response (roughly 60 rows, fewer with include_all_conversions, and breakdown_by_goal adds ~5K characters per ad group on the page); to cover every keyword, follow nextCursor rather than raising limit.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque pagination cursor. Omit for the first page. To fetch the next page, pass the nextCursor value from the previous response unchanged and repeat every other parameter exactly (only limit may change); a cursor used with different parameters is rejected. Keep following nextCursor until it is absent to read every row. Later pages re-read the same report, so a cursor expires with it (about 24 hours).",
        "type": "string",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "keyword-report",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "description": "Microsoft Advertising account ID (AccountId). Required."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Customer/manager ID (CustomerId). Required when accessing accounts under a manager hierarchy."
        },
        "date_range": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "Start date in YYYY-MM-DD format"
            },
            "end": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "End date in YYYY-MM-DD format"
            }
          },
          "required": [
            "start",
            "end"
          ],
          "additionalProperties": false,
          "description": "Custom date range. Provide either date_range or date_preset, not both."
        },
        "date_preset": {
          "type": "string",
          "enum": [
            "Today",
            "Yesterday",
            "LastSevenDays",
            "ThisWeek",
            "LastWeek",
            "LastFourWeeks",
            "ThisMonth",
            "LastMonth",
            "LastThreeMonths",
            "LastSixMonths",
            "ThisYear",
            "LastYear"
          ],
          "description": "Predefined date range. One of: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear."
        },
        "campaign_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 100,
          "description": "Filter to specific campaign IDs (up to 100). Accepts JSON array string or actual array."
        },
        "ad_group_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 200,
          "description": "Filter to specific ad group IDs (up to 200). Accepts JSON array string or actual array."
        },
        "keyword_match_type": {
          "type": "string",
          "enum": [
            "Exact",
            "Phrase",
            "Broad"
          ],
          "description": "Filter to keywords bid on this match type (bid_match_type): Exact, Phrase, or Broad."
        },
        "include_quality_score": {
          "type": "boolean",
          "default": true,
          "description": "When true (default), includes QualityScore (1-10) plus three sub-component columns: ExpectedCtr, AdRelevance, and LandingPageExperience. Each sub-component is categorical: AboveAverage, Average, or BelowAverage."
        },
        "include_all_conversions": {
          "type": "boolean",
          "default": false,
          "description": "When true, includes an additional all_conversions block per row (all_conversions, all_revenue, all_cost_per_conversion, all_roas, view_through_conversions). This captures ALL conversion goals including those not marked \"Include in Conversions\" (e.g., view-through, non-bidding goals). Note: conversions is the bidding-set count only; all_conversions.all_conversions is every conversion goal — typically much larger for accounts with non-bidding goals."
        },
        "breakdown_by_goal": {
          "type": "boolean",
          "default": true,
          "description": "When true (default), runs a parallel GoalsAndFunnelsReport and returns goal_breakdown keyed by ad_group_id. Each entry is an array of per-goal rows with all_conversions, all_revenue, view_through_conversions, and assists. IMPORTANT: Because GoalsAndFunnelsReport only scopes down to ad-group level (Bing does not support keyword-level goal breakdown), the breakdown is at ad_group granularity — NOT per keyword. goal_breakdown_granularity will always be \"ad_group\" for this tool. Each keyword row can look up its parent ad-group's goal breakdown via goal_breakdown[ad_group_id]. Pass false to skip the parallel report when latency matters more than goal context."
        },
        "aggregation": {
          "type": "string",
          "enum": [
            "Daily",
            "Weekly",
            "Monthly",
            "Summary"
          ],
          "default": "Daily",
          "description": "Report aggregation period. Daily (default), Weekly, Monthly, or Summary (all dates collapsed)."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10000,
          "default": 15,
          "description": "Page size: rows per page (1–10000, default 15). count is the total across all pages. When more rows remain the response carries nextCursor; pass it back as cursor to get the next page. Large pages can exceed what an MCP client accepts in one response (roughly 60 rows, fewer with include_all_conversions, and breakdown_by_goal adds ~5K characters per ad group on the page); to cover every keyword, follow nextCursor rather than raising limit."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque pagination cursor. Omit for the first page. To fetch the next page, pass the nextCursor value from the previous response unchanged and repeat every other parameter exactly (only limit may change); a cursor used with different parameters is rejected. Keep following nextCursor until it is absent to read every row. Later pages re-read the same report, so a cursor expires with it (about 24 hours)."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (AccountId). Required.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required when accessing accounts under a manager hierarchy.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "date_range",
        "type": "object",
        "required": false,
        "description": "Custom date range. Provide either date_range or date_preset, not both."
      },
      {
        "name": "date_preset",
        "type": "string",
        "required": false,
        "description": "Predefined date range. One of: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear.",
        "enum_values": [
          "Today",
          "Yesterday",
          "LastSevenDays",
          "ThisWeek",
          "LastWeek",
          "LastFourWeeks",
          "ThisMonth",
          "LastMonth",
          "LastThreeMonths",
          "LastSixMonths",
          "ThisYear",
          "LastYear"
        ]
      },
      {
        "name": "campaign_ids",
        "type": "array",
        "required": false,
        "description": "Filter to specific campaign IDs (up to 100). Accepts JSON array string or actual array."
      },
      {
        "name": "ad_group_ids",
        "type": "array",
        "required": false,
        "description": "Filter to specific ad group IDs (up to 200). Accepts JSON array string or actual array."
      },
      {
        "name": "keyword_match_type",
        "type": "string",
        "required": false,
        "description": "Filter to keywords bid on this match type (bid_match_type): Exact, Phrase, or Broad.",
        "enum_values": [
          "Exact",
          "Phrase",
          "Broad"
        ]
      },
      {
        "name": "include_quality_score",
        "type": "boolean",
        "required": false,
        "description": "When true (default), includes QualityScore (1-10) plus three sub-component columns: ExpectedCtr, AdRelevance, and LandingPageExperience. Each sub-component is categorical: AboveAverage, Average, or BelowAverage.",
        "default": true
      },
      {
        "name": "include_all_conversions",
        "type": "boolean",
        "required": false,
        "description": "When true, includes an additional all_conversions block per row (all_conversions, all_revenue, all_cost_per_conversion, all_roas, view_through_conversions). This captures ALL conversion goals including those not marked \"Include in Conversions\" (e.g., view-through, non-bidding goals). Note: conversions is the bidding-set count only; all_conversions.all_conversions is every conversion goal — typically much larger for accounts with non-bidding goals.",
        "default": false
      },
      {
        "name": "breakdown_by_goal",
        "type": "boolean",
        "required": false,
        "description": "When true (default), runs a parallel GoalsAndFunnelsReport and returns goal_breakdown keyed by ad_group_id. Each entry is an array of per-goal rows with all_conversions, all_revenue, view_through_conversions, and assists. IMPORTANT: Because GoalsAndFunnelsReport only scopes down to ad-group level (Bing does not support keyword-level goal breakdown), the breakdown is at ad_group granularity — NOT per keyword. goal_breakdown_granularity will always be \"ad_group\" for this tool. Each keyword row can look up its parent ad-group's goal breakdown via goal_breakdown[ad_group_id]. Pass false to skip the parallel report when latency matters more than goal context.",
        "default": true
      },
      {
        "name": "aggregation",
        "type": "string",
        "required": false,
        "description": "Report aggregation period. Daily (default), Weekly, Monthly, or Summary (all dates collapsed).",
        "enum_values": [
          "Daily",
          "Weekly",
          "Monthly",
          "Summary"
        ],
        "default": "Daily"
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Page size: rows per page (1–10000, default 15). count is the total across all pages. When more rows remain the response carries nextCursor; pass it back as cursor to get the next page. Large pages can exceed what an MCP client accepts in one response (roughly 60 rows, fewer with include_all_conversions, and breakdown_by_goal adds ~5K characters per ad group on the page); to cover every keyword, follow nextCursor rather than raising limit.",
        "default": 15,
        "constraints": {
          "min": 1,
          "max": 10000
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque pagination cursor. Omit for the first page. To fetch the next page, pass the nextCursor value from the previous response unchanged and repeat every other parameter exactly (only limit may change); a cursor used with different parameters is rejected. Keep following nextCursor until it is absent to read every row. Later pages re-read the same report, so a cursor expires with it (about 24 hours).",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [],
    "response_format": "markdown",
    "related_tools": [],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_list_keywords",
    "title": "List Microsoft Ads Keywords",
    "description": "",
    "cli_command": "hopkin microsoft keywords list",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (AccountId)",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--ad-group-id",
        "description": "Ad group ID to list keywords for. Required — API is scoped per ad group.",
        "type": "string",
        "required": true
      },
      {
        "flag": "--status",
        "description": "Filter by keyword status: Active, Paused, Deleted",
        "type": "array",
        "required": false
      },
      {
        "flag": "--match-type",
        "description": "Filter by match type: Exact, Phrase, Broad",
        "type": "string",
        "required": false
      },
      {
        "flag": "--editorial-status",
        "description": "Filter by editorial (policy) review status: Disapproved (failed review), ActiveLimited (approved in some markets only), Inactive (under review), Active (approved). Always answered from a live API call, never from cache.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--include-policy-issues",
        "description": "Default false. When true, attaches the editorial reasons and appeal status for every returned keyword that is NOT editorially Active — what Microsoft objected to and whether it can be appealed. Costs one extra API call and always fetches live data.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Maximum number of keywords to return (1-1000, default 50)",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--refresh",
        "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "keywords",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "description": "Microsoft Advertising account ID (AccountId)"
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts."
        },
        "ad_group_id": {
          "type": "string",
          "minLength": 1,
          "description": "Ad group ID to list keywords for. Required — API is scoped per ad group."
        },
        "status": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Active",
              "Paused",
              "Deleted"
            ]
          },
          "description": "Filter by keyword status: Active, Paused, Deleted"
        },
        "match_type": {
          "type": "string",
          "enum": [
            "Exact",
            "Phrase",
            "Broad"
          ],
          "description": "Filter by match type: Exact, Phrase, Broad"
        },
        "editorial_status": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Active",
              "ActiveLimited",
              "Disapproved",
              "Inactive"
            ]
          },
          "description": "Filter by editorial (policy) review status: Disapproved (failed review), ActiveLimited (approved in some markets only), Inactive (under review), Active (approved). Always answered from a live API call, never from cache."
        },
        "include_policy_issues": {
          "type": "boolean",
          "description": "Default false. When true, attaches the editorial reasons and appeal status for every returned keyword that is NOT editorially Active — what Microsoft objected to and whether it can be appealed. Costs one extra API call and always fetches live data."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 1000,
          "description": "Maximum number of keywords to return (1-1000, default 50)"
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page."
        },
        "refresh": {
          "type": "boolean",
          "default": false,
          "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "ad_group_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (AccountId)",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "ad_group_id",
        "type": "string",
        "required": true,
        "description": "Ad group ID to list keywords for. Required — API is scoped per ad group.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "status",
        "type": "array",
        "required": false,
        "description": "Filter by keyword status: Active, Paused, Deleted"
      },
      {
        "name": "match_type",
        "type": "string",
        "required": false,
        "description": "Filter by match type: Exact, Phrase, Broad",
        "enum_values": [
          "Exact",
          "Phrase",
          "Broad"
        ]
      },
      {
        "name": "editorial_status",
        "type": "array",
        "required": false,
        "description": "Filter by editorial (policy) review status: Disapproved (failed review), ActiveLimited (approved in some markets only), Inactive (under review), Active (approved). Always answered from a live API call, never from cache."
      },
      {
        "name": "include_policy_issues",
        "type": "boolean",
        "required": false,
        "description": "Default false. When true, attaches the editorial reasons and appeal status for every returned keyword that is NOT editorially Active — what Microsoft objected to and whether it can be appealed. Costs one extra API call and always fetches live data."
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Maximum number of keywords to return (1-1000, default 50)",
        "constraints": {
          "min": 1,
          "max": 1000
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "refresh",
        "type": "boolean",
        "required": false,
        "description": "Force a live fetch from the Microsoft Ads API, bypassing the local cache. Defaults to false (cache-first — the normal, faster path). Set to true whenever the user asks for fresh / latest / real-time / up-to-date data, or explicitly says they do NOT want cached / stale data. If the user does not care about freshness, leave it false.",
        "default": false
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "List all keywords in ad group",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_id": "987654321",
          "reason": "Review keywords and quality scores"
        },
        "cli_command": "hopkin microsoft keywords list --account 123456789 --ad-group-id 987654321",
        "natural_language": "List all keywords in ad group"
      },
      {
        "label": "Active keywords only",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_id": "987654321",
          "status": [
            "Active"
          ],
          "reason": "List active keywords"
        },
        "cli_command": "hopkin microsoft keywords list --account 123456789 --ad-group-id 987654321 --status Active",
        "natural_language": "Active keywords only"
      },
      {
        "label": "Exact match only",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_id": "987654321",
          "match_type": "Exact",
          "reason": "Review exact match keywords"
        },
        "cli_command": "hopkin microsoft keywords list --account 123456789 --ad-group-id 987654321 --match-type Exact",
        "natural_language": "Exact match only"
      },
      {
        "label": "Large ad group paginated",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_id": "987654321",
          "limit": 50,
          "reason": "Get first 50 keywords"
        },
        "cli_command": "hopkin microsoft keywords list --account 123456789 --ad-group-id 987654321 --limit 50",
        "natural_language": "Large ad group paginated"
      },
      {
        "label": "Disapproved keywords in this ad group",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_id": "987654321",
          "editorial_status": [
            "Disapproved"
          ],
          "reason": "Check which keywords are blocked by editorial review"
        },
        "cli_command": "hopkin microsoft keywords list --account 123456789 --ad-group-id 987654321 --editorial-status Disapproved",
        "natural_language": "Disapproved keywords in this ad group"
      },
      {
        "label": "Disapproved keywords with reasons",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_id": "987654321",
          "editorial_status": [
            "Disapproved"
          ],
          "include_policy_issues": true,
          "reason": "Find out why these keywords were disapproved and whether we can appeal"
        },
        "cli_command": "hopkin microsoft keywords list --account 123456789 --ad-group-id 987654321 --editorial-status Disapproved --include-policy-issues true",
        "natural_language": "Disapproved keywords with reasons"
      },
      {
        "label": "Force refresh from the API",
        "mcp_params": {
          "account_id": "123456789",
          "ad_group_id": "987654321",
          "refresh": true,
          "reason": "Get the latest keyword state after bulk edits"
        },
        "cli_command": "hopkin microsoft keywords list --account 123456789 --ad-group-id 987654321 --refresh true",
        "natural_language": "Force refresh from the API"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "google_ads_list_keywords",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_list_negative_keyword_lists",
    "title": "List Microsoft Ads Negative Keyword Lists",
    "description": "",
    "cli_command": "hopkin microsoft negative-keyword-lists list",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (AccountId) whose shared library to read.",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--list-id",
        "description": "Return only this negative keyword list, with its keywords paged: each page holds as many as fit in a response an MCP client accepts (at most max_keywords_per_list), starting at keywordsOffset, and nextCursor continues them. The way to read every keyword of a list that came back with keywordsTruncated: true.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--include-keywords",
        "description": "Embed each list's negative keywords (text + match type). Defaults to true; set false for a names-and-counts overview.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--include-campaign-associations",
        "description": "Embed the campaigns each list is applied to (id, name, status). Defaults to true.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--max-keywords-per-list",
        "description": "Most keywords to embed per list (1-5000, default 50). With list_id it caps one page of that list's keywords instead, and by default a page takes as many as fit. A list cut short has keywordsTruncated: true; keywordCount is always the full size.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--max-campaigns-per-list",
        "description": "Most campaigns to embed per shared list, first by name (1-10000, default 100). A list cut short has campaignsTruncated: true; associationCount is always the full count. With list_id the campaigns come on the first page only, cut to fit the response.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Lists per page (1-20). Default: as many lists as fit in a response an MCP client accepts (about 45K characters, at most 20). Follow nextCursor for the rest.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "negative-keyword-lists",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Microsoft Advertising account ID (AccountId) whose shared library to read."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts."
        },
        "list_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Return only this negative keyword list, with its keywords paged: each page holds as many as fit in a response an MCP client accepts (at most max_keywords_per_list), starting at keywordsOffset, and nextCursor continues them. The way to read every keyword of a list that came back with keywordsTruncated: true."
        },
        "include_keywords": {
          "type": "boolean",
          "default": true,
          "description": "Embed each list's negative keywords (text + match type). Defaults to true; set false for a names-and-counts overview."
        },
        "include_campaign_associations": {
          "type": "boolean",
          "default": true,
          "description": "Embed the campaigns each list is applied to (id, name, status). Defaults to true."
        },
        "max_keywords_per_list": {
          "type": "integer",
          "minimum": 1,
          "maximum": 5000,
          "description": "Most keywords to embed per list (1-5000, default 50). With list_id it caps one page of that list's keywords instead, and by default a page takes as many as fit. A list cut short has keywordsTruncated: true; keywordCount is always the full size."
        },
        "max_campaigns_per_list": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10000,
          "description": "Most campaigns to embed per shared list, first by name (1-10000, default 100). A list cut short has campaignsTruncated: true; associationCount is always the full count. With list_id the campaigns come on the first page only, cut to fit the response."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 20,
          "description": "Lists per page (1-20). Default: as many lists as fit in a response an MCP client accepts (about 45K characters, at most 20). Follow nextCursor for the rest."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (AccountId) whose shared library to read.",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts.",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "list_id",
        "type": "string",
        "required": false,
        "description": "Return only this negative keyword list, with its keywords paged: each page holds as many as fit in a response an MCP client accepts (at most max_keywords_per_list), starting at keywordsOffset, and nextCursor continues them. The way to read every keyword of a list that came back with keywordsTruncated: true.",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "include_keywords",
        "type": "boolean",
        "required": false,
        "description": "Embed each list's negative keywords (text + match type). Defaults to true; set false for a names-and-counts overview.",
        "default": true
      },
      {
        "name": "include_campaign_associations",
        "type": "boolean",
        "required": false,
        "description": "Embed the campaigns each list is applied to (id, name, status). Defaults to true.",
        "default": true
      },
      {
        "name": "max_keywords_per_list",
        "type": "integer",
        "required": false,
        "description": "Most keywords to embed per list (1-5000, default 50). With list_id it caps one page of that list's keywords instead, and by default a page takes as many as fit. A list cut short has keywordsTruncated: true; keywordCount is always the full size.",
        "constraints": {
          "min": 1,
          "max": 5000
        }
      },
      {
        "name": "max_campaigns_per_list",
        "type": "integer",
        "required": false,
        "description": "Most campaigns to embed per shared list, first by name (1-10000, default 100). A list cut short has campaignsTruncated: true; associationCount is always the full count. With list_id the campaigns come on the first page only, cut to fit the response.",
        "constraints": {
          "min": 1,
          "max": 10000
        }
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Lists per page (1-20). Default: as many lists as fit in a response an MCP client accepts (about 45K characters, at most 20). Follow nextCursor for the rest.",
        "constraints": {
          "min": 1,
          "max": 20
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "google_ads_list_negative_keyword_lists",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_list_negative_keywords",
    "title": "List Microsoft Ads Negative Keywords",
    "description": "",
    "cli_command": "hopkin microsoft negative-keywords list",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (AccountId).",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--campaign-ids",
        "description": "Only these campaigns (1-100): their own negatives and, unless level is Campaign, their ad groups'. Omit for the whole account. An id the account does not have is rejected.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--ad-group-ids",
        "description": "Only these ad groups (1-100); implies level AdGroup (level Campaign is rejected). Each ad group's campaign is looked up; pass campaign_ids too when an ad group has not been listed before.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--level",
        "description": "Campaign = negatives added to campaigns, AdGroup = to ad groups, All (default) = both.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--match-type",
        "description": "Only these match types. Microsoft negatives are Exact or Phrase.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Negative keywords per page (1-100). Default: as many as fit in a response an MCP client accepts (about 45K characters). Follow nextCursor for the rest.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "negative-keywords",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Microsoft Advertising account ID (AccountId)."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts."
        },
        "campaign_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20,
            "pattern": "^\\d+$"
          },
          "minItems": 1,
          "maxItems": 100,
          "description": "Only these campaigns (1-100): their own negatives and, unless level is Campaign, their ad groups'. Omit for the whole account. An id the account does not have is rejected."
        },
        "ad_group_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20,
            "pattern": "^\\d+$"
          },
          "minItems": 1,
          "maxItems": 100,
          "description": "Only these ad groups (1-100); implies level AdGroup (level Campaign is rejected). Each ad group's campaign is looked up; pass campaign_ids too when an ad group has not been listed before."
        },
        "level": {
          "type": "string",
          "enum": [
            "Campaign",
            "AdGroup",
            "All"
          ],
          "default": "All",
          "description": "Campaign = negatives added to campaigns, AdGroup = to ad groups, All (default) = both."
        },
        "match_type": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Exact",
              "Phrase",
              "Broad"
            ]
          },
          "minItems": 1,
          "description": "Only these match types. Microsoft negatives are Exact or Phrase."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "description": "Negative keywords per page (1-100). Default: as many as fit in a response an MCP client accepts (about 45K characters). Follow nextCursor for the rest."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (AccountId).",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts.",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "campaign_ids",
        "type": "array",
        "required": false,
        "description": "Only these campaigns (1-100): their own negatives and, unless level is Campaign, their ad groups'. Omit for the whole account. An id the account does not have is rejected."
      },
      {
        "name": "ad_group_ids",
        "type": "array",
        "required": false,
        "description": "Only these ad groups (1-100); implies level AdGroup (level Campaign is rejected). Each ad group's campaign is looked up; pass campaign_ids too when an ad group has not been listed before."
      },
      {
        "name": "level",
        "type": "string",
        "required": false,
        "description": "Campaign = negatives added to campaigns, AdGroup = to ad groups, All (default) = both.",
        "enum_values": [
          "Campaign",
          "AdGroup",
          "All"
        ],
        "default": "All"
      },
      {
        "name": "match_type",
        "type": "array",
        "required": false,
        "description": "Only these match types. Microsoft negatives are Exact or Phrase."
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Negative keywords per page (1-100). Default: as many as fit in a response an MCP client accepts (about 45K characters). Follow nextCursor for the rest.",
        "constraints": {
          "min": 1,
          "max": 100
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "google_ads_list_negative_keywords",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_get_performance_report",
    "title": "Get Microsoft Ads Performance Report",
    "description": "Comprehensive Microsoft Advertising performance report broken down by ENTITY —\none row per campaign, ad group, ad, or keyword — with pre-selected columns.\nCovers the full funnel: Impressions → Clicks → Spend → Conversions → Revenue.\n\nWHEN NOT TO USE THIS: if the user wants a single account-wide total, a \"performance summary /\noverview\" for a whole account, or an account-level conversion-goal breakdown (\"which goals drove\nthe most activity\") that does NOT need to be split per campaign, use microsoft_ads_get_account_summary\ninstead — it is the purpose-built account-level rollup (it also supports breakdown_by_goal) and is\nsimpler and faster for that job. This holds REGARDLESS of the date shape: a custom start/end date\nrange (e.g. \"performance for January 2026\", \"how did my account do from 2026-01-01 to 2026-01-31\")\ndoes NOT make this the right tool — get_account_summary accepts the same date_preset and custom\ndate_range inputs, so an account-level total over a custom range still belongs there. Only the need\nfor per-entity rows (per campaign, ad group, ad, or keyword) justifies reaching for this report.\n\nLevels:\n  • Campaign (default): CampaignId, CampaignName, CampaignStatus, CampaignType + metrics\n  • AdGroup: + AdGroupId, AdGroupName, Status (the ad group's status)\n  • Ad: + AdId, AdType, AdTitle, AdGroupStatus, AdStatus\n  • Keyword: + AdGroupId, AdGroupName, AdGroupStatus, KeywordId, Keyword, BidMatchType,\n             QualityScore, ExpectedCtr, AdRelevance, LandingPageExperience. No CampaignType\n             column, so campaign_type is rejected at this level; use campaign_ids.\n\nAll levels include: TimePeriod, Impressions, Clicks, Spend, AverageCpc, Ctr,\nConversionsQualified (surfaced as conversions), Revenue, derived\ncost_per_conversion.\n\nCONVERSION SURFACE (HOP-50):\n  • conversions is bidding-set only (reads the ConversionsQualified column).\n    The deprecated Conversions, ConversionRate, CostPerConversion, and\n    ReturnOnAdSpend columns are REJECTED at the schema layer — Bing returns\n    0 for them on modern accounts. Derive the rate / per-conversion metrics\n    client-side from spend + conversions.\n  • include_all_conversions: boolean (default false). When true, the tool\n    auto-adds AllConversionsQualified / AllRevenue / ViewThroughConversionsQualified\n    to the column set and attaches an 'all_conversions' sub-object to every row\n    capturing every conversion goal — not just the bidding-set. Mirrors\n    google_ads_get_performance_report's enhanced-conversions flag.\n  • breakdown_by_goal: boolean (default true). Runs a parallel\n    GoalsAndFunnelsReport for the same scope and joins per-goal totals onto\n    the response as 'goal_breakdown' keyed by campaign_id (or account_id\n    when no campaign filter). Set to false to skip the parallel SOAP call\n    when latency matters more than per-goal context.\n\nKEY DIFFERENCES FROM GOOGLE:\n  • Single query (no dual-query needed — Microsoft has no conversion segment restriction)\n  • Spend is in the account's currency (USD, GBP, etc.) — not micros\n  • Conversion split surfaces via include_all_conversions + breakdown_by_goal flags\n\nFor custom column selection, use microsoft_ads_get_insights.\nFor search term analysis, use microsoft_ads_get_search_terms.\nFor impression share metrics, use microsoft_ads_get_impression_share.\n\nPAGING: rows come back one page at a time, sorted Spend highest first. Leave limit unset: each\npage is then sized so the whole response fits what MCP clients accept (up to ${DEFAULT_REPORT_PAGE_ROWS} rows, ending\nearly at about ${RESPONSE_CHAR_BUDGET.toLocaleString('en-US')} characters, so wide Keyword-level rows give fewer rows per page). To get\nmore rows, follow the cursor rather than raising limit; an explicit limit (max ${MAX_REPORT_PAGE_ROWS}) is exact.\ncount is the total across all pages. When\nthe response has a nextCursor, call again with exactly the same parameters plus\ncursor = nextCursor; repeat until nextCursor is absent to collect every row. Later pages re-read\nthe same report rather than running a new one (no queue wait, each row exactly once), and omit\ngoal_breakdown, which only the first page carries.\n\nNote: Microsoft's async report pipeline typically takes 5–30 seconds per call.",
    "cli_command": "hopkin microsoft performance-report get",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising Account ID (numeric string)",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID. Required when accessing accounts through an agency manager.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--level",
        "description": "Report granularity. Campaign (default): one row per campaign. AdGroup: one row per ad group. Ad: one row per ad. Keyword: one row per keyword with BidMatchType and Quality Score components (QualityScore, ExpectedCtr, AdRelevance, LandingPageExperience); campaign_type is not supported at Keyword level.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--date-range",
        "description": "Custom date range { start, end } in YYYY-MM-DD. Use either this or date_preset.",
        "type": "object",
        "required": false
      },
      {
        "flag": "--date-preset",
        "description": "Predefined date range. Options: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--aggregation",
        "description": "Time aggregation. Daily (default): row per day. Weekly: row per week. Monthly: row per month. Summary: single totals row for the full date range.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--campaign-ids",
        "description": "Filter to specific campaign IDs (up to 50).",
        "type": "array",
        "required": false
      },
      {
        "flag": "--campaign-type",
        "description": "Return only rows from campaigns of these types. Allowed: Search, Shopping, PerformanceMax, Audience. Applied to the report rows (Microsoft reports cannot filter by type), so count and goal_breakdown cover only the matching campaigns. If not provided, all campaign types are included. Not supported at level Keyword (Microsoft's keyword report has no campaign type column); use campaign_ids there.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--include-all-conversions",
        "description": "When true, adds AllConversionsQualified, AllRevenue, and ViewThroughConversionsQualified to the column request (mirrors google-ads-mcp enhanced-conversions opt-in). These columns capture ALL conversion goals including non-bidding goals. The response added_columns field lists which columns were automatically included.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--breakdown-by-goal",
        "description": "When true (default), runs a parallel GoalsAndFunnelsReport and returns goal_breakdown keyed by account_id (account-scope) or campaign_id (campaign-scope when campaign_ids filter is set). Each entry is an array of per-goal rows with all_conversions, all_revenue, view_through_conversions, assists. Set goal_breakdown_granularity to \"account\" or \"campaign\" accordingly. Pass false to skip the parallel report when latency matters more than goal context.",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Rows per page (1-10000). Usually omit it: the page is then sized so the whole response fits what MCP clients accept, up to 500 rows and ending early at about 45,000 characters, so wide rows (Keyword level) give fewer rows per page. To get more rows, follow nextCursor rather than raising limit; set limit only if your client takes large responses. The whole report is sorted by Spend (highest first), then by the row's column values, and returned one page at a time. count is the total across all pages; while more rows remain, the response has a top-level nextCursor.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from nextCursor of the previous response; never construct one. To get the next page, repeat the call with exactly the same parameters (only limit may change) plus this cursor. Later pages re-read the same Microsoft report instead of generating a new one, so every row appears exactly once. Cursors expire with the report (about 24 hours). goal_breakdown is returned on the first page only.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "reporting",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Microsoft Advertising Account ID (numeric string)"
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Customer/manager ID. Required when accessing accounts through an agency manager."
        },
        "level": {
          "type": "string",
          "enum": [
            "Campaign",
            "AdGroup",
            "Ad",
            "Keyword"
          ],
          "default": "Campaign",
          "description": "Report granularity. Campaign (default): one row per campaign. AdGroup: one row per ad group. Ad: one row per ad. Keyword: one row per keyword with BidMatchType and Quality Score components (QualityScore, ExpectedCtr, AdRelevance, LandingPageExperience); campaign_type is not supported at Keyword level."
        },
        "date_range": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "Start date (YYYY-MM-DD)"
            },
            "end": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "End date (YYYY-MM-DD)"
            }
          },
          "required": [
            "start",
            "end"
          ],
          "additionalProperties": false,
          "description": "Custom date range { start, end } in YYYY-MM-DD. Use either this or date_preset."
        },
        "date_preset": {
          "type": "string",
          "enum": [
            "Today",
            "Yesterday",
            "LastSevenDays",
            "ThisWeek",
            "LastWeek",
            "LastFourWeeks",
            "ThisMonth",
            "LastMonth",
            "LastThreeMonths",
            "LastSixMonths",
            "ThisYear",
            "LastYear"
          ],
          "description": "Predefined date range. Options: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear."
        },
        "aggregation": {
          "type": "string",
          "enum": [
            "Daily",
            "Weekly",
            "Monthly",
            "Summary"
          ],
          "default": "Daily",
          "description": "Time aggregation. Daily (default): row per day. Weekly: row per week. Monthly: row per month. Summary: single totals row for the full date range."
        },
        "campaign_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 50,
          "description": "Filter to specific campaign IDs (up to 50)."
        },
        "campaign_type": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "Search",
              "Shopping",
              "PerformanceMax",
              "Audience"
            ]
          },
          "description": "Return only rows from campaigns of these types. Allowed: Search, Shopping, PerformanceMax, Audience. Applied to the report rows (Microsoft reports cannot filter by type), so count and goal_breakdown cover only the matching campaigns. If not provided, all campaign types are included. Not supported at level Keyword (Microsoft's keyword report has no campaign type column); use campaign_ids there."
        },
        "include_all_conversions": {
          "type": "boolean",
          "default": false,
          "description": "When true, adds AllConversionsQualified, AllRevenue, and ViewThroughConversionsQualified to the column request (mirrors google-ads-mcp enhanced-conversions opt-in). These columns capture ALL conversion goals including non-bidding goals. The response added_columns field lists which columns were automatically included."
        },
        "breakdown_by_goal": {
          "type": "boolean",
          "default": true,
          "description": "When true (default), runs a parallel GoalsAndFunnelsReport and returns goal_breakdown keyed by account_id (account-scope) or campaign_id (campaign-scope when campaign_ids filter is set). Each entry is an array of per-goal rows with all_conversions, all_revenue, view_through_conversions, assists. Set goal_breakdown_granularity to \"account\" or \"campaign\" accordingly. Pass false to skip the parallel report when latency matters more than goal context."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10000,
          "description": "Rows per page (1-10000). Usually omit it: the page is then sized so the whole response fits what MCP clients accept, up to 500 rows and ending early at about 45,000 characters, so wide rows (Keyword level) give fewer rows per page. To get more rows, follow nextCursor rather than raising limit; set limit only if your client takes large responses. The whole report is sorted by Spend (highest first), then by the row's column values, and returned one page at a time. count is the total across all pages; while more rows remain, the response has a top-level nextCursor."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from nextCursor of the previous response; never construct one. To get the next page, repeat the call with exactly the same parameters (only limit may change) plus this cursor. Later pages re-read the same Microsoft report instead of generating a new one, so every row appears exactly once. Cursors expire with the report (about 24 hours). goal_breakdown is returned on the first page only."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising Account ID (numeric string)",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID. Required when accessing accounts through an agency manager.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "level",
        "type": "string",
        "required": false,
        "description": "Report granularity. Campaign (default): one row per campaign. AdGroup: one row per ad group. Ad: one row per ad. Keyword: one row per keyword with BidMatchType and Quality Score components (QualityScore, ExpectedCtr, AdRelevance, LandingPageExperience); campaign_type is not supported at Keyword level.",
        "enum_values": [
          "Campaign",
          "AdGroup",
          "Ad",
          "Keyword"
        ],
        "default": "Campaign"
      },
      {
        "name": "date_range",
        "type": "object",
        "required": false,
        "description": "Custom date range { start, end } in YYYY-MM-DD. Use either this or date_preset."
      },
      {
        "name": "date_preset",
        "type": "string",
        "required": false,
        "description": "Predefined date range. Options: Today, Yesterday, LastSevenDays, ThisWeek, LastWeek, LastFourWeeks, ThisMonth, LastMonth, LastThreeMonths, LastSixMonths, ThisYear, LastYear.",
        "enum_values": [
          "Today",
          "Yesterday",
          "LastSevenDays",
          "ThisWeek",
          "LastWeek",
          "LastFourWeeks",
          "ThisMonth",
          "LastMonth",
          "LastThreeMonths",
          "LastSixMonths",
          "ThisYear",
          "LastYear"
        ]
      },
      {
        "name": "aggregation",
        "type": "string",
        "required": false,
        "description": "Time aggregation. Daily (default): row per day. Weekly: row per week. Monthly: row per month. Summary: single totals row for the full date range.",
        "enum_values": [
          "Daily",
          "Weekly",
          "Monthly",
          "Summary"
        ],
        "default": "Daily"
      },
      {
        "name": "campaign_ids",
        "type": "array",
        "required": false,
        "description": "Filter to specific campaign IDs (up to 50)."
      },
      {
        "name": "campaign_type",
        "type": "array",
        "required": false,
        "description": "Return only rows from campaigns of these types. Allowed: Search, Shopping, PerformanceMax, Audience. Applied to the report rows (Microsoft reports cannot filter by type), so count and goal_breakdown cover only the matching campaigns. If not provided, all campaign types are included. Not supported at level Keyword (Microsoft's keyword report has no campaign type column); use campaign_ids there."
      },
      {
        "name": "include_all_conversions",
        "type": "boolean",
        "required": false,
        "description": "When true, adds AllConversionsQualified, AllRevenue, and ViewThroughConversionsQualified to the column request (mirrors google-ads-mcp enhanced-conversions opt-in). These columns capture ALL conversion goals including non-bidding goals. The response added_columns field lists which columns were automatically included.",
        "default": false
      },
      {
        "name": "breakdown_by_goal",
        "type": "boolean",
        "required": false,
        "description": "When true (default), runs a parallel GoalsAndFunnelsReport and returns goal_breakdown keyed by account_id (account-scope) or campaign_id (campaign-scope when campaign_ids filter is set). Each entry is an array of per-goal rows with all_conversions, all_revenue, view_through_conversions, assists. Set goal_breakdown_granularity to \"account\" or \"campaign\" accordingly. Pass false to skip the parallel report when latency matters more than goal context.",
        "default": true
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Rows per page (1-10000). Usually omit it: the page is then sized so the whole response fits what MCP clients accept, up to 500 rows and ending early at about 45,000 characters, so wide rows (Keyword level) give fewer rows per page. To get more rows, follow nextCursor rather than raising limit; set limit only if your client takes large responses. The whole report is sorted by Spend (highest first), then by the row's column values, and returned one page at a time. count is the total across all pages; while more rows remain, the response has a top-level nextCursor.",
        "constraints": {
          "min": 1,
          "max": 10000
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from nextCursor of the previous response; never construct one. To get the next page, repeat the call with exactly the same parameters (only limit may change) plus this cursor. Later pages re-read the same Microsoft report instead of generating a new one, so every row appears exactly once. Cursors expire with the report (about 24 hours). goal_breakdown is returned on the first page only.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "Monthly campaign overview",
        "mcp_params": {
          "account_id": "1234567890",
          "level": "Campaign",
          "date_preset": "LastMonth",
          "aggregation": "Daily",
          "reason": "Monthly performance review — show daily trends for all campaigns"
        },
        "cli_command": "hopkin microsoft performance-report get --account 1234567890 --level Campaign --date-preset LastMonth --aggregation Daily",
        "natural_language": "Monthly campaign overview"
      },
      {
        "label": "Ad group drilldown for Q1",
        "mcp_params": {
          "account_id": "1234567890",
          "level": "AdGroup",
          "date_range": {
            "start": "2026-01-01",
            "end": "2026-03-31"
          },
          "aggregation": "Weekly",
          "reason": "Q1 ad group performance with weekly trend for budget reallocation decisions"
        },
        "cli_command": "hopkin microsoft performance-report get --account 1234567890 --level AdGroup --date-range [object Object] --aggregation Weekly",
        "natural_language": "Ad group drilldown for Q1"
      },
      {
        "label": "Keyword Quality Score and ROI summary",
        "mcp_params": {
          "account_id": "1234567890",
          "level": "Keyword",
          "date_preset": "LastMonth",
          "aggregation": "Summary",
          "reason": "Identify low-QS keywords with high spend for optimization"
        },
        "cli_command": "hopkin microsoft performance-report get --account 1234567890 --level Keyword --date-preset LastMonth --aggregation Summary",
        "natural_language": "Keyword Quality Score and ROI summary"
      },
      {
        "label": "Ad performance for specific Search campaigns",
        "mcp_params": {
          "account_id": "1234567890",
          "level": "Ad",
          "date_preset": "LastSevenDays",
          "aggregation": "Daily",
          "campaign_type": [
            "Search"
          ],
          "reason": "A/B test RSA ad copy — compare conversion rates across ad variants"
        },
        "cli_command": "hopkin microsoft performance-report get --account 1234567890 --level Ad --date-preset LastSevenDays --aggregation Daily --campaign-type Search",
        "natural_language": "Ad performance for specific Search campaigns"
      },
      {
        "label": "Managed account (agency) campaign report",
        "mcp_params": {
          "account_id": "9876543210",
          "customer_id": "5555555555",
          "level": "Campaign",
          "date_preset": "ThisMonth",
          "aggregation": "Daily",
          "reason": "Client campaign performance for monthly report"
        },
        "cli_command": "hopkin microsoft performance-report get --account 9876543210 --customer-id 5555555555 --level Campaign --date-preset ThisMonth --aggregation Daily",
        "natural_language": "Managed account (agency) campaign report"
      }
    ],
    "response_format": "json",
    "related_tools": [
      {
        "name": "meta_ads_get_performance_report",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_get_performance_report",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_get_performance_report",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_get_performance_report",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_get_performance_report",
        "relationship": "cross-platform"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_ping",
    "title": "Ping Microsoft Ads MCP Server",
    "description": "Health check for the Microsoft Ads MCP server. Returns server status and version. No authentication required. Use to verify the server is reachable before calling other tools.",
    "cli_command": "hopkin microsoft ping",
    "cli_flags": [
      {
        "flag": "--message",
        "description": "Optional message to echo back",
        "type": "string",
        "required": false
      }
    ],
    "category": "auth",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": true,
      "openWorld": false
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "message": {
          "type": "string",
          "maxLength": 100,
          "description": "Optional message to echo back"
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "message",
        "type": "string",
        "required": false,
        "description": "Optional message to echo back",
        "constraints": {
          "max_length": 100
        }
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "Health check",
        "mcp_params": {
          "reason": "Verify the Microsoft Ads MCP server is running"
        },
        "cli_command": "hopkin microsoft ping",
        "natural_language": "Health check"
      },
      {
        "label": "With echo message",
        "mcp_params": {
          "message": "hello",
          "reason": "Test connectivity before calling data tools"
        },
        "cli_command": "hopkin microsoft ping --message hello",
        "natural_language": "With echo message"
      }
    ],
    "response_format": "markdown",
    "related_tools": [
      {
        "name": "meta_ads_ping",
        "relationship": "cross-platform"
      },
      {
        "name": "google_ads_ping",
        "relationship": "cross-platform"
      },
      {
        "name": "linkedin_ads_ping",
        "relationship": "cross-platform"
      },
      {
        "name": "reddit_ads_ping",
        "relationship": "cross-platform"
      },
      {
        "name": "tiktok_ads_ping",
        "relationship": "cross-platform"
      },
      {
        "name": "chatgpt_ads_ping",
        "relationship": "cross-platform"
      },
      {
        "name": "microsoft_ads_check_auth_status",
        "relationship": "sibling"
      }
    ],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_get_search_terms",
    "title": "Get Microsoft Ads Search Terms",
    "description": "",
    "cli_command": "hopkin microsoft search-terms get",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (AccountId)",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--date-range",
        "description": "Date range for the report. Required.",
        "type": "object",
        "required": true
      },
      {
        "flag": "--campaign-ids",
        "description": "Filter to specific campaign IDs (max 100). Omit to include all campaigns.",
        "type": "array",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Page size: search terms per response (1-10000, default 50). Rows are sorted by Impressions descending, so the first page is the top N. `count` in the response is the total across all pages; when `nextCursor` is present, more rows remain. Large pages can exceed what an MCP client accepts in one response (roughly 100 rows, fewer with include_all_conversions); to cover every term, follow nextCursor rather than raising limit.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor for the next page: pass the `nextCursor` value from the previous response unchanged, together with the same parameters as that call (only limit may change). Omit for the first page. Cursors expire after about 24 hours.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--include-all-conversions",
        "description": "When true, adds the AllConversionsQualified and AllRevenue columns to the report and surfaces them as an all_conversions sub-object on each row. This captures ALL conversion goals including those not marked \"Include in Conversions\" (e.g. cross-device, non-bidding goals). Microsoft's search term report has no view-through conversion column, so there is no view-through count here. The base Conversions field always reflects ConversionsQualified (bidding-set only). Note: breakdown_by_goal is not available on this tool because Bing's search-term report cannot join GoalsAndFunnelsReport at the search-term level (spec §9.3).",
        "type": "boolean",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "search-terms",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "description": "Microsoft Advertising account ID (AccountId)"
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts."
        },
        "date_range": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "Start date in YYYY-MM-DD format"
            },
            "end": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "End date in YYYY-MM-DD format"
            }
          },
          "required": [
            "start",
            "end"
          ],
          "additionalProperties": false,
          "description": "Date range for the report. Required."
        },
        "campaign_ids": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "minItems": 1,
          "maxItems": 100,
          "description": "Filter to specific campaign IDs (max 100). Omit to include all campaigns."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10000,
          "description": "Page size: search terms per response (1-10000, default 50). Rows are sorted by Impressions descending, so the first page is the top N. `count` in the response is the total across all pages; when `nextCursor` is present, more rows remain. Large pages can exceed what an MCP client accepts in one response (roughly 100 rows, fewer with include_all_conversions); to cover every term, follow nextCursor rather than raising limit."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor for the next page: pass the `nextCursor` value from the previous response unchanged, together with the same parameters as that call (only limit may change). Omit for the first page. Cursors expire after about 24 hours."
        },
        "include_all_conversions": {
          "type": "boolean",
          "default": false,
          "description": "When true, adds the AllConversionsQualified and AllRevenue columns to the report and surfaces them as an all_conversions sub-object on each row. This captures ALL conversion goals including those not marked \"Include in Conversions\" (e.g. cross-device, non-bidding goals). Microsoft's search term report has no view-through conversion column, so there is no view-through count here. The base Conversions field always reflects ConversionsQualified (bidding-set only). Note: breakdown_by_goal is not available on this tool because Bing's search-term report cannot join GoalsAndFunnelsReport at the search-term level (spec §9.3)."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "date_range",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (AccountId)",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer/manager ID (CustomerId). Required for agency-managed accounts.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "date_range",
        "type": "object",
        "required": true,
        "description": "Date range for the report. Required."
      },
      {
        "name": "campaign_ids",
        "type": "array",
        "required": false,
        "description": "Filter to specific campaign IDs (max 100). Omit to include all campaigns."
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Page size: search terms per response (1-10000, default 50). Rows are sorted by Impressions descending, so the first page is the top N. `count` in the response is the total across all pages; when `nextCursor` is present, more rows remain. Large pages can exceed what an MCP client accepts in one response (roughly 100 rows, fewer with include_all_conversions); to cover every term, follow nextCursor rather than raising limit.",
        "constraints": {
          "min": 1,
          "max": 10000
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor for the next page: pass the `nextCursor` value from the previous response unchanged, together with the same parameters as that call (only limit may change). Omit for the first page. Cursors expire after about 24 hours.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "include_all_conversions",
        "type": "boolean",
        "required": false,
        "description": "When true, adds the AllConversionsQualified and AllRevenue columns to the report and surfaces them as an all_conversions sub-object on each row. This captures ALL conversion goals including those not marked \"Include in Conversions\" (e.g. cross-device, non-bidding goals). Microsoft's search term report has no view-through conversion column, so there is no view-through count here. The base Conversions field always reflects ConversionsQualified (bidding-set only). Note: breakdown_by_goal is not available on this tool because Bing's search-term report cannot join GoalsAndFunnelsReport at the search-term level (spec §9.3).",
        "default": false
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [
      {
        "label": "Last 30 days, all campaigns",
        "mcp_params": {
          "account_id": "123456789",
          "date_range": {
            "start": "2026-03-18",
            "end": "2026-04-17"
          },
          "reason": "Analyze search query performance"
        },
        "cli_command": "hopkin microsoft search-terms get --account 123456789 --date-range [object Object]",
        "natural_language": "Last 30 days, all campaigns"
      },
      {
        "label": "Next page of the same report",
        "mcp_params": {
          "account_id": "123456789",
          "date_range": {
            "start": "2026-03-18",
            "end": "2026-04-17"
          },
          "cursor": "<nextCursor from the previous response>",
          "reason": "Continue through every search term for the wasted-spend review"
        },
        "cli_command": "hopkin microsoft search-terms get --account 123456789 --date-range [object Object] --cursor <nextCursor from the previous response>",
        "natural_language": "Next page of the same report"
      },
      {
        "label": "Specific campaign, top 100",
        "mcp_params": {
          "account_id": "123456789",
          "date_range": {
            "start": "2026-04-01",
            "end": "2026-04-17"
          },
          "campaign_ids": [
            "987654321"
          ],
          "limit": 100,
          "reason": "Top 100 search terms for brand campaign this month"
        },
        "cli_command": "hopkin microsoft search-terms get --account 123456789 --date-range [object Object] --campaign-ids 987654321 --limit 100",
        "natural_language": "Specific campaign, top 100"
      },
      {
        "label": "Multi-campaign filter",
        "mcp_params": {
          "account_id": "123456789",
          "date_range": {
            "start": "2026-04-01",
            "end": "2026-04-17"
          },
          "campaign_ids": [
            "111111",
            "222222",
            "333333"
          ],
          "reason": "Search terms across three key campaigns"
        },
        "cli_command": "hopkin microsoft search-terms get --account 123456789 --date-range [object Object] --campaign-ids 111111,222222,333333",
        "natural_language": "Multi-campaign filter"
      },
      {
        "label": "Top 50 for quick overview",
        "mcp_params": {
          "account_id": "123456789",
          "date_range": {
            "start": "2026-04-10",
            "end": "2026-04-17"
          },
          "limit": 50,
          "reason": "Quick top 50 search terms overview for the last week"
        },
        "cli_command": "hopkin microsoft search-terms get --account 123456789 --date-range [object Object] --limit 50",
        "natural_language": "Top 50 for quick overview"
      },
      {
        "label": "Full month with managed account",
        "mcp_params": {
          "account_id": "123456789",
          "customer_id": "555666777",
          "date_range": {
            "start": "2026-03-01",
            "end": "2026-03-31"
          },
          "reason": "March search term performance for agency client"
        },
        "cli_command": "hopkin microsoft search-terms get --account 123456789 --customer-id 555666777 --date-range [object Object]",
        "natural_language": "Full month with managed account"
      },
      {
        "label": "Include all-conversions data (non-bidding goals)",
        "mcp_params": {
          "account_id": "123456789",
          "date_range": {
            "start": "2026-04-01",
            "end": "2026-04-30"
          },
          "include_all_conversions": true,
          "reason": "Get full conversion picture including non-bidding goals for search terms"
        },
        "cli_command": "hopkin microsoft search-terms get --account 123456789 --date-range [object Object] --include-all-conversions true",
        "natural_language": "Include all-conversions data (non-bidding goals)"
      }
    ],
    "response_format": "markdown",
    "related_tools": [],
    "tool_type": "standard"
  },
  {
    "name": "microsoft_ads_get_uet_tag_health",
    "title": "Get Microsoft Ads UET Tag Health",
    "description": "",
    "cli_command": "hopkin microsoft uet-tag-health get",
    "cli_flags": [
      {
        "flag": "--account",
        "description": "Microsoft Advertising account ID (AccountId). Conversion goals scoped to this account are included alongside the customer-level ones.",
        "type": "string",
        "required": true
      },
      {
        "flag": "--customer-id",
        "description": "Customer ID that owns the account. UET tags belong to the customer; when omitted it is looked up from the account.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--limit",
        "description": "Maximum number of UET tags per page (1-100, default 50). Each tag carries all of its conversion goals.",
        "type": "integer",
        "required": false
      },
      {
        "flag": "--cursor",
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "type": "string",
        "required": false
      },
      {
        "flag": "--connection-id",
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs.",
        "type": "string",
        "required": false
      }
    ],
    "category": "uet-tag-health",
    "annotations": {
      "readOnly": true,
      "destructive": false,
      "idempotent": false,
      "openWorld": true
    },
    "inputSchema": {
      "type": "object",
      "properties": {
        "account_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Microsoft Advertising account ID (AccountId). Conversion goals scoped to this account are included alongside the customer-level ones."
        },
        "customer_id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 20,
          "pattern": "^\\d+$",
          "description": "Customer ID that owns the account. UET tags belong to the customer; when omitted it is looked up from the account."
        },
        "limit": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "description": "Maximum number of UET tags per page (1-100, default 50). Each tag carries all of its conversion goals."
        },
        "cursor": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page."
        },
        "connection_id": {
          "type": "string",
          "format": "uuid",
          "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
        },
        "reason": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Why this tool call is needed"
        }
      },
      "required": [
        "account_id",
        "reason"
      ],
      "additionalProperties": false,
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "parameters": [
      {
        "name": "account_id",
        "type": "string",
        "required": true,
        "description": "Microsoft Advertising account ID (AccountId). Conversion goals scoped to this account are included alongside the customer-level ones.",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "customer_id",
        "type": "string",
        "required": false,
        "description": "Customer ID that owns the account. UET tags belong to the customer; when omitted it is looked up from the account.",
        "constraints": {
          "min_length": 1,
          "max_length": 20,
          "pattern": "^\\d+$"
        }
      },
      {
        "name": "limit",
        "type": "integer",
        "required": false,
        "description": "Maximum number of UET tags per page (1-100, default 50). Each tag carries all of its conversion goals.",
        "constraints": {
          "min": 1,
          "max": 100
        }
      },
      {
        "name": "cursor",
        "type": "string",
        "required": false,
        "description": "Opaque cursor from the previous response's nextCursor. Pass it with otherwise identical parameters to get the next page; a cursor reused with different filters is rejected. Omit for the first page.",
        "constraints": {
          "min_length": 1
        }
      },
      {
        "name": "connection_id",
        "type": "string",
        "required": false,
        "description": "Optional ID of a specific connection to use for this call. Omit to use the actor's default connection for this network. Call <platform>_list_connections to discover available connection IDs."
      },
      {
        "name": "reason",
        "type": "string",
        "required": true,
        "description": "Why this tool call is needed",
        "constraints": {
          "min_length": 1,
          "max_length": 500
        }
      }
    ],
    "examples": [],
    "response_format": "markdown",
    "related_tools": [],
    "tool_type": "standard"
  }
]