"""Arcade Starter Tools for Datadog

DO NOT EDIT THIS MODULE DIRECTLY.

THIS MODULE WAS AUTO-GENERATED BY TRANSPILING THE API STARTER TOOL JSON DEFINITIONS
IN THE ../wrapper_tools DIRECTORY INTO PYTHON CODE. ANY CHANGES TO THIS MODULE WILL
BE OVERWRITTEN BY THE TRANSPILER.
"""

import asyncio
import json
from enum import Enum
from typing import Annotated, Any

import httpx
import jsonschema
from arcade_tdk import ToolContext, tool
from arcade_tdk.errors import RetryableToolError

from .request_body_schemas import REQUEST_BODY_SCHEMAS

# Retry configuration
INITIAL_RETRY_DELAY = 0.5  # seconds

HTTP_CLIENT = httpx.AsyncClient(
    timeout=httpx.Timeout(60.0, connect=10.0),
    limits=httpx.Limits(max_keepalive_connections=20, max_connections=100),
    transport=httpx.AsyncHTTPTransport(retries=3),
    http2=True,
    follow_redirects=True,
)


class ToolMode(str, Enum):
    """Mode for tools with complex request bodies."""

    GET_REQUEST_SCHEMA = "get_request_schema"
    EXECUTE = "execute"


def remove_none_values(data: dict[str, Any]) -> dict[str, Any]:
    return {k: v for k, v in data.items() if v is not None}


async def make_request(
    url: str,
    method: str,
    params: dict[str, Any] | None = None,
    headers: dict[str, Any] | None = None,
    content: str | None = None,
    data: dict[str, Any] | None = None,
    auth: tuple[str, str] | None = None,
    max_retries: int = 3,
) -> httpx.Response:
    """Make an HTTP request with retry logic for 5xx server errors."""
    for attempt in range(max_retries):
        try:
            response = await HTTP_CLIENT.request(
                url=url,
                auth=auth,
                method=method,
                params=params,
                headers=headers,
                content=content,
            )
            response.raise_for_status()
        except httpx.HTTPStatusError as e:
            # Only retry on 5xx server errors
            if e.response.status_code >= 500 and attempt < max_retries - 1:
                # Exponential backoff: 0.5s, 1s, 2s
                await asyncio.sleep(INITIAL_RETRY_DELAY * (2**attempt))
                continue
            # Re-raise for 4xx errors or if max retries reached
            raise
        except httpx.RequestError:
            # Don't retry request errors (network issues are handled by transport)
            raise
        else:
            return response

    # This should never be reached, but satisfies type checker
    raise httpx.RequestError("Max retries exceeded")  # noqa: TRY003


async def make_request_with_schema_validation(
    url: str,
    method: str,
    request_data: dict[str, Any],
    schema: dict[str, Any],
    params: dict[str, Any] | None = None,
    headers: dict[str, Any] | None = None,
    max_retries: int = 3,
) -> httpx.Response:
    """Make an HTTP request with schema validation on format errors."""
    try:
        response = await make_request(
            url=url,
            method=method,
            params=params,
            headers=headers,
            content=json.dumps(request_data),
            max_retries=max_retries,
        )
    except httpx.HTTPStatusError as e:
        # Only provide schema validation for format-related errors
        if e.response.status_code in (400, 422):
            # Run validation to provide additional context
            is_valid, validation_error = validate_json_against_schema(request_data, schema)

            api_error_details = f"API returned {e.response.status_code}: {e.response.text}"

            if not is_valid:
                # Schema validation found issues - additional context
                additional_context = (
                    f"{api_error_details}\n\n"
                    f"Schema validation found the following issues:\n"
                    f"{validation_error}"
                )
            else:
                # Schema validation passed - just show API error
                additional_context = api_error_details

            raise RetryableToolError(
                message=(f"API request failed with validation error: {e.response.status_code}"),
                developer_message=api_error_details,
                additional_prompt_content=additional_context,
            ) from e
        else:
            # For non-validation errors, re-raise as-is
            raise
    else:
        return response


def validate_json_against_schema(
    json_data: dict[str, Any], schema: dict[str, Any]
) -> tuple[bool, str | None]:
    """Validate JSON data against an OpenAPI/JSON Schema.

    This provides full JSON Schema Draft 7 validation including:
    - Required fields, types, enums
    - Pattern validation (regex)
    - Format validation (email, uuid, date-time, etc.)
    - Min/max length and values
    - oneOf, anyOf, allOf
    - And all other JSON Schema features

    Args:
        json_data: The JSON data to validate
        schema: The JSON Schema to validate against

    Returns:
        Tuple of (is_valid, error_messages). If valid, error_messages is None.
        If invalid, error_messages contains all validation errors.
    """
    try:
        validator = jsonschema.Draft7Validator(
            schema, format_checker=jsonschema.Draft7Validator.FORMAT_CHECKER
        )
        # Collect ALL validation errors
        errors = list(validator.iter_errors(json_data))
        if errors:
            # Format all errors with their paths
            error_messages = []
            for error in errors:
                error_path = ".".join(str(p) for p in error.path) if error.path else "root"
                error_messages.append(f"{error.message} at {error_path}")
            # Join all errors with newlines
            return False, "\n".join(error_messages)
        else:
            return True, None
    except jsonschema.SchemaError as e:
        return False, f"Invalid schema: {e.message}"
    except Exception as e:
        return False, f"Validation error: {e!s}"


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_datadog_datastores(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListDatastores'."]:
    """Retrieve a list of all Datadog datastores.

    Use this tool to obtain a complete list of datastores within your organization on Datadog. Ideal for inventory management and audit purposes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions-datastores".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_new_datastore(
    context: ToolContext,
    datastore_description: Annotated[
        str | None, "A human-readable description about the datastore."
    ] = None,
    datastore_display_name: Annotated[
        str | None,
        "The display name for the new datastore. This should be a human-readable and descriptive name.",  # noqa: E501
    ] = None,
    datastore_id: Annotated[
        str | None,
        "Optional ID for the new datastore. If not provided, a default one will be generated automatically.",  # noqa: E501
    ] = None,
    datastore_resource_type: Annotated[
        str | None, "Specifies the resource type for the datastore. Valid value is 'datastores'."
    ] = "datastores",
    organization_access_level: Annotated[
        str | None,
        "The access level for the datastore within the organization. Options: 'contributor', 'viewer', or 'manager'.",  # noqa: E501
    ] = None,
    primary_key_column_name: Annotated[
        str | None,
        "The name of the primary key column for this datastore. Must follow PostgreSQL naming conventions and not exceed 63 characters.",  # noqa: E501
    ] = None,
    primary_key_generation_strategy: Annotated[
        str | None,
        "Set to `uuid` for automatic primary key generation when new items are added. Default is `none`, requiring manual key assignment.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateDatastore'."]:
    """Creates a new datastore in Datadog.

    Use this tool to create a new datastore within the Datadog platform. It should be called when a new storage resource needs to be initialized or added to the Datadog system."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "description": datastore_description,
                "name": datastore_display_name,
                "org_access": organization_access_level,
                "primary_column_name": primary_key_column_name,
                "primary_key_generation_strategy": primary_key_generation_strategy,
            },
            "id": datastore_id,
            "type": datastore_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions-datastores".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_datadog_datastore(
    context: ToolContext,
    datastore_unique_id: Annotated[
        str, "The unique identifier of the datastore to delete in Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteDatastore'."]:
    """Delete a Datadog datastore using its unique ID.

    Use this tool to delete a specific datastore in Datadog by providing its unique identifier. Call this tool when a datastore is no longer needed and requires removal."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions-datastores/{datastore_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            datastore_id=datastore_unique_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def retrieve_datastore(
    context: ToolContext,
    datastore_identifier: Annotated[
        str, "The unique ID of the datastore to be retrieved from Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetDatastore'."]:
    """Retrieve datastore information by ID.

    Use this tool to access details of a specific datastore by providing its ID. It retrieves information from Datadog's datastore endpoint."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions-datastores/{datastore_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            datastore_id=datastore_identifier,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_datastore_attributes(
    context: ToolContext,
    datastore_unique_identifier: Annotated[
        str, "The unique identifier of the datastore to update in Datadog."
    ],
    datastore_description: Annotated[
        str | None,
        "A human-readable description for the datastore. Use this to provide additional information or context about the datastore.",  # noqa: E501
    ] = None,
    datastore_display_name: Annotated[
        str | None,
        "The display name of the datastore to be updated. Provide a concise, human-readable name.",
    ] = None,
    datastore_update_id: Annotated[
        str | None, "The unique identifier for the datastore that needs to be updated."
    ] = None,
    resource_type_for_datastores: Annotated[
        str | None, "Specifies the resource type for datastores. Must be 'datastores'."
    ] = "datastores",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateDatastore'."]:
    """Update attributes of an existing datastore in Datadog.

    Use this tool to update details of an existing datastore in Datadog by specifying its ID."""
    request_data = remove_none_values({
        "data": {
            "attributes": {"description": datastore_description, "name": datastore_display_name},
            "id": datastore_update_id,
            "type": resource_type_for_datastores,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions-datastores/{datastore_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            datastore_id=datastore_unique_identifier,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_datastore_item(
    context: ToolContext,
    datastore_id: Annotated[
        str,
        "A string representing the unique identifier of the datastore from which the item will be deleted.",  # noqa: E501
    ],
    item_primary_key: Annotated[
        str | None,
        "The primary key value identifying the item to delete. Max length is 256 characters.",
    ] = None,
    item_unique_identifier: Annotated[
        str | None,
        "Optional unique identifier of the item to delete. Use if available for more precise deletion.",  # noqa: E501
    ] = None,
    resource_type_for_datastore_items: Annotated[
        str | None, "The resource type for datastore items. Must be 'items'."
    ] = "items",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteDatastoreItem'."]:
    """Delete an item from a datastore by its key.

    Use this tool to delete a specific item from a datastore when you have the key and datastore ID."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"id": item_unique_identifier, "item_key": item_primary_key},
            "type": resource_type_for_datastore_items,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions-datastores/{datastore_id}/items".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), datastore_id=datastore_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_datastore_items(
    context: ToolContext,
    datastore_identifier: Annotated[
        str, "The unique identifier for the datastore from which to fetch items."
    ],
    item_limit_per_page: Annotated[
        int | None,
        "Limit the number of items to return per page for pagination. Maximum of 100 items per page.",  # noqa: E501
    ] = None,
    pagination_offset: Annotated[
        int | None,
        "Specifies the number of items to skip from the beginning of the result set for pagination.",  # noqa: E501
    ] = None,
    primary_item_key: Annotated[
        str | None,
        "Primary key to retrieve a specific item. Cannot be used with the filter parameter.",
    ] = None,
    search_filter: Annotated[
        str | None,
        "Query filter to search datastore items using the logs search syntax. Cannot be used with item_key.",  # noqa: E501
    ] = None,
    sort_order: Annotated[
        str | None,
        "Sort results by a specific field. Use '-' prefix for descending order (e.g., '-created_at').",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListDatastoreItems'."]:
    """Retrieve items from a specified datastore.

    Use this tool to list items from a datastore. You can filter the results by using either an item key or a filter query parameter, but not both simultaneously. Supports server-side pagination for managing large datasets."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions-datastores/{datastore_id}/items".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            datastore_id=datastore_identifier,
        ),
        method="GET",
        params=remove_none_values({
            "filter": search_filter,
            "item_key": primary_item_key,
            "page[limit]": item_limit_per_page,
            "page[offset]": pagination_offset,
            "sort": sort_order,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_datastore_item(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    datastore_identifier: Annotated[
        str | None,
        "The unique identifier for the datastore that contains the item to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateDatastoreItem'."]:
    """Partially update an item in a datastore by its key.

    Use this tool to modify an existing item in a datastore by specifying its key. It's ideal for making incremental changes without altering the entire record, leveraging Datadog's API.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEDATASTOREITEM_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not datastore_identifier:
        missing_params.append(("datastore_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEDATASTOREITEM_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEDATASTOREITEM_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/actions-datastores/{datastore_id}/items".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            datastore_id=datastore_identifier,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEDATASTOREITEM_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def bulk_delete_datastore_items(
    context: ToolContext,
    datastore_identifier: Annotated[
        str, "The unique ID of the datastore from which items will be deleted."
    ],
    datastore_items_id: Annotated[
        str | None, "ID for the datastore of items you want to delete."
    ] = None,
    item_keys_to_delete: Annotated[
        list[str] | None,
        "List of up to 100 primary keys identifying items to delete from the datastore.",
    ] = None,
    items_resource_type: Annotated[
        str | None, "Specifies the resource type of the items. Must be 'items'."
    ] = "items",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BulkDeleteDatastoreItems'."]:
    """Delete multiple items from a datastore at once.

    Use this tool to delete multiple items from a specified datastore by their keys in a single operation. It's ideal for efficiently removing batches of entries when managing datastore contents."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"item_keys": item_keys_to_delete},
            "id": datastore_items_id,
            "type": items_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions-datastores/{datastore_id}/items/bulk".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            datastore_id=datastore_identifier,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def bulk_update_datastore_items(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    datastore_identifier: Annotated[
        str | None,
        "The unique identifier for the datastore where items will be updated or replaced.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BulkWriteDatastoreItems'."]:
    """Perform bulk creation or replacement of datastore items.

    Use this tool to create or replace multiple items in a Datadog datastore in a single operation by specifying their keys.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "BULKUPDATEDATASTOREITEMS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not datastore_identifier:
        missing_params.append(("datastore_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["BULKUPDATEDATASTOREITEMS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["BULKUPDATEDATASTOREITEMS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/actions-datastores/{datastore_id}/items/bulk".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            datastore_id=datastore_identifier,
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKUPDATEDATASTOREITEMS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_app_key_registrations(
    context: ToolContext,
    page_number: Annotated[
        int | None, "The page number to return for paginating through app key registrations."
    ] = None,
    results_per_page: Annotated[
        int | None, "The number of App Key Registrations to return per page."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListAppKeyRegistrations'."]:
    """Retrieve a list of app key registrations from Datadog.

    Use this tool to get a detailed list of all app key registrations available in your Datadog account. Ideal for managing and auditing app key usage."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions/app_key_registrations".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({"page[size]": results_per_page, "page[number]": page_number}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def unregister_app_key(
    context: ToolContext,
    app_key_id: Annotated[str, "The unique identifier of the application key to be unregistered."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UnregisterAppKey'."]:
    """Unregister an application key to revoke its access.

    Use this tool to revoke access by unregistering a specific application key from Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions/app_key_registrations/{app_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_key_id=app_key_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_app_key_registration(
    context: ToolContext,
    app_key_id: Annotated[str, "The unique ID of the app key to fetch its registration details."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAppKeyRegistration'."]:
    """Retrieve details of an existing App Key Registration.

    This tool retrieves information about a specific App Key Registration by its ID. Use it to obtain details of an app key currently registered in the Datadog system via its unique identifier."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions/app_key_registrations/{app_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_key_id=app_key_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def register_datadog_app_key(
    context: ToolContext,
    app_key_id: Annotated[
        str,
        "The unique identifier for the app key to be registered with Datadog. It must be a valid string.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RegisterAppKey'."]:
    """Register a new app key in Datadog.

    Use this tool to register a new application key with Datadog, enabling API access and integrations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions/app_key_registrations/{app_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_key_id=app_key_id
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_action_connection(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateActionConnection'."]:
    """Create a new action connection in Datadog.

    Use this tool to create a new action connection in Datadog. Ensure you have a registered application key before using this tool.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEACTIONCONNECTION_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEACTIONCONNECTION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEACTIONCONNECTION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/actions/connections".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEACTIONCONNECTION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_action_connection(
    context: ToolContext,
    action_connection_id: Annotated[
        str, "The unique identifier for the action connection to be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteActionConnection'."]:
    """Delete an existing action connection in Datadog.

    Use this tool to delete a specific action connection in Datadog. Appropriate permissions via a registered application key are required to perform this action."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions/connections/{connection_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            connection_id=action_connection_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_action_connection(
    context: ToolContext,
    action_connection_id: Annotated[
        str,
        "The ID of the action connection to retrieve. Required for fetching details of a specific connection.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetActionConnection'."]:
    """Retrieve an existing Action Connection from Datadog.

    Use this tool to get details about an existing Action Connection in Datadog. This requires a registered application key."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/actions/connections/{connection_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            connection_id=action_connection_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_action_connection(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    action_connection_id: Annotated[
        str | None,
        "The unique identifier for the action connection to be updated in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateActionConnection'."]:
    """Update an existing action connection in Datadog.

    This tool updates an action connection in Datadog using the provided connection ID. Ensure you have a registered application key before calling this tool.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEACTIONCONNECTION_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not action_connection_id:
        missing_params.append(("action_connection_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEACTIONCONNECTION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEACTIONCONNECTION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/actions/connections/{connection_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            connection_id=action_connection_id,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEACTIONCONNECTION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_aws_scan_options(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListAwsScanOptions'."]:
    """Fetch AWS scan options for configured accounts.

    Use this tool to retrieve the scan options that have been set up for AWS accounts in Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/aws".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def activate_aws_scan_options(
    context: ToolContext,
    aws_account_id: Annotated[str, "The ID of the AWS account for which to activate scan options."],
    enable_container_vulnerability_scanning: Annotated[
        bool, "Enable scanning for vulnerabilities in containers when set to true."
    ],
    enable_lambda_function_scanning: Annotated[
        bool, "Enable scanning of Lambda functions. Set to true to enable, false to disable."
    ],
    enable_sensitive_data_scanning: Annotated[
        bool, "Indicates if scanning for sensitive data is enabled for the AWS account."
    ],
    enable_vulnerability_scan_host_os: Annotated[
        bool, "Indicates if scanning for vulnerabilities in host operating systems is enabled."
    ],
    resource_type: Annotated[
        str, "Specifies the resource type to activate. Must be 'aws_scan_options'."
    ] = "aws_scan_options",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateAwsScanOptions'."]:
    """Activate Agentless scan options for an AWS account."""
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "lambda": enable_lambda_function_scanning,
                "sensitive_data": enable_sensitive_data_scanning,
                "vuln_containers_os": enable_container_vulnerability_scanning,
                "vuln_host_os": enable_vulnerability_scan_host_os,
            },
            "id": aws_account_id,
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/aws".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_aws_scan_options(
    context: ToolContext,
    aws_account_id: Annotated[
        str, "The unique identifier for the AWS account whose scan options you want to delete."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteAwsScanOptions'."]:
    """Delete Agentless scan options for an AWS account.

    Use this tool to delete Agentless scan options configured for a specific AWS account. Ideal for managing AWS account security settings through Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/aws/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=aws_account_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def fetch_aws_scan_settings(
    context: ToolContext,
    aws_account_id: Annotated[str, "The unique ID of an AWS account for fetching scan options."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAwsScanOptions'."]:
    """Fetches Agentless scan options for AWS accounts.

    Use this tool to retrieve the configuration of Agentless scan options for a specified AWS account."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/aws/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=aws_account_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_aws_scan_options(
    context: ToolContext,
    account_identifier: Annotated[
        str, "The ID of the AWS account that needs scan options updated."
    ],
    aws_account_id: Annotated[str, "The ID of the AWS account for which to update scan options."],
    enable_lambda_scanning: Annotated[
        bool | None, "Set to true to enable scanning of AWS Lambda functions."
    ] = None,
    enable_sensitive_data_scanning: Annotated[
        bool | None, "Enable scanning for sensitive data in the AWS account. Set to true to enable."
    ] = None,
    enable_vulnerability_scanning_for_containers: Annotated[
        bool | None, "Set to true to enable scanning for container vulnerabilities."
    ] = None,
    enable_vulnerability_scanning_in_hosts: Annotated[
        bool | None,
        "Enable scanning for vulnerabilities in hosts. Set to true to enable, false to disable.",
    ] = None,
    resource_type: Annotated[
        str, "Specifies the resource type. Must be set to `aws_scan_options`."
    ] = "aws_scan_options",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateAwsScanOptions'."]:
    """Update Agentless scan options for an AWS account.

    This tool updates the Agentless scan settings for a specified AWS account. It should be called when modifications to scan options are needed for security or compliance purposes."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "lambda": enable_lambda_scanning,
                "sensitive_data": enable_sensitive_data_scanning,
                "vuln_containers_os": enable_vulnerability_scanning_for_containers,
                "vuln_host_os": enable_vulnerability_scanning_in_hosts,
            },
            "id": account_identifier,
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/aws/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=aws_account_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def fetch_azure_scan_options(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListAzureScanOptions'."]:
    """Fetches the scan options for Azure accounts from Datadog.

    Call this tool to retrieve the current scan options configured for Azure accounts in Datadog. It provides details about the configuration settings used in agentless scanning for these accounts."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/azure".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def activate_azure_scan_options(
    context: ToolContext,
    azure_subscription_id: Annotated[
        str | None, "The Azure subscription ID for which to activate the scan options."
    ] = None,
    enable_container_vulnerability_scan: Annotated[
        bool | None, "Set to true to activate scanning for vulnerabilities in containers."
    ] = None,
    enable_vulnerability_scan_hosts: Annotated[
        bool | None,
        "Indicate if scanning for vulnerabilities in Azure hosts is enabled. Set to true to activate.",  # noqa: E501
    ] = None,
    resource_type: Annotated[
        str | None, "Specifies the resource type. Always use 'azure_scan_options'."
    ] = "azure_scan_options",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateAzureScanOptions'."]:
    """Activate Agentless scan options for Azure subscriptions.

    Use this tool to activate Agentless scan options for a specified Azure subscription. This is useful for enabling security scans without deploying agents."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "vuln_containers_os": enable_container_vulnerability_scan,
                "vuln_host_os": enable_vulnerability_scan_hosts,
            },
            "id": azure_subscription_id,
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/azure".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_azure_subscription_scan_options(
    context: ToolContext,
    azure_subscription_id: Annotated[
        str,
        "The unique identifier for the Azure subscription whose scan options you want to delete.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteAzureScanOptions'."]:
    """Delete scan options for an Azure subscription.

    Use this tool to delete agentless scan options for a specified Azure subscription. It should be called when there is a need to remove scan settings associated with Azure resources."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/azure/{subscription_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            subscription_id=azure_subscription_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_agentless_scan_options(
    context: ToolContext,
    azure_subscription_id: Annotated[
        str, "The Azure subscription ID to retrieve the Agentless scan options for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAzureScanOptions'."]:
    """Fetch Azure Agentless scan options for a subscription.

    Use this tool to retrieve the Agentless scan options for an activated Azure subscription. This is useful for understanding the scanning configurations applied to the subscription."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/azure/{subscription_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            subscription_id=azure_subscription_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_azure_scan_options(
    context: ToolContext,
    azure_subscription_id: Annotated[
        str, "The unique identifier for the Azure subscription to update scan options."
    ],
    azure_scan_options_resource_type: Annotated[
        str | None,
        "Specifies the resource type for Azure scan options, must be 'azure_scan_options'.",
    ] = "azure_scan_options",
    azure_subscription_identifier: Annotated[
        str | None, "The Azure subscription ID for which to update scan options."
    ] = None,
    enable_container_vulnerability_scanning: Annotated[
        bool | None,
        "Enable or disable container vulnerability scanning. Set to true to enable, false to disable.",  # noqa: E501
    ] = None,
    enable_scanning_for_host_vulnerabilities: Annotated[
        bool | None, "Enable or disable scanning for vulnerabilities in host operating systems."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateAzureScanOptions'."]:
    """Update Agentless scan options for an Azure subscription.

    Use this tool to update the Agentless scanning preferences for a specified Azure subscription in Datadog."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "vuln_containers_os": enable_container_vulnerability_scanning,
                "vuln_host_os": enable_scanning_for_host_vulnerabilities,
            },
            "id": azure_subscription_identifier,
            "type": azure_scan_options_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/azure/{subscription_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            subscription_id=azure_subscription_id,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def fetch_gcp_scan_options(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListGcpScanOptions'."]:
    """Fetch GCP project scan options.

    This tool retrieves the scan options configured for all Google Cloud Platform (GCP) projects, facilitating the assessment of agentless scanning configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/gcp".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def activate_gcp_scan_options(
    context: ToolContext,
    enable_container_vulnerability_scanning: Annotated[
        bool | None, "Set to true to enable scanning for vulnerabilities in containers."
    ] = None,
    enable_vulnerability_host_scanning: Annotated[
        bool | None,
        "Set to true to enable scanning for vulnerabilities in hosts in the GCP project.",
    ] = None,
    gcp_project_id: Annotated[
        str | None, "The Google Cloud Platform project ID for which to activate the scan options."
    ] = None,
    gcp_scan_resource_type: Annotated[
        str | None, "The type of GCP scan options resource. This is typically 'gcp_scan_options'."
    ] = "gcp_scan_options",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateGcpScanOptions'."]:
    """Activate Agentless scan options for a GCP project.

    Use this tool to enable Agentless scanning for a Google Cloud Platform project. This is useful for setting up security scans without deploying agents."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "vuln_containers_os": enable_container_vulnerability_scanning,
                "vuln_host_os": enable_vulnerability_host_scanning,
            },
            "id": gcp_project_id,
            "type": gcp_scan_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/gcp".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_gcp_scan_options(
    context: ToolContext,
    gcp_project_id: Annotated[
        str,
        "The unique identifier for the Google Cloud Platform (GCP) project whose scan options you wish to delete.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteGcpScanOptions'."]:
    """Delete Agentless scan options for a GCP project.

    Use this tool to remove the agentless scanning configuration for a specified GCP project. Ideal for managing and updating project scan settings."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/gcp/{project_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), project_id=gcp_project_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def retrieve_gcp_scan_settings(
    context: ToolContext,
    gcp_project_id: Annotated[
        str, "The unique ID of the GCP project to retrieve scan options for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetGcpScanOptions'."]:
    """Retrieve GCP project agentless scan options.

    This tool fetches the agentless scanning options for a specified activated GCP project from Datadog, helping to determine scan configurations and settings."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/gcp/{project_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), project_id=gcp_project_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_gcp_scan_options(
    context: ToolContext,
    gcp_project_id: Annotated[
        str, "The Google Cloud Platform project ID to update scan options for."
    ],
    enable_container_vulnerability_scanning: Annotated[
        bool | None, "Enable (True) or disable (False) scanning for vulnerabilities in containers."
    ] = None,
    enable_host_vulnerability_scanning: Annotated[
        bool | None,
        "Indicate if scanning for vulnerabilities in host operating systems is enabled.",
    ] = None,
    gcp_scan_options_resource_type: Annotated[
        str | None,
        "Specifies the GCP scan options resource type, typically set to 'gcp_scan_options'.",
    ] = "gcp_scan_options",
    google_cloud_project_id: Annotated[
        str | None, "The ID of the GCP project to update scan options for, used as an identifier."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateGcpScanOptions'."]:
    """Update scan options for a GCP project in Datadog.

    Use this tool to update the agentless scanning options for an activated Google Cloud Platform project in Datadog. It should be called when changes to the existing scan configuration are needed."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "vuln_containers_os": enable_container_vulnerability_scanning,
                "vuln_host_os": enable_host_vulnerability_scanning,
            },
            "id": google_cloud_project_id,
            "type": gcp_scan_options_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/accounts/gcp/{project_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), project_id=gcp_project_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def fetch_recent_aws_on_demand_tasks(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListAwsOnDemandTasks'."]:
    """Retrieve the latest AWS on demand tasks.

    Fetches the most recent 1000 AWS on demand tasks. Useful for monitoring or analyzing recent AWS activities managed by Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/ondemand/aws".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def trigger_aws_resource_scan(
    context: ToolContext,
    aws_resource_arn: Annotated[
        str, "The ARN of the AWS resource to scan, such as EC2, Lambda, AMI, ECR, RDS, or S3."
    ],
    task_type: Annotated[
        str, "The type of the on-demand task. This must always be set to 'aws_resource'."
    ] = "aws_resource",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateAwsOnDemandTask'."]:
    """Trigger a high-priority scan of an AWS resource.

    Use this tool to initiate a high-priority scan of a specific AWS resource. Ensure agentless scanning is activated for the AWS account containing the resource you wish to scan."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"attributes": {"arn": aws_resource_arn}, "type": task_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/ondemand/aws".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_aws_on_demand_task_data(
    context: ToolContext,
    aws_task_uuid: Annotated[
        str,
        "The UUID of the AWS on-demand task to fetch data for. This is a unique identifier for the task.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAwsOnDemandTask'."]:
    """Fetch data of a specific AWS on-demand task in Datadog.

    Use this tool to retrieve information about a specific AWS on-demand task from Datadog's agentless scanning service. It should be called when detailed information about a task is needed, identified by its task ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/agentless_scanning/ondemand/aws/{task_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), task_id=aws_task_uuid
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_api_keys(
    context: ToolContext,
    api_key_filter: Annotated[
        str | None,
        "String to filter API keys by specified criteria. Use it to narrow down the list based on specific string matches.",  # noqa: E501
    ] = None,
    created_after_date_filter: Annotated[
        str | None, "Include API keys created on or after this date. Expected format: YYYY-MM-DD."
    ] = None,
    filter_api_keys_by_category: Annotated[
        str | None, "Filter API keys by the specified category."
    ] = None,
    filter_by_remote_config_read_enabled: Annotated[
        bool | None,
        "Set to true to filter API keys with remote config read enabled; false otherwise.",
    ] = None,
    filter_created_before_date: Annotated[
        str | None, "Include only API keys created on or before this date in the format YYYY-MM-DD."
    ] = None,
    include_related_resources: Annotated[
        str | None,
        "Comma-separated list of resource paths (`created_by`, `modified_by`) to include related data in the response.",  # noqa: E501
    ] = None,
    modified_after_date_filter: Annotated[
        str | None,
        "Specify a date to include API keys modified on or after this date. Use YYYY-MM-DD format.",
    ] = None,
    modified_before_date: Annotated[
        str | None,
        "Include API keys modified on or before this specified date. Format should be YYYY-MM-DD.",
    ] = None,
    page_size: Annotated[
        int | None,
        "Specifies the number of API keys returned in a single page; maximum value is 100.",
    ] = 10,
    sort_by_attribute: Annotated[
        str | None, "Attribute to sort API keys by. Use a minus sign for descending order."
    ] = "name",
    specific_page_number_to_return: Annotated[
        int | None, "The specific page number to return from the paginated list of API keys."
    ] = 0,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListAPIKeys'."]:
    """Retrieve all API keys for your Datadog account.

    Use this tool to fetch a list of all API keys associated with your Datadog account. It is useful for managing or auditing your API key usage."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/api_keys".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": specific_page_number_to_return,
            "sort": sort_by_attribute,
            "filter": api_key_filter,
            "filter[created_at][start]": created_after_date_filter,
            "filter[created_at][end]": filter_created_before_date,
            "filter[modified_at][start]": modified_after_date_filter,
            "filter[modified_at][end]": modified_before_date,
            "include": include_related_resources,
            "filter[remote_config_read_enabled]": filter_by_remote_config_read_enabled,
            "filter[category]": filter_api_keys_by_category,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_datadog_api_key(
    context: ToolContext,
    api_key_name: Annotated[
        str,
        "Name of the API key to be created in Datadog. This should be a descriptive and unique string identifier.",  # noqa: E501
    ],
    api_keys_resource_type: Annotated[str, "Specify the resource type as 'api_keys'."] = "api_keys",
    apikey_category: Annotated[
        str | None,
        "Specifies the category for the API key. This categorizes the key for organizational purposes.",  # noqa: E501
    ] = None,
    remote_config_read_enabled: Annotated[
        bool | None,
        "Indicates whether to enable read access to remote config for the new API key. Expects a boolean value.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateAPIKey'."]:
    """Creates a new API key in Datadog.

    Use this tool to generate a new API key in Datadog. Ideal for managing authentication and access to Datadog services."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "category": apikey_category,
                "name": api_key_name,
                "remote_config_read_enabled": remote_config_read_enabled,
            },
            "type": api_keys_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/api_keys".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_datadog_api_key(
    context: ToolContext,
    api_key_id: Annotated[str, "The unique identifier of the API key to delete in Datadog."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteAPIKey'."]:
    """Delete an API key from Datadog.

    Use this tool to delete an existing API key in Datadog by providing the API key ID. Call this tool when you need to manage and remove unnecessary or compromised keys from your Datadog account."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/api_keys/{api_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), api_key_id=api_key_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_api_key_details(
    context: ToolContext,
    api_key_id: Annotated[str, "The unique identifier for the Datadog API key to be retrieved."],
    include_related_resources: Annotated[
        str | None,
        "Comma-separated list of resource paths (`created_by`, `modified_by`) to include in the response.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAPIKey'."]:
    """Retrieves details of a specific Datadog API key.

    Use this tool to obtain information about a specific API key from Datadog by providing the API key ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/api_keys/{api_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), api_key_id=api_key_id
        ),
        method="GET",
        params=remove_none_values({"include": include_related_resources}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_datadog_api_key(
    context: ToolContext,
    api_key_id: Annotated[str, "The unique identifier for the API key to be updated in Datadog."],
    api_key_name: Annotated[str, "The new name for the API key to be updated."],
    key_id: Annotated[str, "ID of the API key to be updated in Datadog."],
    api_key_category: Annotated[
        str | None, "The category of the API key for the update operation."
    ] = None,
    api_keys_resource_type: Annotated[
        str, "Specifies the resource type for API keys. Must be 'api_keys'."
    ] = "api_keys",
    enable_remote_config_read: Annotated[
        bool | None,
        "Enable remote config read for the API key. Use true to enable, false to disable.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateAPIKey'."]:
    """Update an API key in Datadog.

    This tool updates an existing API key in Datadog. It should be called when you need to modify the details of an API key for access permissions or other configurations within the Datadog platform."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "category": api_key_category,
                "name": api_key_name,
                "remote_config_read_enabled": enable_remote_config_read,
            },
            "id": key_id,
            "type": api_keys_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/api_keys/{api_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), api_key_id=api_key_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_span_metrics(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListSpansMetrics'."]:
    """Retrieve configured span-based metrics from Datadog.

    Get the list of configured span-based metrics along with their definitions from Datadog's APM configuration."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/apm/config/metrics".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_span_metric(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateSpansMetric'."]:
    """Create a metric based on ingested spans in your organization.

    Use this tool to create a metric using your ingested spans. It will return the created span-based metric object if successful.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATESPANMETRIC_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATESPANMETRIC_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATESPANMETRIC_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/apm/config/metrics".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATESPANMETRIC_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_span_metric(
    context: ToolContext,
    metric_id: Annotated[str, "The unique identifier for the span-based metric to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteSpansMetric'."]:
    """Delete a specific span-based metric from your organization.

    Use this tool to delete a span-based metric by specifying its metric ID. It helps manage and maintain the span metrics within your organization by removing unnecessary or obsolete data."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/apm/config/metrics/{metric_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_id=metric_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_span_metric(
    context: ToolContext,
    metric_name: Annotated[str, "The name of the span-based metric to be retrieved from Datadog."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSpansMetric'."]:
    """Retrieve a specific span-based metric from Datadog.

    Use this tool to get detailed information on a particular span-based metric from your Datadog organization. Ideal for accessing span metrics data by specifying the metric ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/apm/config/metrics/{metric_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_id=metric_name
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_span_metric(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    span_metric_name: Annotated[
        str | None,
        "The name of the span-based metric to update in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateSpansMetric'."]:
    """Update a specific span-based metric in Datadog.

    Use this tool to update a specific span-based metric in your Datadog organization. Call this tool when you need to modify the configuration of an existing span-based metric.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATESPANMETRIC_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not span_metric_name:
        missing_params.append(("span_metric_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATESPANMETRIC_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATESPANMETRIC_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/apm/config/metrics/{metric_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_id=span_metric_name
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATESPANMETRIC_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_apm_retention_filters(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListApmRetentionFilters'."]:
    """Retrieve the list of APM retention filters from Datadog.

    This tool is used to obtain a list of APM retention filters in Datadog. It should be called when there's a need to review or analyze the current retention filter settings in APM configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/apm/config/retention-filters".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_apm_retention_filter(
    context: ToolContext,
    enable_retention_filter: Annotated[
        bool, "Set to true to enable the retention filter or false to disable it."
    ],
    retention_filter_name: Annotated[str, "The name for the retention filter to be created."],
    search_query: Annotated[
        str, "The search query using span search syntax to define criteria for retention."
    ],
    span_sample_rate: Annotated[
        float,
        "Sample rate for spans matching the query. A value of 1.0 processes all matching spans.",
    ],
    resource_type: Annotated[
        str, "Specify the type of the resource, always use 'apm_retention_filter'."
    ] = "apm_retention_filter",
    retention_filter_type: Annotated[
        str, "Set the type of retention filter. Must be 'spans-sampling-processor'."
    ] = "spans-sampling-processor",
    trace_sample_rate: Annotated[
        float | None,
        "Sample rate for traces with spans going through the filter. Use 1.0 to keep all matching traces.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateApmRetentionFilter'."]:
    """Create a retention filter for indexing spans in Datadog.

    Use this tool to create a retention filter in Datadog that indexes spans within your organization. Note that default filters of types spans-errors-sampling-processor and spans-appsec-sampling-processor cannot be created. This tool returns the definition of the newly created retention filter."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "enabled": enable_retention_filter,
                "filter": {"query": search_query},
                "filter_type": retention_filter_type,
                "name": retention_filter_name,
                "rate": span_sample_rate,
                "trace_rate": trace_sample_rate,
            },
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/apm/config/retention-filters".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def reorder_apm_retention_filters(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ReorderApmRetentionFilters'."]:
    """Reorder execution order of APM retention filters.

    Use this tool to change the sequence in which APM retention filters are executed. This is useful for prioritizing certain filters over others in your Datadog configuration.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "REORDERAPMRETENTIONFILTERS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["REORDERAPMRETENTIONFILTERS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["REORDERAPMRETENTIONFILTERS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/apm/config/retention-filters-execution-order".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["REORDERAPMRETENTIONFILTERS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_apm_retention_filter(
    context: ToolContext,
    retention_filter_id: Annotated[
        str, "The ID of the retention filter to delete. Default filters cannot be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteApmRetentionFilter'."]:
    """Delete a specific APM retention filter from your organization.

    Use this tool to delete a specific retention filter in Datadog's APM configuration. Note that default filters with types spans-errors-sampling-processor and spans-appsec-sampling-processor cannot be deleted."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/apm/config/retention-filters/{filter_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), filter_id=retention_filter_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_apm_retention_filter(
    context: ToolContext,
    retention_filter_id: Annotated[
        str, "The unique identifier for the specific APM retention filter to retrieve."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetApmRetentionFilter'."]:
    """Retrieve details of a specific APM retention filter.

    Use this tool to get detailed information about an APM retention filter by specifying the filter ID. It helps in managing and understanding APM data retention configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/apm/config/retention-filters/{filter_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), filter_id=retention_filter_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_apm_retention_filter(
    context: ToolContext,
    enable_retention_filter: Annotated[
        bool, "Set to true to enable or false to disable the retention filter."
    ],
    filter_id: Annotated[
        str, "The unique identifier for the retention filter that you want to update."
    ],
    filter_query: Annotated[
        str, "The search query for the retention filter, following the span search syntax."
    ],
    retention_filter_id: Annotated[str, "The unique ID of the retention filter to be updated."],
    retention_filter_name: Annotated[str, "Specify the name of the retention filter to update."],
    span_sample_rate: Annotated[
        float,
        "Sample rate to apply to spans going through this retention filter. A value of 1.0 keeps all spans matching the query. Expected to be a number between 0 and 1.",  # noqa: E501
    ],
    resource_type: Annotated[
        str,
        "Specifies the type of the APM retention filter resource, should be 'apm_retention_filter'.",  # noqa: E501
    ] = "apm_retention_filter",
    retention_filter_type: Annotated[
        str,
        "Specify the type of retention filter. Valid options are: 'spans-sampling-processor', 'spans-errors-sampling-processor', 'spans-appsec-sampling-processor'.",  # noqa: E501
    ] = "spans-sampling-processor",
    trace_sample_rate: Annotated[
        float | None,
        "Sample rate for traces containing spans that pass through the retention filter. A value of 1.0 keeps all matching traces.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateApmRetentionFilter'."]:
    """Update an APM retention filter in your organization.

    Use this tool to update a retention filter for APM in your Datadog organization. Default filters cannot be renamed or removed."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "enabled": enable_retention_filter,
                "filter": {"query": filter_query},
                "filter_type": retention_filter_type,
                "name": retention_filter_name,
                "rate": span_sample_rate,
                "trace_rate": trace_sample_rate,
            },
            "id": filter_id,
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/apm/config/retention-filters/{filter_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), filter_id=retention_filter_id
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_multiple_datadog_apps(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteApps'."]:
    """Delete multiple apps in Datadog using app IDs.

    This tool deletes multiple applications from Datadog in a single request using a list of app IDs. It requires a registered application key or configured UI permissions.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "DELETEMULTIPLEDATADOGAPPS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["DELETEMULTIPLEDATADOGAPPS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["DELETEMULTIPLEDATADOGAPPS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/app-builder/apps".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="DELETE",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["DELETEMULTIPLEDATADOGAPPS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_apps(
    context: ToolContext,
    filter_by_app_name: Annotated[
        str | None, "Filter the list of apps by specifying the app name."
    ] = None,
    filter_by_creator_email: Annotated[
        str | None,
        "Filter apps by the creator's email address. This is used to narrow down apps created by a specific user.",  # noqa: E501
    ] = None,
    filter_by_creator_uuid: Annotated[
        str | None,
        "Filter apps by the app creator's UUID. Provide the UUID of the user who created the app to narrow down the results.",  # noqa: E501
    ] = None,
    filter_by_query: Annotated[
        str | None, "Filter apps by the app name or the app creator's name."
    ] = None,
    filter_by_tags: Annotated[
        str | None, "Filter apps by specifying tags. Separate multiple tags with commas."
    ] = None,
    filter_self_service_enabled: Annotated[
        bool | None, "Filter apps by self-service enablement. True for enabled, false otherwise."
    ] = None,
    include_published_apps: Annotated[
        bool | None, "Set to true to include only published apps, false to exclude them."
    ] = None,
    page_number: Annotated[int | None, "The page number to return for paginated results."] = None,
    page_size: Annotated[int | None, "The number of apps to return per page."] = None,
    show_only_favorite_apps: Annotated[
        bool | None, "Set to true to filter and show only apps that you have marked as favorites."
    ] = None,
    sort_fields_and_directions: Annotated[
        list[str] | None,
        "An array specifying the fields and directions (e.g., 'name:asc', 'created_at:desc') to sort apps by.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListApps'."]:
    """Retrieve a list of all apps with optional filters and sorting.

    This tool calls the Datadog API to list all registered apps. It supports filtering and sorting options and provides basic information such as app ID, name, and description. Useful for managing and viewing app information through automation."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/app-builder/apps".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "limit": page_size,
            "page": page_number,
            "filter[user_name]": filter_by_creator_email,
            "filter[user_uuid]": filter_by_creator_uuid,
            "filter[name]": filter_by_app_name,
            "filter[query]": filter_by_query,
            "filter[deployed]": include_published_apps,
            "filter[tags]": filter_by_tags,
            "filter[favorite]": show_only_favorite_apps,
            "filter[self_service]": filter_self_service_enabled,
            "sort": sort_fields_and_directions,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_new_app(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateApp'."]:
    """Create a new app and return its ID using Datadog.

    Use this tool to create a new app in Datadog. Ensure you have a registered application key or configured permissions in the UI before calling this tool.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATENEWAPP_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATENEWAPP_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATENEWAPP_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request(
        url="https://{datadog_base_url}/api/v2/app-builder/apps".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=json.dumps(request_data),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_app(
    context: ToolContext,
    app_id: Annotated[
        str,
        "The ID of the app to delete in Datadog. Ensure this ID is accurate to avoid unintentional deletions.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteApp'."]:
    """Delete a specific app in Datadog.

    Use this tool to delete a specific app in Datadog. Ensure you have the necessary application key registered or permissions configured in the UI before attempting to delete."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/app-builder/apps/{app_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_id=app_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_app_details(
    context: ToolContext,
    application_id: Annotated[
        str,
        "The unique ID of the Datadog app to retrieve details for. Required for fetching app information.",  # noqa: E501
    ],
    app_version: Annotated[
        str | None,
        "Specify the app version to retrieve. Use a version number starting from 1, or special values `latest` and `deployed` for the latest or published version, respectively.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetApp'."]:
    """Retrieve comprehensive details of a Datadog app.

    This tool fetches the full definition of a specified app from Datadog. It's useful when you need detailed information about an app for monitoring or management purposes. Ensure you have a registered application key or configured permissions as required by Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/app-builder/apps/{app_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_id=application_id
        ),
        method="GET",
        params=remove_none_values({"version": app_version}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_app_version(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    application_id: Annotated[
        str | None,
        "The unique ID of the app to update. Required for creating a new version.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateApp'."]:
    """Update an app by creating a new version.

    Use this tool to update an existing app by creating a new version through Datadog's API. Ensure you have the necessary registered application key or have configured permissions in the UI before making this call.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEAPPVERSION_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not application_id:
        missing_params.append(("application_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATEAPPVERSION_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATEAPPVERSION_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request(
        url="https://{datadog_base_url}/api/v2/app-builder/apps/{app_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_id=application_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=json.dumps(request_data),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def unpublish_app(
    context: ToolContext,
    app_identifier: Annotated[
        str, "The ID of the app you want to unpublish, removing its live version."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UnpublishApp'."]:
    """Unpublish an app to remove its live version.

    Use this tool to unpublish an app and remove its live version. The app can still be updated and republished in the future. Requires a registered application key."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/app-builder/apps/{app_id}/deployment".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_id=app_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def publish_app_on_datadog(
    context: ToolContext,
    app_id_of_app_to_publish: Annotated[
        str, "The unique identifier of the app you want to publish on Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'PublishApp'."]:
    """Publish an app for user access on Datadog.

    Use this tool to publish an app for use by other users on Datadog. Ensure a restriction policy is set for correct user access. A registered application key is required for this operation."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/app-builder/apps/{app_id}/deployment".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_id=app_id_of_app_to_publish
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_application_keys(
    context: ToolContext,
    created_after_date: Annotated[
        str | None, "Include application keys created on or after this date (YYYY-MM-DD)."
    ] = None,
    created_before_date: Annotated[
        str | None,
        "Filters application keys created on or before this date. Expected format is YYYY-MM-DD.",
    ] = None,
    filter_application_keys: Annotated[
        str | None,
        "Filter the application keys based on a specified string to narrow down the results.",
    ] = None,
    include_related_resource: Annotated[
        str | None, "Specify 'owned_by' to include related resource information in the response."
    ] = None,
    page_number: Annotated[int | None, "Specify the page number to return in the results."] = 0,
    page_size: Annotated[
        int | None, "Specify the number of application keys to return per page. Maximum is 100."
    ] = 10,
    sort_keys_by_attribute: Annotated[
        str | None,
        "Sort application keys by attribute such as 'created_at', 'name', or 'last4'. Use a minus sign for descending order, e.g., '-name'.",  # noqa: E501
    ] = "name",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListApplicationKeys'."]:
    """Retrieve all application keys for your organization.

    This tool calls the Datadog API to list all application keys available for your organization, allowing you to view and manage your organization's credentials."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/application_keys".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "sort": sort_keys_by_attribute,
            "filter": filter_application_keys,
            "filter[created_at][start]": created_after_date,
            "filter[created_at][end]": created_before_date,
            "include": include_related_resource,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_datadog_app_key(
    context: ToolContext,
    application_key_id: Annotated[str, "The unique ID of the Datadog application key to delete."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteApplicationKey'."]:
    """Deletes an application key in Datadog.

    Use this tool to delete an existing application key in Datadog when it is no longer needed or if security requires its removal."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/application_keys/{app_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_key_id=application_key_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_application_key(
    context: ToolContext,
    application_key_id: Annotated[
        str, "The unique identifier for the application key to retrieve details from Datadog."
    ],
    include_related_resource: Annotated[
        str | None,
        "Resource path for related resources to include in the response. Currently, only `owned_by` is supported.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetApplicationKey'."]:
    """Retrieve an application key for your organization from Datadog.

    Use this tool to obtain information about a specific application key in your Datadog account. This can be useful for managing and auditing access keys."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/application_keys/{app_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_key_id=application_key_id
        ),
        method="GET",
        params=remove_none_values({"include": include_related_resource}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_datadog_app_key(
    context: ToolContext,
    app_key_id: Annotated[str, "The unique ID of the Datadog application key to be edited."],
    application_key_id: Annotated[
        str, "The unique identifier for the Datadog application key that needs to be updated."
    ],
    application_key_name: Annotated[
        str | None, "Name of the application key to be updated."
    ] = None,
    application_key_scopes: Annotated[
        list[str] | None,
        "Array of scopes to grant the application key. Each scope is a string specifying a permission level.",  # noqa: E501
    ] = None,
    application_keys_resource_type: Annotated[
        str, "Fixed value for the resource type, which should always be 'application_keys'."
    ] = "application_keys",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateApplicationKey'."]:
    """Edit a Datadog application key by ID.

    Use this tool to update details of a specific Datadog application key by providing its ID. Useful for modifying configurations or permissions of the application key."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"name": application_key_name, "scopes": application_key_scopes},
            "id": app_key_id,
            "type": application_keys_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/application_keys/{app_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_key_id=application_key_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_audit_logs(
    context: ToolContext,
    audit_logs_search_query: Annotated[
        str | None, "Search query using Audit Logs syntax to filter events."
    ] = None,
    cursor_for_following_results: Annotated[
        str | None,
        "Cursor to fetch subsequent pages of results. Use the cursor from the previous query's response.",  # noqa: E501
    ] = None,
    max_event_timestamp: Annotated[
        str | None, "Specify the maximum timestamp for requested events in ISO 8601 format."
    ] = None,
    max_events_per_response: Annotated[
        int | None, "Specifies the maximum number of events to return in the response."
    ] = 10,
    sort_order_of_events: Annotated[
        str | None,
        "Specify the order of events in the results. Use 'timestamp' for ascending and '-timestamp' for descending order.",  # noqa: E501
    ] = None,
    start_time_filter: Annotated[
        str | None,
        "Specify the minimum timestamp for requested events in the format YYYY-MM-DDTHH:MM:SSZ.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListAuditLogs'."]:
    """Retrieve events matching an Audit Logs search query.

    This tool calls the Datadog API to retrieve audit log events that match a specified search query. It provides the latest events with pagination support."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/audit/events".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[query]": audit_logs_search_query,
            "filter[from]": start_time_filter,
            "filter[to]": max_event_timestamp,
            "sort": sort_order_of_events,
            "page[cursor]": cursor_for_following_results,
            "page[limit]": max_events_per_response,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def search_audit_logs(
    context: ToolContext,
    audit_logs_search_query: Annotated[
        str | None,
        "A string representing the search query following the Audit Logs search syntax to filter the logs.",  # noqa: E501
    ] = "*",
    max_events_limit: Annotated[
        int | None,
        "Specify the maximum number of events to include in the response, enabling efficient pagination.",  # noqa: E501
    ] = 10,
    maximum_time_for_requested_events: Annotated[
        str | None,
        "Maximum time for the requested events. Supports date, math, and regular timestamps (in milliseconds).",  # noqa: E501
    ] = "now",
    minimum_time: Annotated[
        str | None,
        "Minimum time for the requested events. Accepts date, math, or timestamps in milliseconds.",
    ] = "now-15m",
    pagination_cursor: Annotated[
        str | None, "Cursor for retrieving subsequent pages of audit log results."
    ] = None,
    sort_parameter: Annotated[
        str | None,
        "Sort events by timestamp. Use 'timestamp' for ascending, '-timestamp' for descending.",
    ] = None,
    time_offset_seconds: Annotated[
        int | None,
        "Time offset in seconds to apply to the query, adjusting the timeframe of the log search.",
    ] = None,
    timezone: Annotated[
        str | None,
        "Specify the timezone for the query, using GMT, UTC, an offset like UTC+1, or a Timezone Database identifier like America/New_York.",  # noqa: E501
    ] = "UTC",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchAuditLogs'."]:
    """Retrieve audit logs events based on a search query.

    Use this tool to filter and search through Datadog audit logs events using complex queries. Results are paginated."""  # noqa: E501
    request_data = remove_none_values({
        "filter": {
            "from": minimum_time,
            "query": audit_logs_search_query,
            "to": maximum_time_for_requested_events,
        },
        "options": {"time_offset": time_offset_seconds, "timezone": timezone},
        "page": {"cursor": pagination_cursor, "limit": max_events_limit},
        "sort": sort_parameter,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/audit/events/search".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_authn_mappings(
    context: ToolContext,
    filter_authn_mappings: Annotated[
        str | None,
        "Filter all authentication mappings using a specific string to refine the results.",
    ] = None,
    filter_by_resource_type: Annotated[
        str | None,
        "Filter results by mapping resource type. Can be 'role' or 'team'. Defaults to 'role'.",
    ] = None,
    page_number: Annotated[
        int | None, "The specific page number to return from the list of AuthN Mappings."
    ] = 0,
    page_size: Annotated[
        int | None, "Number of results per page, with a maximum value of 100."
    ] = 10,
    sort_authn_mappings_by: Annotated[
        str | None,
        "Sort AuthN Mappings by the specified field. Options include fields like created_at, role_id, saml_assertion_attribute_id, etc. Prefix with '-' for descending order.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListAuthNMappings'."]:
    """Retrieve all AuthN Mappings in the organization.

    Use this tool to obtain a comprehensive list of all authentication mappings within your organization using Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/authn_mappings".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "sort": sort_authn_mappings_by,
            "filter": filter_authn_mappings,
            "resource_type": filter_by_resource_type,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_authn_mapping(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateAuthNMapping'."]:
    """Creates a new AuthN Mapping in Datadog.

    This tool calls the Datadog API to create a new AuthN Mapping. It should be used when you need to set up or configure authentication mappings in Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEAUTHNMAPPING_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEAUTHNMAPPING_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEAUTHNMAPPING_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/authn_mappings".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEAUTHNMAPPING_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_authn_mapping(
    context: ToolContext,
    authn_mapping_uuid: Annotated[
        str, "The unique identifier (UUID) of the AuthN Mapping to be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteAuthNMapping'."]:
    """Delete an AuthN Mapping using its UUID.

    Use this tool to delete a specific AuthN Mapping by providing its unique identifier (UUID)."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/authn_mappings/{authn_mapping_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            authn_mapping_id=authn_mapping_uuid,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_authn_mapping(
    context: ToolContext,
    authn_mapping_uuid: Annotated[str, "The UUID of the AuthN Mapping to retrieve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAuthNMapping'."]:
    """Retrieve an AuthN Mapping by its UUID.

    Use this tool to obtain details of a specific AuthN Mapping in Datadog by providing its UUID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/authn_mappings/{authn_mapping_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            authn_mapping_id=authn_mapping_uuid,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def edit_authn_mapping(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    authn_mapping_uuid: Annotated[
        str | None,
        "The UUID of the AuthN Mapping to edit.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateAuthNMapping'."]:
    """Edit an AuthN Mapping in Datadog.

    This tool allows editing an authentication mapping in Datadog. It should be called when modifications to an existing AuthN Mapping are needed.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["EDITAUTHNMAPPING_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not authn_mapping_uuid:
        missing_params.append(("authn_mapping_uuid", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["EDITAUTHNMAPPING_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["EDITAUTHNMAPPING_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/authn_mappings/{authn_mapping_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            authn_mapping_id=authn_mapping_uuid,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["EDITAUTHNMAPPING_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def search_cases(
    context: ToolContext,
    order_ascending: Annotated[
        bool | None, "Set to true for ascending order; false for descending."
    ] = False,
    page_number: Annotated[
        int | None, "The specific page number to return in the search results."
    ] = 0,
    page_size: Annotated[
        int | None, "The number of results per page, with a maximum value of 100."
    ] = 10,
    search_query: Annotated[
        str | None,
        "The search query to filter cases. Use keywords or phrases to specify your search criteria.",  # noqa: E501
    ] = None,
    sort_by_field: Annotated[
        str | None,
        "Specify the field to sort by. Options are 'created_at', 'priority', or 'status'.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchCases'."]:
    """Search and retrieve support cases from Datadog.

    Use this tool to search and retrieve support case information from Datadog. It is useful for accessing case details and statuses."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "sort[field]": sort_by_field,
            "filter": search_query,
            "sort[asc]": order_ascending,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_support_case(
    context: ToolContext,
    case_title: Annotated[
        str,
        "The title of the support case to be created. It should clearly summarize the issue or request.",  # noqa: E501
    ],
    case_type_uuid: Annotated[
        str,
        "UUID representing the case type. Provide a valid UUID to specify the type of case being created.",  # noqa: E501
    ],
    assignee_resource_type: Annotated[
        str | None, "The type of resource for the assignee, usually 'user'."
    ] = "user",
    assignee_user_id: Annotated[
        str | None,
        "A unique identifier for the user assigned to the case. Typically a UUID string.",
    ] = None,
    case_description: Annotated[
        str | None,
        "A detailed description of the support case. Include all relevant information about the issue or request.",  # noqa: E501
    ] = None,
    case_priority: Annotated[
        str | None,
        "The priority of the support case. Valid values are NOT_DEFINED, P1, P2, P3, P4, P5.",
    ] = "NOT_DEFINED",
    case_resource_type: Annotated[
        str, 'Specifies the type of resource being created. Always use "case" for this argument.'
    ] = "case",
    project_id: Annotated[
        str | None, "Provide the unique identifier of the project related to the support case."
    ] = None,
    project_resource_type: Annotated[
        str | None, "Specifies the project resource type. Must be 'project'."
    ] = "project",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCase'."]:
    """Create a new support case in Datadog.

    Use this tool to create a new support case in Datadog. Useful for reporting issues or requesting assistance."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "description": case_description,
                "priority": case_priority,
                "title": case_title,
                "type_id": case_type_uuid,
            },
            "relationships": {
                "assignee": {"data": {"id": assignee_user_id, "type": assignee_resource_type}},
                "project": {"data": {"id": project_id, "type": project_resource_type}},
            },
            "type": case_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_all_projects(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetProjects'."]:
    """Retrieve a list of all projects from Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/projects".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_project(
    context: ToolContext,
    project_key: Annotated[str, "The unique key for the project. Cannot use the value 'CASE'."],
    project_name: Annotated[
        str, "Specify the name of the project to be created. It should be a descriptive string."
    ],
    project_resource_type: Annotated[
        str, "Specifies the project resource type, which must be 'project'."
    ] = "project",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateProject'."]:
    """Create a new project in the system.

    Use this tool to initiate and create a new project effectively. It communicates with the Datadog service to set up the project environment."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"key": project_key, "name": project_name},
            "type": project_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/projects".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def remove_project(
    context: ToolContext,
    project_id: Annotated[str, "The unique identifier (UUID) of the project to be removed."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteProject'."]:
    """Remove a project using its ID.

    Use this tool to remove a project by providing the project's ID to ensure it is no longer available in the system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/projects/{project_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), project_id=project_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_project_details(
    context: ToolContext,
    project_uuid: Annotated[
        str, "The unique identifier (UUID) of the project for which details are required."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetProject'."]:
    """Retrieve details of a specific project using project ID.

    Use this tool to obtain comprehensive details about a particular project by providing the project's ID, utilizing the Datadog API."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/projects/{project_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), project_id=project_uuid
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def retrieve_support_case_types(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAllCaseTypes'."]:
    """Retrieves all available support case types from Datadog.

    This tool should be called to get a complete list of support case types available in Datadog. Useful for understanding the types of support cases that can be submitted."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/types".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_case_type_in_datadog(
    context: ToolContext,
    case_type_name: Annotated[str, "Specify the name of the case type to be created in Datadog."],
    case_type_deleted_timestamp: Annotated[
        str | None,
        "Timestamp indicating when the case type was deleted. Format should be a valid ISO 8601 string.",  # noqa: E501
    ] = None,
    case_type_description: Annotated[
        str | None, "A brief textual description of the case type to be created in Datadog."
    ] = None,
    case_type_emoji: Annotated[
        str | None, "Emoji representing the case type. Use a short, descriptive Unicode emoji."
    ] = None,
    case_type_resource_type: Annotated[
        str, "Specify the resource type for the case. Must be 'case_type'."
    ] = "case_type",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCaseType'."]:
    """Initiate the creation of a new case type in Datadog.

    Use this tool to create a new case type in Datadog, enabling structured classification and management of cases within the platform."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "deleted_at": case_type_deleted_timestamp,
                "description": case_type_description,
                "emoji": case_type_emoji,
                "name": case_type_name,
            },
            "type": case_type_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/types".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_all_custom_attributes(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAllCustomAttributes'."]:
    """Retrieve all custom attributes for cases in Datadog.

    Use this tool to fetch all the custom attributes associated with cases in Datadog. This can be useful for understanding the different custom data fields available for case management."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/types/custom_attributes".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_case_type(
    context: ToolContext,
    case_type_uuid: Annotated[str, "The unique identifier (UUID) of the case type to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCaseType'."]:
    """Delete a specific case type in Datadog.

    Use this tool to delete a specific case type by providing the case type ID. This is useful for managing and organizing case types within Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/types/{case_type_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_type_id=case_type_uuid
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_custom_attribute_configs(
    context: ToolContext,
    case_type_uuid: Annotated[
        str, "UUID for the case type to retrieve its custom attribute configurations."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'GetAllCustomAttributeConfigsByCaseType'."
]:
    """Retrieve all custom attribute configurations for a case type.

    Use this tool to fetch all custom attribute configurations associated with a specific case type. Ideal for understanding or managing attributes for various case types."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/types/{case_type_id}/custom_attributes".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_type_id=case_type_uuid
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_custom_attribute_config(
    context: ToolContext,
    allow_multiple_values: Annotated[
        bool, "Indicates if multiple values can be set for the custom attribute."
    ],
    case_type_uuid: Annotated[
        str, "UUID of the case type for which the custom attribute config is to be created."
    ],
    custom_attribute_display_name: Annotated[str, "The display name for the custom attribute."],
    custom_attribute_key: Annotated[
        str,
        "A string key used to search for the custom attribute. This is the identifier for the attribute.",  # noqa: E501
    ],
    custom_attribute_type: Annotated[
        str, "Type of the custom attribute. Options: 'URL', 'TEXT', or 'NUMBER'."
    ],
    custom_attribute_description: Annotated[
        str | None,
        "Detailed description for the custom attribute. This helps define the attribute's purpose and use.",  # noqa: E501
    ] = None,
    custom_attributes_config_type: Annotated[
        str,
        "Specifies the JSON:API resource type for the custom attributes configuration. Must be 'custom_attribute'.",  # noqa: E501
    ] = "custom_attribute",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCustomAttributeConfig'."]:
    """Create a custom attribute configuration for a specific case type.

    Use this tool to create a custom attribute configuration in Datadog for a specified case type, enhancing data categorization and organization."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "description": custom_attribute_description,
                "display_name": custom_attribute_display_name,
                "is_multi": allow_multiple_values,
                "key": custom_attribute_key,
                "type": custom_attribute_type,
            },
            "type": custom_attributes_config_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/types/{case_type_id}/custom_attributes".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_type_id=case_type_uuid
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_custom_attribute_config(
    context: ToolContext,
    case_type_uuid: Annotated[
        str,
        "The UUID of the case type for which the custom attribute configuration should be deleted.",
    ],
    custom_attribute_uuid: Annotated[str, "The UUID of the case custom attribute to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCustomAttributeConfig'."]:
    """Deletes a custom attribute configuration for a case type.

    Use this tool to delete a specified custom attribute configuration from a case type in Datadog. It should be called when you need to remove an attribute configuration identified by a specific `case_type_id` and `custom_attribute_id`. Returns a confirmation of the deletion."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/types/{case_type_id}/custom_attributes/{custom_attribute_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            case_type_id=case_type_uuid,
            custom_attribute_id=custom_attribute_uuid,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_case_details(
    context: ToolContext,
    case_identifier: Annotated[
        str,
        "The unique identifier for the case, either a UUID or a specific key, required to retrieve case details.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCase'."]:
    """Retrieve detailed information for a specific case.

    This tool retrieves the details of a case using its unique identifier, `case_id`. It should be called when specific information about a case is needed, such as for review, analysis, or reporting."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_id=case_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def archive_case(
    context: ToolContext,
    case_unique_id: Annotated[
        str, "The unique identifier or key of the case to be archived in Datadog."
    ],
    case_resource_type: Annotated[
        str, "Specify 'case' as the resource type to identify the case resource."
    ] = "case",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ArchiveCase'."]:
    """Archive a specific case in Datadog.

    This tool is used to archive a case in Datadog by specifying the case ID. Call this tool when you need to move a case to the archive for record-keeping or compliance."""  # noqa: E501
    request_data = remove_none_values({"data": {"type": case_resource_type}})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/archive".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_id=case_unique_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def assign_case_to_user(
    context: ToolContext,
    assignee_user_id: Annotated[str, "The UUID of the user to whom the case will be assigned."],
    case_id: Annotated[str, "The unique identifier (UUID or key) of the case to be assigned."],
    case_resource_type: Annotated[
        str, "Specify the resource type. Must be set to 'case'."
    ] = "case",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AssignCase'."]:
    """Assign a case to a specific user.

    Use this tool to assign a case to a specific user by providing the case ID. It should be called when you need to allocate a case to an appropriate team member or user."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"attributes": {"assignee_id": assignee_user_id}, "type": case_resource_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/assign".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_id=case_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_case_attributes(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    case_identifier: Annotated[
        str | None,
        "The unique identifier or key for the case to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateAttributes'."]:
    """Update attributes of a specific case.

    Use this tool to update the attributes of a specific case in Datadog by providing the case ID and new attribute values.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATECASEATTRIBUTES_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not case_identifier:
        missing_params.append(("case_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATECASEATTRIBUTES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATECASEATTRIBUTES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/attributes".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_id=case_identifier
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATECASEATTRIBUTES_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def add_comment_to_case(
    context: ToolContext,
    case_identifier: Annotated[
        str, "The unique identifier (UUID or key) for the case to which the comment will be added."
    ],
    comment_message: Annotated[str, "The message content to be added as a comment on the case."],
    case_resource_type: Annotated[
        str, "Specify the type of resource, always set to 'case'."
    ] = "case",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CommentCase'."]:
    """Add a comment to a specific case in Datadog.

    Use this tool to post a comment to a particular case within the Datadog system by providing the case ID."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"attributes": {"comment": comment_message}, "type": case_resource_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/comment".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_id=case_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_case_comment(
    context: ToolContext,
    case_identifier: Annotated[
        str, "The unique identifier or key of the case for which the comment will be deleted."
    ],
    timeline_cell_uuid: Annotated[
        str,
        "The UUID of the timeline cell containing the comment to be deleted. Required for specifying the exact comment.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCaseComment'."]:
    """Deletes a specific comment from a case.

    Use this tool to delete a comment from a specified case by providing the case and comment identifiers. It is useful for managing and maintaining relevant information within cases by removing unnecessary or outdated comments."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/comment/{cell_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            case_id=case_identifier,
            cell_id=timeline_cell_uuid,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_case_custom_attribute(
    context: ToolContext,
    case_custom_attribute_key: Annotated[
        str, "The key of the custom attribute to be removed from a case."
    ],
    case_identifier: Annotated[
        str,
        "The unique identifier or key of the case from which the custom attribute is to be deleted.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCaseCustomAttribute'."]:
    """Removes a custom attribute from a specified case.

    This tool is used to delete a custom attribute from a specific case in Datadog. It should be called when you need to remove a particular custom attribute key from a case."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/custom_attributes/{custom_attribute_key}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            case_id=case_identifier,
            custom_attribute_key=case_custom_attribute_key,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_case_custom_attribute(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    case_identifier: Annotated[
        str | None,
        "The UUID or key of the case to be updated. This identifies the specific case in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    custom_attribute_key: Annotated[
        str | None,
        "The key for the custom attribute of the case to be updated. Provide the exact key name to ensure accurate updates.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCaseCustomAttribute'."]:
    """Update a custom attribute for a specific case in Datadog.

    Use this tool to update the custom attribute of a specific case by providing the case ID and the custom attribute key in Datadog. It should be called when you need to modify the properties of an existing case.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATECASECUSTOMATTRIBUTE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not case_identifier:
        missing_params.append(("case_identifier", "path"))
    if not custom_attribute_key:
        missing_params.append(("custom_attribute_key", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATECASECUSTOMATTRIBUTE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATECASECUSTOMATTRIBUTE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/custom_attributes/{custom_attribute_key}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            case_id=case_identifier,
            custom_attribute_key=custom_attribute_key,
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATECASECUSTOMATTRIBUTE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_case_description(
    context: ToolContext,
    case_uuid_or_key: Annotated[
        str,
        "The unique identifier (UUID or key) for the specific case you want to update in Datadog.",
    ],
    new_case_description: Annotated[
        str,
        "Provide the new description text for the case you wish to update. This replaces the current case description.",  # noqa: E501
    ],
    case_resource_type: Annotated[
        str, "Specify the type of resource for the case. It must be 'case'."
    ] = "case",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCaseDescription'."]:
    """Update the description of a case in Datadog.

    Use this tool to modify the description of a specific case in Datadog by providing the case ID and new description details."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"attributes": {"description": new_case_description}, "type": case_resource_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/description".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_id=case_uuid_or_key
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_case_priority(
    context: ToolContext,
    case_identifier: Annotated[str, "The unique identifier for the case, either UUID or key."],
    case_priority: Annotated[
        str,
        "Specify the priority level of the case. Valid options are: 'NOT_DEFINED', 'P1', 'P2', 'P3', 'P4', 'P5'.",  # noqa: E501
    ] = "NOT_DEFINED",
    case_resource_type: Annotated[
        str, "Specifies the type of resource, should be set to 'case'."
    ] = "case",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdatePriority'."]:
    """Update the priority of a specific case.

    This tool updates the priority level of a given case in the system. It should be called when a change in priority is required for case management."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"attributes": {"priority": case_priority}, "type": case_resource_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/priority".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_id=case_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_case_status(
    context: ToolContext,
    case_status: Annotated[
        str, "Specify the status of the case. Valid values are 'OPEN', 'IN_PROGRESS', 'CLOSED'."
    ],
    case_uuid_or_key: Annotated[
        str, "The unique identifier or key for the case to update its status in Datadog."
    ],
    case_resource_type: Annotated[
        str, "Specifies the resource type of the case. Must be 'case'."
    ] = "case",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateStatus'."]:
    """Update the status of a specific case in Datadog.

    Use this tool to change the status of an existing case in Datadog. It should be called when you need to update a case's progress or condition to a new state."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"attributes": {"status": case_status}, "type": case_resource_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/status".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_id=case_uuid_or_key
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_case_title(
    context: ToolContext,
    case_identifier: Annotated[
        str, "The unique identifier (UUID or key) of the case whose title you want to update."
    ],
    new_case_title: Annotated[str, "The new title for the case to be updated."],
    case_resource_type: Annotated[
        str, "Specify the type of the case resource, which should be 'case'."
    ] = "case",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCaseTitle'."]:
    """Update the title of a specific case by ID.

    Use this tool to change the title of a case in Datadog by providing the specific case ID."""
    request_data = remove_none_values({
        "data": {"attributes": {"title": new_case_title}, "type": case_resource_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/title".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_id=case_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def unarchive_case(
    context: ToolContext,
    case_identifier: Annotated[
        str, "The unique identifier (UUID or key) of the case to be unarchived."
    ],
    case_resource_type: Annotated[str, "The resource type of the case, must be 'case'."] = "case",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UnarchiveCase'."]:
    """Unarchive a specific support case in Datadog.

    Use this tool to restore a previously archived support case based on its ID. This is helpful when you need to reactivate or view the details of a case that was archived."""  # noqa: E501
    request_data = remove_none_values({"data": {"type": case_resource_type}})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/unarchive".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_id=case_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def unassign_case(
    context: ToolContext,
    case_identifier: Annotated[
        str, "The unique UUID or key representing the case to be unassigned in Datadog."
    ],
    case_resource_type: Annotated[
        str, "Specifies the resource type of the case. Must be set to 'case'."
    ] = "case",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UnassignCase'."]:
    """Unassigns a case from its current assignee.

    Use this tool to unassign a specific case from its current assignee within Datadog. This is helpful when you want to reassign or close a support case."""  # noqa: E501
    request_data = remove_none_values({"data": {"type": case_resource_type}})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cases/{case_id}/unassign".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), case_id=case_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_software_catalog_entities(
    context: ToolContext,
    exclude_snapshotted_entities: Annotated[
        str | None, "Set to `true` to exclude entities that are snapshotted."
    ] = None,
    filter_by_kind: Annotated[
        str | None, "Filter entities by specifying the kind of entity, provided as a string."
    ] = None,
    filter_by_name: Annotated[str | None, "Filter entities by specifying their name."] = None,
    filter_by_owner: Annotated[
        str | None, "Filter the entities by their owner using a specific owner name or ID."
    ] = None,
    filter_by_reference: Annotated[
        str | None, "Filter entities by their specific reference string."
    ] = None,
    filter_by_relation_type: Annotated[
        str | None,
        "Specify the relation type to filter entities. Options include: 'RelationTypeOwns', 'RelationTypeOwnedBy', 'RelationTypeDependsOn', 'RelationTypeDependencyOf', 'RelationTypePartsOf', 'RelationTypeHasPart', 'RelationTypeOtherOwns', 'RelationTypeOtherOwnedBy', 'RelationTypeImplementedBy', 'RelationTypeImplements'.",  # noqa: E501
    ] = None,
    filter_by_uuid: Annotated[
        str | None,
        "Filter entities by their UUID. Provide the UUID as a string to retrieve specific entities.",  # noqa: E501
    ] = None,
    include_relationship_data: Annotated[
        str | None,
        "Specify which relationship data to include, such as 'schema', 'raw_schema', 'oncall', 'incident', or 'relation'.",  # noqa: E501
    ] = None,
    max_entities_per_page: Annotated[
        int | None, "Specifies the maximum number of entities to return per page in the response."
    ] = 100,
    pagination_offset: Annotated[
        int | None, "The starting point for pagination of the returned list of entities."
    ] = 0,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCatalogEntity'."]:
    """Retrieve entities from the software catalog.

    Use this tool to get a list of entities from the software catalog, ideal for managing or integrating software components."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/catalog/entity".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[offset]": pagination_offset,
            "page[limit]": max_entities_per_page,
            "filter[id]": filter_by_uuid,
            "filter[ref]": filter_by_reference,
            "filter[name]": filter_by_name,
            "filter[kind]": filter_by_kind,
            "filter[owner]": filter_by_owner,
            "filter[relation][type]": filter_by_relation_type,
            "filter[exclude_snapshot]": exclude_snapshotted_entities,
            "include": include_relationship_data,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def manage_software_catalog_entity(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpsertCatalogEntity'."]:
    """Create or update entities in the Software Catalog.

    Use this tool to either create a new entity or update an existing one within the Software Catalog in Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "MANAGESOFTWARECATALOGENTITY_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["MANAGESOFTWARECATALOGENTITY_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["MANAGESOFTWARECATALOGENTITY_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/catalog/entity".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["MANAGESOFTWARECATALOGENTITY_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_catalog_entity(
    context: ToolContext,
    catalog_entity_identifier: Annotated[
        str, "The UUID or Entity Reference for the entity to be deleted from the Software Catalog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCatalogEntity'."]:
    """Delete a single entity from the Software Catalog.

    This tool deletes a specified entity in the Software Catalog using its entity ID. Call this tool when you need to remove an entity from the Datadog Software Catalog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/catalog/entity/{entity_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            entity_id=catalog_entity_identifier,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_catalog_kinds(
    context: ToolContext,
    filter_entity_name: Annotated[
        str | None, "Filter entities in the Software Catalog by their name using a string value."
    ] = None,
    filter_uuid: Annotated[str | None, "Filter entities by their UUID in the catalog."] = None,
    max_kinds_in_response: Annotated[
        int | None, "Specify the maximum number of entity kinds to be returned in the response."
    ] = 100,
    page_offset: Annotated[
        int | None,
        "Specific offset to use as the beginning of the returned page. It determines where the data will start from in the list.",  # noqa: E501
    ] = 0,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCatalogKind'."]:
    """Retrieve entity kinds from the Software Catalog.

    This tool calls the Datadog API to retrieve a list of entity kinds from the Software Catalog. It should be used when you need information about the types of entities available in the catalog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/catalog/kind".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[offset]": page_offset,
            "page[limit]": max_kinds_in_response,
            "filter[id]": filter_uuid,
            "filter[name]": filter_entity_name,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_software_catalog_kind(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpsertCatalogKind'."]:
    """Create or update kinds in the Software Catalog.

    Use this tool to add new kinds or update existing ones in the Datadog Software Catalog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATESOFTWARECATALOGKIND_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESOFTWARECATALOGKIND_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESOFTWARECATALOGKIND_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/catalog/kind".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATESOFTWARECATALOGKIND_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_catalog_kind(
    context: ToolContext,
    catalog_kind_identifier: Annotated[
        str, "The unique identifier for the kind to delete from the Software Catalog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCatalogKind'."]:
    """Delete a kind from the Software Catalog.

    Use this tool to delete a specific kind from the Software Catalog using its identifier. This is useful for managing and organizing the catalog contents by removing obsolete or incorrect entries."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/catalog/kind/{kind_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), kind_id=catalog_kind_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_catalog_entity_relations(
    context: ToolContext,
    filter_by_first_entity_reference: Annotated[
        str | None, "Filter relations by the reference of the first entity in the relation."
    ] = None,
    filter_relations_by_second_entity_reference: Annotated[
        str | None, "Filter relations by the reference of the second entity in the relation."
    ] = None,
    include_relationship_data: Annotated[
        str | None, "Specify which relationship data to include: 'entity' or 'schema'."
    ] = None,
    maximum_relations_per_page: Annotated[
        int | None, "Maximum number of relations to include in the response."
    ] = 100,
    page_offset: Annotated[int | None, "The starting offset for the returned page of results."] = 0,
    relation_type_filter: Annotated[
        str | None,
        "Filter relations by type using predefined relation types such as 'RelationTypeOwns', 'RelationTypeDependsOn', etc.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCatalogRelation'."]:
    """Retrieve entity relations from the software catalog.

    This tool retrieves a list of entity relations from the Datadog Software Catalog. It's useful for understanding connections and dependencies between different software entities."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/catalog/relation".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[offset]": page_offset,
            "page[limit]": maximum_relations_per_page,
            "filter[type]": relation_type_filter,
            "filter[from_ref]": filter_by_first_entity_reference,
            "filter[to_ref]": filter_relations_by_second_entity_reference,
            "include": include_relationship_data,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def aggregate_pipeline_events(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AggregateCIAppPipelineEvents'."]:
    """Aggregate CI pipeline event metrics and timeseries.

    This tool aggregates CI Visibility pipeline events into buckets of computed metrics and timeseries. Use it when you need to analyze CI pipeline performances and trends.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "AGGREGATEPIPELINEEVENTS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["AGGREGATEPIPELINEEVENTS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["AGGREGATEPIPELINEEVENTS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/ci/pipelines/analytics/aggregate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["AGGREGATEPIPELINEEVENTS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_ci_pipeline_events(
    context: ToolContext,
    cursor_for_next_page: Annotated[
        str | None, "Cursor to paginate through results. Use the cursor from the previous query."
    ] = None,
    event_order: Annotated[
        str | None,
        "Specifies the order of CI pipeline events in the results. Use 'timestamp' for ascending and '-timestamp' for descending order.",  # noqa: E501
    ] = None,
    max_events_response: Annotated[
        int | None,
        "Specify the maximum number of events to include in the response. Accepts an integer value.",  # noqa: E501
    ] = 10,
    maximum_timestamp: Annotated[
        str | None,
        "Specify the maximum timestamp for the requested events. Format as an ISO 8601 string (e.g., 2023-10-01T00:00:00Z).",  # noqa: E501
    ] = None,
    min_timestamp: Annotated[
        str | None,
        "Specify the earliest time for the events you want to retrieve. Use a timestamp string.",
    ] = None,
    search_query: Annotated[
        str | None,
        "A search query using Datadog's log syntax to filter CI pipeline events. Specify criteria to refine results.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCIAppPipelineEvents'."]:
    """Retrieve CI pipeline events based on a search query.

    Use this tool to get a list of continuous integration pipeline events from Datadog that match a specified search query. Results are paginated and similar to how logs are handled, providing insight into your latest pipeline activities."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/ci/pipelines/events".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[query]": search_query,
            "filter[from]": min_timestamp,
            "filter[to]": maximum_timestamp,
            "sort": event_order,
            "page[cursor]": cursor_for_next_page,
            "page[limit]": max_events_response,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def search_ci_pipeline_events(
    context: ToolContext,
    filter_to_time: Annotated[
        str | None,
        "The maximum time for requested events; supports date, math, and timestamps (in milliseconds).",  # noqa: E501
    ] = "now",
    max_events_per_page: Annotated[
        int | None,
        "Specify the maximum number of events to retrieve in a single response. This limits the number of events returned in one page of results.",  # noqa: E501
    ] = 10,
    min_time_for_events: Annotated[
        str | None,
        "Specify the minimum time for the requested events. Supports date, math expressions, and timestamps in milliseconds.",  # noqa: E501
    ] = "now-15m",
    pagination_cursor: Annotated[
        str | None,
        "Use this to fetch the next set of results by providing the cursor value from the previous query response.",  # noqa: E501
    ] = None,
    query_time_offset_seconds: Annotated[
        int | None, "The time offset in seconds to apply to the query for event retrieval."
    ] = None,
    search_query: Annotated[
        str | None,
        "The search query using CI Visibility Explorer search syntax to filter pipeline events.",
    ] = "*",
    sort_events_by: Annotated[
        str | None,
        "Defines the order of CI pipeline events by timestamp. Use 'timestamp' for ascending order and '-timestamp' for descending order.",  # noqa: E501
    ] = None,
    timezone: Annotated[
        str | None,
        "Specify the timezone as GMT, UTC, a UTC offset (like UTC+1), or a Timezone Database identifier (e.g., America/New_York).",  # noqa: E501
    ] = "UTC",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchCIAppPipelineEvents'."]:
    """Retrieve CI pipeline events matching a search query.

    Use this tool to obtain CI Visibility pipeline events that fit a specific search query. It's useful for filtering and searching through pipeline events using complex queries. The results are paginated."""  # noqa: E501
    request_data = remove_none_values({
        "filter": {"from": min_time_for_events, "query": search_query, "to": filter_to_time},
        "options": {"time_offset": query_time_offset_seconds, "timezone": timezone},
        "page": {"cursor": pagination_cursor, "limit": max_events_per_page},
        "sort": sort_events_by,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/ci/pipelines/events/search".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def aggregate_test_metrics(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AggregateCIAppTestEvents'."]:
    """Aggregate CI Visibility test events into metrics and timeseries.

    Use this tool to aggregate CI test events into buckets for computed metrics and timeseries insights.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["AGGREGATETESTMETRICS_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["AGGREGATETESTMETRICS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["AGGREGATETESTMETRICS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/ci/tests/analytics/aggregate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["AGGREGATETESTMETRICS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_ci_test_events(
    context: ToolContext,
    max_events_in_response: Annotated[
        int | None, "Specify the maximum number of CI test events to return in the response."
    ] = 10,
    max_timestamp: Annotated[
        str | None, "Specify the maximum timestamp for the requested events."
    ] = None,
    minimum_timestamp: Annotated[
        str | None,
        "The minimum timestamp to filter requested events. Format is typically ISO 8601.",
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "Cursor for fetching the next set of paginated results, provided by the previous query.",
    ] = None,
    search_query: Annotated[
        str | None, "Search query using log syntax to filter CI Visibility test events."
    ] = None,
    sort_order: Annotated[
        str | None,
        "Specify the order of events by using 'timestamp' for ascending or '-timestamp' for descending.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCIAppTestEvents'."]:
    """Retrieve CI test events based on a search query.

    Use this tool to obtain CI Visibility test events from Datadog that match a specified search query. The results are paginated in a manner similar to logs. This can help in reviewing and analyzing the latest test events."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/ci/tests/events".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[query]": search_query,
            "filter[from]": minimum_timestamp,
            "filter[to]": max_timestamp,
            "sort": sort_order,
            "page[cursor]": pagination_cursor,
            "page[limit]": max_events_in_response,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def search_ci_test_events(
    context: ToolContext,
    maximum_event_time: Annotated[
        str | None,
        "The maximum time for the requested events. Supports date strings, math expressions, or timestamps (in milliseconds).",  # noqa: E501
    ] = "now",
    maximum_events_in_response: Annotated[
        int | None,
        "Specify the maximum number of events to be returned in the response. This limits the size of the result set.",  # noqa: E501
    ] = 10,
    pagination_cursor: Annotated[
        str | None,
        "Cursor for retrieving the next set of paginated results based on previous queries.",
    ] = None,
    search_query: Annotated[
        str | None,
        "The search query using CI Visibility Explorer syntax for filtering test events.",
    ] = "*",
    sort_order: Annotated[
        str | None,
        "Specify the sorting order for events. Use 'timestamp' for ascending or '-timestamp' for descending order.",  # noqa: E501
    ] = None,
    start_time_filter: Annotated[
        str | None,
        "The minimum time for requested events; can be a date, mathematical expression, or timestamp in milliseconds.",  # noqa: E501
    ] = "now-15m",
    time_offset_seconds: Annotated[
        int | None,
        "The time offset, in seconds, to apply to the query for adjusting the search time range.",
    ] = None,
    timezone: Annotated[
        str | None,
        "Specify the timezone as GMT, UTC, a UTC offset (e.g., UTC+1), or a Timezone Database identifier (e.g., America/New_York).",  # noqa: E501
    ] = "UTC",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchCIAppTestEvents'."]:
    """Retrieve CI Visibility test events with advanced search capabilities.

    Use this tool to obtain CI Visibility test events that match specific search criteria. Ideal for building complex event filtering and searching within Datadog's CI data."""  # noqa: E501
    request_data = remove_none_values({
        "filter": {"from": start_time_filter, "query": search_query, "to": maximum_event_time},
        "options": {"time_offset": time_offset_seconds, "timezone": timezone},
        "page": {"cursor": pagination_cursor, "limit": maximum_events_in_response},
        "sort": sort_order,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/ci/tests/events/search".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_custom_security_framework(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCustomFramework'."]:
    """Create a custom security framework in Datadog.

    This tool is used to create a custom security framework in Datadog's cloud security management system. Call this tool when you need to define or customize security protocols and structures.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATECUSTOMSECURITYFRAMEWORK_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATECUSTOMSECURITYFRAMEWORK_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATECUSTOMSECURITYFRAMEWORK_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/cloud_security_management/custom_frameworks".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATECUSTOMSECURITYFRAMEWORK_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_custom_framework(
    context: ToolContext,
    framework_handle: Annotated[
        str, "The unique identifier for the custom framework to be deleted."
    ],
    framework_version: Annotated[str, "Specify the version of the custom framework to delete."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCustomFramework'."]:
    """Delete a custom framework from Datadog.

    This tool deletes a specified custom framework in Datadog's cloud security management system. Use it when you need to remove an existing custom security framework by its handle and version."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cloud_security_management/custom_frameworks/{handle}/{version}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            handle=framework_handle,
            version=framework_version,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_custom_framework(
    context: ToolContext,
    framework_handle: Annotated[str, "The unique identifier for the custom framework to retrieve."],
    framework_version: Annotated[
        str,
        "Specify the version of the framework to retrieve. Use the exact version number or identifier.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCustomFramework'."]:
    """Retrieve a specific custom framework by handle and version.

    Use this tool to get details of a specific custom framework in Datadog by providing its handle and version. This is useful for accessing specific configurations or settings within cloud security management frameworks."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cloud_security_management/custom_frameworks/{handle}/{version}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            handle=framework_handle,
            version=framework_version,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_custom_framework(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    framework_handle: Annotated[
        str | None,
        "The unique identifier for the framework to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    framework_version: Annotated[
        str | None,
        "Specifies the version of the framework to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCustomFramework'."]:
    """Update an existing custom security management framework.

    Use this tool to make changes to an existing custom framework within Datadog's cloud security management. Invoke this when you need to update framework details identified by a specific handle and version.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATECUSTOMFRAMEWORK_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not framework_handle:
        missing_params.append(("framework_handle", "path"))
    if not framework_version:
        missing_params.append(("framework_version", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATECUSTOMFRAMEWORK_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATECUSTOMFRAMEWORK_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/cloud_security_management/custom_frameworks/{handle}/{version}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            handle=framework_handle,
            version=framework_version,
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATECUSTOMFRAMEWORK_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_resource_filters(
    context: ToolContext,
    cloud_provider_account_id: Annotated[
        str | None,
        "Filter resource filters by the cloud provider's account ID. This is valid only when a provider is specified.",  # noqa: E501
    ] = None,
    cloud_provider_filter: Annotated[
        str | None,
        "Specifies the cloud provider to filter resource filters, such as aws, gcp, or azure.",
    ] = None,
    skip_cache_for_resource_filters: Annotated[
        bool | None,
        "Set to true to skip the cache when fetching resource filters. Useful when the latest resource data is needed.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetResourceEvaluationFilters'."]:
    """Retrieve Datadog resource evaluation filters.

    Call this tool to obtain a list of resource evaluation filters for cloud security management in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cloud_security_management/resource_filters".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "cloud_provider": cloud_provider_filter,
            "account_id": cloud_provider_account_id,
            "skip_cache": skip_cache_for_resource_filters,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_resource_filters(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateResourceEvaluationFilters'."]:
    """Update resource filters in cloud security management.

    Use this tool to modify resource filters within Datadog's cloud security management. It should be called when you need to change which resources are included or excluded for evaluation.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATERESOURCEFILTERS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATERESOURCEFILTERS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATERESOURCEFILTERS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/cloud_security_management/resource_filters".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATERESOURCEFILTERS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_container_images(
    context: ToolContext,
    filter_tags: Annotated[
        str | None, "Comma-separated list of tags to filter container images by."
    ] = None,
    group_container_images_by_tags: Annotated[
        str | None,
        "Comma-separated list of tags to group container images by. Helps in organizing images based on specified criteria.",  # noqa: E501
    ] = None,
    max_results_per_page: Annotated[
        int | None, "The maximum number of container image results to return per page."
    ] = 1000,
    next_page_cursor: Annotated[
        str | None,
        "String to query the next page of container image results. Obtain this from the `meta.pagination.next_cursor` in the API response.",  # noqa: E501
    ] = None,
    sort_container_images_by: Annotated[
        str | None, "Attribute to sort Container Images by, such as 'name' or 'date'."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListContainerImages'."]:
    """Retrieve all container images for your organization.

    Use this tool to get a list of all container images associated with your organization. This is useful for monitoring and managing your containerized applications within Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/container_images".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[tags]": filter_tags,
            "group_by": group_container_images_by_tags,
            "sort": sort_container_images_by,
            "page[size]": max_results_per_page,
            "page[cursor]": next_page_cursor,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_all_containers(
    context: ToolContext,
    container_sort_attribute: Annotated[
        str | None,
        "Specify the attribute to sort containers by. Common values include 'name', 'creation_date', etc.",  # noqa: E501
    ] = None,
    filter_by_tags: Annotated[
        str | None,
        "Comma-separated list of tags to filter containers by, narrowing down the results based on specified tags.",  # noqa: E501
    ] = None,
    group_containers_by_tags: Annotated[
        str | None, "Comma-separated list of tags to group containers by."
    ] = None,
    maximum_results_returned: Annotated[
        int | None, "Maximum number of container results to return per page."
    ] = 1000,
    pagination_cursor: Annotated[
        str | None,
        "A string to query the next page of container results, using the `meta.pagination.next_cursor` from the API response.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListContainers'."]:
    """Retrieve all containers within your organization.

    Use this tool to get a comprehensive list of all containers managed by your organization in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/containers".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[tags]": filter_by_tags,
            "group_by": group_containers_by_tags,
            "sort": container_sort_attribute,
            "page[size]": maximum_results_returned,
            "page[cursor]": pagination_cursor,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_custom_allocation_rules(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCustomAllocationRules'."]:
    """Retrieve all custom allocation rules for the organization.

    Use this tool to obtain a list of all custom allocation rules defined in the organization. It provides details about how costs are allocated according to custom rules in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/arbitrary_rule".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_custom_allocation_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCustomAllocationRule'."]:
    """Create a custom allocation rule in Datadog.

    Use this tool to create a custom allocation rule in Datadog with specified filters and allocation strategies. Useful for managing cost allocations with strategies like proportional, even, timeseries, or fixed percentage methods.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATECUSTOMALLOCATIONRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATECUSTOMALLOCATIONRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATECUSTOMALLOCATIONRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/cost/arbitrary_rule".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATECUSTOMALLOCATIONRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def reorder_custom_allocation_rules(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ReorderCustomAllocationRules'."]:
    """Change execution order of custom allocation rules in Datadog.

    Use this tool to reorder custom allocation rules by specifying the complete list of rule IDs in the desired sequence. Lower indices are executed first, so arrange them according to priority.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "REORDERCUSTOMALLOCATIONRULES_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["REORDERCUSTOMALLOCATIONRULES_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["REORDERCUSTOMALLOCATIONRULES_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/cost/arbitrary_rule/reorder".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["REORDERCUSTOMALLOCATIONRULES_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_custom_allocation_rule(
    context: ToolContext,
    custom_allocation_rule_id: Annotated[
        int, "The unique identifier of the custom allocation rule to be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCustomAllocationRule'."]:
    """Delete an existing custom allocation rule by ID.

    Call this tool to delete an existing custom allocation rule using its ID in Datadog's cost management API."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/arbitrary_rule/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            rule_id=custom_allocation_rule_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_custom_allocation_rule(
    context: ToolContext,
    custom_allocation_rule_id: Annotated[
        int, "The unique identifier for retrieving a specific custom allocation rule in Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCustomAllocationRule'."]:
    """Retrieve a custom allocation rule by its ID.

    Call this tool to get detailed information about a specific custom allocation rule using its unique ID. Useful for examining allocation settings in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/arbitrary_rule/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            rule_id=custom_allocation_rule_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_custom_allocation_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    custom_allocation_rule_id: Annotated[
        int | None,
        "The unique identifier for the custom allocation rule to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCustomAllocationRule'."]:
    """Update custom allocation rules with new filters and strategies.

    This tool updates an existing custom allocation rule in Datadog by modifying filters and allocation strategies. It supports various allocation strategies like PROPORTIONAL, EVEN, TIMESERIES, PERCENT, and USAGE_METRIC. Users should specify strategy methods and filter conditions to redefine the allocation. Appropriate strategy fields and filter operators must be used according to the needs.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATECUSTOMALLOCATIONRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not custom_allocation_rule_id:
        missing_params.append(("custom_allocation_rule_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATECUSTOMALLOCATIONRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATECUSTOMALLOCATIONRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/cost/arbitrary_rule/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            rule_id=custom_allocation_rule_id,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATECUSTOMALLOCATIONRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_aws_cur_configs(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCostAWSCURConfigs'."]:
    """Retrieve AWS CUR configuration list from Datadog.

    Call this tool to get a list of AWS Cost and Usage Report (CUR) configurations in Datadog when you need to examine or manage AWS cost data integrations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/aws_cur_config".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_aws_cur_config(
    context: ToolContext,
    aws_account_id: Annotated[
        str | None,
        "The AWS account ID for which the CUR config is created. This is required to specify which AWS account the configuration applies to.",  # noqa: E501
    ] = None,
    aws_bucket_name_for_cur: Annotated[
        str | None, "The AWS bucket name used to store the Cost and Usage Report."
    ] = None,
    aws_cur_config_type: Annotated[
        str,
        "Type of AWS CUR config post request. Choose from available options: 'aws_cur_config_post_request'.",  # noqa: E501
    ] = "aws_cur_config_post_request",
    bucket_region: Annotated[str | None, "The AWS region where the bucket is located."] = None,
    excluded_aws_account_ids: Annotated[
        list[str] | None,
        "List of AWS account IDs to exclude from the billing dataset. Used when `include_new_accounts` is `true`.",  # noqa: E501
    ] = None,
    include_new_member_accounts: Annotated[
        bool | None,
        "Set to true to automatically include new member accounts by default in your billing dataset.",  # noqa: E501
    ] = None,
    included_aws_account_ids: Annotated[
        list[str] | None,
        "List of AWS account IDs to be included in the billing dataset, used when `include_new_accounts` is `false`.",  # noqa: E501
    ] = None,
    report_month: Annotated[
        int | None,
        "Specify the month for the AWS Cost and Usage Report. Use an integer (1-12) to represent the month.",  # noqa: E501
    ] = None,
    report_name: Annotated[
        str | None, "The name of the Cost and Usage Report to create for AWS CUR configuration."
    ] = None,
    report_prefix: Annotated[str | None, "The prefix for the Cost and Usage Report (CUR)."] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCostAWSCURConfig'."]:
    """Create an AWS CUR config for Cloud Cost Management.

    Use this tool to create a Cloud Cost Management account for an AWS Cost and Usage Report (CUR) configuration."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "account_filters": {
                    "excluded_accounts": excluded_aws_account_ids,
                    "include_new_accounts": include_new_member_accounts,
                    "included_accounts": included_aws_account_ids,
                },
                "account_id": aws_account_id,
                "bucket_name": aws_bucket_name_for_cur,
                "bucket_region": bucket_region,
                "months": report_month,
                "report_name": report_name,
                "report_prefix": report_prefix,
            },
            "type": aws_cur_config_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/aws_cur_config".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def archive_cloud_cost_account(
    context: ToolContext,
    cloud_account_id: Annotated[
        int, "The unique identifier for the Cloud Account to be archived in Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCostAWSCURConfig'."]:
    """Archive a Cloud Cost Management Account.

    Use this tool to archive a Cloud Cost Management Account in Datadog, specifically for AWS CUR configurations. Useful for managing or removing outdated or unnecessary cost configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/aws_cur_config/{cloud_account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            cloud_account_id=cloud_account_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_aws_cur_config(
    context: ToolContext,
    cloud_account_id: Annotated[
        int,
        "The unique integer identifier of the AWS cloud account for which to fetch the CUR configuration.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCostAWSCURConfig'."]:
    """Retrieve a specific AWS CUR configuration.

    This tool is used to fetch the AWS Cost and Usage Report (CUR) configuration for a specific cloud account ID. It is ideal for users who need to check or audit AWS usage configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/aws_cur_config/{cloud_account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            cloud_account_id=cloud_account_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_aws_cur_config_status(
    context: ToolContext,
    cloud_account_id: Annotated[int, "The ID of the AWS cloud account to configure in Datadog."],
    automatic_inclusion_of_new_accounts: Annotated[
        bool | None,
        "Set to true to automatically include new member accounts by default in your billing dataset.",  # noqa: E501
    ] = None,
    aws_cur_config_request_type: Annotated[
        str,
        "Specify the type of AWS CUR config Patch Request, usually 'aws_cur_config_patch_request'.",
    ] = "aws_cur_config_patch_request",
    excluded_aws_account_ids: Annotated[
        list[str] | None,
        'List of AWS account IDs to exclude from the billing dataset when "include_new_accounts" is true.',  # noqa: E501
    ] = None,
    included_aws_accounts: Annotated[
        list[str] | None,
        "List of AWS account IDs to be included in the billing dataset when `include_new_accounts` is `false`.",  # noqa: E501
    ] = None,
    is_cost_management_enabled: Annotated[
        bool | None,
        "Indicates whether the Cloud Cost Management account is enabled. Accepts a boolean value.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCostAWSCURConfig'."]:
    """Updates status or configuration of an AWS CUR config.

    Use this tool to update the active/archived status or account filtering settings of an AWS Cost and Usage Report (CUR) configuration in Datadog for a specified cloud account."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "account_filters": {
                    "excluded_accounts": excluded_aws_account_ids,
                    "include_new_accounts": automatic_inclusion_of_new_accounts,
                    "included_accounts": included_aws_accounts,
                },
                "is_enabled": is_cost_management_enabled,
            },
            "type": aws_cur_config_request_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/aws_cur_config/{cloud_account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            cloud_account_id=cloud_account_id,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_azure_configs(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCostAzureUCConfigs'."]:
    """Retrieve Azure configuration list from Datadog.

    Use this tool to obtain a list of Azure configs from Datadog. Useful for viewing or managing current Azure configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/azure_uc_config".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_azure_cost_management_account(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCostAzureUCConfigs'."]:
    """Create a Cloud Cost Management account for Azure.

    Use this tool to set up a Cloud Cost Management account for an Azure configuration using Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEAZURECOSTMANAGEMENTACCOUNT_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEAZURECOSTMANAGEMENTACCOUNT_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEAZURECOSTMANAGEMENTACCOUNT_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/cost/azure_uc_config".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEAZURECOSTMANAGEMENTACCOUNT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def archive_azure_cost_account(
    context: ToolContext,
    azure_cloud_account_id: Annotated[
        int,
        "The ID of the Azure Cloud Cost Management account to archive. This is necessary to identify which account's configurations will be removed.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCostAzureUCConfig'."]:
    """Archive an Azure Cloud Cost Management account in Datadog.

    Use this tool to archive a Cloud Cost Management account associated with Azure in Datadog. This action is irreversible and should be performed when you need to remove cost management configurations for a specific Azure account."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/azure_uc_config/{cloud_account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            cloud_account_id=azure_cloud_account_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_azure_config(
    context: ToolContext,
    azure_cloud_account_id: Annotated[
        int, "The unique identifier for the Azure cloud account to retrieve the configuration."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCostAzureUCConfig'."]:
    """Retrieve details of a specific Azure configuration.

    This tool is used to get detailed information about a specific Azure configuration using the cloud account ID. It should be called when you need to access the configuration details for Azure within the Datadog service."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/azure_uc_config/{cloud_account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            cloud_account_id=azure_cloud_account_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_azure_config_status(
    context: ToolContext,
    cloud_account_id: Annotated[
        int,
        "The identifier for the Azure Cloud account whose configuration status is being updated.",
    ],
    azure_config_patch_request_type: Annotated[
        str,
        "Specify the type of Azure config Patch Request, typically 'azure_uc_config_patch_request'.",  # noqa: E501
    ] = "azure_uc_config_patch_request",
    enable_cloud_cost_management: Annotated[
        bool | None, "Set to true to enable the Cloud Cost Management account, false to disable it."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCostAzureUCConfigs'."]:
    """Update status of Azure config to active or archived.

    This tool updates the status of a specified Azure configuration to either active or archived. It should be called when there is a need to change the operational state of an Azure config."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"is_enabled": enable_cloud_cost_management},
            "type": azure_config_patch_request_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/azure_uc_config/{cloud_account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            cloud_account_id=cloud_account_id,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def manage_budget(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpsertBudget'."]:
    """Create or update a budget in Datadog.

    This tool allows you to create a new budget or update an existing budget using the Datadog API.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["MANAGEBUDGET_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["MANAGEBUDGET_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["MANAGEBUDGET_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/cost/budget".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["MANAGEBUDGET_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_budget(
    context: ToolContext,
    budget_id: Annotated[
        str,
        "The unique identifier for the budget to be deleted. This ID is required to specify which budget to remove from the system.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteBudget'."]:
    """Delete a specified budget.

    Use this tool to delete a budget by its ID. It should be called when you want to remove an existing budget from the system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/budget/{budget_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), budget_id=budget_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_budget_details(
    context: ToolContext,
    budget_identifier: Annotated[
        str, "The unique identifier for the budget to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetBudget'."]:
    """Retrieve detailed information about a specific budget.

    Use this tool to get detailed information about a budget by providing the budget ID. Ideal for tracking budget allocations and spending."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/budget/{budget_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), budget_id=budget_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_budgets(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListBudgets'."]:
    """Fetch a list of budgets from Datadog.

    Use this tool to retrieve the list of budgets set up in Datadog, including their details and configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/budgets".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_custom_costs_files(
    context: ToolContext,
    filter_by_file_status: Annotated[
        str | None,
        "Filter the custom costs files by their status. Accepts a string value representing the status to filter by, such as 'active', 'inactive', or 'pending'.",  # noqa: E501
    ] = None,
    page_number: Annotated[
        int | None, "The page number to retrieve for pagination in the list of custom costs files."
    ] = None,
    pagination_page_size: Annotated[
        int | None, "The number of custom cost files to return per page for pagination."
    ] = 100,
    sort_key: Annotated[
        str | None,
        "Specify the key for sorting the list, with an optional '-' prefix for descending order.",
    ] = "created_at",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCustomCostsFiles'."]:
    """Retrieve a list of custom costs files from Datadog.

    Use this tool to access and list custom costs files stored in Datadog. It is useful when you need to audit or analyze custom cost data."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/custom_costs".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[number]": page_number,
            "page[size]": pagination_page_size,
            "filter[status]": filter_by_file_status,
            "sort": sort_key,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def upload_custom_costs_file(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UploadCustomCostsFile'."]:
    """Upload a custom costs file to Datadog.

    This tool uploads a custom costs file to Datadog. Use it when you need to update or input new cost data into the system.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPLOADCUSTOMCOSTSFILE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPLOADCUSTOMCOSTSFILE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPLOADCUSTOMCOSTSFILE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/cost/custom_costs".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPLOADCUSTOMCOSTSFILE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_custom_cost_file(
    context: ToolContext,
    custom_cost_file_id: Annotated[
        str, "The unique identifier of the custom costs file to be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCustomCostsFile'."]:
    """Delete a specified custom costs file in Datadog.

    Use this tool to delete a specific custom costs file by providing its file ID. It can be called when a user needs to manage their cost files by removing outdated or unnecessary custom cost entries in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/custom_costs/{file_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), file_id=custom_cost_file_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def fetch_custom_costs_file(
    context: ToolContext,
    file_identifier: Annotated[
        str, "A unique identifier for the Custom Costs file to be retrieved from Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCustomCostsFile'."]:
    """Fetch a specified Custom Costs file by file ID from Datadog.

    Use this tool to retrieve a specific Custom Costs file from Datadog by providing the file ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/custom_costs/{file_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), file_id=file_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_gcp_usage_cost_configs(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCostGCPUsageCostConfigs'."]:
    """Retrieve Google Cloud Usage Cost configurations from Datadog.

    Call this tool to obtain a list of Google Cloud Usage Cost configurations in your Datadog account. Useful for monitoring and managing GCP cost settings."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/gcp_uc_config".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_gcp_cost_management_account(
    context: ToolContext,
    gcp_bucket_name: Annotated[
        str | None, "The name of the Google Cloud bucket where the Usage Cost exports are stored."
    ] = None,
    gcp_usage_cost_export_dataset_name: Annotated[
        str | None, "The dataset name used for exporting the Google Cloud Usage Cost report."
    ] = None,
    gcp_usage_cost_report_name: Annotated[
        str | None, "The name of the Google Cloud Usage Cost report to be used for cost management."
    ] = None,
    google_cloud_billing_account_id: Annotated[
        str | None, "The Google Cloud account ID for cost management."
    ] = None,
    google_cloud_export_prefix: Annotated[
        str | None, "The export prefix for the Google Cloud Usage Cost report."
    ] = None,
    google_cloud_service_account_email: Annotated[
        str | None,
        "The unique Google Cloud service account email required for the cost management setup.",
    ] = None,
    usage_cost_config_type: Annotated[
        str,
        'Specifies the type of Google Cloud Usage Cost configuration post request. Use "gcp_uc_config_post_request" to indicate this type.',  # noqa: E501
    ] = "gcp_uc_config_post_request",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCostGCPUsageCostConfig'."]:
    """Create a cost management account for Google Cloud usage.

    This tool creates a Cloud Cost Management account specifically for Google Cloud Usage Cost configurations. It should be called when a user wants to set up or initiate tracking and managing costs related to their Google Cloud resources."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "billing_account_id": google_cloud_billing_account_id,
                "bucket_name": gcp_bucket_name,
                "export_dataset_name": gcp_usage_cost_export_dataset_name,
                "export_prefix": google_cloud_export_prefix,
                "export_project_name": gcp_usage_cost_report_name,
                "service_account": google_cloud_service_account_email,
            },
            "type": usage_cost_config_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/gcp_uc_config".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def archive_gcp_cost_management_account(
    context: ToolContext,
    cloud_account_identifier: Annotated[
        int, "The unique identifier for the GCP cloud account to be archived."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCostGCPUsageCostConfig'."]:
    """Archive a Cloud Cost Management account.

    Use this tool to archive a Google Cloud Platform (GCP) cost management account in Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/gcp_uc_config/{cloud_account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            cloud_account_id=cloud_account_identifier,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_gcp_usage_cost_config(
    context: ToolContext,
    cloud_account_identifier: Annotated[
        int,
        "The unique identifier of the Google Cloud account for which to retrieve the usage cost configuration.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCostGCPUsageCostConfig'."]:
    """Retrieve specific Google Cloud Usage Cost configuration details.

    The tool retrieves a specific Google Cloud Usage Cost configuration using the provided cloud account ID. It is useful for obtaining cost configurations related to Google Cloud usage in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/gcp_uc_config/{cloud_account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            cloud_account_id=cloud_account_identifier,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_gcp_usage_cost_status(
    context: ToolContext,
    cloud_account_id: Annotated[
        int,
        "The ID of the Google Cloud account for which the cost configuration status needs to be updated.",  # noqa: E501
    ],
    cloud_cost_management_enabled: Annotated[
        bool, "Set to 'true' to enable the Cloud Cost Management account or 'false' to disable it."
    ],
    gcp_usage_cost_config_request_type: Annotated[
        str,
        "Type of Google Cloud Usage Cost configuration patch request. Use 'gcp_uc_config_patch_request'.",  # noqa: E501
    ] = "gcp_uc_config_patch_request",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCostGCPUsageCostConfig'."]:
    """Update the status of a GCP Usage Cost config.

    This tool updates the status of a Google Cloud Platform Usage Cost configuration in Datadog, setting it as active or archived. It should be called when you need to change the operational status of a specific GCP cost configuration based on the cloud account ID."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"is_enabled": cloud_cost_management_enabled},
            "type": gcp_usage_cost_config_request_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost/gcp_uc_config/{cloud_account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            cloud_account_id=cloud_account_id,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_active_billing_dimensions(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetActiveBillingDimensions'."]:
    """Retrieve active billing dimensions for cost attribution.

    Call this tool to get active billing dimensions used for attributing costs. Useful for understanding cost distribution across different dimensions. The data is available by the 19th of the following month."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost_by_tag/active_billing_dimensions".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def fetch_monthly_cost_attribution(
    context: ToolContext,
    cost_types_fields: Annotated[
        str,
        "Comma-separated list specifying cost types and proportions. Use `*` to retrieve all fields. Example: `infra_host_on_demand_cost,infra_host_percentage_in_account`.",  # noqa: E501
    ],
    start_month: Annotated[
        str,
        "Datetime in ISO-8601 format, UTC, precise to month `[YYYY-MM]`. Represents the start of the costing period.",  # noqa: E501
    ],
    end_month: Annotated[
        str | None,
        "The final month for cost calculation. Use ISO-8601 format `[YYYY-MM]` to specify the month.",  # noqa: E501
    ] = None,
    include_child_organization_costs: Annotated[
        bool | None, "Include child organization costs in the response. Defaults to true."
    ] = True,
    pagination_next_record_id: Annotated[
        str | None,
        "Identifier for fetching the next set of results in a paginated response. Use the 'next_record_id' from the previous response.",  # noqa: E501
    ] = None,
    sort_by_billing_dimension: Annotated[
        str | None,
        "Billing dimension to sort by. Defaults to sorting by total cost. Example: 'infra_host'.",
    ] = None,
    sort_by_direction: Annotated[
        str | None,
        "Specifies the direction to sort cost attribution data. Use 'desc' for descending or 'asc' for ascending order.",  # noqa: E501
    ] = "desc",
    tag_keys_for_cost_grouping: Annotated[
        str | None,
        "Comma-separated list of tag keys used to group costs. If empty, costs won't be grouped by tag. Check `tag_config_source` in the API response for available tags.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetMonthlyCostAttribution'."]:
    """Retrieve monthly cost attribution data by tag.

    Fetch detailed monthly cost attribution by tag for both multi-org and single root-org accounts. Use this to track and report cost allocations within an organization after the 19th of each month. Ensure to handle pagination if necessary to acquire all records."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/cost_by_tag/monthly_cost_attribution".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "start_month": start_month,
            "end_month": end_month,
            "fields": cost_types_fields,
            "sort_direction": sort_by_direction,
            "sort_name": sort_by_billing_dimension,
            "tag_breakdown_keys": tag_keys_for_cost_grouping,
            "next_record_id": pagination_next_record_id,
            "include_descendants": include_child_organization_costs,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_all_csm_agents(
    context: ToolContext,
    filter_query: Annotated[
        str | None, "A search query string to filter results, e.g., `hostname:COMP-T2H4J27423`."
    ] = None,
    page_size: Annotated[
        int | None, "Specify the number of items to include in a single page for pagination."
    ] = None,
    pagination_page_index: Annotated[
        int | None, "The zero-based index of the page to retrieve for pagination."
    ] = None,
    results_sort_direction: Annotated[
        str | None, "Sets sort order for results. Use 'asc' for ascending or 'desc' for descending."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListAllCSMAgents'."]:
    """Retrieve all CSM Agents running on your infrastructure.

    Use this tool to get a comprehensive list of all Customer Service Management (CSM) Agents currently operational on your hosts and containers. This can be useful for monitoring and managing your service agents."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/csm/onboarding/agents".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page": pagination_page_index,
            "size": page_size,
            "query": filter_query,
            "order_direction": results_sort_direction,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_cloud_accounts_coverage_analysis(
    context: ToolContext,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'GetCSMCloudAccountsCoverageAnalysis'."
]:
    """Retrieve CSM coverage analysis of your cloud accounts.

    Retrieve the CSM Coverage Analysis for cloud accounts to see how many are scanned for security issues."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/csm/onboarding/coverage_analysis/cloud_accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_csm_coverage_analysis(
    context: ToolContext,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'GetCSMHostsAndContainersCoverageAnalysis'."
]:
    """Retrieve CSM coverage analysis for hosts and containers.

    Use this tool to obtain the CSM Coverage Analysis for your hosts and containers based on active agents with CSM features enabled."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/csm/onboarding/coverage_analysis/hosts_and_containers".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_serverless_coverage_analysis(
    context: ToolContext,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'GetCSMServerlessCoverageAnalysis'."
]:
    """Retrieve CSM serverless coverage analysis data from Datadog.

    This tool fetches the CSM Coverage Analysis for serverless resources in Datadog, determined by the number of agents with CSM features enabled."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/csm/onboarding/coverage_analysis/serverless".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_serverless_agents(
    context: ToolContext,
    filter_query: Annotated[
        str | None,
        "A search string to filter serverless agents, such as `hostname:COMP-T2H4J27423`.",
    ] = None,
    items_per_page: Annotated[
        int | None, "The number of items to include in a single page of results."
    ] = None,
    page_index: Annotated[
        int | None, "The zero-based page index for pagination when retrieving serverless agents."
    ] = None,
    sort_direction: Annotated[
        str | None,
        "The direction to sort results: 'asc' for ascending or 'desc' for descending order.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListAllCSMServerlessAgents'."]:
    """Retrieve all running CSM Serverless Agents.

    Use this tool to get a list of all CSM Serverless Agents currently running on your hosts and containers. It's ideal for monitoring and managing your serverless agent deployments."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/csm/onboarding/serverless/agents".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page": page_index,
            "size": items_per_page,
            "query": filter_query,
            "order_direction": sort_direction,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_user_app_keys(
    context: ToolContext,
    created_after_date: Annotated[
        str | None, "Include application keys created on or after this date in the results."
    ] = None,
    filter_by_string: Annotated[
        str | None, "Filter application keys by the specified string to narrow down the results."
    ] = None,
    filter_created_at_end_date: Annotated[
        str | None,
        "Include only application keys created on or before this date. Format: YYYY-MM-DD.",
    ] = None,
    include_related_resources: Annotated[
        str | None, "Specify 'owned_by' to include related resources in the response."
    ] = None,
    page_number: Annotated[
        int | None, "Specify the page number to retrieve application keys from."
    ] = 0,
    page_size: Annotated[
        int | None, "Specify the number of application keys per page. Maximum value is 100."
    ] = 10,
    sort_application_keys: Annotated[
        str | None,
        "Specify the attribute to sort the application keys. Use a minus sign for descending order.",  # noqa: E501
    ] = "name",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCurrentUserApplicationKeys'."]:
    """Retrieve all application keys for the current user.

    This tool retrieves a list of all application keys associated with the current user in Datadog. It should be called when there's a need to view or manage these application keys."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/current_user/application_keys".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "sort": sort_application_keys,
            "filter": filter_by_string,
            "filter[created_at][start]": created_after_date,
            "filter[created_at][end]": filter_created_at_end_date,
            "include": include_related_resources,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_user_application_key(
    context: ToolContext,
    application_key_name: Annotated[
        str, "The name of the application key to be created for the current user."
    ],
    application_key_resource_type: Annotated[
        str, "Specifies the resource type, should always be 'application_keys'."
    ] = "application_keys",
    application_key_scopes: Annotated[
        list[str] | None,
        "List of scopes to grant the application key for accessing specific resources.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCurrentUserApplicationKey'."]:
    """Create an application key for the current user in Datadog.

    Use this tool to generate a new application key for the current Datadog user. This is useful for accessing Datadog APIs that require authentication with user-specific credentials."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"name": application_key_name, "scopes": application_key_scopes},
            "type": application_key_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/current_user/application_keys".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_user_application_key(
    context: ToolContext,
    application_key_id: Annotated[
        str,
        "The ID of the application key to be deleted. Required to identify which key to remove.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCurrentUserApplicationKey'."]:
    """Delete an application key owned by the current user.

    This tool deletes an application key associated with the current user in Datadog. It should be called when the user needs to remove access or invalidate an application key."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/current_user/application_keys/{app_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_key_id=application_key_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_user_application_key(
    context: ToolContext,
    application_key_id: Annotated[
        str, "The ID of the application key to retrieve, owned by the current user."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCurrentUserApplicationKey'."]:
    """Retrieve an application key owned by the current user.

    Use this tool to obtain details about a specific application key that belongs to the currently authenticated Datadog user."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/current_user/application_keys/{app_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_key_id=application_key_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def edit_user_app_key(
    context: ToolContext,
    app_key_identifier: Annotated[str, "ID of the application key to be edited."],
    application_key_id: Annotated[
        str, "The ID of the application key to be edited. Must be a valid string ID."
    ],
    application_key_name: Annotated[str | None, "New name for the application key."] = None,
    application_key_resource_type: Annotated[
        str,
        "Specifies the resource type for the application key. Use the value 'application_keys'.",
    ] = "application_keys",
    application_key_scopes: Annotated[
        list[str] | None,
        "List of scopes to grant the application key. Each scope is a string defining permissions.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCurrentUserApplicationKey'."]:
    """Edit an application key owned by the current user.

    Use this tool to modify an existing application key for the current user. This is useful when you need to update permissions or other details associated with your application keys."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"name": application_key_name, "scopes": application_key_scopes},
            "id": app_key_identifier,
            "type": application_key_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/current_user/application_keys/{app_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_key_id=application_key_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_dashboard_from_list(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    dashboard_list_identifier: Annotated[
        int | None,
        "The unique integer ID of the dashboard list from which dashboards will be deleted.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteDashboardListItems'."]:
    """Remove dashboards from a specified list in Datadog.

    Use this tool to delete one or more dashboards from an existing dashboard list in Datadog by specifying the list ID.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "DELETEDASHBOARDFROMLIST_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not dashboard_list_identifier:
        missing_params.append(("dashboard_list_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["DELETEDASHBOARDFROMLIST_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["DELETEDASHBOARDFROMLIST_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/dashboard/lists/manual/{dashboard_list_id}/dashboards".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            dashboard_list_id=dashboard_list_identifier,
        ),
        method="DELETE",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["DELETEDASHBOARDFROMLIST_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def fetch_dashboard_list_items(
    context: ToolContext,
    dashboard_list_id: Annotated[
        int,
        "The unique integer ID of the dashboard list from which to retrieve dashboard definitions.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetDashboardListItems'."]:
    """Fetch details of dashboards in a list.

    Use this tool to retrieve the details of dashboards contained in a specific dashboard list by providing the list ID. It returns the dashboard definitions for that list."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/dashboard/lists/manual/{dashboard_list_id}/dashboards".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            dashboard_list_id=dashboard_list_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def add_dashboards_to_list(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    dashboard_list_identifier: Annotated[
        int | None,
        "Specify the integer ID of the dashboard list where dashboards will be added.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateDashboardListItems'."]:
    """Add dashboards to an existing list in Datadog.

    Use this tool to append dashboards to a specified existing dashboard list in Datadog. Ideal for organizing and managing dashboards effectively.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["ADDDASHBOARDSTOLIST_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not dashboard_list_identifier:
        missing_params.append(("dashboard_list_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["ADDDASHBOARDSTOLIST_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["ADDDASHBOARDSTOLIST_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/dashboard/lists/manual/{dashboard_list_id}/dashboards".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            dashboard_list_id=dashboard_list_identifier,
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ADDDASHBOARDSTOLIST_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_dashboard_list_items(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    dashboard_list_identifier: Annotated[
        int | None,
        "ID of the dashboard list to update with new items.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateDashboardListItems'."]:
    """Update dashboards in an existing dashboard list.

    Use this tool to update the dashboards contained in a specific dashboard list within Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEDASHBOARDLISTITEMS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not dashboard_list_identifier:
        missing_params.append(("dashboard_list_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEDASHBOARDLISTITEMS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEDASHBOARDLISTITEMS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/dashboard/lists/manual/{dashboard_list_id}/dashboards".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            dashboard_list_id=dashboard_list_identifier,
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEDASHBOARDLISTITEMS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_all_datasets(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAllDatasets'."]:
    """Retrieve all datasets configured for your organization.

    Call this tool to obtain a list of all datasets that have been configured for an organization, using the Datadog API."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/datasets".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_dataset(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateDataset'."]:
    """Create a dataset with specified configurations.

    Use this tool to create a new dataset on Datadog with the desired configurations provided in the request.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEDATASET_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATEDATASET_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATEDATASET_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/datasets".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEDATASET_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_dataset(
    context: ToolContext,
    dataset_identifier: Annotated[
        str, "The unique ID of the dataset to be deleted. Required for deletion."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteDataset'."]:
    """Deletes a dataset using its ID.

    Use this tool to delete a dataset by providing its ID. It should be called when you need to remove an existing dataset from the system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/datasets/{dataset_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), dataset_id=dataset_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def retrieve_dataset_info(
    context: ToolContext,
    dataset_identifier: Annotated[
        str, "The unique identifier of the dataset to retrieve from Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetDataset'."]:
    """Retrieve detailed information about a specific dataset from Datadog.

    Use this tool to get information about a dataset using its unique ID. It retrieves comprehensive details related to the specified dataset from Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/datasets/{dataset_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), dataset_id=dataset_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def edit_dataset(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    dataset_id: Annotated[
        str | None,
        "The unique ID of the dataset to be edited in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateDataset'."]:
    """Edit the dataset using the specified ID.

    Call this tool to edit the details of a dataset in Datadog using its ID. It updates the dataset's configuration or metadata.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["EDITDATASET_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not dataset_id:
        missing_params.append(("dataset_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["EDITDATASET_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["EDITDATASET_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/datasets/{dataset_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), dataset_id=dataset_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["EDITDATASET_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_domain_allowlist(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetDomainAllowlist'."]:
    """Retrieve the domain allowlist for an organization.

    Use this tool to get the list of domains that are allowed within your organization's settings. It helps ensure authorized access and manage security policies."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/domain_allowlist".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_domain_allowlist(
    context: ToolContext,
    allowed_domains_list: Annotated[
        list[str] | None,
        "A list of domains to include in the organization's email domain allowlist.",
    ] = None,
    email_domain_allowlist_type: Annotated[
        str, "Type of email domain allowlist. Valid value: 'domain_allowlist'."
    ] = "domain_allowlist",
    enable_email_domain_allowlist: Annotated[
        bool | None, "Set to true to enable the email domain allowlist for the organization."
    ] = None,
    organization_identifier: Annotated[
        str | None, "The unique identifier for the organization to update the domain allowlist."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'PatchDomainAllowlist'."]:
    """Update the organization's domain allowlist to control domain access."""
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "domains": allowed_domains_list,
                "enabled": enable_email_domain_allowlist,
            },
            "id": organization_identifier,
            "type": email_domain_allowlist_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/domain_allowlist".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_scheduled_downtimes(
    context: ToolContext,
    include_resources_in_response: Annotated[
        str | None,
        "Comma-separated list of resource paths to include in the response, such as `created_by` and `monitor`.",  # noqa: E501
    ] = None,
    max_downtimes_in_response: Annotated[
        int | None, "Maximum number of downtimes to include in the response."
    ] = 30,
    page_offset: Annotated[
        int | None,
        "The starting point for the list of returned scheduled downtimes, used for pagination.",
    ] = 0,
    return_current_downtimes_only: Annotated[
        bool | None, "Set to true to return only downtimes active at the time of the request."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListDowntimes'."]:
    """Retrieve all scheduled downtimes from Datadog.

    Use this tool to get a list of all downtimes that are currently scheduled in Datadog. It should be called when you need information about planned downtime periods."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/downtime".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "current_only": return_current_downtimes_only,
            "include": include_resources_in_response,
            "page[offset]": page_offset,
            "page[limit]": max_downtimes_in_response,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def schedule_downtime(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateDowntime'."]:
    """Schedule downtime for services or systems through Datadog.

    Use this tool to schedule a downtime for services or systems using Datadog's API. It is useful for planning maintenance or handling expected outages by suppressing alerts during the specified period.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["SCHEDULEDOWNTIME_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["SCHEDULEDOWNTIME_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["SCHEDULEDOWNTIME_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/downtime".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SCHEDULEDOWNTIME_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def cancel_downtime(
    context: ToolContext,
    downtime_id: Annotated[str, "Provide the ID of the downtime you wish to cancel."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelDowntime'."]:
    """Cancel an active downtime in Datadog.

    Use this tool to cancel an active downtime in Datadog. The downtime remains visible for about two days in search results until it is permanently removed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/downtime/{downtime_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), downtime_id=downtime_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_downtime_details(
    context: ToolContext,
    downtime_id: Annotated[
        str, "The unique identifier for the downtime period to retrieve details for."
    ],
    include_related_resources: Annotated[
        str | None,
        "Comma-separated list of resource paths to include in the response. Options: `created_by`, `monitor`.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetDowntime'."]:
    """Retrieve details of a specific downtime by ID.

    Use this tool to get detailed information about a specific downtime in Datadog using a downtime ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/downtime/{downtime_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), downtime_id=downtime_id
        ),
        method="GET",
        params=remove_none_values({"include": include_related_resources}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_downtime(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    downtime_id: Annotated[
        str | None,
        "The unique identifier of the downtime to be updated in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateDowntime'."]:
    """Update downtime by its ID in Datadog.

    Use this tool to modify an existing downtime configuration in Datadog by providing the downtime ID.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEDOWNTIME_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not downtime_id:
        missing_params.append(("downtime_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATEDOWNTIME_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATEDOWNTIME_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/downtime/{downtime_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), downtime_id=downtime_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEDOWNTIME_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def search_datadog_issues(
    context: ToolContext,
    end_date: Annotated[
        int,
        "End date (exclusive) for the query in milliseconds since the Unix epoch. Determines up to when the issues are retrieved.",  # noqa: E501
    ],
    object_type: Annotated[
        str, "Specify the type of the object. The value must be 'search_request'."
    ],
    search_event_query: Annotated[
        str, "Search query using the event search syntax to find relevant issues."
    ],
    start_date_millis: Annotated[
        int, "Start date (inclusive) of the query in milliseconds since the Unix epoch."
    ],
    event_track_to_query: Annotated[
        str | None,
        "Specify the track of events to query: 'trace', 'logs', or 'rum'. Either track or persona must be provided.",  # noqa: E501
    ] = None,
    include_relationship_objects: Annotated[
        list[str] | None,
        "List of relationship objects to include in the response, specified as an array of strings.",  # noqa: E501
    ] = None,
    search_persona: Annotated[
        str | None,
        "Persona for the search. Choose from ALL, BROWSER, MOBILE, or BACKEND. Either track(s) or persona(s) must be specified.",  # noqa: E501
    ] = None,
    sort_results_by: Annotated[
        str | None,
        "Attribute to sort the search results. Options: TOTAL_COUNT, FIRST_SEEN, IMPACTED_SESSIONS, PRIORITY.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchIssues'."]:
    """Search and retrieve issues from Datadog using a query.

    Use this tool to programmatically search for issues in your organization via Datadog. It returns a list of issues that match a specified search query, up to 100 per request."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "from": start_date_millis,
                "order_by": sort_results_by,
                "persona": search_persona,
                "query": search_event_query,
                "to": end_date,
                "track": event_track_to_query,
            },
            "type": object_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/error-tracking/issues/search".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({"include": include_relationship_objects}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_error_tracking_issue_details(
    context: ToolContext,
    issue_identifier: Annotated[
        str, "The unique identifier of the error tracking issue to retrieve details for."
    ],
    include_relationship_objects: Annotated[
        list[str] | None,
        "A list of relationship objects to include in the response. Provide as an array of strings.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetIssue'."]:
    """Retrieve full details of a specific error tracking issue.

    Use this tool to obtain comprehensive details about an error tracking issue from Datadog, including its attributes and relationships. Ideal for understanding specific issues and investigating errors."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/error-tracking/issues/{issue_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), issue_id=issue_identifier
        ),
        method="GET",
        params=remove_none_values({"include": include_relationship_objects}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_issue_assignee(
    context: ToolContext,
    issue_identifier: Annotated[str, "The unique identifier for the issue to update the assignee."],
    object_type: Annotated[
        str, "Specifies the type of object being updated. For issue assignee, use 'assignee'."
    ],
    user_identifier: Annotated[str, "The identifier of the user to assign the issue to."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateIssueAssignee'."]:
    """Update the assignee of an issue in Datadog.

    Use this tool to change the assignee of a specific issue in Datadog by providing the issue ID."""  # noqa: E501
    request_data = remove_none_values({"data": {"id": user_identifier, "type": object_type}})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/error-tracking/issues/{issue_id}/assignee".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), issue_id=issue_identifier
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_issue_state(
    context: ToolContext,
    issue_id_value: Annotated[
        str, "The identifier for the issue to update the state of in Datadog."
    ],
    issue_identifier: Annotated[
        str, "The unique identifier for the issue to update its state in Datadog."
    ],
    issue_object_type: Annotated[
        str, "Specifies the type of the object. Accepted value is 'error_tracking_issue'."
    ],
    issue_state: Annotated[
        str,
        "State of the issue, valid values are 'OPEN', 'ACKNOWLEDGED', 'RESOLVED', 'IGNORED', 'EXCLUDED'.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateIssueState'."]:
    """Update the state of an issue in Datadog.

    This tool updates the state of an issue identified by `issue_id` in Datadog, allowing transitions between states such as `OPEN`, `RESOLVED`, or `IGNORED`. It should be called when you need to change an issue's status."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"state": issue_state},
            "id": issue_id_value,
            "type": issue_object_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/error-tracking/issues/{issue_id}/state".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), issue_id=issue_identifier
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_datadog_events(
    context: ToolContext,
    event_search_query: Annotated[
        str | None, "Search query following Datadog's events syntax to filter events."
    ] = None,
    max_timestamp_milliseconds: Annotated[
        str | None,
        "Specify the maximum timestamp for requested events in milliseconds. Use this to limit the latest time of events retrieved.",  # noqa: E501
    ] = None,
    maximum_events_per_page: Annotated[
        int | None, "Sets the maximum number of events to return in the response."
    ] = 10,
    minimum_timestamp_millis: Annotated[
        str | None, "The minimum timestamp in milliseconds for filtering requested events."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "Cursor for paginating through results, provided in the previous query response.",
    ] = None,
    sort_order: Annotated[
        str | None,
        "Specify the order of events: 'timestamp' for ascending, '-timestamp' for descending.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListEvents'."]:
    """Retrieve events from Datadog based on a search query.

    Use this tool to list events from Datadog that match a specific search query. The results are paginated, similar to logs, and this tool is ideal for accessing the latest events."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/events".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[query]": event_search_query,
            "filter[from]": minimum_timestamp_millis,
            "filter[to]": max_timestamp_milliseconds,
            "sort": sort_order,
            "page[cursor]": pagination_cursor,
            "page[limit]": maximum_events_per_page,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def search_datadog_events(
    context: ToolContext,
    event_search_query: Annotated[
        str | None, "The search query using Datadog's event search syntax to filter events."
    ] = "*",
    max_event_time: Annotated[
        str | None,
        "Specify the maximum time for the events. Supports date math and timestamps in milliseconds.",  # noqa: E501
    ] = "now",
    maximum_events_per_page: Annotated[
        int | None,
        "Specify the maximum number of events returned per page in the response. This controls the pagination size.",  # noqa: E501
    ] = 10,
    paging_cursor: Annotated[
        str | None, "The cursor for pagination to retrieve the next set of results."
    ] = None,
    sort_order: Annotated[
        str | None,
        "Specify event sorting order: 'timestamp' for ascending, '-timestamp' for descending.",
    ] = None,
    start_time: Annotated[
        str | None,
        "The earliest time for requested events, using date math or timestamps in milliseconds.",
    ] = "now-15m",
    time_offset_seconds: Annotated[
        int | None,
        "The time offset to apply to the query in seconds. Use an integer to specify the shift in time for the search results.",  # noqa: E501
    ] = None,
    timezone: Annotated[
        str | None,
        "Specify the timezone for the query. It can be GMT, UTC, an offset (like UTC+1), or a Timezone Database identifier (like America/New_York).",  # noqa: E501
    ] = "UTC",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchEvents'."]:
    """Search and filter events in Datadog.

    Use this tool to find and filter events in Datadog using complex search queries. It returns a paginated list of events that match the specified criteria."""  # noqa: E501
    request_data = remove_none_values({
        "filter": {"from": start_time, "query": event_search_query, "to": max_event_time},
        "options": {"timeOffset": time_offset_seconds, "timezone": timezone},
        "page": {"cursor": paging_cursor, "limit": maximum_events_per_page},
        "sort": sort_order,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/events/search".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_event_details(
    context: ToolContext,
    event_unique_id: Annotated[
        str,
        "The unique identifier of the event to retrieve details for. This should be a string representing the event's UID.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetEvent'."]:
    """Retrieve detailed information about a specific event.

    Use this tool to get detailed information about an event by providing its unique event ID. It is useful for obtaining specifics about incidents or activities tracked in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/events/{event_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), event_id=event_unique_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_organization_incidents(
    context: ToolContext,
    include_related_objects: Annotated[
        list[str] | None,
        "List of related object types to include in the response. Specify as an array of strings.",
    ] = None,
    page_offset: Annotated[
        int | None,
        "Specific offset to start the returned page of incidents. Use this to paginate results.",
    ] = 0,
    page_size: Annotated[
        int | None, "Integer specifying the number of incidents per page, up to a maximum of 100."
    ] = 10,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListIncidents'."]:
    """Retrieve all incidents for your organization.

    Call this tool to get a comprehensive list of all incidents associated with the user's organization in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "include": include_related_objects,
            "page[size]": page_size,
            "page[offset]": page_offset,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_incident(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateIncident'."]:
    """Create a new incident in Datadog.

    This tool is used to create a new incident within Datadog's incident management system. Trigger this tool when there is a need to report or manage an incident requiring attention.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEINCIDENT_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATEINCIDENT_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATEINCIDENT_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/incidents".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEINCIDENT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_incident_notification_rules(
    context: ToolContext,
    resources_to_include: Annotated[
        str | None,
        "Comma-separated list of resources to include. Supported values: `created_by_user`, `last_modified_by_user`, `incident_type`, `notification_template`.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListIncidentNotificationRules'."]:
    """Retrieve all incident notification rules for the organization.

    Fetches a list of notification rules for incidents within your organization, with optional filtering by incident type."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/config/notification-rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({"include": resources_to_include}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_incident_notification_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateIncidentNotificationRule'."]:
    """Creates a new incident notification rule in Datadog.

    Use this tool to create and manage notification rules for incidents in Datadog. It should be called when there is a need to set up a new notification rule for monitoring incident alerts.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEINCIDENTNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEINCIDENTNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEINCIDENTNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/incidents/config/notification-rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEINCIDENTNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_incident_notification_rule(
    context: ToolContext,
    notification_rule_id: Annotated[
        str, "The unique identifier of the notification rule to be deleted."
    ],
    include_resources: Annotated[
        str | None,
        "Comma-separated list of resources to include, such as `created_by_user`, `last_modified_by_user`, `incident_type`, `notification_template`.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteIncidentNotificationRule'."]:
    """Delete an incident notification rule by its ID.

    Use this tool to delete a specific incident notification rule in Datadog by providing its ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/config/notification-rules/{id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=notification_rule_id
        ),
        method="DELETE",
        params=remove_none_values({"include": include_resources}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_incident_notification_rule(
    context: ToolContext,
    notification_rule_id: Annotated[
        str, "The unique identifier for the notification rule to retrieve details."
    ],
    include_resources: Annotated[
        str | None,
        "Comma-separated list of resources to include in the response. Options: `created_by_user`, `last_modified_by_user`, `incident_type`, `notification_template`.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetIncidentNotificationRule'."]:
    """Retrieve details of a specific incident notification rule.

    This tool retrieves a specific incident notification rule from Datadog by its ID. It should be called when there is a need to obtain detailed information about a particular notification rule used in incident management."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/config/notification-rules/{id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=notification_rule_id
        ),
        method="GET",
        params=remove_none_values({"include": include_resources}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_incident_notification_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    notification_rule_id: Annotated[
        str | None,
        "The unique identifier for the notification rule to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    include_resources: Annotated[
        str | None,
        "Comma-separated list of resources to include: `created_by_user`, `last_modified_by_user`, `incident_type`, `notification_template`.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateIncidentNotificationRule'."]:
    """Update an incident notification rule in Datadog.

    This tool updates an existing incident notification rule in Datadog with a complete replacement. Use it when you need to modify the settings or parameters of an incident alert rule.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEINCIDENTNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not notification_rule_id:
        missing_params.append(("notification_rule_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEINCIDENTNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEINCIDENTNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/incidents/config/notification-rules/{id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=notification_rule_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEINCIDENTNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"include": include_resources}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_incident_notification_templates(
    context: ToolContext,
    incident_type_id_filter: Annotated[
        str | None, "Optional ID to filter notification templates by incident type."
    ] = None,
    include_relationships: Annotated[
        str | None,
        "Comma-separated list of relationships to include in the response. Supported values are `created_by_user`, `last_modified_by_user`, `incident_type`.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ListIncidentNotificationTemplates'."
]:
    """Retrieve all incident notification templates.

    This tool lists all the notification templates available for incidents in Datadog. It can optionally filter the templates by incident type."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/config/notification-templates".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[incident-type]": incident_type_id_filter,
            "include": include_relationships,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_incident_notification_template(
    context: ToolContext,
    notification_content_body: Annotated[
        str,
        "The body content for the notification template, describing the detailed message of the notification.",  # noqa: E501
    ],
    notification_subject: Annotated[
        str,
        "The subject line for the notification template. This sets the subject of the template being created.",  # noqa: E501
    ],
    notification_template_category: Annotated[str, "The category of the notification template."],
    notification_template_name: Annotated[
        str, "The name for the notification template to be created."
    ],
    resource_type_notification_template: Annotated[
        str,
        "Specify the resource type for notification templates, which should be 'notification_templates'.",  # noqa: E501
    ],
    incident_type_id: Annotated[
        str | None, "The ID of the incident type to associate with the notification template."
    ] = None,
    incident_type_resource_type: Annotated[
        str | None, "The resource type for the incident, which should be 'incident_types'."
    ] = "incident_types",
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateIncidentNotificationTemplate'."
]:
    """Creates a new incident notification template.

    Use this tool to create a new notification template for incidents. It should be called when there is a need to set up or customize notifications related to incident management."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "category": notification_template_category,
                "content": notification_content_body,
                "name": notification_template_name,
                "subject": notification_subject,
            },
            "relationships": {
                "incident_type": {
                    "data": {"id": incident_type_id, "type": incident_type_resource_type}
                }
            },
            "type": resource_type_notification_template,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/config/notification-templates".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_incident_notification_template(
    context: ToolContext,
    notification_template_id: Annotated[
        str, "The unique ID of the incident notification template to be deleted."
    ],
    relationships_to_include: Annotated[
        str | None,
        "Comma-separated list of relationships to include. Options: `created_by_user`, `last_modified_by_user`, `incident_type`.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'DeleteIncidentNotificationTemplate'."
]:
    """Deletes a notification template by its ID.

    Use this tool to delete a specific incident notification template by providing its ID. It should be called when a notification template is no longer needed or requires removal."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/config/notification-templates/{id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=notification_template_id
        ),
        method="DELETE",
        params=remove_none_values({"include": relationships_to_include}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_incident_notification_template(
    context: ToolContext,
    template_id: Annotated[str, "The ID of the notification template to retrieve from Datadog."],
    include_relationships: Annotated[
        str | None,
        "Comma-separated list of relationships to include. Supported values: `created_by_user`, `last_modified_by_user`, `incident_type`.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetIncidentNotificationTemplate'."]:
    """Retrieve a specific incident notification template by ID.

    Use this tool to get details of a specific incident notification template from Datadog by providing its ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/config/notification-templates/{id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=template_id
        ),
        method="GET",
        params=remove_none_values({"include": include_relationships}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_notification_template(
    context: ToolContext,
    notification_template_id: Annotated[
        str, "The unique identifier of the notification template to update."
    ],
    notification_template_resource_type: Annotated[
        str,
        "Specifies the type of the notification template resource. Must be 'notification_templates'.",  # noqa: E501
    ],
    template_id: Annotated[
        str, "The unique identifier of the notification template to be updated."
    ],
    notification_template_category: Annotated[
        str | None, "The category of the notification template to update."
    ] = None,
    notification_template_content: Annotated[
        str | None, "The content body of the notification template to be updated."
    ] = None,
    notification_template_name: Annotated[
        str | None, "The name of the notification template to update in Datadog."
    ] = None,
    notification_template_subject: Annotated[
        str | None, "The subject line of the notification template to be updated in Datadog."
    ] = None,
    relationships_to_include: Annotated[
        str | None,
        "Comma-separated list of relationships to include. Valid values: `created_by_user`, `last_modified_by_user`, `incident_type`.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'UpdateIncidentNotificationTemplate'."
]:
    """Update attributes of a notification template.

    Use this tool to update an existing notification template's attributes in Datadog's incident management system."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "category": notification_template_category,
                "content": notification_template_content,
                "name": notification_template_name,
                "subject": notification_template_subject,
            },
            "id": template_id,
            "type": notification_template_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/config/notification-templates/{id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=notification_template_id
        ),
        method="PATCH",
        params=remove_none_values({"include": relationships_to_include}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_incident_types(
    context: ToolContext,
    include_deleted: Annotated[
        bool | None, "Include deleted incident types in the response when set to true."
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListIncidentTypes'."]:
    """Retrieve all incident types from Datadog.

    This tool calls the Datadog API to list all available incident types. Use it to get information about the types of incidents configured in the system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/config/types".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({"include_deleted": include_deleted}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_incident_type(
    context: ToolContext,
    incident_type_name: Annotated[str, "The name of the incident type to be created in Datadog."],
    creator_user_id: Annotated[
        str | None, "A unique ID representing the user who created the incident type."
    ] = None,
    incident_creation_timestamp: Annotated[
        str | None,
        "Timestamp indicating when the incident type was created. Format should be ISO 8601.",
    ] = None,
    incident_title_prefix: Annotated[
        str | None, "The string prepended to the incident title throughout the Datadog app."
    ] = None,
    incident_type_description: Annotated[
        str | None,
        "Text that describes the incident type. Provide a clear, concise explanation to aid in management and identification.",  # noqa: E501
    ] = None,
    incident_type_resource_type: Annotated[
        str, "Specifies the incident type resource type. Must be 'incident_types'."
    ] = "incident_types",
    last_modified_timestamp: Annotated[
        str | None,
        "Timestamp indicating when the incident type was last modified. Use ISO 8601 format, e.g., '2023-10-01T14:30:00Z'.",  # noqa: E501
    ] = None,
    last_modified_user_id: Annotated[
        str | None, "Unique identifier for the user who last modified the incident type."
    ] = None,
    set_as_default_incident_type: Annotated[
        bool | None,
        "Set to true to make this the default incident type if no type is specified during incident creation.",  # noqa: E501
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateIncidentType'."]:
    """Create a new incident type in Datadog.

    This tool is used to create a new incident type in Datadog, allowing users to define categories for managing incidents effectively."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "createdAt": incident_creation_timestamp,
                "createdBy": creator_user_id,
                "description": incident_type_description,
                "is_default": set_as_default_incident_type,
                "lastModifiedBy": last_modified_user_id,
                "modifiedAt": last_modified_timestamp,
                "name": incident_type_name,
                "prefix": incident_title_prefix,
            },
            "type": incident_type_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/config/types".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_incident_type(
    context: ToolContext,
    incident_type_uuid: Annotated[
        str, "The unique identifier (UUID) of the incident type to be deleted in Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteIncidentType'."]:
    """Deletes a specified incident type from Datadog configuration.

    Use this tool to remove an incident type from your Datadog configuration. It should be called when there is a need to delete a specific incident type identified by its ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/config/types/{incident_type_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            incident_type_id=incident_type_uuid,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_incident_type_details(
    context: ToolContext,
    incident_type_uuid: Annotated[
        str, "The UUID of the specific incident type to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetIncidentType'."]:
    """Retrieve details of a specific incident type.

    Use this tool to obtain detailed information about a specific incident type from Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/config/types/{incident_type_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            incident_type_id=incident_type_uuid,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_incident_type(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    incident_type_uuid: Annotated[
        str | None,
        "The UUID representing the incident type to be updated in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateIncidentType'."]:
    """Update the type of a specific incident in Datadog.

    Use this tool to modify an existing incident type in Datadog by specifying the incident type ID.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEINCIDENTTYPE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not incident_type_uuid:
        missing_params.append(("incident_type_uuid", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEINCIDENTTYPE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEINCIDENTTYPE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/incidents/config/types/{incident_type_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            incident_type_id=incident_type_uuid,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEINCIDENTTYPE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def search_datadog_incidents(
    context: ToolContext,
    incident_query: Annotated[
        str,
        "Query to determine which incidents to return. Use facets joined by `AND` and multiple values by `OR`, e.g., `state:active AND severity:(SEV-2 OR SEV-1)`.",  # noqa: E501
    ],
    include_related_objects: Annotated[
        str | None,
        "Specifies which types of related objects ('users', 'attachments') should be included in the response.",  # noqa: E501
    ] = None,
    page_offset: Annotated[
        int | None, "The starting position offset for returning incidents. Use an integer value."
    ] = 0,
    page_size: Annotated[
        int | None,
        "Specify the number of incidents to return per page. The maximum allowed value is 100.",
    ] = 10,
    sort_order: Annotated[
        str | None,
        "Defines the order of returned incidents. Use 'created' for ascending and '-created' for descending.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchIncidents'."]:
    """Search for incidents in Datadog by query.

    Use this tool to find incidents in Datadog that match a specific query. It helps in quickly retrieving relevant incident information based on search criteria."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/search".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "include": include_related_objects,
            "query": incident_query,
            "sort": sort_order,
            "page[size]": page_size,
            "page[offset]": page_offset,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_incident(
    context: ToolContext,
    incident_uuid: Annotated[str, "The unique identifier (UUID) of the incident to delete."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteIncident'."]:
    """Deletes an existing incident from the organization.

    Use this tool to remove an incident from your organization's records in Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), incident_id=incident_uuid
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_incident_details(
    context: ToolContext,
    incident_uuid: Annotated[str, "The UUID of the incident to retrieve its details."],
    include_related_objects: Annotated[
        list[str] | None,
        "Specify related object types to include in the response, such as users, logs, etc.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetIncident'."]:
    """Retrieve details of a specific incident using its ID.

    Use this tool to obtain detailed information about a specific incident by providing the incident ID. It retrieves all the necessary details from the Datadog service."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), incident_id=incident_uuid
        ),
        method="GET",
        params=remove_none_values({"include": include_related_objects}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_incident(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    incident_uuid: Annotated[
        str | None,
        "The unique identifier (UUID) for the incident to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    include_related_objects: Annotated[
        list[str] | None,
        "List of related object types to include in the response, such as 'users', 'comments', etc.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateIncident'."]:
    """Partially update an incident's details.

    Use this tool to update specific attributes of an existing incident in Datadog. Only the provided attributes will be modified.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEINCIDENT_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not incident_uuid:
        missing_params.append(("incident_uuid", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATEINCIDENT_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATEINCIDENT_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), incident_id=incident_uuid
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEINCIDENT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"include": include_related_objects}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_incident_attachments(
    context: ToolContext,
    incident_uuid: Annotated[
        str, "The unique identifier (UUID) of the incident whose attachments are to be retrieved."
    ],
    attachment_types_to_include: Annotated[
        list[str] | None,
        "List the types of attachments to include in the response. Each type should be a string.",
    ] = None,
    include_related_objects: Annotated[
        list[str] | None,
        "A list of related object types to include in the response, such as 'user', 'tags'.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListIncidentAttachments'."]:
    """Retrieve all attachments for a specified incident.

    This tool retrieves all files and documents attached to a particular incident. It should be called when details about the attachments associated with an incident are needed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/attachments".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), incident_id=incident_uuid
        ),
        method="GET",
        params=remove_none_values({
            "include": include_related_objects,
            "filter[attachment_type]": attachment_types_to_include,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def manage_incident_attachments(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    incident_uuid: Annotated[
        str | None,
        "The UUID of the incident to manage its attachments.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    related_objects_inclusion: Annotated[
        list[str] | None,
        "List of related object types to include in the response (e.g., comments, attachments).  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateIncidentAttachments'."]:
    """Manage attachments for a specific incident in bulk.

    Use this tool to create, update, or delete multiple attachments related to a specific incident. It is called when adjustments to the attachments of an incident are needed.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "MANAGEINCIDENTATTACHMENTS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not incident_uuid:
        missing_params.append(("incident_uuid", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["MANAGEINCIDENTATTACHMENTS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["MANAGEINCIDENTATTACHMENTS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/attachments".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), incident_id=incident_uuid
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["MANAGEINCIDENTATTACHMENTS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"include": related_objects_inclusion}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_incident_impacts(
    context: ToolContext,
    incident_uuid: Annotated[str, "The unique UUID of the incident to retrieve impacts for."],
    include_related_resources: Annotated[
        list[str] | None,
        "Specify which related resources to include in the response as an array of strings.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListIncidentImpacts'."]:
    """Retrieve all impacts for a specified incident.

    Use this tool to fetch all impact details associated with a particular incident, providing insight into the effects and scope of the incident."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/impacts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), incident_id=incident_uuid
        ),
        method="GET",
        params=remove_none_values({"include": include_related_resources}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_incident_impact(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    incident_uuid: Annotated[
        str | None,
        "The unique identifier (UUID) for the incident. This is required to log impact details for the specified incident.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    included_resources: Annotated[
        list[str] | None,
        "List of related resources to include in the response, such as 'users' or 'details'.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateIncidentImpact'."]:
    """Create an impact for a specific incident.

    This tool is used to create an impact for a specified incident. It should be called when there is a need to log or document the impact details of an ongoing or past incident.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEINCIDENTIMPACT_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not incident_uuid:
        missing_params.append(("incident_uuid", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEINCIDENTIMPACT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEINCIDENTIMPACT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/impacts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), incident_id=incident_uuid
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEINCIDENTIMPACT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"include": included_resources}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_incident_impact(
    context: ToolContext,
    incident_id: Annotated[
        str,
        "The UUID of the incident to be deleted. Required for identifying the specific incident.",
    ],
    incident_impact_uuid: Annotated[
        str,
        "The UUID of the incident impact to be deleted. This is required to identify which specific impact to remove.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteIncidentImpact'."]:
    """Delete a specific incident impact by ID.

    Use this tool to delete an impact associated with a specific incident using the incident and impact IDs. Suitable for managing and cleaning up incident data in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/impacts/{impact_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            incident_id=incident_id,
            impact_id=incident_impact_uuid,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_incident_integrations(
    context: ToolContext,
    incident_uuid: Annotated[
        str, "The unique UUID of the incident to retrieve integration metadata."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListIncidentIntegrations'."]:
    """Retrieve integration metadata for a specific incident.

    Use this tool to get all the integration metadata associated with a particular incident. It helps in understanding how different integrations are linked to an incident."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/relationships/integrations".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), incident_id=incident_uuid
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_incident_integration(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    incident_uuid: Annotated[
        str | None,
        "The unique identifier (UUID) of the incident to create integration metadata for.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateIncidentIntegration'."]:
    """Create incident integration metadata for an incident.

    Use this tool to create integration metadata related to a specific incident in Datadog. It facilitates linking external integrations to an incident for enhanced tracking and management.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEINCIDENTINTEGRATION_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not incident_uuid:
        missing_params.append(("incident_uuid", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEINCIDENTINTEGRATION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEINCIDENTINTEGRATION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/relationships/integrations".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), incident_id=incident_uuid
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEINCIDENTINTEGRATION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_incident_integration_metadata(
    context: ToolContext,
    incident_integration_metadata_uuid: Annotated[
        str, "The UUID of the incident integration metadata to be deleted."
    ],
    incident_uuid: Annotated[
        str, "The UUID of the incident you want to delete integration metadata for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteIncidentIntegration'."]:
    """Remove an incident integration metadata entry.

    Use this tool to delete metadata associated with an incident integration. Call it when you need to remove specific integration details from an incident."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/relationships/integrations/{integration_metadata_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            incident_id=incident_uuid,
            integration_metadata_id=incident_integration_metadata_uuid,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_incident_integration_details(
    context: ToolContext,
    incident_integration_metadata_uuid: Annotated[
        str, "The UUID of the incident integration metadata required to fetch its details."
    ],
    incident_uuid: Annotated[
        str, "The UUID of the incident in Datadog for which to obtain integration metadata."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetIncidentIntegration'."]:
    """Fetches details of incident integration metadata.

    Use this tool to obtain detailed metadata about a specific incident integration within Datadog, using the incident ID and integration metadata ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/relationships/integrations/{integration_metadata_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            incident_id=incident_uuid,
            integration_metadata_id=incident_integration_metadata_uuid,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_incident_integration(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    incident_uuid: Annotated[
        str | None,
        "The UUID of the incident. This is a unique identifier used to specify which incident to update the integration metadata for.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    integration_metadata_uuid: Annotated[
        str | None,
        "The UUID of the incident integration metadata to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateIncidentIntegration'."]:
    """Update incident integration metadata in Datadog.

    Call this tool to update the metadata of an existing incident integration in Datadog. Useful for modifying integration details after an incident is created.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEINCIDENTINTEGRATION_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not incident_uuid:
        missing_params.append(("incident_uuid", "path"))
    if not integration_metadata_uuid:
        missing_params.append(("integration_metadata_uuid", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEINCIDENTINTEGRATION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEINCIDENTINTEGRATION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/relationships/integrations/{integration_metadata_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            incident_id=incident_uuid,
            integration_metadata_id=integration_metadata_uuid,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEINCIDENTINTEGRATION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_incident_todos(
    context: ToolContext,
    incident_uuid: Annotated[
        str, "The unique identifier (UUID) of the incident for which to retrieve todos."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListIncidentTodos'."]:
    """Retrieve all todos for a specified incident.

    Use this tool to gather a list of todos associated with a specific incident in Datadog. It is useful for tracking tasks that need to be addressed within an incident context."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/relationships/todos".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), incident_id=incident_uuid
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_incident_todo(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    incident_uuid: Annotated[
        str | None,
        "The UUID of the incident for which the to-do is being created.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateIncidentTodo'."]:
    """Create a task within an incident in Datadog.

    This tool is used to add a to-do item to an incident in Datadog, helping manage and track tasks related to incident resolution.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEINCIDENTTODO_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not incident_uuid:
        missing_params.append(("incident_uuid", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEINCIDENTTODO_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEINCIDENTTODO_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/relationships/todos".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), incident_id=incident_uuid
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEINCIDENTTODO_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_incident_todo(
    context: ToolContext,
    incident_todo_uuid: Annotated[str, "The unique UUID for the incident todo to be deleted."],
    incident_uuid: Annotated[
        str,
        "The unique identifier (UUID) of the incident to which the todo belongs. This is necessary to specify the incident context.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteIncidentTodo'."]:
    """Delete a specified incident todo in Datadog.

    Use this tool to delete a specific todo item associated with an incident in Datadog. This is helpful for managing and updating incident tasks as needed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/relationships/todos/{todo_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            incident_id=incident_uuid,
            todo_id=incident_todo_uuid,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_incident_todo_details(
    context: ToolContext,
    incident_todo_uuid: Annotated[
        str,
        "The UUID of the incident todo to fetch details for. This is essential for identifying the specific todo item linked to an incident.",  # noqa: E501
    ],
    incident_uuid: Annotated[str, "The UUID of the incident to get the todo details for."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetIncidentTodo'."]:
    """Retrieve details of an incident todo item from Datadog.

    Use this tool to obtain specific details about a todo item related to an incident in Datadog. It should be called when detailed information about an incident's todo component is needed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/relationships/todos/{todo_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            incident_id=incident_uuid,
            todo_id=incident_todo_uuid,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_incident_todo(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    incident_uuid: Annotated[
        str | None,
        "The unique identifier (UUID) of the incident to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    incident_todo_uuid: Annotated[
        str | None,
        "The unique identifier (UUID) of the incident todo to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateIncidentTodo'."]:
    """Update a specific incident todo in Datadog.

    Use this tool to update details of a specific todo associated with an incident in Datadog. This is useful when you need to modify tasks related to incident management.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEINCIDENTTODO_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not incident_uuid:
        missing_params.append(("incident_uuid", "path"))
    if not incident_todo_uuid:
        missing_params.append(("incident_todo_uuid", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEINCIDENTTODO_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEINCIDENTTODO_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/incidents/{incident_id}/relationships/todos/{todo_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            incident_id=incident_uuid,
            todo_id=incident_todo_uuid,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEINCIDENTTODO_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_aws_accounts(
    context: ToolContext,
    filter_by_aws_account_id: Annotated[
        str | None,
        "Optional parameter to filter AWS accounts by their ID. Provide a specific AWS Account ID to get its integration config. If omitted, configurations for all accounts are returned.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListAWSAccounts'."]:
    """Retrieve AWS account integration configurations.

    Get a comprehensive list of AWS account integration configurations from Datadog. Useful for managing and reviewing AWS account integrations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/aws/accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({"aws_account_id": filter_by_aws_account_id}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_aws_account_integration(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateAWSAccount'."]:
    """Create a new AWS Account Integration Config in Datadog.

    This tool should be called when you need to integrate an AWS account with Datadog. It will set up a new AWS account configuration for monitoring and managing AWS services through Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEAWSACCOUNTINTEGRATION_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEAWSACCOUNTINTEGRATION_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEAWSACCOUNTINTEGRATION_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/integration/aws/accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEAWSACCOUNTINTEGRATION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_aws_account_config(
    context: ToolContext,
    aws_account_configuration_id: Annotated[
        str,
        "Unique Datadog ID for the AWS Account Integration Config. Obtain this ID via the 'List all AWS integrations' Datadog endpoint.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteAWSAccount'."]:
    """Delete an AWS account integration by config ID.

    Use this tool to delete an AWS account integration configuration in Datadog by specifying the config ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/aws/accounts/{aws_account_config_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            aws_account_config_id=aws_account_configuration_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_aws_account_integration_config(
    context: ToolContext,
    aws_account_integration_config_id: Annotated[
        str,
        "Unique Datadog ID of the AWS Account Integration Config. Obtain it using the List all AWS integrations endpoint.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAWSAccount'."]:
    """Retrieve AWS Account Integration Config by ID.

    Use this tool to obtain detailed information about a specific AWS account integration configuration, identified by its config ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/aws/accounts/{aws_account_config_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            aws_account_config_id=aws_account_integration_config_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_aws_account_integration(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    aws_account_integration_config_id: Annotated[
        str | None,
        "Unique Datadog ID for the AWS Account Integration Config. Retrieve using the List all AWS integrations endpoint and query by AWS Account ID.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateAWSAccount'."]:
    """Update an AWS Account Integration configuration.

    Use this tool to update the configuration of an AWS Account Integration by specifying the configuration ID. Ideal for modifying existing AWS integration settings.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEAWSACCOUNTINTEGRATION_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not aws_account_integration_config_id:
        missing_params.append(("aws_account_integration_config_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEAWSACCOUNTINTEGRATION_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEAWSACCOUNTINTEGRATION_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/integration/aws/accounts/{aws_account_config_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            aws_account_config_id=aws_account_integration_config_id,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEAWSACCOUNTINTEGRATION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_aws_cloudwatch_namespaces(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListAWSNamespaces'."]:
    """Retrieve available AWS CloudWatch namespaces for Datadog integration.

    This tool fetches a list of AWS CloudWatch namespaces that can send metrics to Datadog. It should be called when needing to understand which AWS services can integrate with Datadog for monitoring and analysis purposes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/aws/available_namespaces".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def generate_aws_external_id(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateNewAWSExternalID'."]:
    """Generate a new external ID for AWS authentication.

    This tool is used to generate a new external ID for AWS role-based authentication through Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/aws/generate_new_external_id".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def fetch_aws_integration_permissions(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAWSIntegrationIAMPermissions'."]:
    """Retrieve AWS IAM permissions for Datadog integration.

    Call this tool to obtain the list of AWS IAM permissions required for integrating AWS with Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/aws/iam_permissions".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_aws_iam_permissions(
    context: ToolContext,
) -> Annotated[
    dict[str, Any],
    "Response from the API endpoint 'GetAWSIntegrationIAMPermissionsResourceCollection'.",
]:
    """Get required AWS IAM permissions for resource collection.

    This tool retrieves all necessary AWS IAM permissions for integrating with AWS. Use it to ensure that the integration has the required permissions."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/aws/iam_permissions/resource_collection".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_aws_integration_iam_permissions(
    context: ToolContext,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'GetAWSIntegrationIAMPermissionsStandard'."
]:
    """Fetch standard AWS IAM permissions for integration.

    Retrieve all standard AWS IAM permissions needed for setting up AWS integration."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/aws/iam_permissions/standard".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_aws_logs_services(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListAWSLogsServices'."]:
    """Retrieve AWS services for logging to Datadog.

    Use this tool to obtain a list of AWS services that have the capability to send log data to Datadog. This can help in configuring and troubleshooting integrations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/aws/logs/services".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_gcp_sts_accounts(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListGCPSTSAccounts'."]:
    """Retrieve all GCP STS-enabled service accounts from Datadog.

    Use this tool to list all Google Cloud Platform (GCP) STS-enabled service accounts configured in your Datadog account. It should be called when you need to view or manage the integration of GCP accounts within Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/gcp/accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_gcp_sts_account(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateGCPSTSAccount'."]:
    """Create a new GCP STS account entry in Datadog.

    Use this tool to create a new entry within Datadog for your STS-enabled Google Cloud Platform service account.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEGCPSTSACCOUNT_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEGCPSTSACCOUNT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEGCPSTSACCOUNT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/integration/gcp/accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEGCPSTSACCOUNT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_gcp_sts_account(
    context: ToolContext,
    gcp_sts_account_id: Annotated[
        str, "The unique ID of the GCP STS-enabled service account to delete from Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteGCPSTSAccount'."]:
    """Delete an STS-enabled GCP account in Datadog.

    Use this tool to remove a specified STS-enabled Google Cloud Platform account from Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/gcp/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=gcp_sts_account_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_gcp_sts_account(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    service_account_id: Annotated[
        str | None,
        "Unique ID of your GCP STS-enabled service account to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateGCPSTSAccount'."]:
    """Update an STS-enabled GCP service account configuration.

    Call this tool to modify the configuration of an existing STS-enabled Google Cloud Platform service account in Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEGCPSTSACCOUNT_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not service_account_id:
        missing_params.append(("service_account_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEGCPSTSACCOUNT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEGCPSTSACCOUNT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/integration/gcp/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=service_account_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEGCPSTSACCOUNT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_gcp_sts_delegate(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetGCPSTSDelegate'."]:
    """Retrieve the Datadog-GCP STS delegate account configuration.

    Use this tool to list the configured GCP STS delegate account in your Datadog account. It provides information about the integration between Datadog and Google Cloud Platform for security token service (STS) delegation."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/gcp/sts_delegate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_datadog_gcp_principal(
    context: ToolContext,
    delegate_service_account_data: Annotated[
        dict[str, str] | None,
        "JSON object containing details for creating a delegate service account within Datadog. Include necessary account parameters.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'MakeGCPSTSDelegate'."]:
    """Create a Datadog GCP principal.

    Use this tool to create a Google Cloud Platform (GCP) principal for integration with Datadog. It facilitates the setting up of GCP STS (Security Token Service) delegates needed for Datadog services."""  # noqa: E501
    request_data = remove_none_values(delegate_service_account_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/gcp/sts_delegate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_datadog_channel_info(
    context: ToolContext,
    datadog_channel_name: Annotated[
        str,
        "The name of the channel in the Datadog Microsoft Teams integration. Required to retrieve channel details.",  # noqa: E501
    ],
    team_name: Annotated[
        str,
        "Specify the name of the team for which you want to retrieve channel ID details in the Datadog Microsoft Teams integration.",  # noqa: E501
    ],
    tenant_name: Annotated[
        str,
        "The name of the tenant for which you want to get the channel information in Datadog's Microsoft Teams integration.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetChannelByName'."]:
    """Retrieve channel ID details for Datadog MS Teams integration.

    Use this tool to obtain the tenant, team, and channel ID for a specific channel in the Datadog Microsoft Teams integration. Call this tool when you need to look up channel configuration details within a Datadog integration."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/ms-teams/configuration/channel/{tenant_name}/{team_name}/{channel_name}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            tenant_name=tenant_name,
            team_name=team_name,
            channel_name=datadog_channel_name,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_tenant_based_handles(
    context: ToolContext,
    tenant_handle_name: Annotated[
        str | None,
        "The name of your tenant-based handle in the Datadog Microsoft Teams integration.",
    ] = None,
    tenant_identifier: Annotated[
        str | None, "The ID of your tenant in Datadog to retrieve handles for MS Teams integration."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListTenantBasedHandles'."]:
    """Retrieve Datadog's tenant-based handles for MS Teams integration.

    Call this tool to obtain a list of all tenant-based handles configured in the Datadog Microsoft Teams integration."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/ms-teams/configuration/tenant-based-handles".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({"tenant_id": tenant_identifier, "name": tenant_handle_name}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_tenant_based_handle(
    context: ToolContext,
    channel_id: Annotated[
        str, "ID of the Microsoft Teams channel to associate with the tenant-based handle."
    ],
    handle_name: Annotated[
        str,
        "The name for the tenant-based handle you wish to create in the Datadog Microsoft Teams integration.",  # noqa: E501
    ],
    team_id: Annotated[
        str, "The ID of the Microsoft Teams team to associate with the Datadog handle."
    ],
    tenant_id: Annotated[
        str, "The unique identifier for the tenant in the Datadog Microsoft Teams integration."
    ],
    resource_type: Annotated[
        str, "Specifies the resource type as 'tenant-based-handle'."
    ] = "tenant-based-handle",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateTenantBasedHandle'."]:
    """Create a tenant-based handle in Datadog for Teams.

    Use this tool to create a new tenant-based handle for the Microsoft Teams integration within Datadog. It should be called when you need to configure or add a handle specific to a tenant in Datadog's Microsoft Teams integration."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "channel_id": channel_id,
                "name": handle_name,
                "team_id": team_id,
                "tenant_id": tenant_id,
            },
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/ms-teams/configuration/tenant-based-handles".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_ms_teams_tenant_handle(
    context: ToolContext,
    tenant_handle_id: Annotated[
        str,
        "The unique identifier for the tenant-based handle to be deleted from the Microsoft Teams integration.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteTenantBasedHandle'."]:
    """Delete a tenant-based handle from Datadog's Microsoft Teams integration.

    Use this tool to remove a specific tenant-based handle from the Microsoft Teams integration in Datadog. This is useful for managing or updating integration settings."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/ms-teams/configuration/tenant-based-handles/{handle_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), handle_id=tenant_handle_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_teams_integration_info(
    context: ToolContext,
    tenant_handle_id: Annotated[
        str,
        "The tenant-based handle ID for the Microsoft Teams integration used to retrieve tenant, team, and channel information.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTenantBasedHandle'."]:
    """Retrieve tenant, team, and channel info for a handle.

    This tool gets the tenant, team, and channel information for a specified tenant-based handle from the Datadog Microsoft Teams integration. It should be called to obtain detailed configuration data related to a specific integration handle."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/ms-teams/configuration/tenant-based-handles/{handle_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), handle_id=tenant_handle_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_ms_teams_tenant_handle(
    context: ToolContext,
    tenant_handle_id: Annotated[
        str, "The unique ID of the tenant-based handle to update in Datadog."
    ],
    channel_id: Annotated[
        str | None, "The ID of the Microsoft Teams channel to update for the tenant-based handle."
    ] = None,
    team_id: Annotated[
        str | None,
        "The Microsoft Teams Team ID for the tenant-based handle. Required for updating handle configurations.",  # noqa: E501
    ] = None,
    tenant_handle_name: Annotated[
        str | None,
        "Tenant-based handle name for the Microsoft Teams integration in Datadog. This specifies the handle's identifier within the configuration.",  # noqa: E501
    ] = None,
    tenant_handle_resource_type: Annotated[
        str,
        "Specifies the resource type for the tenant-based handle, usually 'tenant-based-handle'.",
    ] = "tenant-based-handle",
    tenant_id: Annotated[
        str | None,
        "The unique identifier for the tenant. Used to specify which tenant's handle is being updated.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateTenantBasedHandle'."]:
    """Update a Microsoft Teams tenant-based handle in Datadog.

    Use this tool to update a tenant-based handle for the Datadog Microsoft Teams integration. This is useful when you need to modify existing handle configurations for tenant-based integrations."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "channel_id": channel_id,
                "name": tenant_handle_name,
                "team_id": team_id,
                "tenant_id": tenant_id,
            },
            "type": tenant_handle_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/ms-teams/configuration/tenant-based-handles/{handle_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), handle_id=tenant_handle_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_ms_teams_workflow_webhooks(
    context: ToolContext,
    webhook_handle_name: Annotated[
        str | None, "Specifies the name of your Workflows webhook handle to filter the list."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListWorkflowsWebhookHandles'."]:
    """Retrieve all Microsoft Teams workflow webhook handles from Datadog.

    Use this tool to obtain a list of all workflow webhook handles associated with the Datadog Microsoft Teams integration. It helps in managing and monitoring webhook configurations efficiently."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/ms-teams/configuration/workflows-webhook-handles".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({"name": webhook_handle_name}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_workflow_webhook_handle(
    context: ToolContext,
    webhook_handle_name: Annotated[
        str, "Name of the Workflows Webhook handle for Datadog Microsoft Teams integration."
    ],
    webhook_url: Annotated[
        str, "The URL for the Workflows Webhook in the Datadog Microsoft Teams integration."
    ],
    webhook_handle_resource_type: Annotated[
        str,
        "Specifies the resource type for the Workflows webhook handle. Must be 'workflows-webhook-handle'.",  # noqa: E501
    ] = "workflows-webhook-handle",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateWorkflowsWebhookHandle'."]:
    """Create a webhook handle for Datadog Microsoft Teams integration.

    Use this tool to create a Workflows webhook handle within the Datadog Microsoft Teams integration. It is useful when setting up or managing integrations between Datadog workflows and Microsoft Teams for real-time alerts and notifications."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"name": webhook_handle_name, "url": webhook_url},
            "type": webhook_handle_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/ms-teams/configuration/workflows-webhook-handles".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_workflow_webhook_handle(
    context: ToolContext,
    webhook_handle_id: Annotated[
        str, "The unique identifier of the Workflows webhook handle to delete."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteWorkflowsWebhookHandle'."]:
    """Delete a Workflows webhook handle in Datadog.

    Use this tool to delete a specific Workflows webhook handle from the Datadog Microsoft Teams integration. This should be called when you need to remove an existing webhook handle."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/ms-teams/configuration/workflows-webhook-handles/{handle_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), handle_id=webhook_handle_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_ms_teams_workflow_webhook_name(
    context: ToolContext,
    workflow_webhook_handle_id: Annotated[
        str,
        "The ID of the Workflows webhook handle to retrieve the name for. This is specific to the Datadog Microsoft Teams integration.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetWorkflowsWebhookHandle'."]:
    """Retrieve the name of a MS Teams workflow webhook handle.

    Use this tool to get the name of a Microsoft Teams Workflows webhook handle using the Datadog integration. Useful for managing or verifying webhook configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/ms-teams/configuration/workflows-webhook-handles/{handle_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            handle_id=workflow_webhook_handle_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_workflows_webhook_handle(
    context: ToolContext,
    webhook_handle_id: Annotated[
        str, "The unique identifier for the Workflows webhook handle to be updated."
    ],
    webhook_handle_name: Annotated[
        str | None,
        "The name of the Workflows Webhook handle to be updated. This should be a descriptive string identifying the webhook.",  # noqa: E501
    ] = None,
    webhook_handle_resource_type: Annotated[
        str, "Specifies the Workflows webhook handle resource type. Use 'workflows-webhook-handle'."
    ] = "workflows-webhook-handle",
    workflows_webhook_url: Annotated[
        str | None, "The URL for the Workflows Webhook. Specify the endpoint to send requests to."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateWorkflowsWebhookHandle'."]:
    """Update a webhook handle in Datadog's Microsoft Teams integration.

    This tool updates a Workflows webhook handle for the Datadog Microsoft Teams integration. Use it to modify existing webhook configurations."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"name": webhook_handle_name, "url": workflows_webhook_url},
            "type": webhook_handle_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/ms-teams/configuration/workflows-webhook-handles/{handle_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), handle_id=webhook_handle_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_opsgenie_services(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListOpsgenieServices'."]:
    """Retrieve all services from Datadog Opsgenie integration.

    Call this tool to get a comprehensive list of services integrated with Datadog's Opsgenie. Useful for managing or reviewing service integrations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/opsgenie/services".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_opsgenie_service(
    context: ToolContext,
    opsgenie_api_key: Annotated[
        str,
        "The API key required to authenticate your Opsgenie service within Datadog. This key must be a valid string associated with your Opsgenie account.",  # noqa: E501
    ],
    opsgenie_service_name: Annotated[
        str, "The name for the Opsgenie service to be created in the Datadog integration."
    ],
    opsgenie_service_region: Annotated[
        str, "The region for the Opsgenie service. Choose from 'us', 'eu', or 'custom'."
    ],
    custom_region_url: Annotated[
        str | None,
        "The custom URL for a specific Opsgenie region. Used to connect to a custom region.",
    ] = None,
    opsgenie_service_resource_type: Annotated[
        str, "Specify the Opsgenie service resource type, which must be 'opsgenie-service'."
    ] = "opsgenie-service",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateOpsgenieService'."]:
    """Create a new Opsgenie service in Datadog integration.

    This tool is used to create a new service object within the Opsgenie integration on Datadog. It should be called when you need to set up or add a new Opsgenie service to monitor and manage incidents or alerts."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "custom_url": custom_region_url,
                "name": opsgenie_service_name,
                "opsgenie_api_key": opsgenie_api_key,
                "region": opsgenie_service_region,
            },
            "type": opsgenie_service_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/opsgenie/services".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_opsgenie_service(
    context: ToolContext,
    service_uuid: Annotated[
        str, "The UUID of the service to be deleted in the Datadog Opsgenie integration."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteOpsgenieService'."]:
    """Delete a service in Datadog's Opsgenie integration.

    Use this tool to remove a specific service from the Datadog Opsgenie integration. Call this when you need to delete an Opsgenie service using its integration service ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/opsgenie/services/{integration_service_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            integration_service_id=service_uuid,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_opsgenie_service(
    context: ToolContext,
    service_uuid: Annotated[str, "The UUID of the Datadog Opsgenie service to retrieve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetOpsgenieService'."]:
    """Retrieve a single Opsgenie service from Datadog.

    This tool retrieves details about a specific service integrated with Datadog's Opsgenie. Use it to access information about service configurations or to manage operational alerts."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/opsgenie/services/{integration_service_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            integration_service_id=service_uuid,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_opsgenie_service(
    context: ToolContext,
    opsgenie_service_id: Annotated[
        str, "The unique identifier of the Opsgenie service to be updated."
    ],
    service_uuid: Annotated[
        str, "The UUID of the service to be updated in the Datadog Opsgenie integration."
    ],
    custom_region_url: Annotated[
        str | None,
        "The custom URL for a specific Opsgenie region. Specify if using a custom region.",
    ] = None,
    opsgenie_api_key: Annotated[
        str | None,
        "The API key for your Opsgenie service, needed to authenticate the update request.",
    ] = None,
    opsgenie_service_name: Annotated[
        str | None,
        "The name for the Opsgenie service to update. It should uniquely identify the service within your Opsgenie account.",  # noqa: E501
    ] = None,
    opsgenie_service_region: Annotated[
        str | None,
        "Specify the region for the Opsgenie service. Allowed values are 'us', 'eu', or 'custom'.",
    ] = None,
    opsgenie_service_resource_type: Annotated[
        str, "Specify as 'opsgenie-service' to denote the Opsgenie service resource type."
    ] = "opsgenie-service",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateOpsgenieService'."]:
    """Update a service in the Datadog Opsgenie integration.

    This tool updates a specified service object within the Datadog Opsgenie integration. It should be called when you need to modify an existing Opsgenie service."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "custom_url": custom_region_url,
                "name": opsgenie_service_name,
                "opsgenie_api_key": opsgenie_api_key,
                "region": opsgenie_service_region,
            },
            "id": opsgenie_service_id,
            "type": opsgenie_service_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integration/opsgenie/services/{integration_service_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            integration_service_id=service_uuid,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_cloudflare_accounts(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCloudflareAccounts'."]:
    """Retrieve a list of Cloudflare accounts from Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/cloudflare/accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_cloudflare_account(
    context: ToolContext,
    cloudflare_account_name: Annotated[str, "The name for the Cloudflare account to be created."],
    cloudflare_api_key: Annotated[
        str,
        "The API key or token for the Cloudflare account required to authenticate and connect with the Cloudflare service.",  # noqa: E501
    ],
    cloudflare_account_email: Annotated[
        str | None,
        "The email associated with the Cloudflare account. Required if using an API key instead of a token.",  # noqa: E501
    ] = None,
    json_api_type: Annotated[
        str, "Specifies the JSON:API type, must be 'cloudflare-accounts'."
    ] = "cloudflare-accounts",
    resources_allowlist: Annotated[
        list[str] | None,
        "List of resources such as 'web', 'dns', 'lb', or 'worker' to restrict metric pulling.",
    ] = None,
    zone_allowlist: Annotated[
        list[str] | None, "A list of zones for restricting metric data collection."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCloudflareAccount'."]:
    """Create a Cloudflare account through Datadog integration.

    This tool is used to create a Cloudflare account using Datadog's integration API. It should be called when you wish to initiate a new Cloudflare account while leveraging Datadog services."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "api_key": cloudflare_api_key,
                "email": cloudflare_account_email,
                "name": cloudflare_account_name,
                "resources": resources_allowlist,
                "zones": zone_allowlist,
            },
            "type": json_api_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/cloudflare/accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_cloudflare_account(
    context: ToolContext,
    cloudflare_account_id: Annotated[
        str,
        "The ID of the Cloudflare account to delete from Datadog. This should be a string matching the account ID format.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCloudflareAccount'."]:
    """Delete a Cloudflare account via Datadog integration.

    Use this tool to delete a specified Cloudflare account from Datadog's integrations. Call this tool when you need to remove an account linked to Cloudflare within the Datadog platform."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/cloudflare/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            account_id=cloudflare_account_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_cloudflare_account(
    context: ToolContext,
    cloudflare_account_id: Annotated[
        str,
        "The unique identifier for the Cloudflare account to retrieve details from. This is required to access account-specific information.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCloudflareAccount'."]:
    """Retrieve details of a Cloudflare account via Datadog.

    Call this tool to get information about a specific Cloudflare account linked to Datadog. Use it when you need to access account details for management or monitoring purposes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/cloudflare/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            account_id=cloudflare_account_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_cloudflare_account(
    context: ToolContext,
    cloudflare_account_id: Annotated[
        str, "The unique identifier for the Cloudflare account to be updated."
    ],
    allowed_resource_types_for_metrics: Annotated[
        list[str] | None,
        "An array of resource types ('web', 'dns', 'lb', 'worker') to allow for metrics collection.",  # noqa: E501
    ] = None,
    cloudflare_account_email: Annotated[
        str | None,
        "The email associated with the Cloudflare account. Required if using an API key instead of a token.",  # noqa: E501
    ] = None,
    cloudflare_account_name: Annotated[
        str | None, "The name of the Cloudflare account to be updated."
    ] = None,
    cloudflare_api_key: Annotated[
        str | None, "The API key for the Cloudflare account, required for authentication."
    ] = None,
    json_api_type: Annotated[
        str | None, "The JSON:API type for this API. Always use `cloudflare-accounts`."
    ] = "cloudflare-accounts",
    zone_allowlist: Annotated[
        list[str] | None,
        "A list of zone identifiers to restrict which metrics can be pulled for Cloudflare.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCloudflareAccount'."]:
    """Update details of a Cloudflare account.

    Use this tool to update the information of an existing Cloudflare account linked with Datadog. It should be called whenever modifications to a Cloudflare account are required."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "api_key": cloudflare_api_key,
                "email": cloudflare_account_email,
                "name": cloudflare_account_name,
                "resources": allowed_resource_types_for_metrics,
                "zones": zone_allowlist,
            },
            "type": json_api_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/cloudflare/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            account_id=cloudflare_account_id,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_confluent_accounts(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListConfluentAccount'."]:
    """Retrieve a list of Confluent accounts.

    Use this tool to get a list of Confluent accounts that are integrated with Datadog. It helps in managing and monitoring Confluent Cloud integrations efficiently."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/confluent-cloud/accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_confluent_account(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateConfluentAccount'."]:
    """Create a Confluent account on Datadog.

    Use this tool to create a new Confluent account within the Datadog platform. Ideal for setting up new Confluent integrations.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATECONFLUENTACCOUNT_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATECONFLUENTACCOUNT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATECONFLUENTACCOUNT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/integrations/confluent-cloud/accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATECONFLUENTACCOUNT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_confluent_account(
    context: ToolContext,
    confluent_account_id: Annotated[
        str, "The unique identifier for the Confluent Account to be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteConfluentAccount'."]:
    """Delete a Confluent account using the account ID.

    This tool deletes a specified Confluent account by account ID in Datadog's system. It should be used when a user wants to remove a Confluent Cloud integration account."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/confluent-cloud/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=confluent_account_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_confluent_account_info(
    context: ToolContext,
    confluent_account_id: Annotated[
        str, "The unique identifier for the Confluent account to retrieve details from."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetConfluentAccount'."]:
    """Retrieve Confluent account information by account ID.

    Use this tool to obtain details for a Confluent account using its account ID. Useful for accessing specific account configurations or status within Datadog's Confluent integrations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/confluent-cloud/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=confluent_account_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_confluent_account(
    context: ToolContext,
    confluent_account_id: Annotated[str, "The unique ID of the Confluent account to be updated."],
    confluent_api_key: Annotated[
        str, "Provide the API key associated with your Confluent account."
    ],
    confluent_api_secret: Annotated[
        str,
        "The API secret for the Confluent account. Required to authenticate and update the account details.",  # noqa: E501
    ],
    api_type: Annotated[
        str,
        "Set this to `confluent-cloud-accounts` to specify the JSON:API type for the update request.",  # noqa: E501
    ] = "confluent-cloud-accounts",
    tags_list: Annotated[
        list[str] | None,
        "A list of tag strings for the account. Use single keys or key-value pairs separated by a colon.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateConfluentAccount'."]:
    """Updates the Confluent account details.

    Use this tool to update the details of a Confluent account by providing the account ID. It ensures the account information is current and accurately reflects any required changes."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "api_key": confluent_api_key,
                "api_secret": confluent_api_secret,
                "tags": tags_list,
            },
            "type": api_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/confluent-cloud/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=confluent_account_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_confluent_resource(
    context: ToolContext,
    confluent_account_id: Annotated[
        str,
        "Enter the Confluent Account ID to retrieve the resource details linked to this account.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListConfluentResource'."]:
    """Retrieve Confluent resource details for a specific account ID.

    Use this tool to get information about a Confluent resource linked to a specific account using the account ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/confluent-cloud/accounts/{account_id}/resources".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=confluent_account_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_confluent_resource(
    context: ToolContext,
    confluent_account_id: Annotated[
        str, "The ID of the Confluent account for which to create the resource."
    ],
    confluent_resource_id: Annotated[
        str, "The unique ID for the Confluent resource to be created or managed."
    ],
    resource_type: Annotated[
        str,
        "The type of Confluent resource to create: `kafka`, `connector`, `ksql`, or `schema_registry`.",  # noqa: E501
    ],
    enable_custom_metrics: Annotated[
        bool | None,
        "Set to true to enable the `custom.consumer_lag_offset` metric with extra tags, false to disable.",  # noqa: E501
    ] = False,
    json_api_request_type: Annotated[
        str, "The JSON:API type for this request. Must be 'confluent-cloud-resources'."
    ] = "confluent-cloud-resources",
    resource_tags: Annotated[
        list[str] | None,
        "A list of tag strings for the Confluent resource. Use key-value pairs separated by colons or single keys.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateConfluentResource'."]:
    """Create a Confluent resource for a specified account.

    Use this tool to create a Confluent resource within a specific account using its ID. It's useful for managing Confluent resources in Datadog integrations."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "enable_custom_metrics": enable_custom_metrics,
                "resource_type": resource_type,
                "tags": resource_tags,
            },
            "id": confluent_resource_id,
            "type": json_api_request_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/confluent-cloud/accounts/{account_id}/resources".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=confluent_account_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_confluent_resource(
    context: ToolContext,
    confluent_account_id: Annotated[
        str, "The unique identifier for the Confluent account linked to the resource to be deleted."
    ],
    confluent_resource_id: Annotated[
        str, "A string representing the unique ID of the Confluent resource to be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteConfluentResource'."]:
    """Deletes a specified Confluent resource in a Datadog account.

    Use this tool to delete a Confluent resource associated with a specific account in Datadog by providing the account ID and resource ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/confluent-cloud/accounts/{account_id}/resources/{resource_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            account_id=confluent_account_id,
            resource_id=confluent_resource_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def fetch_confluent_resource(
    context: ToolContext,
    confluent_account_id: Annotated[
        str,
        "The ID of the Confluent account to retrieve the resource for. This should be a string value representing the account identifier.",  # noqa: E501
    ],
    confluent_resource_id: Annotated[
        str,
        "The ID of the Confluent resource associated with the specified account. Provide this to retrieve the resource details.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetConfluentResource'."]:
    """Retrieve a Confluent resource using account and resource IDs.

    Use this tool to obtain information about a specific Confluent resource linked to a particular account by providing the account and resource IDs."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/confluent-cloud/accounts/{account_id}/resources/{resource_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            account_id=confluent_account_id,
            resource_id=confluent_resource_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_confluent_resource(
    context: ToolContext,
    confluent_account_id: Annotated[
        str, "The ID of the Confluent account associated with the resource to be updated."
    ],
    confluent_resource_id: Annotated[
        str, "The unique identifier for the Confluent resource to be updated."
    ],
    resource_id: Annotated[str, "The ID of the Confluent account resource to update."],
    resource_type: Annotated[
        str,
        "Specifies the resource type of the Confluent resource. Valid values are 'kafka', 'connector', 'ksql', or 'schema_registry'.",  # noqa: E501
    ],
    enable_custom_metrics: Annotated[
        bool | None,
        "Set to true to enable the `custom.consumer_lag_offset` metric which includes extra metric tags.",  # noqa: E501
    ] = False,
    resource_data_type: Annotated[
        str, "The JSON:API type for this request. Must be 'confluent-cloud-resources'."
    ] = "confluent-cloud-resources",
    tags_list: Annotated[
        list[str] | None,
        "A list of tags for the resource. Each tag can be a single key or a key-value pair separated by a colon.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateConfluentResource'."]:
    """Update a Confluent resource linked to a specified account.

    Use this tool to update a Confluent resource associated with a given account by specifying the resource and account IDs."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "enable_custom_metrics": enable_custom_metrics,
                "resource_type": resource_type,
                "tags": tags_list,
            },
            "id": confluent_resource_id,
            "type": resource_data_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/confluent-cloud/accounts/{account_id}/resources/{resource_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            account_id=confluent_account_id,
            resource_id=resource_id,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_fastly_accounts(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListFastlyAccounts'."]:
    """Retrieve a list of Fastly accounts integrated with Datadog.

    Call this tool to obtain a list of Fastly accounts that are currently integrated with your Datadog instance. This can help in managing, auditing, or configuring integrations between Datadog and Fastly."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/fastly/accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_fastly_account(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateFastlyAccount'."]:
    """Create a new Fastly account through Datadog integration.

    Use this tool to create a new Fastly account using Datadog's integration service. Ideal for setting up Fastly accounts quickly and efficiently.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEFASTLYACCOUNT_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEFASTLYACCOUNT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEFASTLYACCOUNT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/integrations/fastly/accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEFASTLYACCOUNT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_fastly_account(
    context: ToolContext,
    fastly_account_id: Annotated[
        str,
        "The unique identifier of the Fastly account to delete. Required for the deletion process.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteFastlyAccount'."]:
    """Deletes a specified Fastly account integration.

    Use this tool to delete a Fastly account integration from Datadog. Ideal when an integration is no longer needed or requires removal."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/fastly/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=fastly_account_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_fastly_account_info(
    context: ToolContext,
    fastly_account_id: Annotated[
        str, "The unique identifier for the Fastly account to retrieve information about."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetFastlyAccount'."]:
    """Retrieves detailed information for a specific Fastly account.

    Use this tool to obtain details about a specific Fastly account integrated with Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/fastly/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=fastly_account_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_fastly_account(
    context: ToolContext,
    fastly_account_id: Annotated[str, "The unique identifier for the Fastly account to update."],
    fastly_account_name: Annotated[str | None, "The name of the Fastly account to update."] = None,
    fastly_api_key: Annotated[
        str | None, "The API key for the Fastly account to be updated."
    ] = None,
    json_api_type: Annotated[
        str | None, "Specifies the type for the Fastly account API. Must be 'fastly-accounts'."
    ] = "fastly-accounts",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateFastlyAccount'."]:
    """Updates a Fastly account via Datadog integration.

    Call this tool to update details of a Fastly account through the Datadog API. Useful for managing Fastly integration settings."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"api_key": fastly_api_key, "name": fastly_account_name},
            "type": json_api_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/fastly/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=fastly_account_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_fastly_services(
    context: ToolContext,
    fastly_account_id: Annotated[
        str, "The unique identifier for a Fastly account to retrieve its services."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListFastlyServices'."]:
    """Retrieve Fastly services for a specific account.

    Use this tool to get a list of all Fastly services associated with a particular account in Datadog. Ideal for checking service configurations or integrations linked to your Fastly account."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/fastly/accounts/{account_id}/services".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=fastly_account_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_fastly_service(
    context: ToolContext,
    fastly_account_id: Annotated[str, "Provide the Fastly Account ID to create the service under."],
    fastly_service_id: Annotated[
        str, "The ID of the Fastly service to create. Provide a valid Fastly service ID."
    ],
    fastly_service_tags: Annotated[
        list[str] | None,
        "A list of tags for the Fastly service to help categorize and organize the service.",
    ] = None,
    jsonapi_type_for_fastly_service: Annotated[
        str, "The JSON:API type, always set to 'fastly-services', for creating a Fastly service."
    ] = "fastly-services",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateFastlyService'."]:
    """Create a Fastly service for a specific account in Datadog.

    Use this tool to create a Fastly service associated with a given account within Datadog. Ideal when setting up or expanding Fastly integrations."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"tags": fastly_service_tags},
            "id": fastly_service_id,
            "type": jsonapi_type_for_fastly_service,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/fastly/accounts/{account_id}/services".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=fastly_account_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_fastly_service(
    context: ToolContext,
    fastly_account_id: Annotated[
        str, "The ID of the Fastly account associated with the service to be deleted."
    ],
    fastly_service_id: Annotated[
        str,
        "The unique identifier for the Fastly service to delete. Required to specify the exact service.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteFastlyService'."]:
    """Delete a Fastly service for an account.

    Use this tool to delete a specific Fastly service associated with an account in Datadog. Call this tool when you need to remove an existing service configured through the Fastly integration."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/fastly/accounts/{account_id}/services/{service_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            account_id=fastly_account_id,
            service_id=fastly_service_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_fastly_service_info(
    context: ToolContext,
    fastly_account_id: Annotated[
        str, "The unique ID of the Fastly account for which to retrieve service details."
    ],
    fastly_service_id: Annotated[
        str,
        "The ID of the Fastly service to retrieve details for, linked to the specified account.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetFastlyService'."]:
    """Retrieve Fastly service details for a specific account.

    Call this tool to get information about a Fastly service linked to a Datadog account using the account ID and the service ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/fastly/accounts/{account_id}/services/{service_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            account_id=fastly_account_id,
            service_id=fastly_service_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_fastly_service(
    context: ToolContext,
    fastly_account_id: Annotated[str, "The unique ID of the Fastly account to be updated."],
    fastly_service_id: Annotated[
        str, "Provide the Fastly Service ID to specify which service to update."
    ],
    fastly_service_identifier: Annotated[str, "The ID of the Fastly service to be updated."],
    fastly_service_json_api_type: Annotated[
        str, "The JSON:API type for this API, which should always be `fastly-services`."
    ] = "fastly-services",
    fastly_service_tags: Annotated[
        list[str] | None,
        "A list of tags to update the Fastly service with. Each tag should be a string.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateFastlyService'."]:
    """Update a Fastly service for an account in Datadog.

    Use this tool to update specific details of a Fastly service associated with an account in Datadog. Useful for modifying service configurations."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"tags": fastly_service_tags},
            "id": fastly_service_identifier,
            "type": fastly_service_json_api_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/fastly/accounts/{account_id}/services/{service_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            account_id=fastly_account_id,
            service_id=fastly_service_id,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_okta_accounts(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListOktaAccounts'."]:
    """Retrieve a list of Okta accounts linked to Datadog.

    Use this tool to obtain information about Okta accounts that are integrated with Datadog, allowing for management and analysis of these linked accounts."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/okta/accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_okta_account(
    context: ToolContext,
    okta_account_domain: Annotated[str, "The domain of the Okta account to be created."],
    okta_account_name: Annotated[
        str, "The name of the Okta account to be created via Datadog API integration."
    ],
    okta_auth_method: Annotated[str, "Specify the authorization method for the Okta account."],
    client_secret: Annotated[
        str | None,
        "The client secret associated with the Okta app integration. This is required for authentication.",  # noqa: E501
    ] = None,
    okta_account_id: Annotated[
        str | None, "The ID of the Okta account, which is a UUID hash of the account name."
    ] = None,
    okta_account_type: Annotated[
        str,
        "Specifies the type of account for the Okta account. The value should be 'okta-accounts'.",
    ] = "okta-accounts",
    okta_api_key: Annotated[
        str | None,
        "The API key for the Okta account integration. This key is used for authenticating the account with Datadog.",  # noqa: E501
    ] = None,
    okta_client_id: Annotated[
        str | None, "The Client ID for the Okta app integration, necessary for the account setup."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateOktaAccount'."]:
    """Create an Okta account via Datadog integration.

    Use this tool to create a new Okta account through Datadog's API integration. This is useful when setting up user accounts for Okta within the Datadog platform."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "api_key": okta_api_key,
                "auth_method": okta_auth_method,
                "client_id": okta_client_id,
                "client_secret": client_secret,
                "domain": okta_account_domain,
                "name": okta_account_name,
            },
            "id": okta_account_id,
            "type": okta_account_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/okta/accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_okta_account(
    context: ToolContext,
    okta_account_id: Annotated[
        str, "A string representing the ID of the Okta account to be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteOktaAccount'."]:
    """Delete an Okta account from Datadog integration.

    Use this tool to delete an Okta account integrated with Datadog. Call this tool when an Okta account needs to be removed from Datadog's integration platform."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/okta/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=okta_account_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_okta_account_info(
    context: ToolContext,
    okta_account_id: Annotated[
        str, "The unique identifier for the Okta account to retrieve information for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetOktaAccount'."]:
    """Retrieve detailed information about a specific Okta account.

    This tool fetches details of an Okta account using the given account ID. It should be called when detailed information about an Okta account is required."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/okta/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=okta_account_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_okta_account(
    context: ToolContext,
    account_id: Annotated[str, "The unique identifier for the Okta account to be updated."],
    account_type: Annotated[
        str | None, "Specify the type of the Okta account. Must be 'okta-accounts'."
    ] = "okta-accounts",
    authorization_method: Annotated[
        str | None,
        "Specify the authorization method for the Okta account. This is a required string value.",
    ] = None,
    okta_account_api_key: Annotated[
        str | None, "The API key for authenticating the Okta account."
    ] = None,
    okta_client_id: Annotated[
        str | None, "The Client ID of the Okta app integration to update."
    ] = None,
    okta_client_secret: Annotated[
        str | None,
        "The client secret for the Okta app integration to be updated. Ensure this is kept secure.",
    ] = None,
    okta_domain: Annotated[
        str | None, "The domain associated with the Okta account to update."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateOktaAccount'."]:
    """Update details of an existing Okta account.

    Call this tool to update information for a specific Okta account using its account ID."""
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "api_key": okta_account_api_key,
                "auth_method": authorization_method,
                "client_id": okta_client_id,
                "client_secret": okta_client_secret,
                "domain": okta_domain,
            },
            "type": account_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/integrations/okta/accounts/{account_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), account_id=account_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_ip_allowlist(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetIPAllowlist'."]:
    """Retrieve the IP allowlist and its status.

    Use this tool to obtain the current IP allowlist and check whether it is enabled or disabled."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/ip_allowlist".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_ip_allowlist(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateIPAllowlist'."]:
    """Edit and toggle the IP allowlist settings in Datadog.

    This tool updates the entries in the Datadog IP allowlist, enabling or disabling it as needed.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEIPALLOWLIST_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEIPALLOWLIST_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEIPALLOWLIST_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/ip_allowlist".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEIPALLOWLIST_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def aggregate_logs(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AggregateLogs'."]:
    """Aggregate logs to compute metrics and timeseries.

    This tool aggregates log events into buckets to compute relevant metrics and timeseries. It's useful for analyzing large volumes of log data to extract meaningful insights.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["AGGREGATELOGS_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["AGGREGATELOGS_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["AGGREGATELOGS_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/logs/analytics/aggregate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["AGGREGATELOGS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_current_archive_order(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetLogsArchiveOrder'."]:
    """Retrieve the current order of logs archives.

    This tool retrieves the current order of your logs archives from Datadog. Use this tool when you need to understand how archives are currently organized."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/archive-order".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_logs_archive_order(
    context: ToolContext,
    archive_ids_order: Annotated[
        list[str] | None,
        "An ordered list of `<ARCHIVE_ID>` strings to define the new archives order in Datadog.",
    ] = None,
    archive_order_type: Annotated[
        str | None, "Specifies the type for the archive order definition. Must be 'archive_order'."
    ] = "archive_order",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateLogsArchiveOrder'."]:
    """Updates the order of log archives in Datadog.

    This tool updates the sequence in which log archives are processed within Datadog. Reordering them may impact the structure and content of logs processed by other archives. Use this when you need to change how logs are archived."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"attributes": {"archive_ids": archive_ids_order}, "type": archive_order_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/archive-order".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_logs_archives(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListLogsArchives'."]:
    """Get the list of configured logs archives.

    Retrieve the definitions of all logs archives configured in Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/archives".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_logs_archive(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateLogsArchive'."]:
    """Create an archive of logs in your organization.

    Use this tool to create a new archive for logs within your organization through Datadog. This is useful for organizing and storing logs for compliance or analysis purposes.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATELOGSARCHIVE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATELOGSARCHIVE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATELOGSARCHIVE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/logs/config/archives".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATELOGSARCHIVE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_logs_archive(
    context: ToolContext,
    archive_id: Annotated[str, "The unique identifier for the archive to be deleted from Datadog."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteLogsArchive'."]:
    """Delete a specific logs archive from your organization.

    Use this tool to permanently delete a specified logs archive in your Datadog organization. The tool should be called when you need to remove an unnecessary or outdated archive."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/archives/{archive_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), archive_id=archive_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_specific_logs_archive(
    context: ToolContext,
    archive_id: Annotated[
        str, "The unique identifier for the logs archive to retrieve from Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetLogsArchive'."]:
    """Retrieve a specific logs archive from Datadog.

    Use this tool to get a specific archive of logs from your Datadog organization by providing the archive ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/archives/{archive_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), archive_id=archive_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_archive_configuration(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    archive_identifier: Annotated[
        str | None,
        "The unique identifier for the archive you wish to update in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateLogsArchive'."]:
    """Replace an existing archive configuration in Datadog.

    Use this tool to update an archive configuration by replacing the current settings with new ones for a specified archive in your Datadog organization.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEARCHIVECONFIGURATION_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not archive_identifier:
        missing_params.append(("archive_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEARCHIVECONFIGURATION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEARCHIVECONFIGURATION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/logs/config/archives/{archive_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), archive_id=archive_identifier
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEARCHIVECONFIGURATION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def remove_role_from_archive(
    context: ToolContext,
    archive_id: Annotated[str, "The ID of the archive from which the role will be removed."],
    role_type: Annotated[
        str | None, "The type of role to be removed, typically set to 'roles'."
    ] = "roles",
    role_unique_identifier: Annotated[
        str | None, "The unique identifier of the role to be removed from the archive."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveRoleFromArchive'."]:
    """Removes a role from a specified archive in Datadog.

    This tool should be called to remove a user role from a specific archive in Datadog. It helps revoke access permissions associated with that role for the archive."""  # noqa: E501
    request_data = remove_none_values({"data": {"id": role_unique_identifier, "type": role_type}})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/archives/{archive_id}/readers".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), archive_id=archive_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_archive_read_roles(
    context: ToolContext,
    archive_identifier: Annotated[
        str, "The unique identifier for the archive to retrieve read access roles from."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListArchiveReadRoles'."]:
    """Retrieve roles with read access to a specific archive.

    Use this tool to obtain a list of all roles that have read access to a specified archive in Datadog. It helps manage and review access control for archive data."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/archives/{archive_id}/readers".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), archive_id=archive_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def add_read_role_to_archive(
    context: ToolContext,
    archive_id: Annotated[
        str,
        "The unique identifier for the archive to which a read role will be added. This is required to specify the target archive for access management.",  # noqa: E501
    ],
    role_type: Annotated[str | None, "The type of role to be added. Must be 'roles'."] = "roles",
    role_unique_identifier: Annotated[
        str | None, "The unique identifier for the role to be added to the archive."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddReadRoleToArchive'."]:
    """Adds a read role to a specified archive.

    Use this tool to grant read access to a specific archive by adding a read role. Ideal for managing permissions and ensuring the right users or groups have access to log data archives."""  # noqa: E501
    request_data = remove_none_values({"data": {"id": role_unique_identifier, "type": role_type}})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/archives/{archive_id}/readers".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), archive_id=archive_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_custom_log_destinations(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListLogsCustomDestinations'."]:
    """Retrieve configured custom log destinations from Datadog.

    Use this tool to get a list of custom log destinations configured in your Datadog organization, including their definitions."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/custom-destinations".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_custom_log_destination(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateLogsCustomDestination'."]:
    """Create a custom log destination in Datadog.

    This tool creates a custom destination for logs in your Datadog organization. Use it to configure where your logs should be sent.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATECUSTOMLOGDESTINATION_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATECUSTOMLOGDESTINATION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATECUSTOMLOGDESTINATION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/logs/config/custom-destinations".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATECUSTOMLOGDESTINATION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_custom_log_destination(
    context: ToolContext,
    custom_destination_id: Annotated[
        str, "The unique identifier for the custom log destination to be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteLogsCustomDestination'."]:
    """Delete a specific custom log destination.

    Call this tool to delete a specific custom log destination within your organization on Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/custom-destinations/{custom_destination_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            custom_destination_id=custom_destination_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_custom_destination(
    context: ToolContext,
    custom_destination_id: Annotated[
        str, "The ID of the custom destination to retrieve details from your organization."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetLogsCustomDestination'."]:
    """Retrieve details of a specific custom log destination.

    Use this tool to get information about a specific custom destination for logs in your organization. Useful for managing and configuring log destinations effectively."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/custom-destinations/{custom_destination_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            custom_destination_id=custom_destination_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_logs_custom_destination(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    custom_destination_id: Annotated[
        str | None,
        "The unique identifier for the custom logs destination to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateLogsCustomDestination'."]:
    """Update specific fields of a custom logs destination.

    Use this tool to update the selected fields of a specific custom logs destination within your organization. This is useful for modifying destination configurations in Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATELOGSCUSTOMDESTINATION_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not custom_destination_id:
        missing_params.append(("custom_destination_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATELOGSCUSTOMDESTINATION_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATELOGSCUSTOMDESTINATION_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/logs/config/custom-destinations/{custom_destination_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            custom_destination_id=custom_destination_id,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATELOGSCUSTOMDESTINATION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_logs_metrics_list(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListLogsMetrics'."]:
    """Retrieve a list of log-based metrics and their definitions.

    Use this tool to obtain the list of configured log-based metrics from Datadog, along with their definitions. This can be helpful for monitoring and analyzing log data."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/metrics".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_log_based_metric(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateLogsMetric'."]:
    """Create a metric from your ingested logs.

    Use this tool to create a metric based on the logs ingested in your Datadog organization. It returns the log-based metric object when the request is successful.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATELOGBASEDMETRIC_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATELOGBASEDMETRIC_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATELOGBASEDMETRIC_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/logs/config/metrics".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATELOGBASEDMETRIC_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_log_metric(
    context: ToolContext,
    log_metric_name: Annotated[str, "The name of the log-based metric you want to delete."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteLogsMetric'."]:
    """Delete a specific log-based metric from your organization.

    Call this tool to remove a specific log-based metric from your Datadog organization by specifying the metric ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/metrics/{metric_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_id=log_metric_name
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_log_based_metric(
    context: ToolContext,
    log_based_metric_name: Annotated[
        str, "The name of the log-based metric to retrieve from Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetLogsMetric'."]:
    """Retrieve a specific log-based metric from Datadog.

    Use this tool to access detailed information about a specific log-based metric from your Datadog account. This is useful for monitoring and analyzing your organization's log metrics."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/metrics/{metric_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_id=log_based_metric_name
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_log_based_metric(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    log_metric_name: Annotated[
        str | None,
        "The name of the log-based metric to be updated. It specifies which metric to modify in your organization.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateLogsMetric'."]:
    """Update a specific log-based metric in your organization.

    Call this tool to update a particular log-based metric within your organization using Datadog's API. Ideal for modifying metrics configurations to refine log analysis and monitoring.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATELOGBASEDMETRIC_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not log_metric_name:
        missing_params.append(("log_metric_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATELOGBASEDMETRIC_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATELOGBASEDMETRIC_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/logs/config/metrics/{metric_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_id=log_metric_name
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATELOGBASEDMETRIC_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_restriction_queries(
    context: ToolContext,
    page_number: Annotated[
        int | None,
        "The specific page number of results to return. Useful for paginating through result sets.",
    ] = 0,
    page_size: Annotated[
        int | None, "The number of results to return per page. Maximum value is 100."
    ] = 10,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListRestrictionQueries'."]:
    """Retrieve all restriction queries with their details.

    This tool retrieves all restriction queries from Datadog, including their names and IDs. Use it when you need to review or manage restriction queries within your Datadog configuration."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/restriction_queries".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({"page[size]": page_size, "page[number]": page_number}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_datadog_restriction_query(
    context: ToolContext,
    restriction_query: Annotated[
        str | None,
        "A string representing the restriction query to manage log access and configurations.",
    ] = None,
    restriction_query_resource_type: Annotated[
        str | None,
        "Specifies the type of restriction query resource. Must be 'logs_restriction_queries'.",
    ] = "logs_restriction_queries",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateRestrictionQuery'."]:
    """Create a new restriction query in Datadog.

    This tool creates a restriction query for your organization in Datadog. Use it to manage log access and configurations."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"restriction_query": restriction_query},
            "type": restriction_query_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/restriction_queries".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_role_restriction_query(
    context: ToolContext,
    role_id: Annotated[
        str, "The unique identifier for the role whose restriction query you want to retrieve."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetRoleRestrictionQuery'."]:
    """Retrieve the restriction query for a specific role.

    Use this tool to get the restriction query details associated with a specific role in Datadog. The tool fetches information relevant to access restrictions for the role identified."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/restriction_queries/role/{role_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), role_id=role_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_user_restriction_queries(
    context: ToolContext,
    user_identifier: Annotated[
        str, "The unique ID of the user for retrieving restriction queries."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListUserRestrictionQueries'."]:
    """Retrieve restriction queries for a specific user.

    Use this tool to get all restriction queries associated with a specified user in Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/restriction_queries/user/{user_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), user_id=user_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_restriction_query(
    context: ToolContext,
    restriction_query_id: Annotated[
        str, "The unique ID of the restriction query to be deleted from Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteRestrictionQuery'."]:
    """Deletes a restriction query from Datadog logs configuration.

    Use this tool to delete a specific restriction query within Datadog's logs configuration. It should be called when you need to remove a restriction query by its unique ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/restriction_queries/{restriction_query_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            restriction_query_id=restriction_query_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_restriction_query(
    context: ToolContext,
    restriction_query_id: Annotated[
        str, "The unique identifier for the restriction query to retrieve its details."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetRestrictionQuery'."]:
    """Retrieve a restriction query by its ID within Datadog.

    Use this tool to access the details of a specific restriction query in your Datadog organization by providing the `restriction_query_id`."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/restriction_queries/{restriction_query_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            restriction_query_id=restriction_query_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def edit_restriction_query(
    context: ToolContext,
    restriction_query_id: Annotated[str, "The ID of the restriction query to be edited."],
    restriction_query_resource_type: Annotated[
        str | None, "The type of restriction query resource. Must be 'logs_restriction_queries'."
    ] = "logs_restriction_queries",
    restriction_query_string: Annotated[
        str | None, "The restriction query string to update for the restriction query in Datadog."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateRestrictionQuery'."]:
    """Edit an existing restriction query in Datadog.

    Use this tool to modify restriction queries in Datadog's log configuration. Call this when you need to update specific restriction parameters for log queries."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"restriction_query": restriction_query_string},
            "type": restriction_query_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/restriction_queries/{restriction_query_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            restriction_query_id=restriction_query_id,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def remove_role_from_restriction_query(
    context: ToolContext,
    restriction_query_id: Annotated[
        str, "The ID of the restriction query to remove the role from."
    ],
    role_type: Annotated[str | None, "The type of the role, must be 'roles'."] = "roles",
    role_unique_identifier: Annotated[
        str | None, "The unique identifier of the role to be removed from the restriction query."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveRoleFromRestrictionQuery'."]:
    """Removes a role from a Datadog restriction query.

    Use this tool to delete a specific role from a restriction query in Datadog. Appropriate for situations where you need to manage access by modifying restriction queries."""  # noqa: E501
    request_data = remove_none_values({"data": {"id": role_unique_identifier, "type": role_type}})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/restriction_queries/{restriction_query_id}/roles".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            restriction_query_id=restriction_query_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_restriction_query_roles(
    context: ToolContext,
    restriction_query_id: Annotated[
        str, "The unique identifier of the restriction query to fetch associated roles."
    ],
    page_number: Annotated[
        int | None,
        "The specific page number to return in the response. Use this to navigate through paginated results.",  # noqa: E501
    ] = 0,
    page_size: Annotated[
        int | None, "Specify the number of results per page. The maximum allowed value is 100."
    ] = 10,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListRestrictionQueryRoles'."]:
    """Retrieve roles associated with a specific restriction query.

    This tool fetches all roles that are linked to a specified restriction query within Datadog. Use this when you need to identify which roles have permissions based on a specific restriction query."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/restriction_queries/{restriction_query_id}/roles".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            restriction_query_id=restriction_query_id,
        ),
        method="GET",
        params=remove_none_values({"page[size]": page_size, "page[number]": page_number}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def add_role_to_restriction_query(
    context: ToolContext,
    restriction_query_id: Annotated[
        str, "The ID of the restriction query to which a role will be added."
    ],
    role_type: Annotated[str | None, "The type of the role, expected to be 'roles'."] = "roles",
    role_unique_identifier: Annotated[
        str | None, "The unique identifier of the role to be added to the restriction query."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddRoleToRestrictionQuery'."]:
    """Adds a role to a restriction query for logs configuration.

    This tool is used to add a specific role to an existing restriction query in Datadog's logs configuration. Call this tool when you need to update restriction queries by including additional roles."""  # noqa: E501
    request_data = remove_none_values({"data": {"id": role_unique_identifier, "type": role_type}})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/config/restriction_queries/{restriction_query_id}/roles".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            restriction_query_id=restriction_query_id,
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_logs_matching_query(
    context: ToolContext,
    max_timestamp_for_logs: Annotated[
        str | None,
        "Specify the maximum timestamp for the requested logs. This represents the latest time point for log retrieval.",  # noqa: E501
    ] = None,
    maximum_logs_in_response: Annotated[
        int | None, "Maximum number of logs to include in the response. Specify an integer value."
    ] = 10,
    minimum_timestamp_for_logs: Annotated[
        str | None,
        "Specify the earliest timestamp for the logs to be retrieved. Use ISO 8601 format for the timestamp.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "Cursor for pagination to retrieve the next set of log results. Use the cursor from the previous query to continue fetching results.",  # noqa: E501
    ] = None,
    search_indexes: Annotated[
        list[str] | None, "Specify the indexes to search. Defaults to '*' for all indexes."
    ] = None,
    search_query: Annotated[
        str | None, "Search query using logs syntax to filter specific logs from Datadog."
    ] = None,
    sort_order: Annotated[
        str | None,
        "Specify the order of logs in results: 'timestamp' for ascending or '-timestamp' for descending.",  # noqa: E501
    ] = None,
    storage_type: Annotated[
        str | None,
        "Specifies the storage type. Options are 'indexes', 'online-archives', or 'flex'.",
    ] = "indexes",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListLogsGet'."]:
    """Retrieve logs that match a search query with pagination.

    Use this tool to search and filter logs in Datadog based on a specified query. It's ideal for retrieving specific log data for analysis or monitoring purposes. For extensive archiving, consider Datadog's archive features instead."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/events".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[query]": search_query,
            "filter[indexes]": search_indexes,
            "filter[from]": minimum_timestamp_for_logs,
            "filter[to]": max_timestamp_for_logs,
            "filter[storage_tier]": storage_type,
            "sort": sort_order,
            "page[cursor]": pagination_cursor,
            "page[limit]": maximum_logs_in_response,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_logs(
    context: ToolContext,
    indexes_to_search: Annotated[
        list[str] | None, "Specify the indexes to search. Defaults to ['*'] for all indexes."
    ] = None,
    max_log_time: Annotated[
        str | None,
        "The maximum time for the requested logs. Supports date math and regular timestamps (milliseconds).",  # noqa: E501
    ] = "now",
    maximum_logs_in_response: Annotated[
        int | None,
        "Specifies the maximum number of logs to return in the response, allowing control over pagination size.",  # noqa: E501
    ] = 10,
    minimum_time: Annotated[
        str | None,
        "The minimum time for the requested logs, supports date math and regular timestamps (milliseconds).",  # noqa: E501
    ] = "now-15m",
    pagination_cursor: Annotated[
        str | None,
        "Cursor for retrieving the next set of paginated log results from a previous query.",
    ] = None,
    query_timezone: Annotated[
        str | None,
        "Specify the timezone as GMT, UTC, an offset (e.g., UTC+1), or a Timezone Database ID (e.g., America/New_York).",  # noqa: E501
    ] = "UTC",
    search_query: Annotated[
        str | None, "The search query following Datadog's log search syntax to filter logs."
    ] = "*",
    sort_order: Annotated[
        str | None,
        "Defines how logs are sorted: 'timestamp' for ascending order and '-timestamp' for descending order.",  # noqa: E501
    ] = None,
    storage_type: Annotated[
        str | None, 'Specify the storage type: "indexes", "online-archives", or "flex".'
    ] = "indexes",
    time_offset_seconds: Annotated[
        int | None, "The time offset in seconds to apply to the log search query."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListLogs'."]:
    """Retrieve logs based on a search query with pagination.

    Use this tool to search and filter logs according to specified criteria. Note that results are paginated. Consider Datadog's archive capabilities for organizational log archiving needs."""  # noqa: E501
    request_data = remove_none_values({
        "filter": {
            "from": minimum_time,
            "indexes": indexes_to_search,
            "query": search_query,
            "storage_tier": storage_type,
            "to": max_log_time,
        },
        "options": {"timeOffset": time_offset_seconds, "timezone": query_timezone},
        "page": {"cursor": pagination_cursor, "limit": maximum_logs_in_response},
        "sort": sort_order,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/logs/events/search".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_metric_tag_configurations(
    context: ToolContext,
    filter_by_query_status: Annotated[
        bool | None,
        "Filter custom metrics that have or have not been queried within the specified time window.",  # noqa: E501
    ] = None,
    filter_metrics_by_tags: Annotated[
        str | None,
        "Filter metrics by tags using boolean/wildcard expressions; combine with queried filter only.",  # noqa: E501
    ] = None,
    filter_metrics_used_in_assets: Annotated[
        bool | None, "Boolean to filter metrics used in dashboards, monitors, notebooks, and SLOs."
    ] = None,
    filter_tag_configurations: Annotated[
        str | None,
        "Filter tag configurations by specified configured tags. Use string values representing specific criteria for filtering.",  # noqa: E501
    ] = None,
    include_metrics_with_configured_tags: Annotated[
        bool | None, "Set to true to filter and include only custom metrics with configured tags."
    ] = None,
    include_percentile_aggregations: Annotated[
        bool | None,
        "Set to true to include distributions with additional percentile aggregations enabled. Set to false to exclude them.",  # noqa: E501
    ] = None,
    look_back_seconds: Annotated[
        int | None,
        "The number of seconds to look back for applying a filter on tags or queried metrics. Defaults to 3600 seconds (1 hour) with a max of 2,592,000 seconds (30 days).",  # noqa: E501
    ] = None,
    max_results_per_page: Annotated[
        int | None, "Maximum number of metric configurations to return per page."
    ] = 10000,
    metric_type_filter: Annotated[
        str | None, "Filter metrics by type. Options are 'non_distribution' or 'distribution'."
    ] = "distribution",
    pagination_cursor: Annotated[
        str | None,
        "String to query the next page of metric results. Use 'next_cursor' from the previous response. Null when all pages are retrieved.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListTagConfigurations'."]:
    """Retrieve all metrics configurable in Datadog's Metrics Summary.

    Use this tool to get a list of all metrics that can be configured on the Metrics Summary page or with Metrics without Limits™. It supports pagination through cursor-based navigation."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/metrics".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[configured]": include_metrics_with_configured_tags,
            "filter[tags_configured]": filter_tag_configurations,
            "filter[metric_type]": metric_type_filter,
            "filter[include_percentiles]": include_percentile_aggregations,
            "filter[queried]": filter_by_query_status,
            "filter[tags]": filter_metrics_by_tags,
            "filter[related_assets]": filter_metrics_used_in_assets,
            "window[seconds]": look_back_seconds,
            "page[size]": max_results_per_page,
            "page[cursor]": pagination_cursor,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_bulk_tags_metrics(
    context: ToolContext,
    metric_name_prefix: Annotated[
        str, "A text prefix to match against metric names for tag deletion."
    ],
    metric_bulk_configure_tags_resource: Annotated[
        str,
        "The identifier for the metric bulk configure tags resource, which should be 'metric_bulk_configure_tags'.",  # noqa: E501
    ] = "metric_bulk_configure_tags",
    notification_emails: Annotated[
        list[str] | None, "A list of account emails to notify when the configuration is applied."
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'DeleteBulkTagsMetricsConfiguration'."
]:
    """Delete custom lists of queryable tag keys for metrics.

    This tool deletes all custom queryable tag keys for a given set of metrics identified by a name prefix. It is used with application keys that have 'Manage Tags for Metrics' permission and can notify specified account emails on completion."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"emails": notification_emails},
            "id": metric_name_prefix,
            "type": metric_bulk_configure_tags_resource,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/metrics/config/bulk-tags".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def configure_bulk_tags_for_metrics(
    context: ToolContext,
    metric_name_prefix: Annotated[
        str, "A text prefix used to match against metric names for bulk tags configuration."
    ],
    actively_queried_tags_window_seconds: Annotated[
        float | None,
        "Time window in seconds for configuring actively queried tags for matching metrics. Minimum is 1 second, maximum is 7,776,000 seconds (90 days).",  # noqa: E501
    ] = None,
    exclude_configured_tags: Annotated[
        bool | None,
        "Set to true to exclude configured tags and include all other tags. Defaults to false.",
    ] = None,
    metric_bulk_configure_tags_resource: Annotated[
        str,
        "The resource identifier for configuring bulk tags for metrics. Must be set to 'metric_bulk_configure_tags'.",  # noqa: E501
    ] = "metric_bulk_configure_tags",
    notification_emails: Annotated[
        list[str] | None, "A list of account emails to notify when the configuration is applied."
    ] = None,
    override_existing_configurations: Annotated[
        bool | None,
        "Set to true to override any existing configurations for the metric with the new tags. Defaults to true.",  # noqa: E501
    ] = None,
    tags_to_apply: Annotated[
        list[str] | None, "A list of tag names to apply to the metric configuration in Datadog."
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateBulkTagsMetricsConfiguration'."
]:
    """Configure bulk tags for specified metrics in Datadog.

    Use this tool to create and define queryable tag keys for existing count, gauge, rate, and distribution metrics in Datadog by specifying a metric name prefix. It manages tags configurations and sends results to account email addresses. Suitable for users with 'Manage Tags for Metrics' permission."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "emails": notification_emails,
                "exclude_tags_mode": exclude_configured_tags,
                "include_actively_queried_tags_window": actively_queried_tags_window_seconds,
                "override_existing_configurations": override_existing_configurations,
                "tags": tags_to_apply,
            },
            "id": metric_name_prefix,
            "type": metric_bulk_configure_tags_resource,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/metrics/config/bulk-tags".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_active_metric_configurations(
    context: ToolContext,
    metric_name: Annotated[
        str, "Specify the name of the metric to retrieve active tags and aggregations for."
    ],
    lookback_seconds: Annotated[
        int | None,
        "Number of seconds to look back from the current time. Defaults to 604800 seconds (1 week). Minimum is 7200 seconds (2 hours), and maximum is 2630000 seconds (1 month).",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListActiveMetricConfigurations'."]:
    """Retrieve active metric tags and aggregations for a given metric name.

    Use this tool to get the currently active tags and aggregations for dashboards, monitors, and APIs associated with a specified metric name. It helps to understand how metrics are being actively used across various platforms."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/metrics/{metric_name}/active-configurations".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_name=metric_name
        ),
        method="GET",
        params=remove_none_values({"window[seconds]": lookback_seconds}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_metric_tags(
    context: ToolContext,
    metric_name: Annotated[
        str,
        "The name of the metric to retrieve indexed tag key-value pairs from over the past hour.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListTagsByMetricName'."]:
    """Retrieve indexed tags for a metric over the last hour.

    Use this tool to get the indexed tag key-value pairs for a specific metric name within the past hour. Useful for analyzing and viewing metric-related tag data from Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/metrics/{metric_name}/all-tags".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_name=metric_name
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_metric_assets(
    context: ToolContext,
    metric_name: Annotated[
        str,
        "The specific name of the metric to retrieve associated assets for. This is essential for querying the correct data.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListMetricAssets'."]:
    """Retrieve assets associated with a specific metric.

    This tool returns a list of dashboards, monitors, notebooks, and SLOs where a specific metric is used. Useful for tracking where a metric appears across Datadog's tools, updated every 24 hours."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/metrics/{metric_name}/assets".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_name=metric_name
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def estimate_metrics_output(
    context: ToolContext,
    metric_name: Annotated[
        str, "Specifies the name of the metric for which to estimate cardinality."
    ],
    filtered_tag_keys: Annotated[
        str | None,
        "Filtered tag keys that the metric is configured to query with, specified as a string.",
    ] = None,
    ignore_num_aggregations: Annotated[
        int | None,
        "This argument is deprecated and has no impact on volume estimation. It is ignored in the current tool implementation.",  # noqa: E501
    ] = None,
    include_percentile_aggregators: Annotated[
        bool | None,
        "Boolean to estimate cardinality if distribution metrics have additional percentile aggregators.",  # noqa: E501
    ] = None,
    lookback_hours: Annotated[
        int | None,
        "Specify the number of hours to look back from the current time to estimate cardinality. Defaults to 0 if not provided.",  # noqa: E501
    ] = None,
    lookback_timespan_hours: Annotated[
        int | None,
        "A window, in hours, from the lookback to estimate cardinality. Minimum is 1 hour.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'EstimateMetricsOutputSeries'."]:
    """Estimate cardinality of a metric with specific settings.

    Use this tool to get the estimated cardinality for a metric based on a specific tag, percentile, and number of aggregations configuration. This is useful for analyzing metric data within Datadog using Metrics without Limits™."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/metrics/{metric_name}/estimate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_name=metric_name
        ),
        method="GET",
        params=remove_none_values({
            "filter[groups]": filtered_tag_keys,
            "filter[hours_ago]": lookback_hours,
            "filter[num_aggregations]": ignore_num_aggregations,
            "filter[pct]": include_percentile_aggregators,
            "filter[timespan_h]": lookback_timespan_hours,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_metric_tag_cardinality(
    context: ToolContext,
    metric_name: Annotated[
        str, "The name of the metric for which cardinality details of tags are to be retrieved."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetMetricTagCardinalityDetails'."]:
    """Retrieve cardinality details of tags for a specific metric.

    This tool is used to get the cardinality details of tags associated with a specific metric in Datadog. It's useful for understanding the distribution and uniqueness of tags related to the specified metric."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/metrics/{metric_name}/tag-cardinalities".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_name=metric_name
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_metric_tag_configuration(
    context: ToolContext,
    metric_name: Annotated[
        str,
        "The name of the metric whose tag configuration is to be deleted. Ensure the application key has the necessary permissions.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteTagConfiguration'."]:
    """Delete a metric's tag configuration.

    Use this tool to delete the tag configuration for a specific metric in Datadog. Ensure the application key has 'Manage Tags for Metrics' permission."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/metrics/{metric_name}/tags".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_name=metric_name
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_metric_tag_configuration(
    context: ToolContext,
    metric_name: Annotated[
        str, "The name of the metric for which to retrieve the tag configuration in Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListTagConfigurationByName'."]:
    """Retrieve the tag configuration for a specific metric.

    Use this tool to fetch the tag configuration associated with a specific metric name in Datadog. This is helpful for accessing detailed tagging information for metrics."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/metrics/{metric_name}/tags".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_name=metric_name
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_metric_tag_configuration(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    metric_name: Annotated[
        str | None,
        "Specify the name of the metric whose tag configuration you wish to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateTagConfiguration'."]:
    """Update the tag configuration of a metric in Datadog.

    This tool updates the tag configuration for a specific metric in Datadog. It supports updating percentile or custom aggregations, and changing tag exclusion modes. Use it when you need to manage or modify metric tags, especially to switch from an allow-list to a deny-list. Requires specific permissions and an existing configuration.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEMETRICTAGCONFIGURATION_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not metric_name:
        missing_params.append(("metric_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEMETRICTAGCONFIGURATION_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEMETRICTAGCONFIGURATION_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/metrics/{metric_name}/tags".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_name=metric_name
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEMETRICTAGCONFIGURATION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_metric_tag_configuration(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    metric_name: Annotated[
        str | None,
        "The name of the existing metric for which the tag configuration is to be created. This is required to identify the specific metric in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateTagConfiguration'."]:
    """Create queryable tag configurations for metrics.

    This tool is used to create and define a list of queryable tag keys for an existing metric in Datadog, such as count, gauge, rate, or distribution metrics. It allows for optional percentile aggregations on distribution metrics. By setting the 'exclude_tags_mode' to true, users can switch from an allow-list to a deny-list mode, where defined tags are not queryable. This tool requires the `Manage Tags for Metrics` permission.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEMETRICTAGCONFIGURATION_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not metric_name:
        missing_params.append(("metric_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEMETRICTAGCONFIGURATION_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEMETRICTAGCONFIGURATION_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/metrics/{metric_name}/tags".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_name=metric_name
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEMETRICTAGCONFIGURATION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_metric_volumes(
    context: ToolContext,
    metric_name: Annotated[str, "The name of the metric to retrieve volumes for."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListVolumesByMetricName'."]:
    """Retrieve distinct metric volumes by name.

    Use this tool to view distinct volumes for a specified metric name. Note that custom metrics generated in-app may return `null` for ingested volumes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/metrics/{metric_name}/volumes".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_name=metric_name
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_monitor_notification_rules(
    context: ToolContext,
    filter_criteria: Annotated[
        str | None,
        "JSON string to filter rules by text, tags, or recipients. Use keys: `text`, `tags`, `recipients`.",  # noqa: E501
    ] = None,
    include_related_resources: Annotated[
        str | None,
        "Specify related resources to include in the response, such as `created_by`. Use a comma-separated list.",  # noqa: E501
    ] = None,
    number_of_rules_per_page: Annotated[
        int | None, "The number of rules to return per page. Defaults to 100 if not specified."
    ] = None,
    sort_order: Annotated[
        str | None,
        "String for sort order. Example: `name:asc`. Directions: `asc`, `desc`. Fields: `name`, `created_at`.",  # noqa: E501
    ] = None,
    starting_page_number: Annotated[
        int | None,
        "The page number to begin pagination. Defaults to the first page if not specified.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetMonitorNotificationRules'."]:
    """Retrieve all monitor notification rules from Datadog.

    Call this tool to get a comprehensive list of all monitor notification rules configured in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/monitor/notification_rule".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page": starting_page_number,
            "per_page": number_of_rules_per_page,
            "sort": sort_order,
            "filters": filter_criteria,
            "include": include_related_resources,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_monitor_notification_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateMonitorNotificationRule'."]:
    """Creates a monitor notification rule in Datadog.

    Use this tool to set up a notification rule for a specific monitor in Datadog. This allows for alerting based on monitor condition changes.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEMONITORNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEMONITORNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEMONITORNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/monitor/notification_rule".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEMONITORNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_monitor_notification_rule(
    context: ToolContext,
    monitor_notification_rule_id: Annotated[
        str, "The unique identifier for the monitor notification rule to delete in Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteMonitorNotificationRule'."]:
    """Delete a monitor notification rule by ID.

    Use this tool to delete a specific monitor notification rule in Datadog by providing the rule ID. Useful for managing and cleaning up notification rules that are no longer needed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/monitor/notification_rule/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            rule_id=monitor_notification_rule_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_monitor_notification_rule(
    context: ToolContext,
    monitor_rule_id: Annotated[
        str,
        "ID of the monitor notification rule to fetch. This is required to retrieve specific rule details.",  # noqa: E501
    ],
    include_related_resources: Annotated[
        str | None,
        "Comma-separated list of related resource paths to include in the response, such as `created_by`.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetMonitorNotificationRule'."]:
    """Retrieve a monitor notification rule by its ID.

    Use this tool to fetch details of a specific monitor notification rule in Datadog by providing the rule ID. It can be useful for checking the configuration of alerting and notification rules."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/monitor/notification_rule/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), rule_id=monitor_rule_id
        ),
        method="GET",
        params=remove_none_values({"include": include_related_resources}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_monitor_notification_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    monitor_notification_rule_id: Annotated[
        str | None,
        "ID of the monitor notification rule to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateMonitorNotificationRule'."]:
    """Updates a Datadog monitor notification rule.

    Use this tool to update a notification rule for a monitor in Datadog by specifying the `rule_id`. It should be called when you need to change the settings or behavior of an existing monitor notification rule.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEMONITORNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not monitor_notification_rule_id:
        missing_params.append(("monitor_notification_rule_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEMONITORNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEMONITORNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/monitor/notification_rule/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            rule_id=monitor_notification_rule_id,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEMONITORNOTIFICATIONRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_monitor_config_policies(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListMonitorConfigPolicies'."]:
    """Retrieve all monitor configuration policies.

    Use this tool to get a comprehensive list of monitor configuration policies from Datadog. This can be useful for managing and understanding monitoring setups."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/monitor/policy".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_monitor_config_policy(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateMonitorConfigPolicy'."]:
    """Create a new monitor configuration policy in Datadog.

    Use this tool to create monitor configuration policies within Datadog, specifying the desired configurations and parameters.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEMONITORCONFIGPOLICY_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEMONITORCONFIGPOLICY_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEMONITORCONFIGPOLICY_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/monitor/policy".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEMONITORCONFIGPOLICY_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_monitor_policy(
    context: ToolContext,
    monitor_policy_id: Annotated[str, "ID of the monitor configuration policy to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteMonitorConfigPolicy'."]:
    """Deletes a specific monitor configuration policy."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/monitor/policy/{policy_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), policy_id=monitor_policy_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_monitor_configuration_policy(
    context: ToolContext,
    monitor_policy_id: Annotated[
        str, "ID of the monitor configuration policy to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetMonitorConfigPolicy'."]:
    """Retrieve a monitor's configuration policy using its ID.

    This tool fetches detailed information about a specific monitor configuration policy from Datadog using the policy ID. It should be called when you need to access the configuration details of a monitor policy."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/monitor/policy/{policy_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), policy_id=monitor_policy_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def edit_monitor_config_policy(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    monitor_policy_id: Annotated[
        str | None,
        "The ID of the monitor configuration policy to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateMonitorConfigPolicy'."]:
    """Edit an existing monitor configuration policy in Datadog.

    This tool allows you to update the configuration of an existing monitor policy in Datadog by specifying the policy ID. Use it when changes to monitor policies are required.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "EDITMONITORCONFIGPOLICY_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not monitor_policy_id:
        missing_params.append(("monitor_policy_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["EDITMONITORCONFIGPOLICY_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["EDITMONITORCONFIGPOLICY_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/monitor/policy/{policy_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), policy_id=monitor_policy_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["EDITMONITORCONFIGPOLICY_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_all_monitor_user_templates(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListMonitorUserTemplates'."]:
    """Retrieve all monitor user templates from Datadog.

    Use this tool to get a list of all monitor user templates available in Datadog. It can help in managing and reviewing template configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/monitor/template".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_monitor_user_template(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateMonitorUserTemplate'."]:
    """Create a new monitor user template in Datadog.

    This tool should be called to create a new user template for monitoring within Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEMONITORUSERTEMPLATE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEMONITORUSERTEMPLATE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEMONITORUSERTEMPLATE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/monitor/template".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEMONITORUSERTEMPLATE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def validate_monitor_user_template(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ValidateMonitorUserTemplate'."]:
    """Validate the structure and content of a monitor user template.

    Use this tool to ensure that a monitor user template in Datadog meets the required structural and content standards. This is essential for verifying templates before implementation.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "VALIDATEMONITORUSERTEMPLATE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["VALIDATEMONITORUSERTEMPLATE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["VALIDATEMONITORUSERTEMPLATE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/monitor/template/validate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["VALIDATEMONITORUSERTEMPLATE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_monitor_user_template(
    context: ToolContext,
    monitor_user_template_id: Annotated[
        str, "ID of the monitor user template to be deleted in Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteMonitorUserTemplate'."]:
    """Deletes a monitor user template by its ID on Datadog.

    Use this tool to delete an existing monitor user template in Datadog by specifying the template's ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/monitor/template/{template_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            template_id=monitor_user_template_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_monitor_user_template(
    context: ToolContext,
    template_id: Annotated[
        str, "The unique identifier for the specific monitor user template to retrieve."
    ],
    include_all_versions: Annotated[
        bool | None, "Include all versions of the template in the response if true."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetMonitorUserTemplate'."]:
    """Retrieve a monitor user template by ID from Datadog.

    Use this tool to get detailed information about a specific monitor user template in Datadog by providing the template ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/monitor/template/{template_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), template_id=template_id
        ),
        method="GET",
        params=remove_none_values({"with_all_versions": include_all_versions}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_monitor_user_template(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    monitor_user_template_id: Annotated[
        str | None,
        "ID of the monitor user template to update with a new version.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateMonitorUserTemplate'."]:
    """Creates a new version of a monitor user template in Datadog.

    Use this tool to create a new version of an existing monitor user template in Datadog when updates or changes are needed to a monitor's settings.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEMONITORUSERTEMPLATE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not monitor_user_template_id:
        missing_params.append(("monitor_user_template_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEMONITORUSERTEMPLATE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEMONITORUSERTEMPLATE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/monitor/template/{template_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            template_id=monitor_user_template_id,
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEMONITORUSERTEMPLATE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def validate_monitor_template(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    monitor_template_id: Annotated[
        str | None,
        "ID of the monitor user template to be validated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ValidateExistingMonitorUserTemplate'."
]:
    """Validate the structure and content of a monitor template update.

    This tool is used to validate the structure and content of an existing monitor user template being updated to a new version. It ensures that the changes align with the required format and content guidelines.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "VALIDATEMONITORTEMPLATE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not monitor_template_id:
        missing_params.append(("monitor_template_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["VALIDATEMONITORTEMPLATE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["VALIDATEMONITORTEMPLATE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/monitor/template/{template_id}/validate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), template_id=monitor_template_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["VALIDATEMONITORTEMPLATE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_monitor_downtimes(
    context: ToolContext,
    monitor_id: Annotated[
        int,
        "The ID of the monitor for which to retrieve active downtimes. This should be provided as an integer.",  # noqa: E501
    ],
    maximum_downtime_count: Annotated[
        int | None, "Specifies the maximum number of downtime entries to return in the response."
    ] = 30,
    pagination_offset: Annotated[
        int | None,
        "Specify the offset for the beginning of the returned page, to manage pagination.",
    ] = 0,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListMonitorDowntimes'."]:
    """Retrieve active downtimes for a specified monitor.

    Use this tool to get all active downtimes associated with a specific monitor ID in Datadog. It should be called when you need to check the downtime status of a monitor."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/monitor/{monitor_id}/downtime_matches".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), monitor_id=monitor_id
        ),
        method="GET",
        params=remove_none_values({
            "page[offset]": pagination_offset,
            "page[limit]": maximum_downtime_count,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_device_list(
    context: ToolContext,
    filter_by_tag: Annotated[str | None, "Filter devices by a specified tag."] = None,
    page_number: Annotated[
        int | None, "Specific page number to return when fetching the devices list."
    ] = 0,
    page_size: Annotated[
        int | None, "Specify the size for a given page, with a maximum allowed value of 100."
    ] = 10,
    sort_devices_by: Annotated[
        str | None, "Specify the field by which to sort the devices list."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListDevices'."]:
    """Retrieve a list of devices from Datadog.

    Use this tool to get a comprehensive list of all devices managed by the Datadog service. It retrieves data from the specified API endpoint and provides an overview of available devices."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/ndm/devices".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "sort": sort_devices_by,
            "filter[tag]": filter_by_tag,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_device_details(
    context: ToolContext,
    device_id: Annotated[str, "The unique identifier of the device to retrieve details for."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetDevice'."]:
    """Retrieve specific device details.

    Call this tool to obtain detailed information about a specified device using its device ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/ndm/devices/{device_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), device_id=device_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_device_interfaces(
    context: ToolContext,
    device_identifier: Annotated[
        str, "The unique ID of the device to retrieve interfaces for. Expected to be a string."
    ],
    include_ip_addresses: Annotated[
        bool | None,
        "Specify true to include IP addresses of the interfaces, or false to exclude them.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetInterfaces'."]:
    """Fetches the list of interfaces for a given device.

    Use this tool to obtain the list of interfaces associated with a particular device from Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/ndm/interfaces".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "device_id": device_identifier,
            "get_ip_addresses": include_ip_addresses,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_device_user_tags(
    context: ToolContext,
    device_identifier: Annotated[
        str, "The unique identifier of the device to fetch tags for from Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListDeviceUserTags'."]:
    """Retrieve the list of tags for a specific device.

    This tool calls the Datadog API to fetch user tags for a given device, identified by its ID. Use this when you need to understand or manage tags associated with a device for tracking or organizational purposes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/ndm/tags/devices/{device_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), device_id=device_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_device_tags(
    context: ToolContext,
    device_identifier: Annotated[str, "The ID of the device for which tags are being updated."],
    device_id_for_tags: Annotated[
        str | None, "The ID of the device for which the tags will be updated."
    ] = None,
    device_tags: Annotated[
        list[str] | None, "A list of tags to update for the device. Each tag should be a string."
    ] = None,
    resource_type: Annotated[str | None, "The type of resource, always set to 'tags'."] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateDeviceUserTags'."]:
    """Update the tags for a specified device.

    Use this tool to modify the tags associated with a device in Datadog. It's called when there's a need to update or change the tags for device management. The tool confirms the modifications made to the device tags."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"tags": device_tags},
            "id": device_id_for_tags,
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/ndm/tags/devices/{device_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), device_id=device_identifier
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_aggregated_connections(
    context: ToolContext,
    connection_limit: Annotated[
        int | None, "Set the number of connections to be returned, maximum 7500, default 100."
    ] = 100,
    end_query_window: Annotated[
        int | None,
        "Unix timestamp for the end of the query window. Defaults to current time if not provided.",
    ] = None,
    filter_by_tags: Annotated[
        str | None,
        "Comma-separated list of tags to filter connections by for more targeted querying.",
    ] = None,
    group_by_fields: Annotated[
        str | None,
        "Comma-separated list of fields to group connections by, with a maximum of 10 fields.",
    ] = None,
    start_time_unix_timestamp: Annotated[
        int | None,
        "Unix timestamp for the start of the query window. Defaults to 15 minutes before `end_time_unix_timestamp` if not provided.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAggregatedConnections'."]:
    """Retrieve all aggregated network connections from Datadog.

    This tool is used to get a comprehensive view of all aggregated network connections. It's ideal for monitoring and analyzing network activity."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/network/connections/aggregate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "from": start_time_unix_timestamp,
            "to": end_query_window,
            "group_by": group_by_fields,
            "tags": filter_by_tags,
            "limit": connection_limit,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_aggregated_dns_traffic(
    context: ToolContext,
    end_timestamp: Annotated[
        int | None,
        "Unix timestamp (seconds since epoch) for the end of the query window. Defaults to current time if not provided.",  # noqa: E501
    ] = None,
    filter_dns_traffic_by_tags: Annotated[
        str | None, "Comma-separated list of tags to filter the DNS traffic data within the query."
    ] = None,
    group_dns_traffic_by_fields: Annotated[
        str | None,
        "Comma-separated list of fields to group DNS traffic by. Defaults to `network.dns_query` if unspecified. Use `server_ungrouped` to avoid grouping. Maximum of 10 fields.",  # noqa: E501
    ] = None,
    max_dns_entries: Annotated[
        int | None,
        "Number of aggregated DNS entries to return, up to a maximum of 7500. Default is 100.",
    ] = 100,
    start_query_timestamp: Annotated[
        int | None,
        "Unix timestamp for the query window start. Defaults to 15 min before `to` timestamp if not specified.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAggregatedDns'."]:
    """Retrieve all aggregated DNS traffic data.

    This tool fetches all the aggregated DNS traffic data using Datadog's API. It should be called when you need information about DNS traffic patterns or metrics."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/network/dns/aggregate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "from": start_query_timestamp,
            "to": end_timestamp,
            "group_by": group_dns_traffic_by_fields,
            "tags": filter_dns_traffic_by_tags,
            "limit": max_dns_entries,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_on_call_escalation_policy(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    include_relationships: Annotated[
        str | None,
        "Comma-separated list of included relationships to return. Allowed values: teams, steps, steps.targets.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateOnCallEscalationPolicy'."]:
    """Create a new On-Call escalation policy in Datadog.

    This tool is used to create a new On-Call escalation policy in Datadog. Call this tool when you need to set up or adjust escalation protocols within your team for handling alerts.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEONCALLESCALATIONPOLICY_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEONCALLESCALATIONPOLICY_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEONCALLESCALATIONPOLICY_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/on-call/escalation-policies".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEONCALLESCALATIONPOLICY_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"include": include_relationships}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_escalation_policy(
    context: ToolContext,
    escalation_policy_id: Annotated[str, "The unique ID of the escalation policy to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteOnCallEscalationPolicy'."]:
    """Delete an On-Call escalation policy.

    Use this tool to delete an existing On-Call escalation policy by providing its policy ID. This is useful for managing or updating your escalation policies within the Datadog platform."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/on-call/escalation-policies/{policy_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), policy_id=escalation_policy_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_on_call_escalation_policy(
    context: ToolContext,
    escalation_policy_id: Annotated[
        str, "The unique identifier for the on-call escalation policy to retrieve."
    ],
    include_relationships: Annotated[
        str | None,
        "A comma-separated list of relationships to include in the response. Allowed values: `teams`, `steps`, `steps.targets`.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetOnCallEscalationPolicy'."]:
    """Retrieve details of an On-Call escalation policy.

    This tool is used to retrieve information about a specific on-call escalation policy from Datadog. It should be called when there's a need to understand the configuration or details of an on-call policy by providing the policy ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/on-call/escalation-policies/{policy_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), policy_id=escalation_policy_id
        ),
        method="GET",
        params=remove_none_values({"include": include_relationships}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_on_call_escalation_policy(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    escalation_policy_id: Annotated[
        str | None,
        "The unique identifier of the escalation policy to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    include_relationships: Annotated[
        str | None,
        "Comma-separated list of relationships to be returned. Options: `teams`, `steps`, `steps.targets`.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateOnCallEscalationPolicy'."]:
    """Update an On-Call escalation policy in Datadog.

    This tool allows updating an existing On-Call escalation policy in Datadog. Use it to modify the details of a specific policy by providing the necessary updates.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEONCALLESCALATIONPOLICY_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not escalation_policy_id:
        missing_params.append(("escalation_policy_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEONCALLESCALATIONPOLICY_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEONCALLESCALATIONPOLICY_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/on-call/escalation-policies/{policy_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), policy_id=escalation_policy_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEONCALLESCALATIONPOLICY_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"include": include_relationships}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def trigger_on_call_page(
    context: ToolContext,
    issue_summary: Annotated[
        str | None, "A short summary of the issue or context for the On-Call Page."
    ] = None,
    on_call_page_urgency_level: Annotated[
        str | None, "Specifies the urgency level of the On-Call Page. Accepts 'low' or 'high'."
    ] = "high",
    page_title: Annotated[
        str | None,
        "The title of the On-Call Page. Provide a concise and clear title to identify the issue or alert.",  # noqa: E501
    ] = None,
    resource_type_for_on_call: Annotated[
        str | None, "Specify the type of resource for creating an On-Call Page. Use `pages`."
    ] = "pages",
    tags_for_categorization: Annotated[
        list[str] | None, "An array of tags for categorizing or filtering the On-Call page."
    ] = None,
    target_identifier: Annotated[
        str | None,
        "Identifier for the target, such as a team handle or user ID, used to specify the intended recipient of the On-Call Page.",  # noqa: E501
    ] = None,
    target_type: Annotated[
        str | None, "Specify the kind of target: 'team_id', 'team_handle', or 'user_id'."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateOnCallPage'."]:
    """Triggers a new On-Call Page in Datadog.

    Use this tool to trigger a new On-Call Page in Datadog whenever immediate attention is required. Ideal for alerting on-call team members."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "description": issue_summary,
                "tags": tags_for_categorization,
                "target": {"identifier": target_identifier, "type": target_type},
                "title": page_title,
                "urgency": on_call_page_urgency_level,
            },
            "type": resource_type_for_on_call,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/on-call/pages".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def acknowledge_on_call_page(
    context: ToolContext,
    on_call_page_id: Annotated[str, "The unique identifier for the On-Call Page to acknowledge."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AcknowledgeOnCallPage'."]:
    """Acknowledge an On-Call Page alert in Datadog.

    Use this tool to acknowledge an On-Call Page alert for a specified page in Datadog. This can be used to indicate that an alert is being addressed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/on-call/pages/{page_id}/acknowledge".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), page_id=on_call_page_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def escalate_on_call_page(
    context: ToolContext,
    on_call_page_id: Annotated[str, "The unique identifier for the on-call page to be escalated."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'EscalateOnCallPage'."]:
    """Escalate an on-call page to notify the responsible team.

    Use this tool to escalate an on-call page when immediate attention is needed from the responsible team."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/on-call/pages/{page_id}/escalate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), page_id=on_call_page_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def resolve_on_call_page(
    context: ToolContext,
    on_call_page_id: Annotated[
        str, "The unique identifier of the on-call page to resolve in Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ResolveOnCallPage'."]:
    """Resolves an On-Call Page in Datadog.

    This tool resolves an open on-call page in Datadog, which is useful for indicating that an incident or issue has been addressed. Use it when you need to confirm the resolution of a specific on-call incident."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/on-call/pages/{page_id}/resolve".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), page_id=on_call_page_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_on_call_schedule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    include_relationships: Annotated[
        str | None,
        "Comma-separated list of relationships to return with the schedule. Options: `teams`, `layers`, `layers.members`, `layers.members.user`.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateOnCallSchedule'."]:
    """Create a new on-call schedule in Datadog.

    This tool is used to create a new on-call schedule in Datadog. It should be called when there is a need to set up a schedule for on-call duties, allowing teams to manage availability and response times. The tool returns a confirmation of the schedule created along with its details.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEONCALLSCHEDULE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEONCALLSCHEDULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEONCALLSCHEDULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/on-call/schedules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEONCALLSCHEDULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"include": include_relationships}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_on_call_schedule(
    context: ToolContext,
    schedule_id: Annotated[
        str,
        "The unique identifier for the on-call schedule you wish to delete in Datadog. This is a required field.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteOnCallSchedule'."]:
    """Delete an On-Call schedule in Datadog.

    Use this tool to delete a specific on-call schedule identified by its schedule ID in Datadog when managing schedules."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/on-call/schedules/{schedule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), schedule_id=schedule_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_on_call_schedule(
    context: ToolContext,
    schedule_id: Annotated[str, "The unique ID of the on-call schedule to be retrieved."],
    include_relationships: Annotated[
        str | None,
        "Comma-separated list of relationships to include in the response. Options: `teams`, `layers`, `layers.members`, `layers.members.user`.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetOnCallSchedule'."]:
    """Retrieve an On-Call schedule from Datadog.

    Use this tool to access detailed information regarding a specific On-Call schedule in Datadog by providing the schedule ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/on-call/schedules/{schedule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), schedule_id=schedule_id
        ),
        method="GET",
        params=remove_none_values({"include": include_relationships}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_on_call_schedule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    schedule_id: Annotated[
        str | None,
        "The unique identifier for the on-call schedule to update in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    relationships_to_include: Annotated[
        str | None,
        "Comma-separated relationships to return, e.g., `teams`, `layers`. Allowed values: `teams`, `layers`, `layers.members`, `layers.members.user`.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateOnCallSchedule'."]:
    """Update an existing on-call schedule in Datadog.

    Use this tool to update the details of an existing on-call schedule in Datadog. It should be called when there is a need to modify the configuration or timing of an on-call schedule.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEONCALLSCHEDULE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not schedule_id:
        missing_params.append(("schedule_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEONCALLSCHEDULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEONCALLSCHEDULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/on-call/schedules/{schedule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), schedule_id=schedule_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEONCALLSCHEDULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"include": relationships_to_include}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_on_call_user(
    context: ToolContext,
    schedule_id: Annotated[str, "The unique ID of the schedule to retrieve the on-call user from."],
    include_related_resources: Annotated[
        str | None,
        "Specifies related resources to include in the response. Use 'user' to include user details.",  # noqa: E501
    ] = None,
    timestamp_for_on_call_user: Annotated[
        str | None,
        "Retrieves the on-call user at the specified timestamp (ISO-8601). Defaults to current time if omitted.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetScheduleOnCallUser'."]:
    """Retrieve the current on-call user for a specific schedule.

    Use this tool to find out which user is currently on-call for a given schedule. It is helpful for monitoring and response teams to know who is available at any given moment."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/on-call/schedules/{schedule_id}/on-call".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), schedule_id=schedule_id
        ),
        method="GET",
        params=remove_none_values({
            "include": include_related_resources,
            "filter[at_ts]": timestamp_for_on_call_user,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_team_on_call_users(
    context: ToolContext,
    team_id: Annotated[
        str, "The unique identifier for the team whose on-call users are being retrieved."
    ],
    include_relationships: Annotated[
        str | None,
        "Comma-separated list of relationships to include in the response: `responders`, `escalations`, `escalations.responders`.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTeamOnCallUsers'."]:
    """Retrieve on-call users for a specific team.

    Fetch the list of users who are on-call for a specified team at a given time using Datadog's API."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/on-call/teams/{team_id}/on-call".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), team_id=team_id
        ),
        method="GET",
        params=remove_none_values({"include": include_relationships}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_team_on_call_routing_rules(
    context: ToolContext,
    team_identifier: Annotated[
        str, "The unique identifier for the team whose on-call routing rules are to be retrieved."
    ],
    include_relationships: Annotated[
        str | None,
        "Comma-separated list of relationships to return, such as `rules` or `rules.policy`.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetOnCallTeamRoutingRules'."]:
    """Retrieve a team's on-call routing rules from Datadog.

    Use this tool to obtain the on-call routing rules for a specific team in Datadog by providing the team ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/on-call/teams/{team_id}/routing-rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), team_id=team_identifier
        ),
        method="GET",
        params=remove_none_values({"include": include_relationships}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def set_on_call_team_routing_rules(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the team whose routing rules are being set. It should be a string.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    include_relationships: Annotated[
        str | None,
        "Comma-separated list of relationships to include in the response. Allowed: `rules`, `rules.policy`.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SetOnCallTeamRoutingRules'."]:
    """Set or update a team's On-Call routing rules in Datadog.

    Use this tool to configure or modify the On-Call routing rules for a specific team in Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "SETONCALLTEAMROUTINGRULES_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not team_identifier:
        missing_params.append(("team_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["SETONCALLTEAMROUTINGRULES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["SETONCALLTEAMROUTINGRULES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/on-call/teams/{team_id}/routing-rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), team_id=team_identifier
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SETONCALLTEAMROUTINGRULES_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"include": include_relationships}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_org_configs(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListOrgConfigs'."]:
    """Retrieve all organization configurations.

    Call this tool to get a list of all organization configurations, providing details like name, description, and value."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/org_configs".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_organization_config_details(
    context: ToolContext,
    organization_config_name: Annotated[
        str, "The name of the organization configuration to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetOrgConfig'."]:
    """Retrieve organization configuration details by name.

    Fetches the name, description, and value of a specific organization configuration from Datadog, identified by its name."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/org_configs/{org_config_name}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            org_config_name=organization_config_name,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_org_config(
    context: ToolContext,
    org_config_data_type: Annotated[
        str, "The data type of the organization configuration, which should be 'org_configs'."
    ],
    org_config_value: Annotated[
        str,
        "The new value for the organization configuration. Provide the desired value to update the specific Org Config.",  # noqa: E501
    ],
    organization_configuration_name: Annotated[
        str, "The name of the organization configuration to update in Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateOrgConfig'."]:
    """Update a specified organization configuration in Datadog.

    Use this tool to update the value of a specific organization configuration in Datadog. Suitable for changing settings like feature toggles or other organizational configurations."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"attributes": {"value": org_config_value}, "type": org_config_data_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/org_configs/{org_config_name}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            org_config_name=organization_configuration_name,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_org_connections(
    context: ToolContext,
    entry_limit: Annotated[
        int | None, "Specifies the maximum number of entries to return. Default is 1000."
    ] = None,
    pagination_offset: Annotated[
        int | None,
        "The pagination offset to start querying from, with a default of 0. Use this for paginated results.",  # noqa: E501
    ] = None,
    sink_organization_id: Annotated[
        str | None,
        "The organization ID of the sink org. It identifies the destination organization in the connections list.",  # noqa: E501
    ] = None,
    source_organization_id: Annotated[
        str | None, "The ID of the source organization to query connections from."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListOrgConnections'."]:
    """Retrieve a list of organization connections from Datadog.

    Use this tool to get a comprehensive list of all organization connections within Datadog. This can be useful for managing and analyzing connected organizations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/org_connections".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "sink_org_id": sink_organization_id,
            "source_org_id": source_organization_id,
            "limit": entry_limit,
            "offset": pagination_offset,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_org_connection(
    context: ToolContext,
    connection_types: Annotated[
        list[str], "List of connection types to establish between the organizations."
    ],
    organization_connection_type: Annotated[
        str, "Specify the type of the organization connection. Must be 'org_connection'."
    ],
    organization_relationship_type: Annotated[
        str | None, "Specifies the type of the organization relationship. Must be 'orgs'."
    ] = None,
    target_org_name: Annotated[
        str | None, "The name of the target organization to connect with."
    ] = None,
    target_org_uuid: Annotated[
        str | None, "The UUID of the target organization to connect to."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateOrgConnections'."]:
    """Creates a new organization connection in Datadog.

    Use this tool to create a connection between the current organization and a target organization within Datadog. Ideal for linking organizational resources and sharing data across teams."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"connection_types": connection_types},
            "relationships": {
                "sink_org": {
                    "data": {
                        "id": target_org_uuid,
                        "name": target_org_name,
                        "type": organization_relationship_type,
                    }
                }
            },
            "type": organization_connection_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/org_connections".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_org_connection(
    context: ToolContext,
    connection_id: Annotated[
        str, "The unique identifier of the organization connection to be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteOrgConnections'."]:
    """Delete an existing organization connection.

    Call this tool to delete a specified organization connection. Use it when you need to remove a link between organizations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/org_connections/{connection_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), connection_id=connection_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_org_connection(
    context: ToolContext,
    org_connection_id: Annotated[
        str, "The unique identifier of the organization connection in Datadog."
    ],
    org_connection_type: Annotated[
        str, "Specifies the type of organization connection. Must be 'org_connection'."
    ],
    organization_connection_id: Annotated[
        str, "The unique identifier of the organization connection to be updated."
    ],
    updated_connection_types: Annotated[
        list[str], "A list of updated connection types for the organization connection."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateOrgConnections'."]:
    """Update an existing organization connection in Datadog.

    Use this tool to update details of an existing organization connection in Datadog. Useful for modifying connection configurations or settings."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"connection_types": updated_connection_types},
            "id": organization_connection_id,
            "type": org_connection_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/org_connections/{connection_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), connection_id=org_connection_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_permissions(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListPermissions'."]:
    """Retrieve all permissions from Datadog.

    Use this tool to get a comprehensive list of all available permissions, including their names, descriptions, and IDs, from Datadog's system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/permissions".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_findings(
    context: ToolContext,
    filter_by_evaluation_change_date: Annotated[
        str | None,
        "Specify a date (Unix ms) or date range (using comparison operators) to find results altered from pass to fail or vice versa.",  # noqa: E501
    ] = None,
    filter_by_resource_id: Annotated[
        str | None, "Return only findings for the specified resource ID."
    ] = None,
    filter_by_resource_type: Annotated[
        str | None,
        "Return findings only for the specified resource type. Use to narrow down results to specific resource types, such as 'aws', 'gcp', etc.",  # noqa: E501
    ] = None,
    filter_by_rule_id: Annotated[
        str | None, "Provide a specific rule ID to filter findings related to that rule."
    ] = None,
    filter_by_rule_name: Annotated[
        str | None,
        "Specify the rule name to return findings associated with it. This filters findings based on the provided rule name.",  # noqa: E501
    ] = None,
    filter_by_tags: Annotated[
        str | None,
        "Specify tags to filter findings. Use the format `tag_key:tag_value`. Supports multiple tags separated by commas.",  # noqa: E501
    ] = None,
    filter_discovery_timestamp: Annotated[
        str | None,
        "Return findings discovered at a specific time (Unix ms) or within a date range using comparison operators (e.g., `>`, `<=`).",  # noqa: E501
    ] = None,
    filter_evaluation_status: Annotated[
        str | None, "Specify to return only findings that are either 'pass' or 'fail'."
    ] = None,
    include_detailed_findings: Annotated[
        bool | None,
        "Set to true to retrieve additional fields like external ID, description, and IP addresses for some findings.",  # noqa: E501
    ] = None,
    max_findings_limit: Annotated[
        int | None, "Set the maximum number of findings to return, up to a limit of 1000."
    ] = 100,
    next_page_cursor: Annotated[
        str | None,
        "Use this to return the next page of findings starting from this cursor's position.",
    ] = None,
    return_muted_findings: Annotated[
        bool | None, "Set to `true` to return muted findings. Set to `false` to exclude them."
    ] = None,
    snapshot_timestamp: Annotated[
        int | None,
        "Specify the Unix timestamp (in milliseconds) to get findings for a specific snapshot of time.",  # noqa: E501
    ] = None,
    status_filter: Annotated[
        str | None,
        "Specify the status of findings to return: critical, high, medium, low, or info.",
    ] = None,
    vulnerability_type_filters: Annotated[
        list[str] | None,
        "A list of strings to filter findings by matching vulnerability types. Repeatable for multiple types.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListFindings'."]:
    """Retrieve a list of security findings from Datadog.

    Use this tool to obtain a list of security findings, including misconfigurations and identity risks from Datadog. You can apply filters to customize the search results and include additional details by specifying parameters. The response includes summary details of each finding along with pagination metadata."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/posture_management/findings".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[limit]": max_findings_limit,
            "snapshot_timestamp": snapshot_timestamp,
            "page[cursor]": next_page_cursor,
            "filter[tags]": filter_by_tags,
            "filter[evaluation_changed_at]": filter_by_evaluation_change_date,
            "filter[muted]": return_muted_findings,
            "filter[rule_id]": filter_by_rule_id,
            "filter[rule_name]": filter_by_rule_name,
            "filter[resource_type]": filter_by_resource_type,
            "filter[@resource_id]": filter_by_resource_id,
            "filter[discovery_timestamp]": filter_discovery_timestamp,
            "filter[evaluation]": filter_evaluation_status,
            "filter[status]": status_filter,
            "filter[vulnerability_type]": vulnerability_type_filters,
            "detailed_findings": include_detailed_findings,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_security_finding(
    context: ToolContext,
    finding_id: Annotated[str, "The unique ID of the security finding to retrieve details for."],
    snapshot_unix_timestamp: Annotated[
        int | None,
        "Return the finding for a specific snapshot in time, given in Unix milliseconds.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetFinding'."]:
    """Retrieve details of a specific security finding for analysis.

    This tool retrieves the message and resource configuration details of a specified security finding, helping in security analysis and incident response."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/posture_management/findings/{finding_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), finding_id=finding_id
        ),
        method="GET",
        params=remove_none_values({"snapshot_timestamp": snapshot_unix_timestamp}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_powerpacks(
    context: ToolContext,
    max_powerpacks_limit: Annotated[
        int | None, "Maximum number of powerpacks to include in the response."
    ] = 25,
    page_offset: Annotated[
        int | None,
        "The specific offset to start returning powerpacks from, allowing for pagination.",
    ] = 0,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListPowerpacks'."]:
    """Retrieve a list of all powerpacks from Datadog.

    Use this tool to get the complete list of powerpacks available in Datadog. It should be called when you need to access or display all the powerpacks."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/powerpacks".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[limit]": max_powerpacks_limit,
            "page[offset]": page_offset,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_powerpack(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreatePowerpack'."]:
    """Creates a new powerpack in Datadog.

    Use this tool to initiate a powerpack creation in Datadog when managing monitoring configurations is required.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEPOWERPACK_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATEPOWERPACK_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATEPOWERPACK_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/powerpacks".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEPOWERPACK_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_powerpack(
    context: ToolContext,
    powerpack_id: Annotated[str, "The unique identifier for the powerpack to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeletePowerpack'."]:
    """Delete a specified powerpack from Datadog.

    This tool deletes a powerpack in Datadog using the specified powerpack ID. Call this tool when you need to remove a powerpack from your Datadog account."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/powerpacks/{powerpack_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), powerpack_id=powerpack_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_powerpack(
    context: ToolContext,
    powerpack_identifier: Annotated[
        str, "The unique identifier for the desired powerpack, used to retrieve its details."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetPowerpack'."]:
    """Retrieve details of a specific powerpack.

    This tool should be called to obtain detailed information about a specific powerpack by providing its ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/powerpacks/{powerpack_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            powerpack_id=powerpack_identifier,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_powerpack(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    powerpack_id: Annotated[
        str | None,
        "The unique identifier for the powerpack to update in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdatePowerpack'."]:
    """Update the details of a specific powerpack in Datadog.

    Use this tool to modify the properties of an existing powerpack in Datadog by providing the specific powerpack ID.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEPOWERPACK_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not powerpack_id:
        missing_params.append(("powerpack_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATEPOWERPACK_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATEPOWERPACK_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/powerpacks/{powerpack_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), powerpack_id=powerpack_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEPOWERPACK_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_all_organization_processes(
    context: ToolContext,
    filter_by_tags: Annotated[
        str | None,
        "A comma-separated list of tags to filter the processes by. Use specific tags relevant to your organization's processes for targeted results.",  # noqa: E501
    ] = None,
    max_results_per_page: Annotated[
        int | None, "Maximum number of process results to return in one page."
    ] = 1000,
    pagination_cursor: Annotated[
        str | None,
        "String token to retrieve the next page of process results. Use the value from `meta.page.after` provided in the previous API response.",  # noqa: E501
    ] = None,
    query_window_end_timestamp: Annotated[
        int | None,
        "Unix timestamp (seconds since epoch) marking the end of the query window. Defaults to 15 minutes after 'from' if not provided. If 'from' and 'to' are omitted, defaults to 15 minutes from current time.",  # noqa: E501
    ] = None,
    query_window_start_timestamp: Annotated[
        int | None,
        "Unix timestamp marking the start of the query window. Defaults to 15 minutes before the end of the window if not provided.",  # noqa: E501
    ] = None,
    search_string_for_processes: Annotated[
        str | None, "String used to search and filter processes by specific criteria."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListProcesses'."]:
    """Retrieve all processes for your organization from Datadog.

    This tool is used to fetch a list of all processes associated with your organization in Datadog. It should be called when you need detailed information about the processes running across your organization's systems."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/processes".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "search": search_string_for_processes,
            "tags": filter_by_tags,
            "from": query_window_start_timestamp,
            "to": query_window_end_timestamp,
            "page[limit]": max_results_per_page,
            "page[cursor]": pagination_cursor,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def query_scalar_data(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'QueryScalarData'."]:
    """Query scalar values from diverse data sources.

    Use this tool to query scalar values from various data sources, with options to apply formulas and functions, ideal for insights in widgets like Query Value, Table, and Toplist.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["QUERYSCALARDATA_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["QUERYSCALARDATA_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["QUERYSCALARDATA_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/query/scalar".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["QUERYSCALARDATA_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def query_timeseries_data(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'QueryTimeseriesData'."]:
    """Query and process timeseries data from multiple sources.

    This tool queries timeseries data across various data sources and processes it by applying specified formulas and functions.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["QUERYTIMESERIESDATA_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["QUERYTIMESERIESDATA_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["QUERYTIMESERIESDATA_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/query/timeseries".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["QUERYTIMESERIESDATA_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_reference_tables(
    context: ToolContext,
    exact_table_name_filter: Annotated[
        str | None, "Filter by an exact table name match to retrieve specific reference tables."
    ] = None,
    filter_by_table_status: Annotated[
        str | None,
        "Filter tables by their status. Accepts status as a string, such as 'active', 'inactive', etc.",  # noqa: E501
    ] = None,
    filter_table_name_contains: Annotated[
        str | None, "Filter tables by name containing this substring."
    ] = None,
    number_of_tables_to_return: Annotated[
        int | None, "Specify the number of tables to return in the response. Use an integer value."
    ] = 15,
    pagination_offset: Annotated[int | None, "Number of tables to skip for pagination."] = 0,
    sort_field_and_direction: Annotated[
        str | None,
        'Specify the field to sort by and the direction. Use a field name for ascending, prefix with "-" for descending. Options include: updated_at, table_name, status, and their descending counterparts.',  # noqa: E501
    ] = "-updated_at",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListTables'."]:
    """Retrieve all reference tables in the organization.

    Use this tool to fetch a list of all reference tables available in the current organization. It provides an overview of the reference tables configured in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/reference-tables/tables".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "limit": number_of_tables_to_return,
            "offset": pagination_offset,
            "sort": sort_field_and_direction,
            "filter[status]": filter_by_table_status,
            "filter[table_name][exact]": exact_table_name_filter,
            "filter[table_name][contains]": filter_table_name_contains,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_reference_table(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateReferenceTable'."]:
    """Create a new reference table in Datadog.

    This tool creates a new reference table in Datadog. It can be called by either uploading CSV data to get an upload ID or by providing cloud storage access details. Use this when you need to set up a reference table with data from CSV files.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEREFERENCETABLE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEREFERENCETABLE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEREFERENCETABLE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/reference-tables/tables".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEREFERENCETABLE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_reference_table(
    context: ToolContext,
    reference_table_id: Annotated[
        str, "The unique identifier of the reference table to be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteTable'."]:
    """Delete a reference table by ID.

    Use this tool to delete a specific reference table by providing its ID. Useful for managing and cleaning up reference data in your Datadog environment."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/reference-tables/tables/{id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=reference_table_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_reference_table(
    context: ToolContext,
    reference_table_id: Annotated[
        str, "The unique ID of the reference table to retrieve details from Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTable'."]:
    """Retrieve details of a reference table by its ID.

    Use this tool to fetch information about a specific reference table in Datadog using its unique ID. This is helpful for accessing configuration or metadata details related to the table."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/reference-tables/tables/{id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=reference_table_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_reference_table(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    reference_table_id: Annotated[
        str | None,
        "The ID of the reference table that needs to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateReferenceTable'."]:
    """Update data, description, and tags of a reference table.

    This tool updates a reference table by its ID, allowing changes to the data, description, or tags. It is useful when modifications are needed for existing reference tables, including those with different source types like LOCAL_FILE, S3, GCS, or AZURE.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEREFERENCETABLE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not reference_table_id:
        missing_params.append(("reference_table_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEREFERENCETABLE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEREFERENCETABLE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/reference-tables/tables/{id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=reference_table_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEREFERENCETABLE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_table_rows_by_id(
    context: ToolContext,
    reference_table_id: Annotated[
        str, "The unique ID of the reference table to retrieve rows from."
    ],
    row_ids_to_retrieve: Annotated[
        list[str],
        "List of primary key values to specify which rows to retrieve from the reference table.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetRowsByID'."]:
    """Retrieve reference table rows using primary key values.

    Call this tool to access rows from a Datadog reference table by specifying their primary key values. It's useful for obtaining specific entries in the table."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/reference-tables/tables/{id}/rows".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=reference_table_id
        ),
        method="GET",
        params=remove_none_values({"row_id": row_ids_to_retrieve}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_reference_table_upload(
    context: ToolContext,
    file_upload_headers: Annotated[
        list[str] | None,
        "An array of strings representing the headers of the file to upload for the reference table.",  # noqa: E501
    ] = None,
    part_size_bytes: Annotated[
        int | None,
        "The size of each part in the upload in bytes. For multipart uploads (part_count > 1), all parts except the last one must be at least 5,000,000 bytes. For single-part uploads, any size is allowed.",  # noqa: E501
    ] = None,
    reference_table_name: Annotated[
        str | None, "The name of the reference table for the upload."
    ] = None,
    upload_id: Annotated[str | None, "The unique ID for the upload process in Datadog."] = None,
    upload_part_count: Annotated[
        int | None, "Specify the number of parts in the upload. Used for multipart uploads."
    ] = None,
    upload_resource_type: Annotated[
        str | None, "Specifies the resource type for the upload. Must be set to 'upload'."
    ] = "upload",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateReferenceTableUpload'."]:
    """Create a reference table upload for bulk data ingestion.

    Use this tool to initiate the creation of a reference table upload in Datadog, facilitating bulk data ingestion for analytics or monitoring purposes."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "headers": file_upload_headers,
                "part_count": upload_part_count,
                "part_size": part_size_bytes,
                "table_name": reference_table_name,
            },
            "id": upload_id,
            "type": upload_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/reference-tables/uploads".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_waf_custom_rules(
    context: ToolContext,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ListApplicationSecurityWAFCustomRules'."
]:
    """Retrieve a list of WAF custom rules.

    Use this tool to get a list of custom rules for the Web Application Firewall (WAF) in Datadog. It's useful for monitoring and managing security configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/asm/waf/custom_rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_waf_custom_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateApplicationSecurityWafCustomRule'."
]:
    """Create a new web application firewall custom rule.

    Use this tool to define a new custom rule for the web application firewall, enhancing security configurations.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEWAFCUSTOMRULE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEWAFCUSTOMRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEWAFCUSTOMRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/remote_config/products/asm/waf/custom_rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEWAFCUSTOMRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_waf_custom_rule(
    context: ToolContext,
    custom_rule_id: Annotated[
        str,
        "The unique identifier for the WAF custom rule to be deleted. Required for identifying the specific rule.",  # noqa: E501
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'DeleteApplicationSecurityWafCustomRule'."
]:
    """Delete a specific WAF custom rule by ID.

    Use this tool to delete a WAF custom rule using its unique identifier. Applicable when managing application security or modifying WAF configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/asm/waf/custom_rules/{custom_rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), custom_rule_id=custom_rule_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_waf_custom_rule_by_id(
    context: ToolContext,
    custom_rule_id: Annotated[
        str, "The unique identifier for the WAF custom rule to retrieve from Datadog."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'GetApplicationSecurityWafCustomRule'."
]:
    """Retrieve a WAF custom rule by ID from Datadog.

    Use this tool to obtain details of a specific Web Application Firewall (WAF) custom rule from Datadog by providing the rule's ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/asm/waf/custom_rules/{custom_rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), custom_rule_id=custom_rule_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_waf_custom_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    custom_rule_id: Annotated[
        str | None,
        "Specify the ID of the custom WAF rule to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'UpdateApplicationSecurityWafCustomRule'."
]:
    """Update a specific WAF custom rule in Datadog.

    Use this tool to update a specific Web Application Firewall (WAF) custom rule in Datadog. It returns the updated custom rule object upon successful execution.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEWAFCUSTOMRULE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not custom_rule_id:
        missing_params.append(("custom_rule_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEWAFCUSTOMRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEWAFCUSTOMRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/remote_config/products/asm/waf/custom_rules/{custom_rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), custom_rule_id=custom_rule_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEWAFCUSTOMRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_waf_exclusion_filters(
    context: ToolContext,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ListApplicationSecurityWafExclusionFilters'."
]:
    """Retrieve a list of WAF exclusion filters."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/asm/waf/exclusion_filters".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_waf_exclusion_filter(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateApplicationSecurityWafExclusionFilter'."
]:
    """Create a new WAF exclusion filter in Datadog.

    Use this tool to create a new Web Application Firewall (WAF) exclusion filter in Datadog. Exclusion filters help in preventing certain requests from being processed by the WAF, acting as passlist entries. Ideal for managing application security settings.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEWAFEXCLUSIONFILTER_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEWAFEXCLUSIONFILTER_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEWAFEXCLUSIONFILTER_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/remote_config/products/asm/waf/exclusion_filters".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEWAFEXCLUSIONFILTER_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_waf_exclusion_filter(
    context: ToolContext,
    waf_exclusion_filter_id: Annotated[
        str,
        "The unique identifier of the WAF exclusion filter to be deleted. Use this ID to specify which filter to remove.",  # noqa: E501
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'DeleteApplicationSecurityWafExclusionFilter'."
]:
    """Delete a WAF exclusion filter using its ID.

    Use this tool to delete a specific Web Application Firewall (WAF) exclusion filter by providing its identifier. Call this tool when you need to remove an unnecessary or outdated exclusion filter from the configuration."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/asm/waf/exclusion_filters/{exclusion_filter_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            exclusion_filter_id=waf_exclusion_filter_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_waf_exclusion_filter(
    context: ToolContext,
    waf_exclusion_filter_id: Annotated[
        str, "The unique identifier of the WAF exclusion filter to retrieve."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'GetApplicationSecurityWafExclusionFilter'."
]:
    """Retrieve a specific WAF exclusion filter by ID.

    Use this tool to obtain details about a specific Web Application Firewall (WAF) exclusion filter by providing its unique identifier."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/asm/waf/exclusion_filters/{exclusion_filter_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            exclusion_filter_id=waf_exclusion_filter_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_waf_exclusion_filter(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    waf_exclusion_filter_identifier: Annotated[
        str | None,
        "The unique identifier of the WAF exclusion filter to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'UpdateApplicationSecurityWafExclusionFilter'."
]:
    """Updates a WAF exclusion filter by its identifier.

    Use this tool to update a specific Web Application Firewall (WAF) exclusion filter using its ID. It returns the updated exclusion filter object upon a successful request.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEWAFEXCLUSIONFILTER_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not waf_exclusion_filter_identifier:
        missing_params.append(("waf_exclusion_filter_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEWAFEXCLUSIONFILTER_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEWAFEXCLUSIONFILTER_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/remote_config/products/asm/waf/exclusion_filters/{exclusion_filter_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            exclusion_filter_id=waf_exclusion_filter_identifier,
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEWAFEXCLUSIONFILTER_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_workload_protection_agent_rules(
    context: ToolContext,
    agent_policy_id: Annotated[
        str | None,
        "The ID of the Agent policy to retrieve the list of workload protection agent rules.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCSMThreatsAgentRules'."]:
    """Retrieve the list of Workload Protection agent rules.

    Use this tool to get the current list of agent rules for workload protection from Datadog. This is not available for the Government (US1-FED) site."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/cws/agent_rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({"policy_id": agent_policy_id}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_workload_protection_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCSMThreatsAgentRule'."]:
    """Create a new Workload Protection agent rule.

    Use this tool to create a new Workload Protection agent rule with specified parameters. Note that this endpoint is not available for the Government (US1-FED) site.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEWORKLOADPROTECTIONRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEWORKLOADPROTECTIONRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEWORKLOADPROTECTIONRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/remote_config/products/cws/agent_rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEWORKLOADPROTECTIONRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_workload_protection_agent_rule(
    context: ToolContext,
    agent_rule_identifier: Annotated[
        str, "The unique identifier for the Agent rule to be deleted."
    ],
    agent_policy_id: Annotated[
        str | None, "The unique identifier of the Workload Protection agent policy to delete."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCSMThreatsAgentRule'."]:
    """Delete a specific Workload Protection agent rule.

    This tool deletes a specified Workload Protection agent rule in Datadog, excluding the Government (US1-FED) site. Use this when needing to remove an existing agent rule by its unique identifier."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/cws/agent_rules/{agent_rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            agent_rule_id=agent_rule_identifier,
        ),
        method="DELETE",
        params=remove_none_values({"policy_id": agent_policy_id}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_threat_protection_agent_rule(
    context: ToolContext,
    agent_rule_id: Annotated[
        str, "The unique identifier for the specific Agent rule to retrieve details for."
    ],
    agent_policy_id: Annotated[
        str | None, "The ID of the agent policy to retrieve the specific rule for."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCSMThreatsAgentRule'."]:
    """Retrieve details of a specific Workload Protection agent rule.

    Use this tool to get information about a particular Workload Protection agent rule in Datadog. Note: This functionality is unavailable for the US1-FED site."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/cws/agent_rules/{agent_rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), agent_rule_id=agent_rule_id
        ),
        method="GET",
        params=remove_none_values({"policy_id": agent_policy_id}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_workload_protection_agent_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    agent_rule_id: Annotated[
        str | None,
        "The unique identifier of the specific Agent rule to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    agent_policy_id: Annotated[
        str | None,
        "The ID of the Agent policy to be updated in the Workload Protection Agent rule.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCSMThreatsAgentRule'."]:
    """Update a specific Workload Protection Agent rule.

    Use this tool to update a specific Workload Protection Agent rule. This is helpful when you need to modify existing rules for workload protection. Please note that this endpoint is not available for the Government (US1-FED) site.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEWORKLOADPROTECTIONAGENTRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not agent_rule_id:
        missing_params.append(("agent_rule_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEWORKLOADPROTECTIONAGENTRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEWORKLOADPROTECTIONAGENTRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/remote_config/products/cws/agent_rules/{agent_rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), agent_rule_id=agent_rule_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEWORKLOADPROTECTIONAGENTRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"policy_id": agent_policy_id}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_workload_protection_policies(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCSMThreatsAgentPolicies'."]:
    """Retrieve a list of Workload Protection policies from Datadog.

    This tool calls the Datadog API to retrieve a list of Workload Protection policies. It is used to obtain information about the configurations for workload security. Note: This endpoint is not available for the US1-FED site."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/cws/policy".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_workload_protection_policy(
    context: ToolContext,
    policy_name: Annotated[str, "The name of the workload protection policy to be created."],
    host_tags_configuration: Annotated[
        list[dict[str, str]] | None,
        "Host tags for policy deployment: inner values linked with AND, outer with OR.",
    ] = None,
    host_tags_for_policy_deployment: Annotated[
        list[str] | None,
        "List of host tags to define where the policy is deployed. Each tag must be a string.",
    ] = None,
    policy_description: Annotated[
        str | None, "Provide a description for the new workload protection policy."
    ] = None,
    policy_enabled: Annotated[
        bool | None, "Set to true to enable the workload protection policy."
    ] = None,
    resource_type: Annotated[
        str, "Specifies the type of the resource. Must always be set to 'policy'."
    ] = "policy",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCSMThreatsAgentPolicy'."]:
    """Create a new Workload Protection policy for cloud workloads.

    This tool allows you to create a new Workload Protection policy using specified parameters. It is used to enhance security for cloud workloads by defining protection policies. Note: This endpoint is not available for the Government (US1-FED) site."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "description": policy_description,
                "enabled": policy_enabled,
                "hostTags": host_tags_for_policy_deployment,
                "hostTagsLists": host_tags_configuration,
                "name": policy_name,
            },
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/cws/policy".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def download_csm_threats_policy(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DownloadCSMThreatsPolicy'."]:
    """Generate and download Workload Protection policy file.

    This tool generates a Workload Protection policy file based on active agent rules and downloads it as a `.policy` file. It is used to update the policy running in your environment. Note that this is not available for US1-FED site."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/cws/policy/download".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_workload_protection_policy(
    context: ToolContext,
    agent_policy_id: Annotated[str, "The unique ID of the agent policy to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCSMThreatsAgentPolicy'."]:
    """Delete a specific Workload Protection policy.

    Use this tool to remove a specified Workload Protection policy from Datadog. Note: Not available for the Government (US1-FED) site."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/cws/policy/{policy_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), policy_id=agent_policy_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_workload_protection_policy_details(
    context: ToolContext,
    agent_policy_id: Annotated[
        str, "The unique ID of the Workload Protection Agent policy to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCSMThreatsAgentPolicy'."]:
    """Get details of a specific Workload Protection policy.

    Fetches information about a specific Workload Protection policy from Datadog. Note that this endpoint is not available for the Government (US1-FED) site."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/cws/policy/{policy_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), policy_id=agent_policy_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_workload_protection_policy(
    context: ToolContext,
    agent_policy_id: Annotated[str, "The unique identifier for the Agent policy to update."],
    host_tags_conditions: Annotated[
        list[dict[str, str]] | None,
        "Array of host tags specifying policy deployment conditions. Inner values use AND logic, outer values use OR logic.",  # noqa: E501
    ] = None,
    host_tags_for_policy_deployment: Annotated[
        list[str] | None,
        "An array of strings representing the host tags defining where this policy is deployed.",
    ] = None,
    policy_description: Annotated[
        str | None,
        "A string that provides the description of the Workload Protection policy. This should explain the policy's purpose or key features.",  # noqa: E501
    ] = None,
    policy_enabled: Annotated[
        bool | None, "Indicates if the policy is enabled. Use true for enabled, false for disabled."
    ] = None,
    policy_id: Annotated[
        str | None, "The unique identifier of the Agent policy to be updated."
    ] = None,
    policy_name: Annotated[
        str | None, "The name of the Workload Protection policy to be updated."
    ] = None,
    resource_type: Annotated[str, "The type of the resource, always set to 'policy'."] = "policy",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCSMThreatsAgentPolicy'."]:
    """Update a specific Workload Protection policy in Datadog.

    This tool updates a specified Workload Protection policy using the Datadog API. It should be called when modifications to security policies are needed. Note that the endpoint is not available for the Government (US1-FED) site."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "description": policy_description,
                "enabled": policy_enabled,
                "hostTags": host_tags_for_policy_deployment,
                "hostTagsLists": host_tags_conditions,
                "name": policy_name,
            },
            "id": policy_id,
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/cws/policy/{policy_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), policy_id=agent_policy_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_pipelines(
    context: ToolContext,
    page_number: Annotated[
        int | None,
        "Specify the page number of pipelines to retrieve. Use this to navigate through the pages of results returned by the API.",  # noqa: E501
    ] = 0,
    page_size: Annotated[
        int | None, "Number of pipelines to return per page, up to a maximum of 100."
    ] = 10,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListPipelines'."]:
    """Retrieve a list of pipelines from Datadog.

    Use this tool to get a list of all pipelines available in the Datadog system. It should be called whenever you need information on existing pipelines."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/obs_pipelines/pipelines".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({"page[size]": page_size, "page[number]": page_number}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_pipeline(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreatePipeline'."]:
    """Create a new pipeline in Datadog's system.

    Use this tool to create a new pipeline within the Datadog service. It should be called when a user needs to set up or initialize a new data pipeline in their Datadog environment.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEPIPELINE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATEPIPELINE_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATEPIPELINE_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/remote_config/products/obs_pipelines/pipelines".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEPIPELINE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def validate_pipeline_config(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ValidatePipeline'."]:
    """Validate a pipeline configuration without making changes.

    Use this tool to check the correctness of a pipeline configuration in Datadog. It helps identify any potential errors without altering existing resources.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "VALIDATEPIPELINECONFIG_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["VALIDATEPIPELINECONFIG_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["VALIDATEPIPELINECONFIG_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/remote_config/products/obs_pipelines/pipelines/validate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["VALIDATEPIPELINECONFIG_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_data_pipeline(
    context: ToolContext,
    pipeline_identifier: Annotated[
        str, "The unique ID of the pipeline that you want to delete from Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeletePipeline'."]:
    """Deletes a data pipeline from the Datadog configuration.

    Use this tool to remove a specific data pipeline by its ID from the Datadog configuration. Useful for managing and maintaining pipeline configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/obs_pipelines/pipelines/{pipeline_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), pipeline_id=pipeline_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_specific_pipeline_by_id(
    context: ToolContext,
    pipeline_id: Annotated[str, "The unique ID of the pipeline to retrieve from Datadog."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetPipeline'."]:
    """Retrieve specific pipeline details by ID.

    Use this tool to get details of a specific pipeline by providing its ID. It fetches the configuration and status of the pipeline from Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/remote_config/products/obs_pipelines/pipelines/{pipeline_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), pipeline_id=pipeline_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_pipeline(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    pipeline_id: Annotated[
        str | None,
        "The unique ID of the pipeline to update in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdatePipeline'."]:
    """Update a pipeline in Datadog's remote config.

    Use this tool to update specific pipelines in Datadog's remote configuration system using the pipeline ID.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEPIPELINE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not pipeline_id:
        missing_params.append(("pipeline_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATEPIPELINE_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATEPIPELINE_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/remote_config/products/obs_pipelines/pipelines/{pipeline_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), pipeline_id=pipeline_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEPIPELINE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_restriction_policy(
    context: ToolContext,
    resource_identifier: Annotated[
        str,
        "Identifier formatted as `type:id` for the resource. Supported types: `dashboard`, `integration-service`, `integration-webhook`, `notebook`, and more.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteRestrictionPolicy'."]:
    """Delete a restriction policy for a specified resource.

    Use this tool to delete the restriction policy associated with a specific resource by providing its ID. It should be called when a policy needs to be removed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/restriction_policy/{resource_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), resource_id=resource_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def retrieve_resource_restriction_policy(
    context: ToolContext,
    resource_identifier: Annotated[
        str,
        "The ID of the resource, formatted as `type:id`. Supported types: `dashboard`, `integration-service`, `integration-webhook`, `notebook`, `reference-table`, `security-rule`, `slo`, `workflow`, `app-builder-app`, `connection`, `connection-group`, `rum-application`, `cross-org-connection`, `spreadsheet`, `on-call-schedule`, `on-call-escalation-policy`, `on-call-team-routing-rules`.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetRestrictionPolicy'."]:
    """Retrieve restriction policy for a specific resource.

    This tool is used to obtain the restriction policy linked to a particular resource by providing the resource ID. It should be called when you need to understand access restrictions or permissions associated with the resource."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/restriction_policy/{resource_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), resource_id=resource_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_restriction_policy(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    resource_identifier: Annotated[
        str | None,
        "Identifier of the resource, formatted as `type:id`. Includes supported types like `dashboard`, `integration-service`, `notebook`, and others.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    allow_self_lockout: Annotated[
        bool | None,
        "Set to true to allow admins to remove their own access from the resource. Default is false, preventing self-lockout.  Only used when mode is 'execute'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateRestrictionPolicy'."]:
    """Update the restriction policy for a Datadog resource.

    Use this tool to update the restriction policy associated with various Datadog resources, such as dashboards, integration services, notebooks, and more. You can specify the resource type and the new access permissions like viewer, editor, or others depending on the resource.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path, query parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path, query parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATERESTRICTIONPOLICY_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path, query parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not resource_identifier:
        missing_params.append(("resource_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATERESTRICTIONPOLICY_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATERESTRICTIONPOLICY_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/restriction_policy/{resource_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), resource_id=resource_identifier
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATERESTRICTIONPOLICY_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"allow_self_lockout": allow_self_lockout}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_user_roles(
    context: ToolContext,
    page_number: Annotated[int | None, "Specific page number to return."] = 0,
    page_size: Annotated[
        int | None, "The number of roles to return per page, with a maximum of 100."
    ] = 10,
    role_filter_string: Annotated[
        str | None, "Filter roles using a specific string to match their details."
    ] = None,
    role_id_filter: Annotated[
        str | None, "List of role IDs to filter roles by, supporting comma-separated values."
    ] = None,
    sort_roles_by: Annotated[
        str | None,
        "Sort roles based on a specified field. Use prefixes to set ascending or descending order, e.g., '-name' for descending by name.",  # noqa: E501
    ] = "name",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListRoles'."]:
    """Retrieve all roles from Datadog.

    This tool fetches all roles from Datadog, providing their names and unique identifiers. It should be called when there's a need to view or manage role information."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/roles".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "sort": sort_roles_by,
            "filter": role_filter_string,
            "filter[id]": role_id_filter,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_role(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateRole'."]:
    """Create a new role for your organization in Datadog.

    This tool should be called when you need to create a new organizational role within the Datadog system. It returns details about the newly created role, helping to manage access and permissions for users.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEROLE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATEROLE_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATEROLE_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/roles".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEROLE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_role_templates(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListRoleTemplates'."]:
    """Retrieve all role templates from Datadog.

    This tool retrieves a list of all role templates from Datadog. It should be called when you need to access or review the role templates defined within Datadog's system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/roles/templates".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def disable_role(
    context: ToolContext,
    role_identifier: Annotated[
        str, "The unique identifier for the role to be disabled in Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteRole'."]:
    """Disables a specified role within Datadog.

    Call this tool to disable a specific role in Datadog by providing its role ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/roles/{role_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), role_id=role_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_organization_role(
    context: ToolContext,
    role_id: Annotated[str, "The unique identifier of the role in the organization."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetRole'."]:
    """Retrieve details of a role using its role ID in the organization.

    This tool gets details of a specific role within an organization by using the role's unique ID. It is useful for obtaining role-specific information needed for organizational management or role auditing."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/roles/{role_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), role_id=role_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def edit_role(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    role_identifier: Annotated[
        str | None,
        "The unique identifier for the role to be edited.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateRole'."]:
    """Edit a role with administrator application keys.

    Use this tool to edit an existing role in Datadog. It's designed for administrators using application keys to make updates to specified roles.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["EDITROLE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not role_identifier:
        missing_params.append(("role_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["EDITROLE_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["EDITROLE_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/roles/{role_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), role_id=role_identifier
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["EDITROLE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def clone_existing_role(
    context: ToolContext,
    new_role_name: Annotated[str, "Name of the new role that is cloned from an existing role."],
    role_unique_identifier: Annotated[str, "The unique identifier of the role to be cloned."],
    role_type: Annotated[
        str, "Specifies the type of role for the clone operation. Must be 'roles'."
    ] = "roles",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CloneRole'."]:
    """Clone an existing role based on role ID.

    This tool allows cloning of an existing role by using its unique role ID. It should be called when you need to create a duplicate of an existing role with identical permissions."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"attributes": {"name": new_role_name}, "type": role_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/roles/{role_id}/clone".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), role_id=role_unique_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def remove_permission_from_role(
    context: ToolContext,
    role_identifier: Annotated[
        str, "The unique identifier for the role from which a permission will be removed."
    ],
    permission_id: Annotated[
        str | None, "ID of the permission to be removed from the specified role."
    ] = None,
    permission_resource_type: Annotated[
        str | None, "This should be set to 'permissions' to specify the permissions resource type."
    ] = "permissions",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemovePermissionFromRole'."]:
    """Removes a permission from a specified role in Datadog.

    Use this tool to remove a specific permission from a role in Datadog's system. Ideal for managing role permissions and access control."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"id": permission_id, "type": permission_resource_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/roles/{role_id}/permissions".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), role_id=role_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_role_permissions(
    context: ToolContext,
    role_identifier: Annotated[
        str, "The unique identifier for the role whose permissions need to be retrieved."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListRolePermissions'."]:
    """Retrieve all permissions for a specific role.

    Call this tool to get a list of all permissions associated with a particular role in Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/roles/{role_id}/permissions".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), role_id=role_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def add_permission_to_role(
    context: ToolContext,
    role_identifier: Annotated[
        str, "The unique identifier of the role to which the permission will be added."
    ],
    permission_id: Annotated[str | None, "ID of the permission to be added to the role."] = None,
    permissions_resource_type: Annotated[
        str | None, "The resource type for the permission, should be 'permissions'."
    ] = "permissions",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddPermissionToRole'."]:
    """Assigns a specific permission to a given role.

    Use this tool to add a permission to a specific role within Datadog. It should be called when you need to assign new permissions to a role to adjust user access and capabilities."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"id": permission_id, "type": permissions_resource_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/roles/{role_id}/permissions".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), role_id=role_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def remove_user_from_role(
    context: ToolContext,
    role_identifier: Annotated[str, "The unique identifier of the role to remove the user from."],
    user_identifier: Annotated[
        str, "The unique identifier representing the user to be removed from the role."
    ],
    user_resource_type: Annotated[
        str, "Specifies the resource type, which should be set as 'users'."
    ] = "users",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveUserFromRole'."]:
    """Remove a user from a specified role in Datadog.

    Use this tool to remove a specified user from a role in Datadog when updating user permissions or cleaning up role assignments."""  # noqa: E501
    request_data = remove_none_values({"data": {"id": user_identifier, "type": user_resource_type}})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/roles/{role_id}/users".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), role_id=role_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_role_users(
    context: ToolContext,
    role_identifier: Annotated[
        str, "The unique identifier of the role to list its associated users."
    ],
    page_number: Annotated[
        int | None, "The specific page number to return in the list of users."
    ] = 0,
    page_size: Annotated[int | None, "Size for a given page, with a maximum value of 100."] = 10,
    user_filter_string: Annotated[
        str | None, "Filter users by a specific string. Defaults to no filtering."
    ] = None,
    user_sort_order: Annotated[
        str | None,
        "Attribute to sort users by. Prefix with '-' for descending. Options: 'name', 'email', 'status'.",  # noqa: E501
    ] = "name",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListRoleUsers'."]:
    """Retrieve all users belonging to a specific role.

    Call this tool to obtain a list of users assigned to a particular role in Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/roles/{role_id}/users".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), role_id=role_identifier
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "sort": user_sort_order,
            "filter": user_filter_string,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def add_user_to_role(
    context: ToolContext,
    role_unique_identifier: Annotated[
        str, "The unique identifier for the role you want to assign the user to."
    ],
    user_unique_identifier: Annotated[
        str, "A unique identifier representing the user to be added to the role."
    ],
    users_resource_type: Annotated[
        str, "Specifies the type of resource as 'users'. Always use 'users'."
    ] = "users",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddUserToRole'."]:
    """Adds a user to a specific role in Datadog.

    Use this tool to add a user to a specific role in Datadog's system. It is called when you need to assign user permissions via roles."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"id": user_unique_identifier, "type": users_resource_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/roles/{role_id}/users".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), role_id=role_unique_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def aggregate_rum_events(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AggregateRUMEvents'."]:
    """Aggregate RUM events into computed metrics and timeseries.

    This tool aggregates Real User Monitoring (RUM) events into buckets of computed metrics and timeseries. It is used when there is a need to analyze and visualize RUM data effectively.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["AGGREGATERUMEVENTS_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["AGGREGATERUMEVENTS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["AGGREGATERUMEVENTS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/rum/analytics/aggregate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["AGGREGATERUMEVENTS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_rum_applications(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetRUMApplications'."]:
    """Retrieve all RUM applications within your organization from Datadog.

    This tool is used to list all the Real User Monitoring (RUM) applications configured in your organization to help monitor and analyze user interactions."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/applications".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_rum_application(
    context: ToolContext,
    rum_application_name: Annotated[str, "The name of the RUM application to be created."],
    product_analytics_retention_state: Annotated[
        str | None,
        "Set the retention policy for Product Analytics data from RUM events. Options are 'MAX' or 'NONE'.",  # noqa: E501
    ] = None,
    rum_application_creation_type: Annotated[
        str, "Specifies the type for creating a RUM application. Use `rum_application_create`."
    ] = "rum_application_create",
    rum_application_type: Annotated[
        str | None,
        "Specifies the type of the RUM application. Expected values: `browser`, `ios`, `android`, `react-native`, `flutter`, `roku`, `electron`, `unity`, `kotlin-multiplatform`.",  # noqa: E501
    ] = None,
    rum_event_processing_state: Annotated[
        str | None,
        "Configures which RUM events are processed and stored for the application. Accepted values are 'ALL', 'ERROR_FOCUSED_MODE', or 'NONE'.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateRUMApplication'."]:
    """Create a new RUM application within your organization.

    This tool is used to create a new RUM (Real User Monitoring) application in your organization using Datadog's API."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "name": rum_application_name,
                "product_analytics_retention_state": product_analytics_retention_state,
                "rum_event_processing_state": rum_event_processing_state,
                "type": rum_application_type,
            },
            "type": rum_application_creation_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/applications".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def order_rum_retention_filters(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    rum_application_id: Annotated[
        str | None,
        "The ID of the RUM application for which to order retention filters.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'OrderRetentionFilters'."]:
    """Order RUM retention filters for a RUM application.

    Use this tool to reorder the RUM retention filters for a specified RUM application. It returns the updated RUM retention filter objects without attributes when successful.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "ORDERRUMRETENTIONFILTERS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not rum_application_id:
        missing_params.append(("rum_application_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["ORDERRUMRETENTIONFILTERS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["ORDERRUMRETENTIONFILTERS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/rum/applications/{app_id}/relationships/retention_filters".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_id=rum_application_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ORDERRUMRETENTIONFILTERS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_rum_retention_filters(
    context: ToolContext,
    rum_application_id: Annotated[
        str, "The unique identifier for the RUM application to retrieve retention filters."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListRetentionFilters'."]:
    """Retrieve RUM retention filters for a specific application.

    Use this tool to obtain the list of RUM retention filters for a given RUM application by specifying its application ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/applications/{app_id}/retention_filters".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_id=rum_application_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_rum_retention_filter(
    context: ToolContext,
    rum_application_id: Annotated[
        str, "RUM application ID for which the retention filter is to be created."
    ],
    rum_event_type_filter: Annotated[
        str,
        "Specify the type of RUM events to filter. Options include: session, view, action, error, resource, long_task, vital.",  # noqa: E501
    ],
    rum_retention_filter_name: Annotated[
        str, "The name assigned to the RUM retention filter, used for identification."
    ],
    rum_retention_filter_sample_rate: Annotated[
        int, "The sample rate for a RUM retention filter, an integer between 0 and 100."
    ],
    enable_retention_filter: Annotated[
        bool | None, "Set true to enable the retention filter, false to disable it."
    ] = None,
    resource_type_for_retention: Annotated[
        str,
        "Specifies the resource type as 'retention_filters'. This value should always be 'retention_filters'.",  # noqa: E501
    ] = "retention_filters",
    rum_retention_filter_query: Annotated[
        str | None,
        "The query string that defines the filtering criteria for the RUM retention filter.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateRetentionFilter'."]:
    """Create a RUM retention filter for a RUM application.

    Use this tool to create a data retention filter for an RUM application, which controls the retention rules of your application's RUM data."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "enabled": enable_retention_filter,
                "event_type": rum_event_type_filter,
                "name": rum_retention_filter_name,
                "query": rum_retention_filter_query,
                "sample_rate": rum_retention_filter_sample_rate,
            },
            "type": resource_type_for_retention,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/applications/{app_id}/retention_filters".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), app_id=rum_application_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_rum_retention_filter(
    context: ToolContext,
    retention_filter_id: Annotated[
        str, "The ID of the retention filter to delete from the RUM application."
    ],
    rum_application_id: Annotated[
        str, "The ID of the RUM application from which to delete the retention filter."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteRetentionFilter'."]:
    """Deletes a RUM retention filter for a specific application.

    This tool deletes a specified RUM retention filter for a given application in Datadog. It should be called when you need to remove a retention filter from a RUM application to stop retaining specific types of data."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/applications/{app_id}/retention_filters/{rf_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            app_id=rum_application_id,
            rf_id=retention_filter_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_rum_retention_filter(
    context: ToolContext,
    retention_filter_id: Annotated[
        str, "The ID of the retention filter to retrieve for a RUM application."
    ],
    rum_application_id: Annotated[
        str,
        "The unique identifier for the RUM application. Required to fetch the retention filter.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetRetentionFilter'."]:
    """Retrieve a RUM retention filter for a RUM application.

    Fetches details of a specific RUM retention filter for a given RUM application based on its ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/applications/{app_id}/retention_filters/{rf_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            app_id=rum_application_id,
            rf_id=retention_filter_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_rum_retention_filter(
    context: ToolContext,
    filter_id: Annotated[str, "UUID of the retention filter to be updated."],
    retention_filter_id: Annotated[str, "The unique ID of the retention filter to be updated."],
    rum_application_id: Annotated[
        str,
        "The ID of the RUM application to update the retention filter. Required for identifying the application.",  # noqa: E501
    ],
    enable_retention_filter: Annotated[
        bool | None, "Set to true to enable the retention filter. Set to false to disable it."
    ] = None,
    filter_name: Annotated[str | None, "The name of the RUM retention filter to update."] = None,
    resource_type: Annotated[
        str, "Specifies the resource type for the retention filter. Must be 'retention_filters'."
    ] = "retention_filters",
    rum_event_type_filter: Annotated[
        str | None,
        "Specifies the type of RUM events to filter on, such as 'session', 'view', 'action', etc.",
    ] = None,
    rum_retention_filter_query: Annotated[
        str | None, "The query string used to define criteria for the RUM retention filter."
    ] = None,
    sample_rate: Annotated[
        int | None,
        "The sample rate for a RUM retention filter, an integer between 0 and 100, specifying the percentage of data to sample.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateRetentionFilter'."]:
    """Update a RUM retention filter for a RUM application.

    Use this tool to modify the retention filter settings of a RUM application. It returns the updated RUM retention filter details if the request succeeds."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "enabled": enable_retention_filter,
                "event_type": rum_event_type_filter,
                "name": filter_name,
                "query": rum_retention_filter_query,
                "sample_rate": sample_rate,
            },
            "id": filter_id,
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/applications/{app_id}/retention_filters/{rf_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            app_id=rum_application_id,
            rf_id=retention_filter_id,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_rum_application(
    context: ToolContext,
    rum_application_id: Annotated[
        str, "The unique identifier for the RUM application you wish to delete."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteRUMApplication'."]:
    """Deletes an existing RUM application in your organization.

    Use this tool to delete an existing Real User Monitoring (RUM) application. Called when you need to remove a RUM application from your Datadog organization."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/applications/{id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=rum_application_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_rum_application_by_id(
    context: ToolContext,
    rum_application_id: Annotated[
        str,
        "The ID of the RUM application to retrieve details for. This is a required string value.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetRUMApplication'."]:
    """Retrieve RUM application details by ID.

    Use this tool to obtain details about a RUM application within your organization by specifying its ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/applications/{id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=rum_application_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_rum_application(
    context: ToolContext,
    rum_app_id: Annotated[str, "The unique ID of the RUM application to update."],
    rum_application_id: Annotated[str, "The ID of the RUM application to update."],
    product_analytics_retention_state: Annotated[
        str | None,
        "Set the retention policy for Product Analytics data derived from RUM events. Accepted values: 'MAX', 'NONE'.",  # noqa: E501
    ] = None,
    rum_application_name: Annotated[
        str | None, "The name of the RUM application to be updated."
    ] = None,
    rum_application_type: Annotated[
        str | None,
        "Specify the type of RUM application. Valid options: `browser`, `ios`, `android`, `react-native`, `flutter`, `roku`, `electron`, `unity`, `kotlin-multiplatform`.",  # noqa: E501
    ] = None,
    rum_application_update_type: Annotated[
        str, "Specifies the RUM application update type. Allowed value is 'rum_application_update'."
    ] = "rum_application_update",
    rum_event_processing_state: Annotated[
        str | None,
        "Configures which RUM events are processed and stored. Accepts 'ALL', 'ERROR_FOCUSED_MODE', or 'NONE'.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateRUMApplication'."]:
    """Update settings of a specific RUM application by ID.

    Use this tool to modify the configuration or details of a RUM (Real User Monitoring) application within your organization by providing its ID."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "name": rum_application_name,
                "product_analytics_retention_state": product_analytics_retention_state,
                "rum_event_processing_state": rum_event_processing_state,
                "type": rum_application_type,
            },
            "id": rum_app_id,
            "type": rum_application_update_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/applications/{id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), id=rum_application_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_rum_metrics(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListRumMetrics'."]:
    """Retrieve configured RUM-based metrics and their definitions.

    Use this tool to obtain a detailed list of metrics related to real user monitoring (RUM), including their configurations and definitions."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/config/metrics".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_rum_metric(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateRumMetric'."]:
    """Create a metric based on RUM data.

    Use this tool to create a metric using your organization's RUM data via Datadog. It returns the metric object if successful.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATERUMMETRIC_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATERUMMETRIC_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATERUMMETRIC_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/rum/config/metrics".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATERUMMETRIC_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_rum_metric(
    context: ToolContext,
    rum_metric_id: Annotated[
        str, "The ID of the RUM-based metric to be deleted from your Datadog organization."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteRumMetric'."]:
    """Delete a specific RUM-based metric from your organization.

    Call this tool to remove a specific RUM-based metric using its ID from your Datadog organization."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/config/metrics/{metric_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_id=rum_metric_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_rum_metric(
    context: ToolContext,
    metric_identifier: Annotated[
        str,
        "The unique identifier for the RUM-based metric you want to retrieve from your organization.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetRumMetric'."]:
    """Retrieve a specific RUM-based metric for your organization.

    Call this tool to get detailed data about a specific Real User Monitoring (RUM) metric identified by its metric ID from your organization on Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/config/metrics/{metric_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_id=metric_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_rum_metric(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    metric_name: Annotated[
        str | None,
        "The name of the rum-based metric to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateRumMetric'."]:
    """Update a specific rum-based metric in your organization.

    Use this tool to update details of a specific rum-based metric within your organization's Datadog settings. Ideal for modifying existing metrics to adjust configurations.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATERUMMETRIC_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not metric_name:
        missing_params.append(("metric_name", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATERUMMETRIC_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATERUMMETRIC_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/rum/config/metrics/{metric_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), metric_id=metric_name
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATERUMMETRIC_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_rum_events(
    context: ToolContext,
    event_sort_order: Annotated[
        str | None,
        "Specify the order of events: 'timestamp' for ascending or '-timestamp' for descending.",
    ] = None,
    maximum_event_count: Annotated[
        int | None, "Specify the maximum number of events to retrieve in a single response."
    ] = 10,
    maximum_timestamp: Annotated[
        str | None, "Maximum timestamp for requested events in ISO 8601 format."
    ] = None,
    minimum_timestamp: Annotated[
        str | None,
        "The starting timestamp for the requested RUM events. Use a string format compatible with the API.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "Cursor for fetching the next set of paginated results in the queried RUM events.",
    ] = None,
    rum_search_query: Annotated[
        str | None, "Search query following RUM syntax for filtering events."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListRUMEvents'."]:
    """Retrieve RUM events matching a search query.

    This tool is used to list RUM (Real User Monitoring) events that match a specific search query. It's useful for retrieving your latest RUM events, and results are paginated to handle large datasets."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/events".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[query]": rum_search_query,
            "filter[from]": minimum_timestamp,
            "filter[to]": maximum_timestamp,
            "sort": event_sort_order,
            "page[cursor]": pagination_cursor,
            "page[limit]": maximum_event_count,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def search_rum_events(
    context: ToolContext,
    filter_max_time: Annotated[
        str | None,
        "Specify the maximum event time in ISO 8601 format, mathematical expressions, or milliseconds.",  # noqa: E501
    ] = "now",
    maximum_events_in_response: Annotated[
        int | None, "Specifies the maximum number of RUM events to return in the response."
    ] = 10,
    minimum_event_time: Annotated[
        str | None,
        "The minimum time for events in ISO 8601 format, math expressions, or milliseconds.",
    ] = "now-15m",
    pagination_cursor: Annotated[
        str | None, "Provide the cursor to fetch the next set of results from a previous query."
    ] = None,
    rum_search_query: Annotated[
        str | None, "The search query following the RUM search syntax to filter events."
    ] = "*",
    sort_order: Annotated[
        str | None,
        "Specify the sort order for events by timestamp. Use 'timestamp' for ascending order and '-timestamp' for descending order.",  # noqa: E501
    ] = None,
    time_offset_seconds: Annotated[
        int | None, "The time offset in seconds to apply to the query."
    ] = None,
    timezone: Annotated[
        str | None,
        "Specify the timezone as GMT, UTC, an offset (like UTC+1), or a Timezone Database identifier (like America/New_York).",  # noqa: E501
    ] = "UTC",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchRUMEvents'."]:
    """Search and filter RUM events based on a query.

    This tool retrieves RUM events that match a specified search query. It's useful for building complex filtering and search operations on RUM events. Results are paginated for easy navigation."""  # noqa: E501
    request_data = remove_none_values({
        "filter": {"from": minimum_event_time, "query": rum_search_query, "to": filter_max_time},
        "options": {"time_offset": time_offset_seconds, "timezone": timezone},
        "page": {"cursor": pagination_cursor, "limit": maximum_events_in_response},
        "sort": sort_order,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/rum/events/search".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def fetch_all_scorecard_outcomes(
    context: ToolContext,
    filter_by_rule_id: Annotated[str | None, "Filter outcomes based on specific rule ID."] = None,
    filter_by_rule_name: Annotated[
        str | None, "Filter outcomes based on a specific rule name."
    ] = None,
    filter_outcomes_by_service_name: Annotated[
        str | None,
        "Filter outcomes based on a specific service name. Provide the service name as a string.",
    ] = None,
    filter_rule_enabled: Annotated[
        bool | None,
        "Filter outcomes based on whether a rule is enabled (true) or disabled (false).",
    ] = None,
    include_rule_details: Annotated[
        str | None, "Specify if related rule details should be included in the response."
    ] = None,
    outcome_state_filter: Annotated[
        str | None,
        "Filter the scorecard outcomes by a specific state. Accepts a state value as a string.",
    ] = None,
    page_offset: Annotated[
        int | None,
        "Specific offset to use as the beginning of the returned page, represented as an integer.",
    ] = 0,
    page_size: Annotated[int | None, "The number of results per page, with a maximum of 100."] = 10,
    rule_fields_to_return: Annotated[
        str | None, "Specify which fields to return in the included rule details."
    ] = None,
    specified_outcome_values: Annotated[
        str | None,
        "Comma-separated list of specific outcome attributes to return. Limits the response to these attributes.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListScorecardOutcomes'."]:
    """Retrieve all rule outcomes from scorecards.

    Use this tool to fetch all outcomes from scorecards in Datadog. It's useful for obtaining detailed results of applied rules."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/scorecard/outcomes".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[offset]": page_offset,
            "include": include_rule_details,
            "fields[outcome]": specified_outcome_values,
            "fields[rule]": rule_fields_to_return,
            "filter[outcome][service_name]": filter_outcomes_by_service_name,
            "filter[outcome][state]": outcome_state_filter,
            "filter[rule][enabled]": filter_rule_enabled,
            "filter[rule][id]": filter_by_rule_id,
            "filter[rule][name]": filter_by_rule_name,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_scorecard_outcomes(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateScorecardOutcomesAsync'."]:
    """Update multiple scorecard rule outcomes in Datadog.

    Use this tool to update several scorecard rule outcomes in Datadog with a single request. Ideal for synchronizing or modifying multiple outcomes efficiently.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATESCORECARDOUTCOMES_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESCORECARDOUTCOMES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESCORECARDOUTCOMES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/scorecard/outcomes".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATESCORECARDOUTCOMES_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def set_service_rule_outcomes_batch(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateScorecardOutcomesBatch'."]:
    """Batch set multiple service-rule outcomes.

    This tool is used to set multiple service-rule outcomes in a single batched request to Datadog, making it efficient for updating multiple outcomes simultaneously.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "SETSERVICERULEOUTCOMESBATCH_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["SETSERVICERULEOUTCOMESBATCH_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["SETSERVICERULEOUTCOMESBATCH_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/scorecard/outcomes/batch".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SETSERVICERULEOUTCOMESBATCH_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def fetch_scorecard_rules(
    context: ToolContext,
    filter_custom_rules_only: Annotated[
        bool | None, "Set to true to include only custom rules in the results."
    ] = None,
    filter_for_enabled_rules: Annotated[
        bool | None, "Set to true to filter for enabled rules only."
    ] = None,
    filter_rule_by_id: Annotated[
        str | None, "Filter the rules based on a specific rule ID."
    ] = None,
    filter_rule_description: Annotated[
        str | None,
        "Filter rules based on their description. Provide a string to match against the rule descriptions.",  # noqa: E501
    ] = None,
    filter_rules_by_name: Annotated[
        str | None, "Specify a rule name to filter the scorecard rules."
    ] = None,
    include_scorecard_details: Annotated[
        str | None, "Specify related scorecard details to include in the response."
    ] = None,
    page_offset: Annotated[
        int | None,
        "Specific offset to use as the beginning of the returned page for fetching scorecard rules.",  # noqa: E501
    ] = 0,
    page_size: Annotated[
        int | None, "Number of rules to return per page. Maximum value is 100."
    ] = 10,
    specific_rule_fields: Annotated[
        str | None,
        "Specify which rule fields to include in the response. Provide a comma-separated list of field names.",  # noqa: E501
    ] = None,
    specific_scorecard_fields: Annotated[
        str | None,
        "Specify which fields to include in the response for scorecard attributes. Use comma-separated values.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListScorecardRules'."]:
    """Fetch all scorecard rules from Datadog.

    Use this tool to retrieve a list of all scorecard rules from Datadog's API. This is useful for managing and reviewing rules related to scorecards."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/scorecard/rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[offset]": page_offset,
            "include": include_scorecard_details,
            "filter[rule][id]": filter_rule_by_id,
            "filter[rule][enabled]": filter_for_enabled_rules,
            "filter[rule][custom]": filter_custom_rules_only,
            "filter[rule][name]": filter_rules_by_name,
            "filter[rule][description]": filter_rule_description,
            "fields[rule]": specific_rule_fields,
            "fields[scorecard]": specific_scorecard_fields,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_scorecard_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateScorecardRule'."]:
    """Create a new scorecard rule in Datadog.

    Use this tool to create a new scorecard rule in Datadog when you need to automate monitoring tasks or enforce policies. Ideal for setting up performance metrics or compliance checks.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATESCORECARDRULE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESCORECARDRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESCORECARDRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/scorecard/rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATESCORECARDRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_scorecard_rule(
    context: ToolContext,
    rule_id: Annotated[str, "The ID of the scorecard rule to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteScorecardRule'."]:
    """Deletes a scorecard rule by its ID.

    Use this tool to delete a specific rule in a scorecard by providing its ID. This is useful for managing and updating scorecard configurations by removing obsolete or incorrect rules."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/scorecard/rules/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), rule_id=rule_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_scorecard_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    rule_id: Annotated[
        str | None,
        "A unique identifier for the scorecard rule to be updated in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateScorecardRule'."]:
    """Updates an existing scorecard rule in Datadog.

    Use this tool to modify an existing scorecard rule in the Datadog platform by specifying the rule ID and the new details for the rule.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATESCORECARDRULE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not rule_id:
        missing_params.append(("rule_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESCORECARDRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESCORECARDRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/scorecard/rules/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), rule_id=rule_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATESCORECARDRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def download_cloud_workload_policy(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DownloadCloudWorkloadPolicyFile'."]:
    """Downloads a Workload Protection policy file for agents.

    Generates and downloads a Workload Protection policy file from active agent rules, specifically for the US1-FED site. This file is used to update the policy on your agents."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security/cloud_workload/policy/download".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_cloud_workload_security_agent_rules(
    context: ToolContext,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ListCloudWorkloadSecurityAgentRules'."
]:
    """Retrieve the list of cloud workload security agent rules.

    Use this tool to get a list of agent rules specifically for the Government (US1-FED) site."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/cloud_workload_security/agent_rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_cloud_workload_security_agent_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateCloudWorkloadSecurityAgentRule'."
]:
    """Create a new cloud workload security agent rule.

    This tool creates a new security agent rule for cloud workload monitoring. It should be called when there is a need to set up or update security rules specifically for the Government (US1-FED) site.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATECLOUDWORKLOADSECURITYAGENTRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS[
                        "CREATECLOUDWORKLOADSECURITYAGENTRULE_REQUEST_BODY_SCHEMA"
                    ],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS[
                        "CREATECLOUDWORKLOADSECURITYAGENTRULE_REQUEST_BODY_SCHEMA"
                    ],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/security_monitoring/cloud_workload_security/agent_rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATECLOUDWORKLOADSECURITYAGENTRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_cloud_workload_security_agent_rule(
    context: ToolContext,
    agent_rule_identifier: Annotated[
        str, "The unique identifier for the specific agent rule to delete."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'DeleteCloudWorkloadSecurityAgentRule'."
]:
    """Delete a specific cloud workload security agent rule.

    This tool deletes a specific cloud workload security agent rule in the Datadog system, applicable only for the Government (US1-FED) site."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/cloud_workload_security/agent_rules/{agent_rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            agent_rule_id=agent_rule_identifier,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_cloud_workload_security_agent_rule_details(
    context: ToolContext,
    agent_rule_identifier: Annotated[
        str, "Unique identifier for the cloud workload security agent rule to retrieve details."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'GetCloudWorkloadSecurityAgentRule'."
]:
    """Retrieve details of a cloud workload security agent rule.

    Use this tool to get information about a specific security agent rule in a cloud workload. It's specifically for the Datadog Government (US1-FED) site."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/cloud_workload_security/agent_rules/{agent_rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            agent_rule_id=agent_rule_identifier,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_cloud_workload_security_agent_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    agent_rule_identifier: Annotated[
        str | None,
        "The unique identifier for the agent rule to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'UpdateCloudWorkloadSecurityAgentRule'."
]:
    """Update a specific cloud workload security agent rule.

    Use this tool to update a particular security agent rule for cloud workloads. This should only be used for the Government (US1-FED) site and returns the updated agent rule object upon success.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATECLOUDWORKLOADSECURITYAGENTRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not agent_rule_identifier:
        missing_params.append(("agent_rule_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS[
                        "UPDATECLOUDWORKLOADSECURITYAGENTRULE_REQUEST_BODY_SCHEMA"
                    ],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS[
                        "UPDATECLOUDWORKLOADSECURITYAGENTRULE_REQUEST_BODY_SCHEMA"
                    ],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/security_monitoring/cloud_workload_security/agent_rules/{agent_rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            agent_rule_id=agent_rule_identifier,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATECLOUDWORKLOADSECURITYAGENTRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_security_filters(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListSecurityFilters'."]:
    """Retrieve configured security filters from Datadog.

    Use this tool to get a list of all configured security filters along with their definitions. It is useful for monitoring and managing security configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/security_filters".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_security_filter(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateSecurityFilter'."]:
    """Create a security filter using Datadog's API.

    Use this tool to create a security filter for monitoring purposes in Datadog. Ideal for setting up security monitoring based on predefined conditions.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATESECURITYFILTER_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESECURITYFILTER_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESECURITYFILTER_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/security_filters".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATESECURITYFILTER_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_security_filter(
    context: ToolContext,
    security_filter_id: Annotated[str, "The ID of the security filter to delete in Datadog."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteSecurityFilter'."]:
    """Delete a specific security filter in Datadog.

    Use this tool to delete a specified security filter by its ID in Datadog's security monitoring configuration."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/security_filters/{security_filter_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            security_filter_id=security_filter_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_security_filter_details(
    context: ToolContext,
    security_filter_id: Annotated[
        str, "The unique identifier for the security filter to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSecurityFilter'."]:
    """Retrieve the details of a specific security filter.

    Use this tool to get detailed information about a security filter in Datadog's security monitoring configuration. Ideal for understanding specific filter configurations and settings."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/security_filters/{security_filter_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            security_filter_id=security_filter_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_security_filter(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    security_filter_id: Annotated[
        str | None,
        "The ID of the specific security filter to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateSecurityFilter'."]:
    """Update a specific security filter's configuration.

    Use this tool to update the configuration of a specific security filter in Datadog. It returns the updated security filter object when successful.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATESECURITYFILTER_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not security_filter_id:
        missing_params.append(("security_filter_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESECURITYFILTER_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESECURITYFILTER_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/security_filters/{security_filter_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            security_filter_id=security_filter_id,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATESECURITYFILTER_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_suppression_rules(
    context: ToolContext,
    suppression_query_string: Annotated[
        str | None, "A query string to filter suppression rules. Use to specify search criteria."
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ListSecurityMonitoringSuppressions'."
]:
    """Retrieve the list of security monitoring suppression rules.

    This tool retrieves all the suppression rules configured in Datadog's security monitoring system. Use this tool to view the current set of rules that suppress certain alerts."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/suppressions".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({"query": suppression_query_string}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_suppression_rule(
    context: ToolContext,
    enable_suppression_rule: Annotated[
        bool, "Enable the suppression rule. Use true to enable, false to disable."
    ],
    rule_query: Annotated[
        str, "The rule criteria for the suppression rule using detection rules syntax."
    ],
    suppression_rule_name: Annotated[str, "The name of the suppression rule to be created."],
    data_exclusion_query: Annotated[
        str | None,
        "An exclusion query for input data to ignore events in suppression rules, applicable to logs, Agent events, etc.",  # noqa: E501
    ] = None,
    expiration_date_unix_ms: Annotated[
        int | None,
        "A Unix millisecond timestamp for rule expiration. After this date, the rule will not suppress signals.",  # noqa: E501
    ] = None,
    resource_type: Annotated[
        str, "The type of the resource, which should always be `suppressions`."
    ] = "suppressions",
    start_date_timestamp: Annotated[
        int | None,
        "A Unix millisecond timestamp indicating when the suppression rule begins to suppress signals.",  # noqa: E501
    ] = None,
    suppression_query: Annotated[
        str | None,
        "The query used to suppress signals in the security rule. Matches are not triggered.",
    ] = None,
    suppression_rule_description: Annotated[
        str | None,
        "A description for the suppression rule. Provide a clear and concise explanation of the rule's purpose.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateSecurityMonitoringSuppression'."
]:
    """Create a new security monitoring suppression rule.

    Use this tool to create a new suppression rule in Datadog's security monitoring. It should be called when you need to suppress specific security alerts."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "data_exclusion_query": data_exclusion_query,
                "description": suppression_rule_description,
                "enabled": enable_suppression_rule,
                "expiration_date": expiration_date_unix_ms,
                "name": suppression_rule_name,
                "rule_query": rule_query,
                "start_date": start_date_timestamp,
                "suppression_query": suppression_query,
            },
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/suppressions".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_future_rule_suppressions(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'GetSuppressionsAffectingFutureRule'."
]:
    """Retrieve suppressions affecting a future security rule.

    Use this tool to get a list of suppressions that would impact a specified security monitoring rule in the future.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "GETFUTURERULESUPPRESSIONS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["GETFUTURERULESUPPRESSIONS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["GETFUTURERULESUPPRESSIONS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/suppressions/rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["GETFUTURERULESUPPRESSIONS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_suppressions_for_rule(
    context: ToolContext,
    rule_id: Annotated[
        str, "The unique identifier of the specific rule for which to retrieve suppressions."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSuppressionsAffectingRule'."]:
    """Retrieve suppressions affecting a specific rule by ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/suppressions/rules/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), rule_id=rule_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def validate_suppression_rule(
    context: ToolContext,
    is_suppression_rule_enabled: Annotated[
        bool, "Indicates whether the suppression rule is currently active."
    ],
    suppression_rule_name: Annotated[str, "The name of the suppression rule to be validated."],
    suppression_rule_query: Annotated[
        str, "The rule query for the suppression rule, using detection rules search bar syntax."
    ],
    exclusion_query_on_input_data: Annotated[
        str | None,
        "An exclusion query for input data, such as logs or events. Events matching this are ignored by detection rules in the suppression rule.",  # noqa: E501
    ] = None,
    resource_type: Annotated[
        str, "Defines the type of the resource. Always set to `suppressions`."
    ] = "suppressions",
    suppression_query: Annotated[
        str | None,
        "The query for the suppression rule. Signals matching this query are suppressed, using Signals Explorer syntax.",  # noqa: E501
    ] = None,
    suppression_rule_description: Annotated[
        str | None,
        "A text description of the suppression rule, explaining its purpose and details.",
    ] = None,
    suppression_rule_expiration_date: Annotated[
        int | None,
        "A Unix millisecond timestamp for when the suppression rule expires and stops suppressing signals.",  # noqa: E501
    ] = None,
    suppression_rule_start_date: Annotated[
        int | None,
        "Unix millisecond timestamp for the start date of the suppression rule, when it begins suppressing signals.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ValidateSecurityMonitoringSuppression'."
]:
    """Validate a suppression rule in Datadog's monitoring system.

    This tool validates suppression rules in Datadog's security monitoring configuration. Use it to ensure rules are correctly set up and functioning as intended."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "data_exclusion_query": exclusion_query_on_input_data,
                "description": suppression_rule_description,
                "enabled": is_suppression_rule_enabled,
                "expiration_date": suppression_rule_expiration_date,
                "name": suppression_rule_name,
                "rule_query": suppression_rule_query,
                "start_date": suppression_rule_start_date,
                "suppression_query": suppression_query,
            },
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/suppressions/validation".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_suppression_rule(
    context: ToolContext,
    suppression_rule_id: Annotated[
        str, "The unique identifier of the suppression rule to be deleted in Datadog."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'DeleteSecurityMonitoringSuppression'."
]:
    """Delete a specific suppression rule in Datadog.

    Use this tool to delete a specific suppression rule from Datadog's security monitoring configuration. Call it when you need to remove an existing suppression rule using its ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/suppressions/{suppression_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            suppression_id=suppression_rule_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_suppression_rule_details(
    context: ToolContext,
    suppression_rule_id: Annotated[
        str, "The unique ID of the suppression rule you wish to retrieve details for."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'GetSecurityMonitoringSuppression'."
]:
    """Get details of a specific security suppression rule.

    Fetch the details of a suppression rule by providing the suppression ID. Useful for understanding the configuration and parameters of a specific rule in the security monitoring system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/suppressions/{suppression_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            suppression_id=suppression_rule_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_suppression_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    suppression_rule_id: Annotated[
        str | None,
        "The unique identifier of the suppression rule to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'UpdateSecurityMonitoringSuppression'."
]:
    """Update a specific suppression rule in Datadog.

    Use this tool to modify an existing suppression rule within Datadog's security monitoring configuration. Call this when you need to change the settings of a particular suppression rule identified by its ID.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATESUPPRESSIONRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not suppression_rule_id:
        missing_params.append(("suppression_rule_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESUPPRESSIONRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESUPPRESSIONRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/security_monitoring/configuration/suppressions/{suppression_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            suppression_id=suppression_rule_id,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATESUPPRESSIONRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_security_monitoring_rules(
    context: ToolContext,
    page_number: Annotated[
        int | None, "The specific page number to return when listing the security monitoring rules."
    ] = 0,
    page_size: Annotated[
        int | None, "Size for a given page. The maximum allowed value is 100. Use an integer value."
    ] = 10,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListSecurityMonitoringRules'."]:
    """Retrieve a list of security monitoring rules.

    Use this tool to get a list of security monitoring rules from Datadog. Call it when you need to view or manage security monitoring rules."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({"page[size]": page_size, "page[number]": page_number}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_security_monitoring_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateSecurityMonitoringRule'."]:
    """Create a detection rule for monitoring security events.

    This tool is used to create a new detection rule for monitoring security events through Datadog. It involves setting up criteria and policies to trigger alerts for specific security-related incidents.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATESECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/security_monitoring/rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATESECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def convert_rule_json_to_terraform(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[
    dict[str, Any],
    "Response from the API endpoint 'ConvertSecurityMonitoringRuleFromJSONToTerraform'.",
]:
    """Converts Datadog security rules from JSON to Terraform format.

    Use this tool to convert a Datadog security monitoring rule from JSON format to Terraform format. It is helpful when you need to transform security rules for infrastructure as code purposes.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CONVERTRULEJSONTOTERRAFORM_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CONVERTRULEJSONTOTERRAFORM_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CONVERTRULEJSONTOTERRAFORM_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/security_monitoring/rules/convert".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CONVERTRULEJSONTOTERRAFORM_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def test_security_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'TestSecurityMonitoringRule'."]:
    """Test a security monitoring rule.

    Use this tool to test a security monitoring rule within Datadog's system. It should be called when you need to verify the effectiveness or functionality of a specific rule.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["TESTSECURITYRULE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["TESTSECURITYRULE_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["TESTSECURITYRULE_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/security_monitoring/rules/test".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["TESTSECURITYRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def validate_security_monitoring_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ValidateSecurityMonitoringRule'."]:
    """Validate a detection rule in Datadog.

    Use this tool to validate the configuration of a detection rule in Datadog's security monitoring. It checks if the rule meets all necessary criteria and returns whether it is valid.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "VALIDATESECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["VALIDATESECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["VALIDATESECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/security_monitoring/rules/validation".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["VALIDATESECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_security_monitoring_rule(
    context: ToolContext,
    security_rule_id: Annotated[
        str,
        "The unique identifier of the security rule to be deleted. Default rules cannot be deleted.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteSecurityMonitoringRule'."]:
    """Delete an existing security monitoring rule.

    Use this tool to delete a specified security monitoring rule in Datadog. Note that default rules cannot be deleted."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/rules/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), rule_id=security_rule_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_security_monitoring_rule_details(
    context: ToolContext,
    security_rule_id: Annotated[
        str,
        "The unique identifier for the security monitoring rule you want to retrieve details for.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSecurityMonitoringRule'."]:
    """Retrieve detailed information about a specific security rule.

    Use this tool to get comprehensive details about a security monitoring rule by specifying its ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/rules/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), rule_id=security_rule_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_security_monitoring_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    security_rule_id: Annotated[
        str | None,
        "The ID of the security monitoring rule to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateSecurityMonitoringRule'."]:
    """Update an existing Datadog security monitoring rule.

    Use this tool to update an existing security monitoring rule in Datadog. Ensure all 'cases', 'queries', and 'options' fields are included when making updates. Default rules can only have specific updates allowed.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATESECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not security_rule_id:
        missing_params.append(("security_rule_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/security_monitoring/rules/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), rule_id=security_rule_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATESECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def convert_security_rule_to_terraform(
    context: ToolContext,
    rule_id: Annotated[str, "The ID of the Datadog security monitoring rule to convert."],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ConvertExistingSecurityMonitoringRule'."
]:
    """Convert existing security rule from JSON to Terraform.

    This tool converts an existing Datadog security monitoring rule from its JSON format to a Terraform configuration. Use it to facilitate the integration and management of your rules using Terraform."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/rules/{rule_id}/convert".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), rule_id=rule_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def test_security_monitoring_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    security_monitoring_rule_id: Annotated[
        str | None,
        "The ID of the existing security monitoring rule to test in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'TestExistingSecurityMonitoringRule'."
]:
    """Test an existing security monitoring rule in Datadog.

    Use this tool to test an existing security monitoring rule in Datadog by specifying the rule ID. It will return the results of the test, helping to ensure the rule is functioning as expected.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "TESTSECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not security_monitoring_rule_id:
        missing_params.append(("security_monitoring_rule_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["TESTSECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["TESTSECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/security_monitoring/rules/{rule_id}/test".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            rule_id=security_monitoring_rule_id,
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["TESTSECURITYMONITORINGRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_rule_version_history(
    context: ToolContext,
    rule_id: Annotated[
        str, "The unique identifier for the rule. Required to fetch its version history in Datadog."
    ],
    page_number: Annotated[int | None, "The specific page number to return in the results."] = 0,
    page_size: Annotated[int | None, "Size for a given page, maximum value is 100."] = 10,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetRuleVersionHistory'."]:
    """Retrieve a rule's version history.

    Use this tool to obtain the version history of a specific security monitoring rule by its ID in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/rules/{rule_id}/version_history".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), rule_id=rule_id
        ),
        method="GET",
        params=remove_none_values({"page[size]": page_size, "page[number]": page_number}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_security_signals(
    context: ToolContext,
    cursor_based_pagination: Annotated[
        str | None, "Cursor for pagination to continue retrieving results from a previous query."
    ] = None,
    max_security_signals_response: Annotated[
        int | None, "Specify the maximum number of security signals to return in the response."
    ] = 10,
    max_timestamp_for_security_signals: Annotated[
        str | None, "Specify the maximum timestamp for retrieving security signals."
    ] = None,
    minimum_timestamp: Annotated[
        str | None, "The minimum timestamp to filter security signals. Format: ISO 8601 string."
    ] = None,
    result_sort_order: Annotated[
        str | None,
        "Specify the sort order for the security signals. Use 'timestamp' for ascending order, '-timestamp' for descending order.",  # noqa: E501
    ] = None,
    search_query_for_security_signals: Annotated[
        str | None, "The search query string used to filter security signals from Datadog."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListSecurityMonitoringSignals'."]:
    """Retrieve security signals that match a search query.

    This tool retrieves a list of security signals from Datadog based on a search query, using the GET method for the security monitoring signals endpoint."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/signals".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[query]": search_query_for_security_signals,
            "filter[from]": minimum_timestamp,
            "filter[to]": max_timestamp_for_security_signals,
            "sort": result_sort_order,
            "page[cursor]": cursor_based_pagination,
            "page[limit]": max_security_signals_response,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def search_security_signals(
    context: ToolContext,
    maximum_signals_per_response: Annotated[
        int | None, "The maximum number of security signals to return in the response."
    ] = 10,
    maximum_timestamp_for_signals: Annotated[
        str | None,
        "The latest date and time for security signals to be included in the search results, formatted as a string.",  # noqa: E501
    ] = None,
    minimum_timestamp: Annotated[
        str | None,
        "The minimum timestamp for requested security signals. Use ISO 8601 format, e.g., '2023-10-05T14:48:00Z'.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "The cursor to continue listing results from the previous query. Use it for paginating results.",  # noqa: E501
    ] = None,
    search_query: Annotated[
        str | None,
        "A string used to search and filter the security signals based on specific criteria.",
    ] = None,
    sort_order: Annotated[
        str | None,
        "Specify how to sort the security signals. Use 'timestamp' for ascending and '-timestamp' for descending order.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchSecurityMonitoringSignals'."]:
    """Retrieve security signals based on a search query.

    Use this tool to find security signals that match specific search criteria. Ideal for monitoring security alerts and potential threats."""  # noqa: E501
    request_data = remove_none_values({
        "filter": {
            "from": minimum_timestamp,
            "query": search_query,
            "to": maximum_timestamp_for_signals,
        },
        "page": {"cursor": pagination_cursor, "limit": maximum_signals_per_response},
        "sort": sort_order,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/signals/search".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_security_signal_details(
    context: ToolContext,
    signal_id: Annotated[
        str,
        "The unique identifier for the security monitoring signal to retrieve details for. This ID is used to specify which signal's details to fetch.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSecurityMonitoringSignal'."]:
    """Retrieve details of a security monitoring signal.

    This tool is used to obtain detailed information about a specific security monitoring signal in Datadog. It should be called when you need to understand the context or specifics of a particular signal identified by its unique signal ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/signals/{signal_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), signal_id=signal_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def edit_signal_assignee(
    context: ToolContext,
    assignee_uuid: Annotated[
        str, "UUID assigned by Datadog to identify the user account for the signal's assignee."
    ],
    signal_id: Annotated[
        str, "The unique identifier for the security signal to modify its assignee."
    ],
    assignee_gravatar_icon: Annotated[
        str | None, "URL for the Gravatar icon associated with the user account."
    ] = None,
    assignee_name: Annotated[
        str | None, "The name for the user account to be assigned the security signal."
    ] = None,
    signal_version_number: Annotated[
        int | None,
        "Integer representing the version of the updated signal. If the server-side version is higher, the update will be rejected.",  # noqa: E501
    ] = None,
    user_account_handle: Annotated[
        str | None, "The handle for the user account to be assigned the security signal."
    ] = None,
    user_account_numerical_id: Annotated[
        int | None,
        "Numerical ID assigned by Datadog to the user account. Required for identifying the assignee.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'EditSecurityMonitoringSignalAssignee'."
]:
    """Modify the triage assignee of a security signal.

    Use this tool to change the person assigned to handle a security monitoring signal in Datadog. This is helpful when reassigning tasks to different team members for better workload distribution or expertise alignment."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "assignee": {
                    "handle": user_account_handle,
                    "icon": assignee_gravatar_icon,
                    "id": user_account_numerical_id,
                    "name": assignee_name,
                    "uuid": assignee_uuid,
                },
                "version": signal_version_number,
            }
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/signals/{signal_id}/assignee".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), signal_id=signal_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def edit_security_signal_incidents(
    context: ToolContext,
    incident_ids: Annotated[
        list[int], "An array of incident IDs to associate with the security signal."
    ],
    signal_id: Annotated[str, "The unique identifier for the security signal to modify."],
    signal_version: Annotated[
        int | None,
        "Version of the updated signal. Ensure the client-side version is not lower than the server-side version to avoid rejection.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'EditSecurityMonitoringSignalIncidents'."
]:
    """Modify incidents linked to a security signal.

    This tool allows for updating the incidents related to a specific security monitoring signal. Use it when you need to change how a security signal is associated with incidents."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"attributes": {"incident_ids": incident_ids, "version": signal_version}}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/signals/{signal_id}/incidents".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), signal_id=signal_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def change_signal_state(
    context: ToolContext,
    new_triage_state: Annotated[
        str,
        "The new triage state of the signal. Valid options are 'open', 'archived', or 'under_review'.",  # noqa: E501
    ],
    signal_id: Annotated[str, "The unique identifier of the security signal to be updated."],
    archive_comment: Annotated[
        str | None,
        "Optional comment to display on archived signals. Useful for context or documentation.",
    ] = None,
    archive_reason: Annotated[
        str | None,
        "Reason for archiving the signal. Options include 'none', 'false_positive', 'testing_or_maintenance', 'investigated_case_opened', or 'other'.",  # noqa: E501
    ] = None,
    event_type: Annotated[
        str | None, "The type of event, must be 'signal_metadata'."
    ] = "signal_metadata",
    security_signal_unique_id: Annotated[
        str | None, "The unique identifier for the security signal to be modified."
    ] = None,
    updated_signal_version: Annotated[
        int | None,
        "The version number of the signal to update. The update is rejected if the server's version is higher.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'EditSecurityMonitoringSignalState'."
]:
    """Change the triage state of a security signal.

    This tool is used to modify the triage state of a specific security signal in Datadog's security monitoring system. It is useful for managing signal priorities and responses."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "archive_comment": archive_comment,
                "archive_reason": archive_reason,
                "state": new_triage_state,
                "version": updated_signal_version,
            },
            "id": security_signal_unique_id,
            "type": event_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/security_monitoring/signals/{signal_id}/state".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), signal_id=signal_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_scanning_groups(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListScanningGroups'."]:
    """Retrieve all scanning groups in your organization with Datadog's API."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/sensitive-data-scanner/config".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def reorder_scanning_groups(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ReorderScanningGroups'."]:
    """Reorder the list of scanning groups.

    This tool is used to change the order of sensitive data scanning groups in Datadog. Call this tool when you need to reorder the existing groups.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "REORDERSCANNINGGROUPS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["REORDERSCANNINGGROUPS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["REORDERSCANNINGGROUPS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/sensitive-data-scanner/config".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["REORDERSCANNINGGROUPS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_scanning_group(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateScanningGroup'."]:
    """Create a new scanning group in Datadog.

    This tool creates a new scanning group in the Datadog sensitive data scanner. It allows for the configuration relationship to be included but does not support creating rules simultaneously. The new group is added last in the configuration order.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATESCANNINGGROUP_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESCANNINGGROUP_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESCANNINGGROUP_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/sensitive-data-scanner/config/groups".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATESCANNINGGROUP_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_scanning_group(
    context: ToolContext,
    group_id: Annotated[str, "The ID of the scanning group to be deleted."],
    api_version: Annotated[
        int | None, "Optional version number of the API to use for the request."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteScanningGroup'."]:
    """Delete a specified scanning group in Datadog.

    Use this tool to delete a specified scanning group in Datadog's sensitive data scanner configuration. It should be called when you need to remove an existing group by its ID."""  # noqa: E501
    request_data = remove_none_values({"meta": {"version": api_version}})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/sensitive-data-scanner/config/groups/{group_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), group_id=group_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_scanning_group(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    group_id: Annotated[
        str | None,
        "The ID of the scanning group whose rules are being updated. This is required to identify the group.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateScanningGroup'."]:
    """Update a scanning group's rule order in Datadog.

    Use this tool to update and reorder the rules within a specific scanning group in Datadog. Ensure all current rules are included in the update to maintain the group integrity.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATESCANNINGGROUP_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not group_id:
        missing_params.append(("group_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESCANNINGGROUP_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESCANNINGGROUP_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/sensitive-data-scanner/config/groups/{group_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), group_id=group_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATESCANNINGGROUP_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_scanning_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateScanningRule'."]:
    """Create a scanning rule in a sensitive data group.

    Use this tool to create a new scanning rule within a sensitive data scanner group. The rule requires a group relationship and either a standard pattern or a regex attribute, but not both. If no attributes are specified, it will scan all except excluded ones.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATESCANNINGRULE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESCANNINGRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESCANNINGRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/sensitive-data-scanner/config/rules".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATESCANNINGRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_scanning_rule(
    context: ToolContext,
    rule_id: Annotated[str, "The unique identifier for the scanning rule to be deleted."],
    api_version: Annotated[
        int | None, "Specify the API version to use for the request (optional)."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteScanningRule'."]:
    """Delete a specific scanning rule by ID.

    Use this tool to remove a sensitive data scanning rule in Datadog by providing the rule's ID. It should be called when a specific scanning rule needs to be deleted."""  # noqa: E501
    request_data = remove_none_values({"meta": {"version": api_version}})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/sensitive-data-scanner/config/rules/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), rule_id=rule_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_scanning_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    rule_id: Annotated[
        str | None,
        "The unique identifier for the scanning rule to be updated. This value is required.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateScanningRule'."]:
    """Update a scanning rule in Datadog.

    Use this tool to update a scanning rule in Datadog's sensitive data scanner. It's important to note that the request must not attempt to edit the regex attribute of rules that include a standard_pattern relationship, as these are non-editable.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATESCANNINGRULE_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not rule_id:
        missing_params.append(("rule_id", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESCANNINGRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATESCANNINGRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/sensitive-data-scanner/config/rules/{rule_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), rule_id=rule_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATESCANNINGRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_standard_patterns(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListStandardPatterns'."]:
    """Retrieve all standard patterns for sensitive data scanning.

    This tool is used to fetch a list of all standard patterns available for sensitive data scanning with Datadog. Call this tool when you need access to predefined patterns used for identifying sensitive data."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/sensitive-data-scanner/config/standard-patterns".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_service_account(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateServiceAccount'."]:
    """Create a service account for your organization.



    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATESERVICEACCOUNT_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESERVICEACCOUNT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATESERVICEACCOUNT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/service_accounts".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATESERVICEACCOUNT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_service_account_app_keys(
    context: ToolContext,
    service_account_id: Annotated[
        str,
        "The unique identifier for the service account whose application keys are to be retrieved.",
    ],
    created_at_start_filter: Annotated[
        str | None, "Include application keys created on or after this date. Use format YYYY-MM-DD."
    ] = None,
    created_before_date: Annotated[
        str | None, "Include application keys created on or before this date."
    ] = None,
    filter_string_for_application_keys: Annotated[
        str | None,
        "Specify a string to filter the application keys by. Only keys containing the string will be shown.",  # noqa: E501
    ] = None,
    page_number: Annotated[int | None, "Specify the page number to be returned."] = 0,
    page_size: Annotated[
        int | None,
        "Number of application keys to retrieve per page. The maximum allowed value is 100.",
    ] = 10,
    sort_order_attribute: Annotated[
        str | None,
        "Attribute to sort application keys. Prefix with '-' for descending order. Options: created_at, last4, name.",  # noqa: E501
    ] = "name",
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ListServiceAccountApplicationKeys'."
]:
    """Retrieve all app keys for a specific service account.

    Use this tool to list all application keys available for a specified service account in Datadog. It is useful when you need to manage or audit the application keys associated with a service account."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/service_accounts/{service_account_id}/application_keys".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            service_account_id=service_account_id,
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "sort": sort_order_attribute,
            "filter": filter_string_for_application_keys,
            "filter[created_at][start]": created_at_start_filter,
            "filter[created_at][end]": created_before_date,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_service_account_app_key(
    context: ToolContext,
    application_key_name: Annotated[
        str, "The name for the application key to be created for the service account."
    ],
    service_account_identifier: Annotated[
        str,
        "The unique identifier of the service account for which the application key will be created.",  # noqa: E501
    ],
    application_key_scopes: Annotated[
        list[str] | None,
        "List of scopes to assign to the application key for specific permissions.",
    ] = None,
    application_keys_resource_type: Annotated[
        str,
        "Specify the resource type for the application key. This should always be 'application_keys'.",  # noqa: E501
    ] = "application_keys",
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateServiceAccountApplicationKey'."
]:
    """Create an application key for a service account.

    Use this tool to create a new application key for a specified service account on Datadog. It should be called when an application key is needed for service account integration or access."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"name": application_key_name, "scopes": application_key_scopes},
            "type": application_keys_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/service_accounts/{service_account_id}/application_keys".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            service_account_id=service_account_identifier,
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_service_account_app_key(
    context: ToolContext,
    application_key_id: Annotated[
        str, "The unique identifier for the application key to be deleted."
    ],
    service_account_id: Annotated[
        str,
        "The unique identifier for the service account from which the application key will be deleted.",  # noqa: E501
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'DeleteServiceAccountApplicationKey'."
]:
    """Delete an application key from a service account.

    Call this tool to remove an application key associated with a specific service account in Datadog. This tool is useful for managing access and maintaining security by ensuring unused or compromised keys are deleted."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/service_accounts/{service_account_id}/application_keys/{app_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            service_account_id=service_account_id,
            app_key_id=application_key_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_datadog_service_account_app_key(
    context: ToolContext,
    application_key_id: Annotated[
        str, "The ID of the application key for the Datadog service account."
    ],
    service_account_id: Annotated[
        str,
        "The unique identifier for the Datadog service account to retrieve the application key from.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetServiceAccountApplicationKey'."]:
    """Retrieve a specific application key for a Datadog service account.

    Use this tool to obtain details of an application key owned by a specific Datadog service account, identified by its service account ID and application key ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/service_accounts/{service_account_id}/application_keys/{app_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            service_account_id=service_account_id,
            app_key_id=application_key_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def edit_service_account_key(
    context: ToolContext,
    app_key_identifier: Annotated[
        str, "The unique identifier of the application key to be edited."
    ],
    application_key_id: Annotated[
        str, "The unique identifier for the application key to be edited."
    ],
    service_account_id: Annotated[str, "The unique identifier for the service account."],
    application_key_name: Annotated[
        str | None, "Name of the application key to be updated."
    ] = None,
    application_key_resource_type: Annotated[
        str, "Specify the type of resource for the application key. Must be 'application_keys'."
    ] = "application_keys",
    application_key_scopes: Annotated[
        list[str] | None,
        "Array of scopes to grant the application key. Each scope is a string representing a permission or capability.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'UpdateServiceAccountApplicationKey'."
]:
    """Edit an application key for a service account.

    Use this tool to modify the details of an existing application key owned by a specific service account. This can help in updating permissions or other key parameters as needed."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"name": application_key_name, "scopes": application_key_scopes},
            "id": app_key_identifier,
            "type": application_key_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/service_accounts/{service_account_id}/application_keys/{app_key_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            service_account_id=service_account_id,
            app_key_id=application_key_id,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_service_definitions(
    context: ToolContext,
    page_number: Annotated[
        int | None, "The specific page number to retrieve from the service definitions list."
    ] = 0,
    page_size: Annotated[
        int | None, "Specify the number of items per page. The maximum allowed value is 100."
    ] = 10,
    response_schema_version: Annotated[
        str | None,
        "Specifies the version of the schema to be returned in the response. Acceptable values are 'v1', 'v2', 'v2.1', or 'v2.2'.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListServiceDefinitions'."]:
    """Retrieve all service definitions from the Datadog Service Catalog.

    Use this tool to get a comprehensive list of service definitions from Datadog's Service Catalog. Ideal for scenarios where service information retrieval is required."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/services/definitions".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "schema_version": response_schema_version,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_or_update_service_definitions(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateOrUpdateServiceDefinitions'."
]:
    """Create or update service definitions in Datadog.

    Use this tool to create a new service definition or update an existing one in the Datadog Service Catalog. Call this when needing to manage service details within Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEORUPDATESERVICEDEFINITIONS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEORUPDATESERVICEDEFINITIONS_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEORUPDATESERVICEDEFINITIONS_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/services/definitions".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEORUPDATESERVICEDEFINITIONS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_service_definition(
    context: ToolContext,
    service_name: Annotated[
        str, "The name of the service to delete from the Datadog Service Catalog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteServiceDefinition'."]:
    """Deletes a service definition from Datadog.

    Use this tool to remove a specific service definition from the Datadog Service Catalog. Ideal for cleaning up or managing services that are no longer needed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/services/definitions/{service_name}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), service_name=service_name
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_service_definition(
    context: ToolContext,
    service_name: Annotated[
        str, "The exact name of the service to retrieve from Datadog's Service Catalog."
    ],
    desired_schema_version: Annotated[
        str | None,
        "Specify the schema version for the response. Options: 'v1', 'v2', 'v2.1', 'v2.2'.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetServiceDefinition'."]:
    """Retrieve a service definition from Datadog's Service Catalog.

    Call this tool to obtain specific details about a service defined in the Datadog Service Catalog. Useful for monitoring and managing services."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/services/definitions/{service_name}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), service_name=service_name
        ),
        method="GET",
        params=remove_none_values({"schema_version": desired_schema_version}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_security_monitoring_signals(
    context: ToolContext,
    max_security_signals: Annotated[
        int | None,
        "The maximum number of security signals to return in the response. Specify an integer value.",  # noqa: E501
    ] = 10,
    maximum_timestamp_for_signals: Annotated[
        str | None, "The latest timestamp to fetch security signals up to, formatted as a string."
    ] = None,
    minimum_timestamp: Annotated[
        str | None, "The minimum timestamp for requested security signals in ISO 8601 format."
    ] = None,
    results_page_cursor: Annotated[
        str | None, "Cursor for paginated results, using the cursor from the previous query."
    ] = None,
    search_query: Annotated[
        str | None,
        "The search query to filter security signals. Use this to specify criteria for filtering the results.",  # noqa: E501
    ] = None,
    sort_order: Annotated[
        str | None,
        "Determine the order of security signals: 'timestamp' for ascending, '-timestamp' for descending.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ListSecurityMonitoringHistsignals'."
]:
    """Retrieve a list of security monitoring hist signals.

    This tool calls the Datadog API to list historical security monitoring signals, providing insights into detected security events over time."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/siem-historical-detections/histsignals".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[query]": search_query,
            "filter[from]": minimum_timestamp,
            "filter[to]": maximum_timestamp_for_signals,
            "sort": sort_order,
            "page[cursor]": results_page_cursor,
            "page[limit]": max_security_signals,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def find_security_alerts(
    context: ToolContext,
    max_security_signals: Annotated[
        int | None, "Specify the maximum number of security signals to retrieve in the response."
    ] = 10,
    maximum_timestamp_for_signals: Annotated[
        str | None, "The maximum timestamp for requested security signals, formatted as a string."
    ] = None,
    minimum_timestamp: Annotated[
        str | None,
        "The start timestamp for filtering requested security signals. Use ISO 8601 format.",
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "String used to fetch the next set of results based on a previous query's cursor.",
    ] = None,
    search_query_for_security_signals: Annotated[
        str | None,
        "Search query to filter and list specific security signals. Use keywords and operators to refine results.",  # noqa: E501
    ] = None,
    sort_order: Annotated[
        str | None, "The criteria for sorting security signals, either 'timestamp' or '-timestamp'."
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'SearchSecurityMonitoringHistsignals'."
]:
    """Retrieve historical security monitoring signals from Datadog.

    This tool is used to search and retrieve historical security monitoring signals in Datadog. Ideal for extracting past security incident data and analysis."""  # noqa: E501
    request_data = remove_none_values({
        "filter": {
            "from": minimum_timestamp,
            "query": search_query_for_security_signals,
            "to": maximum_timestamp_for_signals,
        },
        "page": {"cursor": pagination_cursor, "limit": max_security_signals},
        "sort": sort_order,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/siem-historical-detections/histsignals/search".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_hist_signal_details(
    context: ToolContext,
    historical_signal_id: Annotated[
        str, "The ID of the historical signal to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSecurityMonitoringHistsignal'."]:
    """Retrieve details of a specific hist signal.

    This tool fetches detailed information about a specified hist signal from Datadog's SIEM (Security Information and Event Management) service. It should be used when you need to access detailed data on a particular historical signal for security monitoring purposes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/siem-historical-detections/histsignals/{histsignal_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            histsignal_id=historical_signal_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_historical_jobs(
    context: ToolContext,
    filter_query: Annotated[
        str | None,
        "A query string to filter items in the list of historical jobs. Use to specify criteria for narrowing down the results.",  # noqa: E501
    ] = None,
    page_number: Annotated[int | None, "The specific page number to return from the results."] = 0,
    page_size: Annotated[
        int | None, "Specifies the number of results per page, with a maximum of 100."
    ] = 10,
    sort_order: Annotated[
        str | None,
        "Specifies the order in which jobs are returned, such as ascending or descending.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListHistoricalJobs'."]:
    """Retrieve a list of historical SIEM detection jobs from Datadog.

    Use this tool to get a list of historical jobs related to SIEM detections in Datadog. This is useful for analyzing past job executions and reviewing detection history."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/siem-historical-detections/jobs".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "sort": sort_order,
            "filter[query]": filter_query,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def run_historical_detection_job(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RunHistoricalJob'."]:
    """Initiate a historical detection job in Datadog.

    Use this tool to start a historical detection job via Datadog's SIEM API. It is called when there's a need to analyze past data using historical detections.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "RUNHISTORICALDETECTIONJOB_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["RUNHISTORICALDETECTIONJOB_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["RUNHISTORICALDETECTIONJOB_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/siem-historical-detections/jobs".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["RUNHISTORICALDETECTIONJOB_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def convert_job_result_to_signal(
    context: ToolContext,
    job_result_ids: Annotated[
        list[str] | None, "Array of job result IDs to convert into signals."
    ] = None,
    notifications_sent: Annotated[
        list[str] | None, "List of notification types sent related to the signal."
    ] = None,
    payload_type: Annotated[
        str | None, "Type of payload, must be 'historicalDetectionsJobResultSignalConversion'."
    ] = None,
    request_id: Annotated[
        str | None,
        "A unique identifier for the request that is used to convert the job result to a signal.",
    ] = None,
    signal_message: Annotated[
        str | None, "Message content of the generated signals to be converted."
    ] = None,
    signal_severity: Annotated[
        str | None,
        "Severity level of the security signal. Accepts values: info, low, medium, high, critical.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ConvertJobResultToSignal'."]:
    """Convert a job result to a signal for detection purposes.

    Use this tool to transform a job result into a signal, useful for detection and monitoring in Datadog."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "id": request_id,
                "jobResultIds": job_result_ids,
                "notifications": notifications_sent,
                "signalMessage": signal_message,
                "signalSeverity": signal_severity,
            },
            "type": payload_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/siem-historical-detections/jobs/signal_convert".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_historical_detection_job(
    context: ToolContext,
    job_id: Annotated[str, "The unique identifier for the historical job to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteHistoricalJob'."]:
    """Delete an existing historical detection job in Datadog.

    Use this tool to delete a specific historical detection job in Datadog's SIEM. This is helpful when you need to manage or clean up old detection jobs by specifying the job ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/siem-historical-detections/jobs/{job_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), job_id=job_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_historical_job_details(
    context: ToolContext,
    job_id: Annotated[str, "The unique identifier for the job whose details you want to retrieve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetHistoricalJob'."]:
    """Retrieve details of a specific historical job from Datadog.

    Use this tool to get the details of a specific historical job from Datadog's SIEM historical detections by providing the job ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/siem-historical-detections/jobs/{job_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), job_id=job_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def cancel_historical_job(
    context: ToolContext,
    job_id: Annotated[
        str, "The unique identifier of the historical job to be canceled in Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelHistoricalJob'."]:
    """Cancel a historical job in Datadog.

    Use this tool to cancel an ongoing historical job in Datadog, identified by its job ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/siem-historical-detections/jobs/{job_id}/cancel".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), job_id=job_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_historical_security_signals(
    context: ToolContext,
    job_identifier: Annotated[
        str,
        "The unique identifier for the job whose historical security signals you want to retrieve.",
    ],
    max_security_signals: Annotated[
        int | None, "The maximum number of security signals to retrieve in the response."
    ] = 10,
    max_timestamp_for_signals: Annotated[
        str | None, "The latest timestamp for the requested security signals."
    ] = None,
    minimum_timestamp: Annotated[
        str | None,
        "The earliest timestamp for retrieving security signals. Format should be ISO 8601 (e.g., '2023-10-01T00:00:00Z').",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None, "Use the cursor from the previous query to paginate results."
    ] = None,
    security_signal_search_query: Annotated[
        str | None, "The search query to filter security signals."
    ] = None,
    signal_sort_order: Annotated[
        str | None,
        "Specify the sort order of the security signals, either 'timestamp' for ascending or '-timestamp' for descending.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'GetSecurityMonitoringHistsignalsByJobId'."
]:
    """Retrieve historical security signals by job ID.

    Use this tool to obtain historical security signals associated with a specific job ID in the Datadog system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/siem-historical-detections/jobs/{job_id}/histsignals".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), job_id=job_identifier
        ),
        method="GET",
        params=remove_none_values({
            "filter[query]": security_signal_search_query,
            "filter[from]": minimum_timestamp,
            "filter[to]": max_timestamp_for_signals,
            "sort": signal_sort_order,
            "page[cursor]": pagination_cursor,
            "page[limit]": max_security_signals,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_slo_report_job(
    context: ToolContext,
    from_timestamp_epoch_seconds: Annotated[
        int,
        "The starting timestamp for the SLO report in epoch seconds. Specifies when the report should begin.",  # noqa: E501
    ],
    report_to_timestamp: Annotated[
        int, "The epoch timestamp representing the end time for the SLO report."
    ],
    slo_query_filter: Annotated[
        str, "The query string to filter SLO results, e.g., 'service:<service-name>' or 'slo-name'."
    ],
    report_generation_frequency: Annotated[
        str | None, "The frequency for generating report data. Options: daily, weekly, monthly."
    ] = None,
    report_timezone: Annotated[
        str | None,
        "The timezone to determine the start and end of each interval for the SLO report. It affects how intervals such as weekly start at 12am on Sunday in the specified timezone.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateSLOReportJob'."]:
    """Initiate the generation of an SLO report in Datadog.

    Use this tool to start an SLO report job in Datadog. Once created, the job processes asynchronously, and a CSV report becomes available for download. Utilize the returned `report_id` to check status and access the report."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "from_ts": from_timestamp_epoch_seconds,
                "interval": report_generation_frequency,
                "query": slo_query_filter,
                "timezone": report_timezone,
                "to_ts": report_to_timestamp,
            }
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/slo/report".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def download_slo_report(
    context: ToolContext,
    report_id: Annotated[str, "The unique identifier for the SLO report job to download."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSLOReport'."]:
    """Download a completed SLO report from Datadog.

    Use this tool to download an SLO report from Datadog after the report job has completed. It is recommended to download the report as soon as it becomes available, as reports may not be stored indefinitely."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/slo/report/{report_id}/download".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), report_id=report_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_slo_report_status(
    context: ToolContext,
    slo_report_id: Annotated[
        str, "The unique identifier of the SLO report job to check its current status."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSLOReportJobStatus'."]:
    """Retrieve the status of a specific SLO report job.

    Use this tool to check the current status of a Service Level Objective (SLO) report job by providing the report ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/slo/report/{report_id}/status".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), report_id=slo_report_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_spark_job_recommendations(
    context: ToolContext,
    spark_job_service_name: Annotated[
        str, "The service name for the Spark job to retrieve recommendations."
    ],
    spark_job_shard_identifier: Annotated[
        str,
        "The shard identifier for a Spark job, differentiating jobs within the same service with distinct resource requirements.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSPARecommendations'."]:
    """Retrieve resource recommendations for a Spark job.

    Use this tool to obtain structured recommendations for Spark job driver and executor resources, based on specified service name and shard identifier."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/spa/recommendations/{service}/{shard}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            shard=spark_job_shard_identifier,
            service=spark_job_service_name,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def aggregate_spans_metrics(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AggregateSpans'."]:
    """Aggregate spans to compute metrics and timeseries.

    Use this tool to aggregate spans into buckets and compute metrics and timeseries data. This is useful for analyzing performance and trends in systems. Note: Limited to 300 requests per hour.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "AGGREGATESPANSMETRICS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["AGGREGATESPANSMETRICS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["AGGREGATESPANSMETRICS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/spans/analytics/aggregate".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["AGGREGATESPANSMETRICS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_latest_spans(
    context: ToolContext,
    max_spans_limit: Annotated[
        int | None,
        "The maximum number of spans to return in the response. Specify an integer value to limit the results.",  # noqa: E501
    ] = 10,
    max_timestamp_for_spans: Annotated[
        str | None,
        "Maximum timestamp for requested spans. Use ISO8601, date math, or millisecond timestamps.",
    ] = None,
    minimum_timestamp: Annotated[
        str | None,
        "Minimum timestamp for requested spans. Accepts ISO8601, date math, or timestamps in milliseconds.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None, "Cursor for paginating results, provided by the previous query execution."
    ] = None,
    sort_order_of_spans: Annotated[
        str | None,
        "Specify the order of spans in the results. Use 'timestamp' for ascending and '-timestamp' for descending order.",  # noqa: E501
    ] = None,
    span_search_query: Annotated[
        str | None,
        "A search query following the spans syntax to filter the spans you want to retrieve.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListSpansGet'."]:
    """Retrieve the latest spans based on a search query.

    This tool retrieves spans from Datadog that match a specified search query. Use this to see your latest spans. Note that results are paginated, and the endpoint is rate limited to 300 requests per hour."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/spans/events".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[query]": span_search_query,
            "filter[from]": minimum_timestamp,
            "filter[to]": max_timestamp_for_spans,
            "sort": sort_order_of_spans,
            "page[cursor]": pagination_cursor,
            "page[limit]": max_spans_limit,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_spans(
    context: ToolContext,
    end_time_filter: Annotated[
        str | None,
        "The maximum time for requested spans. Supports ISO8601, date math, or timestamps in milliseconds.",  # noqa: E501
    ] = "now",
    max_number_of_spans: Annotated[
        int | None, "Maximum number of spans to return in the response. Integer value expected."
    ] = 10,
    minimum_time_filter: Annotated[
        str | None,
        "The minimum time for the requested spans. Supports ISO8601, date math, and timestamps (milliseconds).",  # noqa: E501
    ] = "now-15m",
    pagination_cursor: Annotated[
        str | None, "A string cursor to fetch the next set of results from the previous query."
    ] = None,
    resource_type: Annotated[
        str | None, "The type of resource; must be set to 'search_request' for the query."
    ] = "search_request",
    sort_order_for_spans: Annotated[
        str | None,
        "Set the sort order for querying spans. Use 'timestamp' for ascending or '-timestamp' for descending.",  # noqa: E501
    ] = None,
    span_search_query: Annotated[
        str | None, "The search query string following the span search syntax to filter spans."
    ] = "*",
    time_offset_seconds: Annotated[
        int | None, "The time offset in seconds to apply to the query for adjusting the time frame."
    ] = None,
    timezone_option: Annotated[
        str | None,
        "Specify the timezone using GMT, UTC, an offset (e.g., UTC+1), or a Timezone Database ID (e.g., America/New_York).",  # noqa: E501
    ] = "UTC",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListSpans'."]:
    """Fetch spans based on a search query with pagination.

    This tool retrieves spans from Datadog that match a specified search query. Use it to filter and search spans for analysis or monitoring. Note that the endpoint supports paginated results and is subject to rate limits of 300 requests per hour."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "filter": {
                    "from": minimum_time_filter,
                    "query": span_search_query,
                    "to": end_time_filter,
                },
                "options": {"timeOffset": time_offset_seconds, "timezone": timezone_option},
                "page": {"cursor": pagination_cursor, "limit": max_number_of_spans},
                "sort": sort_order_for_spans,
            },
            "type": resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/spans/events/search".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_on_demand_concurrency_cap(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetOnDemandConcurrencyCap'."]:
    """Retrieve the on-demand concurrency cap value from Datadog.

    Use this tool to get the current on-demand concurrency cap configured in Datadog's synthetics settings. This information can help monitor and manage concurrency limits effectively."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/synthetics/settings/on_demand_concurrency_cap".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def set_on_demand_concurrency_cap(
    context: ToolContext,
    on_demand_concurrency_cap_value: Annotated[
        float | None, "Specify the new value for the on-demand concurrency cap in Datadog."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SetOnDemandConcurrencyCap'."]:
    """Update the on-demand concurrency cap setting in Datadog.

    Use this tool to save a new value for the on-demand concurrency cap in the Datadog Synthetics settings. This updates the maximum number of on-demand tests that can run concurrently."""  # noqa: E501
    request_data = remove_none_values({
        "on_demand_concurrency_cap": on_demand_concurrency_cap_value
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/synthetics/settings/on_demand_concurrency_cap".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_tag_pipeline_rulesets(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListTagPipelinesRulesets'."]:
    """Retrieve all tag pipeline rulesets for the organization.

    This tool retrieves a comprehensive list of all tag pipeline rulesets associated with the organization. It should be called when you need to examine or manage these rulesets."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/tags/enrichment".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_tag_pipeline_ruleset(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateTagPipelinesRuleset'."]:
    """Create a tag pipeline ruleset with specified rules.

    This tool creates a new tag pipeline ruleset using the specified rules and configurations in Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATETAGPIPELINERULESET_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATETAGPIPELINERULESET_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATETAGPIPELINERULESET_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/tags/enrichment".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATETAGPIPELINERULESET_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def reorder_tag_pipeline_rulesets(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ReorderTagPipelinesRulesets'."]:
    """Change the execution order of tag pipeline rulesets.

    Use this tool to modify the order in which tag pipeline rulesets are executed in Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "REORDERTAGPIPELINERULESETS_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["REORDERTAGPIPELINERULESETS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["REORDERTAGPIPELINERULESETS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/tags/enrichment/reorder".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["REORDERTAGPIPELINERULESETS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def validate_tag_pipeline_query(
    context: ToolContext,
    query_attributes: Annotated[
        str | None,
        "The tag pipeline query to validate. Ensure it is correctly structured and free of syntax errors.",  # noqa: E501
    ] = None,
    query_request_data_id: Annotated[
        str | None, "The unique identifier for the RulesValidateQueryRequestData."
    ] = None,
    query_resource_type: Annotated[
        str | None,
        "Specify the type of resource for query validation, always use 'validate_query'.",
    ] = "validate_query",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ValidateQuery'."]:
    """Validate the syntax and structure of a tag pipeline query.

    Use this tool to ensure that a tag pipeline query is correctly structured and free of syntax errors."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"Query": query_attributes},
            "id": query_request_data_id,
            "type": query_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/tags/enrichment/validate-query".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_tag_pipeline_ruleset(
    context: ToolContext,
    ruleset_id: Annotated[str, "The unique identifier for the tag pipeline ruleset to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteTagPipelinesRuleset'."]:
    """Delete an existing tag pipeline ruleset by ID.

    Use this tool to delete a specific tag pipeline ruleset in Datadog by providing its ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/tags/enrichment/{ruleset_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), ruleset_id=ruleset_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_tag_pipeline_ruleset(
    context: ToolContext,
    ruleset_identifier: Annotated[
        str, "The unique identifier for the tag pipeline ruleset to retrieve."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTagPipelinesRuleset'."]:
    """Retrieve a specific tag pipeline ruleset by its ID.

    Use this tool to get detailed information about a specific tag pipeline ruleset within Datadog by providing its ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/tags/enrichment/{ruleset_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), ruleset_id=ruleset_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_tag_pipeline_ruleset(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    ruleset_identifier: Annotated[
        str | None,
        "A unique string identifier for the tag pipeline ruleset to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateTagPipelinesRuleset'."]:
    """Update an existing tag pipeline ruleset with new rules.

    Use this tool to modify the rules and configuration of an existing tag pipeline ruleset in Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATETAGPIPELINERULESET_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not ruleset_identifier:
        missing_params.append(("ruleset_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATETAGPIPELINERULESET_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATETAGPIPELINERULESET_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/tags/enrichment/{ruleset_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), ruleset_id=ruleset_identifier
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATETAGPIPELINERULESET_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_teams(
    context: ToolContext,
    fields_to_fetch: Annotated[
        list[str] | None, "List of fields to retrieve for each team."
    ] = None,
    include_only_user_teams: Annotated[
        bool | None, "When true, only teams the current user belongs to are returned."
    ] = None,
    include_related_resources: Annotated[
        list[str] | None,
        "Specify related resources to include. Options: `team_links`, `user_team_permissions`.",
    ] = None,
    page_number: Annotated[
        int | None, "The specific page number to return for the list of teams."
    ] = 0,
    page_size: Annotated[
        int | None, "Specify the number of teams to return per page, up to a maximum of 100."
    ] = 10,
    search_query_for_teams: Annotated[
        str | None, "Search for teams by name, handle, or team member email."
    ] = None,
    sort_order: Annotated[
        str | None,
        "Determines the order of the returned teams. Options: 'name', '-name', 'user_count', '-user_count'.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListTeams'."]:
    """Retrieve all teams with optional filters.

    Fetches a list of all teams, allowing optional filters by keyword or user."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[number]": page_number,
            "page[size]": page_size,
            "sort": sort_order,
            "include": include_related_resources,
            "filter[keyword]": search_query_for_teams,
            "filter[me]": include_only_user_teams,
            "fields[team]": fields_to_fetch,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_new_team(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateTeam'."]:
    """Create a new team and add specified members.

    This tool creates a new team and adds specified user IDs to it. Use it when you need to establish a team and assign users to it in Datadog.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATENEWTEAM_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATENEWTEAM_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["CREATENEWTEAM_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/team".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATENEWTEAM_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def sync_datadog_teams_with_github(
    context: ToolContext,
    source_platform: Annotated[
        str,
        'Specify the external source platform for team synchronization. Only "github" is supported.',  # noqa: E501
    ],
    synchronization_type: Annotated[
        str,
        'Type of synchronization operation. Only "link" is supported to match existing teams by name.',  # noqa: E501
    ],
    team_sync_bulk_type: Annotated[
        str, "Specifies the type for bulk team synchronization. Use 'team_sync_bulk'."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SyncTeams'."]:
    """Link existing Datadog teams with GitHub teams by name matching.

    Synchronizes Datadog teams with GitHub teams by name, evaluating all current teams without making modifications. Requires a connected GitHub organization and appropriate permissions."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"source": source_platform, "type": synchronization_type},
            "type": team_sync_bulk_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/sync".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_all_member_teams(
    context: ToolContext,
    super_team_identifier: Annotated[
        str, "The unique identifier for the super team whose member teams you want to retrieve."
    ],
    fields_to_fetch: Annotated[
        list[str] | None,
        "A list of field names to be fetched for each team. Specify the fields you need details on.",  # noqa: E501
    ] = None,
    page_number: Annotated[int | None, "Specific page number to return in the list of teams."] = 0,
    page_size: Annotated[int | None, "Size for a given page. Must be an integer up to 100."] = 10,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListMemberTeams'."]:
    """Retrieve all member teams for a super team.

    Use this tool to get a comprehensive list of all member teams associated with a specific super team in Datadog. Call this tool when you need information on team memberships within a specified structure."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{super_team_id}/member_teams".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            super_team_id=super_team_identifier,
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "fields[team]": fields_to_fetch,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def add_member_team_to_super_team(
    context: ToolContext,
    member_team_identifier: Annotated[
        str, "The unique identifier of the member team to be added to the super team."
    ],
    super_team_identifier: Annotated[
        str,
        "The ID of the super team to which the member team will be added. It is a string value.",
    ],
    member_team_type: Annotated[
        str, "Specifies the type of member team to be added. Must be 'member_teams'."
    ] = "member_teams",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddMemberTeam'."]:
    """Add a member team to a super team.

    Use this tool to add a specified team as a member of an existing super team by providing the super team's ID."""  # noqa: E501
    request_data = remove_none_values({
        "data": {"id": member_team_identifier, "type": member_team_type}
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{super_team_id}/member_teams".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            super_team_id=super_team_identifier,
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def remove_team_member(
    context: ToolContext,
    member_team_identifier: Annotated[
        str, "The unique ID of the member team to be removed from the super team."
    ],
    super_team_id: Annotated[
        str, "The unique identifier for the super team from which a member team will be removed."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveMemberTeam'."]:
    """Removes a member team from a super team.

    Use this tool to remove a specific member team from a super team based on the provided member team ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{super_team_id}/member_teams/{member_team_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            super_team_id=super_team_id,
            member_team_id=member_team_identifier,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_team(
    context: ToolContext,
    team_identifier: Annotated[str, "The unique identifier for the team to be deleted in Datadog."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteTeam'."]:
    """Remove a team using its ID in Datadog.

    This tool should be called to permanently delete a team in Datadog by specifying the team's unique ID. Use this when a team is no longer needed and should be removed from the system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), team_id=team_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_single_team_info(
    context: ToolContext,
    team_id: Annotated[
        str, "The unique identifier for the team. Provide this to retrieve specific team details."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTeam'."]:
    """Retrieve details of a team using its ID.

    Use this tool to obtain details about a specific team by providing the team's ID. Ideal for applications needing team-specific information."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), team_id=team_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_team_info(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the team to be updated. Must be a valid string representing the team's ID in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateTeam'."]:
    """Update and modify a team's configuration in Datadog.

    Use this tool to update a team in Datadog by modifying its configuration using the team's ID. You can adjust team links and other settings as needed.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATETEAMINFO_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not team_identifier:
        missing_params.append(("team_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATETEAMINFO_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["UPDATETEAMINFO_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/team/{team_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), team_id=team_identifier
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATETEAMINFO_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_all_team_links(
    context: ToolContext,
    team_identifier: Annotated[
        str, "The unique identifier for the team whose links are to be retrieved."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTeamLinks'."]:
    """Retrieve all links for a specific team.

    Use this tool to obtain all the links associated with a given team in Datadog. It is helpful when you need to gather all relevant links for team collaboration or resource management."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}/links".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), team_id=team_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def add_team_link(
    context: ToolContext,
    link_label: Annotated[
        str,
        "The label for the link to be added to the team. This should be a descriptive text for the link.",  # noqa: E501
    ],
    link_url: Annotated[
        str, "The URL to be added as a link for the team. This should be a valid web address."
    ],
    target_team_id: Annotated[
        str, "The unique identifier for the team to which the link will be added."
    ],
    link_position: Annotated[
        int | None, "The position of the link in the team's list, used for sorting links."
    ] = None,
    team_id_for_link: Annotated[
        str | None,
        "ID of the team the link is associated with. This should be a unique identifier for the specific team to which you want to add the link.",  # noqa: E501
    ] = None,
    team_link_type: Annotated[
        str, "Specify the type of team link. Must be 'team_links'."
    ] = "team_links",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateTeamLink'."]:
    """Add a new link to a Datadog team.

    Use this tool to add a new link to a specific team's page in Datadog. Suitable for enhancing team resources or documentation by linking additional references."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "label": link_label,
                "position": link_position,
                "team_id": team_id_for_link,
                "url": link_url,
            },
            "type": team_link_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}/links".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), team_id=target_team_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def remove_team_link(
    context: ToolContext,
    link_identifier: Annotated[
        str, "The unique identifier of the link to be removed from the team."
    ],
    team_id: Annotated[
        str,
        "The unique identifier of the team from which the link will be removed. Required for identifying the specific team.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteTeamLink'."]:
    """Remove a link from a team.

    Use this tool to remove a specific link from a team by providing the team and link identifiers. Useful for managing team associations and cleaning up unnecessary links."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}/links/{link_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            team_id=team_id,
            link_id=link_identifier,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_team_link(
    context: ToolContext,
    link_id: Annotated[
        str,
        "The unique identifier for the specific link you want to retrieve for a team in Datadog.",
    ],
    team_id: Annotated[str, "The unique identifier for the team whose link you want to retrieve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTeamLink'."]:
    """Retrieve a specific link for a team.

    Use this tool to get detailed information about a specific link associated with a team in Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}/links/{link_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            team_id=team_id,
            link_id=link_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_team_link(
    context: ToolContext,
    link_identifier: Annotated[str, "The unique identifier for the link you want to update."],
    link_label: Annotated[
        str,
        "Specify the label for the link. This is used to identify or name the link within the team's list of links.",  # noqa: E501
    ],
    link_url: Annotated[str, "The URL for the team link. Provide a valid, well-formed URL."],
    team_identifier: Annotated[
        str, "The unique string identifier for the team related to the link."
    ],
    link_position: Annotated[
        int | None, "The position of the link in the list, used to sort links for the team."
    ] = None,
    team_id_associated_with_link: Annotated[
        str | None, "The ID of the team associated with the link to be updated."
    ] = None,
    team_link_type: Annotated[
        str, "Specifies the type of team link. Must be 'team_links'."
    ] = "team_links",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateTeamLink'."]:
    """Updates a team link in Datadog.

    Use this tool to update the details of a specific team link within the Datadog service. It's useful for modifying or adjusting link information associated with a team."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "label": link_label,
                "position": link_position,
                "team_id": team_id_associated_with_link,
                "url": link_url,
            },
            "type": team_link_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}/links/{link_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            team_id=team_identifier,
            link_id=link_identifier,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_team_members(
    context: ToolContext,
    team_id: Annotated[
        str, "The unique identifier for the team whose members are to be retrieved."
    ],
    page_number: Annotated[
        int | None, "The specific page number to retrieve from the list of team members."
    ] = 0,
    page_size: Annotated[
        int | None, "Specify the number of team members to return per page. Maximum is 100."
    ] = 10,
    search_query: Annotated[
        str | None, "Search query for filtering members by user email or name."
    ] = None,
    sort_order: Annotated[
        str | None,
        "Specify the order of returned team memberships. Options include 'manager_name', '-manager_name', 'name', '-name', 'handle', '-handle', 'email', '-email'.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTeamMemberships'."]:
    """Retrieve a list of team members.

    Retrieve a paginated list of members for a specified team using their team ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}/memberships".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), team_id=team_id
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "sort": sort_order,
            "filter[keyword]": search_query,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def add_user_to_team(
    context: ToolContext,
    team_id: Annotated[
        str,
        "The ID of the team to which the user will be added. This is required to specify the target team.",  # noqa: E501
    ],
    provisioned_user_or_service_account_id: Annotated[
        str | None,
        "UUID of the User or Service Account who provisioned the team membership, or null if done via SAML mapping.",  # noqa: E501
    ] = None,
    provisioning_mechanism: Annotated[
        str | None,
        'Mechanism responsible for provisioning the team relationship. Possible values: null for user-added, "service_account" for service account, "saml_mapping" for SAML mapping.',  # noqa: E501
    ] = None,
    team_identifier: Annotated[
        str | None, "The unique ID of the team to which the user will be added."
    ] = None,
    team_membership_type: Annotated[
        str, "Specify the type of team membership. Use 'team_memberships'."
    ] = "team_memberships",
    user_id: Annotated[str | None, "The ID of the user to be added to the team in Datadog."] = None,
    user_role_in_team: Annotated[
        str | None,
        "Specifies the user's role within the team. Currently, only 'admin' is supported as a role.",  # noqa: E501
    ] = None,
    user_team_type: Annotated[
        str | None, "Specifies the type for the team relationship, fixed as 'team'."
    ] = "team",
    user_team_user_type: Annotated[
        str | None, "Set to 'users' as the type for the user in the team."
    ] = "users",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateTeamMembership'."]:
    """Add a user to a team in Datadog.

    Use this tool to add a specified user to an existing team within Datadog by providing the team ID."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "provisioned_by": provisioning_mechanism,
                "provisioned_by_id": provisioned_user_or_service_account_id,
                "role": user_role_in_team,
            },
            "relationships": {
                "team": {"data": {"id": team_identifier, "type": user_team_type}},
                "user": {"data": {"id": user_id, "type": user_team_user_type}},
            },
            "type": team_membership_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}/memberships".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), team_id=team_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def remove_user_from_team(
    context: ToolContext,
    team_identifier: Annotated[
        str,
        "A string representing the unique identifier of the team from which the user will be removed.",  # noqa: E501
    ],
    user_identifier_for_removal: Annotated[
        str, "The unique identifier of the user to be removed from the team."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteTeamMembership'."]:
    """Remove a user from a specified team.

    Use this tool when you need to remove a user's membership from a specific team in Datadog."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}/memberships/{user_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            team_id=team_identifier,
            user_id=user_identifier_for_removal,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_user_team_membership(
    context: ToolContext,
    team_id: Annotated[
        str, "The unique identifier of the team to update the user's membership attributes."
    ],
    user_identifier: Annotated[
        str,
        "The unique identifier for the user whose membership is being updated. This is required to specify which user's attributes will be changed.",  # noqa: E501
    ],
    provisioning_identifier: Annotated[
        str | None,
        "UUID of the User or Service Account who provisioned this team membership, or null if provisioned via SAML mapping.",  # noqa: E501
    ] = None,
    provisioning_mechanism: Annotated[
        str | None,
        'Specifies how the team relationship was provisioned. Options: null, "service_account", "saml_mapping".',  # noqa: E501
    ] = None,
    team_membership_type: Annotated[
        str, "Specify the type of team membership. The value must be 'team_memberships'."
    ] = "team_memberships",
    user_role_in_team: Annotated[
        str | None, "Specify the user's role within the team. Accepts 'admin'."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateTeamMembership'."]:
    """Update a user's membership attributes on a team.

    Use this tool to update specific membership attributes for a user within a team's membership in Datadog. This can be useful when user roles or permissions need modification."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "provisioned_by": provisioning_mechanism,
                "provisioned_by_id": provisioning_identifier,
                "role": user_role_in_team,
            },
            "type": team_membership_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}/memberships/{user_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            team_id=team_id,
            user_id=user_identifier,
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_team_permission_settings(
    context: ToolContext,
    team_identifier: Annotated[
        str, "The unique identifier for the team whose permission settings are to be retrieved."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTeamPermissionSettings'."]:
    """Retrieve permission settings for a specific team.

    Call this tool to obtain all permission settings associated with a specific team in Datadog. Ideal for checking or managing team permissions."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}/permission-settings".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), team_id=team_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_team_permission(
    context: ToolContext,
    action_to_update: Annotated[
        str,
        "The specific action to update in the team's permission setting, specifying what can be performed.",  # noqa: E501
    ],
    team_identifier: Annotated[
        str, "The unique identifier of the team for which the permission setting will be updated."
    ],
    allowed_user_type_for_action: Annotated[
        str | None,
        "Specify the user type permitted to perform the action. Options: admins, members, organization, user_access_manage, teams_manage.",  # noqa: E501
    ] = None,
    team_permission_setting_type: Annotated[
        str, "Specify the team permission setting type. Required value: 'team_permission_settings'."
    ] = "team_permission_settings",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateTeamPermissionSetting'."]:
    """Update a team's permission setting in Datadog.

    Use this tool to update the permission setting for a specific team in Datadog, allowing for customized access control adjustments."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"value": allowed_user_type_for_action},
            "type": team_permission_setting_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/team/{team_id}/permission-settings/{action}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            team_id=team_identifier,
            action=action_to_update,
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def search_flaky_tests(
    context: ToolContext,
    filter_query: Annotated[
        str | None,
        "Search query for filtering flaky tests using log syntax. Keys include 'flaky_test_state', 'flaky_test_category', '@test.name', '@test.suite', '@test.module', '@test.service', '@git.repository.id_v2', '@git.branch', '@test.codeowners', 'env'.",  # noqa: E501
    ] = "*",
    maximum_flaky_tests_limit: Annotated[
        int | None, "Specify the maximum number of flaky tests to include in the response."
    ] = 10,
    pagination_cursor: Annotated[
        str | None, "A cursor from the previous request to fetch the following results."
    ] = None,
    request_data_type: Annotated[
        str | None,
        "Defines the data structure type for the Flaky Tests Search request. Use 'search_flaky_tests_request'.",  # noqa: E501
    ] = None,
    sort_flaky_tests: Annotated[
        str | None,
        "Sort flaky test results by specified criteria: FQN, first or last flaked, failure rate, etc. Use prefixed '-' for descending order.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchFlakyTests'."]:
    """Retrieve a list of flaky tests with pagination support.

    Use this tool to fetch information about flaky tests from the Flaky Test Management system. Useful for identifying tests that frequently fail or are unreliable."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {
                "filter": {"query": filter_query},
                "page": {"cursor": pagination_cursor, "limit": maximum_flaky_tests_limit},
                "sort": sort_flaky_tests,
            },
            "type": request_data_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/test/flaky-test-management/tests".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_billing_dimension_mapping(
    context: ToolContext,
    billing_dimension_view: Annotated[
        str | None,
        "Specify 'active' for current contract mappings or 'all' for all mappings. Defaults to 'active'.",  # noqa: E501
    ] = "active",
    billing_month: Annotated[
        str | None,
        "Date in ISO-8601 format (UTC) for the starting month of mappings. Defaults to the current month.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetBillingDimensionMapping'."]:
    """Retrieve the mapping of billing dimensions to API keys.

    Use this tool to obtain the mapping of billing dimensions to corresponding keys for usage metering API endpoints. This information is updated monthly and accessible only to parent-level organizations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/usage/billing_dimension_mapping".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[month]": billing_month,
            "filter[view]": billing_dimension_view,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_estimated_cost_datadog(
    context: ToolContext,
    cost_end_month: Annotated[
        str | None,
        "Specify the ending month for the estimated cost in ISO-8601 format, UTC (`[YYYY-MM]`).",
    ] = None,
    cost_view_level: Annotated[
        str | None,
        "Specify if cost is broken down at the parent-org level (summary) or sub-org level (sub-org). Defaults to summary.",  # noqa: E501
    ] = None,
    end_date: Annotated[
        str | None,
        "Specify the end date for cost estimation in ISO-8601 format (YYYY-MM-DD). It marks the last day of the period for which you need cost data.",  # noqa: E501
    ] = None,
    include_connected_accounts: Annotated[
        bool | None,
        "Include accounts connected as partner customers in Datadog's partner network program. Defaults to `false`.",  # noqa: E501
    ] = False,
    initial_cost_month: Annotated[
        str | None,
        "ISO-8601 month format `[YYYY-MM]`, specifying the start month for cost data. Cannot be older than two months. Provide `end_month` for month-over-month cost.",  # noqa: E501
    ] = None,
    start_date_for_cost: Annotated[
        str | None,
        "Datetime in ISO-8601 format, UTC, precise to day: `[YYYY-MM-DD]` for cost beginning this day. Either specify `start_date_for_cost` or `start_month_for_cost`, but not both. The date cannot be more than two months in the past. Use with `end_date_for_cost` for cumulative day-over-day cost.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetEstimatedCostByOrg'."]:
    """Retrieve estimated cost data for multi-org Datadog accounts.

    This tool retrieves the estimated cost for the current and previous month across multi-org and single root-org Datadog accounts. The data is only available for parent-level organizations and is delayed by up to 72 hours."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/usage/estimated_cost".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "view": cost_view_level,
            "start_month": initial_cost_month,
            "end_month": cost_end_month,
            "start_date": start_date_for_cost,
            "end_date": end_date,
            "include_connected_accounts": include_connected_accounts,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_historical_cost_by_org(
    context: ToolContext,
    start_month_utc: Annotated[
        str,
        "ISO-8601 date format `[YYYY-MM]`, UTC timezone, to specify the start month for cost calculation.",  # noqa: E501
    ],
    cost_view_level: Annotated[
        str | None,
        "Specify cost breakdown level: 'summary' for parent-org or 'sub-org' for sub-org level. Defaults to 'summary'.",  # noqa: E501
    ] = None,
    end_month: Annotated[
        str | None,
        "Datetime in ISO-8601 format, UTC, precise to month `[YYYY-MM]` indicating the ending month for cost data.",  # noqa: E501
    ] = None,
    include_connected_accounts: Annotated[
        bool | None,
        "Include accounts connected as partner customers in Datadog's network. Defaults to false.",
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetHistoricalCostByOrg'."]:
    """Retrieve historical cost data across different organizations.

    This tool retrieves historical cost data for multi-org and single root-org accounts from Datadog. The cost data for a given month becomes available by the 16th of the following month. Accessible only for parent-level organizations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/usage/historical_cost".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "start_month": start_month_utc,
            "view": cost_view_level,
            "end_month": end_month,
            "include_connected_accounts": include_connected_accounts,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_hourly_usage_by_product_family(
    context: ToolContext,
    product_families_to_retrieve: Annotated[
        str,
        "Comma-separated list of product families to retrieve usage data for. Available options include all, analyzed_logs, application_security, etc. Note: audit_logs is deprecated.",  # noqa: E501
    ],
    start_timestamp: Annotated[
        str,
        "Datetime in ISO-8601 format, UTC, precise to hour. Specify the start of usage collection, e.g., '2023-10-05T14'.",  # noqa: E501
    ],
    end_timestamp: Annotated[
        str | None,
        "Datetime in ISO-8601 format (UTC) for usage ending before this hour. Format: [YYYY-MM-DDThh].",  # noqa: E501
    ] = None,
    include_connected_accounts: Annotated[
        bool | None,
        "Include accounts connected as partner customers in the response. Defaults to false.",
    ] = False,
    include_descendants_usage: Annotated[
        bool | None,
        "Include child organization usage in the response. Set to true to include, false to exclude.",  # noqa: E501
    ] = False,
    include_usage_breakdown: Annotated[
        bool | None,
        "Boolean to include breakdown of usage by subcategories for product family logs. Defaults to false.",  # noqa: E501
    ] = False,
    maximum_results_limit: Annotated[
        int | None,
        "Set the maximum number of results to return per page, between 1 and 500. Defaults to 500.",
    ] = 500,
    next_record_id: Annotated[
        str | None,
        "ID to continue listing results from the last query. Use the ID from previous queries to paginate through results.",  # noqa: E501
    ] = None,
    product_family_versions: Annotated[
        str | None,
        "Comma-separated list of product family versions in the format `product_family:version`. Defaults to latest if not specified.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetHourlyUsage'."]:
    """Fetch hourly usage data by product family from Datadog.

    Use this tool to retrieve detailed hourly usage metrics categorized by product family from Datadog. Ideal for monitoring and analysis applications that require precise usage insights."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/usage/hourly_usage".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "filter[timestamp][start]": start_timestamp,
            "filter[timestamp][end]": end_timestamp,
            "filter[product_families]": product_families_to_retrieve,
            "filter[include_descendants]": include_descendants_usage,
            "filter[include_connected_accounts]": include_connected_accounts,
            "filter[include_breakdown]": include_usage_breakdown,
            "filter[versions]": product_family_versions,
            "page[limit]": maximum_results_limit,
            "page[next_record_id]": next_record_id,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_projected_cost(
    context: ToolContext,
    cost_view_level: Annotated[
        str | None,
        "Specify cost breakdown level: `summary` for parent-org or `sub-org` for sub-org level. Defaults to `summary`.",  # noqa: E501
    ] = None,
    include_connected_accounts: Annotated[
        bool | None,
        "Include accounts connected as partner customers in the Datadog partner network. Defaults to `false`.",  # noqa: E501
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetProjectedCost'."]:
    """Retrieve projected cost for multi-org and single root-org accounts.

    This tool fetches projected cost data, available for the current month, for multi-org and single root-org accounts, becoming accessible around the 12th of the month. It's used for parent-level organizations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/usage/projected_cost".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "view": cost_view_level,
            "include_connected_accounts": include_connected_accounts,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def send_invitations(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SendInvitations'."]:
    """Invite users to join the organization via email.

    This tool sends invitation emails to specified users, asking them to join the organization. It should be used when you need to extend an invite to new members through email.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["SENDINVITATIONS_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["SENDINVITATIONS_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["SENDINVITATIONS_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/user_invitations".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SENDINVITATIONS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_user_invitation(
    context: ToolContext,
    user_invitation_uuid: Annotated[
        str, "The unique UUID of the user invitation required to retrieve its details."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetInvitation'."]:
    """Retrieve a user invitation using its UUID.

    Use this tool to obtain details of a specific user invitation from Datadog by providing the unique identifier (UUID)."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/user_invitations/{user_invitation_uuid}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            user_invitation_uuid=user_invitation_uuid,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_all_organization_users(
    context: ToolContext,
    order_users_by: Annotated[
        str | None,
        "Specify the user attribute to order results by. Options include `name`, `modified_at`, and `user_count`. Use a negative sign for descending order, e.g., `-name`.",  # noqa: E501
    ] = "name",
    page_number: Annotated[
        int | None, "The specific page number of users to return. Use for pagination."
    ] = 0,
    page_size: Annotated[
        int | None,
        "Specifies the number of users to be returned in a single page. The maximum value allowed is 100.",  # noqa: E501
    ] = 10,
    sort_direction: Annotated[
        str | None,
        "Direction of sort for user listing. Options: 'asc' for ascending, 'desc' for descending.",
    ] = "desc",
    user_filter_string: Annotated[
        str | None, "String to filter users by. Defaults to no filtering if blank or omitted."
    ] = None,
    user_status_filter: Annotated[
        str | None,
        "Filter users by status. Comma separated values: `Active`, `Pending`, `Disabled`. Defaults to no filtering.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListUsers'."]:
    """Retrieve all users in the organization including inactive ones.

    Use this tool to get a complete list of users within the organization, covering active, deactivated, and unverified accounts."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/users".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="GET",
        params=remove_none_values({
            "page[size]": page_size,
            "page[number]": page_number,
            "sort": order_users_by,
            "sort_dir": sort_direction,
            "filter": user_filter_string,
            "filter[status]": user_status_filter,
        }),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_organization_user(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateUser'."]:
    """Create a user for your organization in Datadog.

    This tool allows you to create a new user for your organization in Datadog. Use it when you need to add a team member to your Datadog account.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEORGANIZATIONUSER_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEORGANIZATIONUSER_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEORGANIZATIONUSER_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/users".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEORGANIZATIONUSER_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def disable_user(
    context: ToolContext,
    user_identifier: Annotated[str, "The unique identifier of the user to be disabled."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DisableUser'."]:
    """Disable a specific user in the system.

    This tool disables a user account and should be used by administrator users to manage user access. It indicates whether the operation was successful or not."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/users/{user_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), user_id=user_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_user_details(
    context: ToolContext,
    user_id: Annotated[
        str, "The unique identifier for the user whose details are being retrieved."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetUser'."]:
    """Retrieve details of a specific user by their user ID.

    Use this tool to access detailed information about a user in your organization by specifying their unique user ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/users/{user_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), user_id=user_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_datadog_user(
    context: ToolContext,
    user_id: Annotated[str, "The unique identifier for the user to be updated in Datadog."],
    user_identifier: Annotated[str, "The unique ID of the user to be updated in Datadog."],
    disable_user: Annotated[
        bool | None, "Boolean value to set if the user is disabled (true) or enabled (false)."
    ] = None,
    user_email: Annotated[
        str | None, "The email address of the user to be updated in Datadog."
    ] = None,
    user_name: Annotated[str | None, "The name of the user to be updated in Datadog."] = None,
    user_resource_type: Annotated[
        str, "Specifies the resource type for the user. Must be set to 'users'."
    ] = "users",
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateUser'."]:
    """Update a user's information in Datadog.

    Use this tool to edit the details of a user in Datadog. It requires an admin user's application key. Ideal for managing user access or updating user profiles as needed."""  # noqa: E501
    request_data = remove_none_values({
        "data": {
            "attributes": {"disabled": disable_user, "email": user_email, "name": user_name},
            "id": user_identifier,
            "type": user_resource_type,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/users/{user_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), user_id=user_id
        ),
        method="PATCH",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_user_organizations(
    context: ToolContext,
    user_identifier: Annotated[
        str, "The unique ID of the user whose organizations and information are to be retrieved."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListUserOrganizations'."]:
    """Retrieve a user's organizations and information.

    Use this tool to get information about a specific user and list all the organizations they have joined. Useful for understanding user affiliations within Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/users/{user_id}/orgs".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), user_id=user_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_user_permissions(
    context: ToolContext,
    user_identifier: Annotated[
        str, "The unique identifier for the Datadog user whose permissions you want to retrieve."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListUserPermissions'."]:
    """Retrieve a user's permissions from Datadog.

    This tool fetches the permissions assigned to a user based on their roles in Datadog. It should be called when you need to know the specific access rights a user has."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/users/{user_id}/permissions".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), user_id=user_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_user_memberships(
    context: ToolContext,
    user_uuid: Annotated[
        str, "The unique identifier for the user whose memberships are to be retrieved."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetUserMemberships'."]:
    """Retrieve a user's memberships from Datadog.

    This tool fetches a list of group memberships for a specified user using their UUID in Datadog's system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/users/{user_uuid}/memberships".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), user_uuid=user_uuid
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def create_datadog_workflow(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateWorkflow'."]:
    """Creates a new workflow in Datadog and returns its ID.

    This tool is used to create a new workflow in Datadog, returning the workflow ID. It requires having a registered application key or configured permissions in the UI. This can be useful for automating processes and managing workflows effectively within the Datadog platform.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEDATADOGWORKFLOW_REQUEST_BODY_SCHEMA"
            ],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEDATADOGWORKFLOW_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["CREATEDATADOGWORKFLOW_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/workflows".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL")
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEDATADOGWORKFLOW_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def delete_workflow(
    context: ToolContext,
    workflow_id: Annotated[
        str,
        "The ID of the workflow to be deleted. Ensure it is a valid and existing workflow ID in Datadog.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteWorkflow'."]:
    """Delete a specified workflow by its ID.

    Use this tool to delete an existing workflow by providing the workflow ID. Ensure you have the necessary permissions or a registered application key from Datadog before calling this tool."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/workflows/{workflow_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), workflow_id=workflow_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_workflow_by_id(
    context: ToolContext,
    workflow_identifier: Annotated[
        str, "The unique ID of the workflow to retrieve details for within Datadog."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetWorkflow'."]:
    """Retrieve workflow details using a unique ID.

    Use this tool to get detailed information about a specific workflow by providing its unique ID. This is useful for tracking and managing workflows within Datadog."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/workflows/{workflow_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), workflow_id=workflow_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def update_workflow_by_id(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    workflow_identifier: Annotated[
        str | None,
        "The unique identifier for the workflow you wish to update in Datadog.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateWorkflow'."]:
    """Update a specific workflow by its ID.

    Use this tool to update the details of a workflow in Datadog by providing its ID. Ensure required permissions via a registered application key or configure them in the UI.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEWORKFLOWBYID_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not workflow_identifier:
        missing_params.append(("workflow_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEWORKFLOWBYID_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(
                    REQUEST_BODY_SCHEMAS["UPDATEWORKFLOWBYID_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/workflows/{workflow_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), workflow_id=workflow_identifier
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEWORKFLOWBYID_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def list_workflow_instances(
    context: ToolContext,
    workflow_id: Annotated[str, "The ID of the workflow to retrieve instances for."],
    page_number: Annotated[
        int | None, "The specific page number to return when listing workflow instances."
    ] = 0,
    page_size: Annotated[
        int | None, "Size for a given page. Must be an integer with a maximum value of 100."
    ] = 10,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListWorkflowInstances'."]:
    """Retrieve all instances of a specific workflow from Datadog.

    Use this tool to get a list of all instances for a specified workflow within Datadog. Ensure you have a registered application key or the necessary permissions configured in the UI."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/workflows/{workflow_id}/instances".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), workflow_id=workflow_id
        ),
        method="GET",
        params=remove_none_values({"page[size]": page_size, "page[number]": page_number}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def execute_workflow(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    workflow_identifier: Annotated[
        str | None,
        "The unique ID of the Datadog workflow to be executed.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateWorkflowInstance'."]:
    """Execute a specified workflow in Datadog.

    Initiates the execution of a workflow in Datadog using the provided workflow ID. Ensure that a registered application key or appropriate UI configuration is used for authentication.

    Note: Understanding the request schema is necessary to properly create
    the stringified JSON input object for execution.\n\nThis operation also requires path parameters.

    Modes:
    - GET_REQUEST_SCHEMA: Returns the schema. Only call if you don't
      already have it. Do NOT call repeatedly if you already received
      the schema.
    - EXECUTE: Performs the operation with the provided request body
      JSON.\n      Note: You must also provide the required path parameters when executing.

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["EXECUTEWORKFLOW_REQUEST_BODY_SCHEMA"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated the object following the schema "
                "structure and requirements, call this tool again with "
                "mode='execute' and the stringified JSON as the "
                "request_body parameter along with the required path parameters. "
                "Do NOT call the schema mode again - you already have "
                "the schema now."
            ),
        }

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not workflow_identifier:
        missing_params.append(("workflow_identifier", "path"))

    if missing_params:
        param_names = [p[0] for p in missing_params]
        param_details = ", ".join([f"{p[0]} ({p[1]})" for p in missing_params])
        raise RetryableToolError(
            message=f"Missing required parameters: {param_names}",
            developer_message=(f"Required parameters validation failed: {param_details}"),
            additional_prompt_content=(
                f"The following required parameters are missing: "
                f"{param_details}. Please call this tool again with all "
                "required parameters."
            ),
        )

    # Validate request body is provided (not None or empty string)
    # Note: Empty objects like {} are allowed - schema validation will check if valid
    if request_body is None or request_body.strip() == "":
        raise RetryableToolError(
            message="Request body is required when mode is 'execute'",
            developer_message="The request_body parameter was null or empty string",
            additional_prompt_content=(
                "The request body is required to perform this operation. "
                "Use the schema below to construct a valid JSON object, "
                "then call this tool again in execute mode with the "
                "stringified JSON as the request_body parameter.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["EXECUTEWORKFLOW_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        )

    # Parse JSON
    try:
        request_data = json.loads(request_body)
    except json.JSONDecodeError as e:
        raise RetryableToolError(
            message=f"Invalid JSON in request body: {e!s}",
            developer_message=f"JSON parsing failed: {e!s}",
            additional_prompt_content=(
                f"The request body contains invalid JSON. Error: {e!s}\n\n"
                "Please provide a valid JSON string that matches the schema "
                "below, then call this tool again in execute mode.\n\n"
                "Schema:\n\n"
                + json.dumps(REQUEST_BODY_SCHEMAS["EXECUTEWORKFLOW_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://{datadog_base_url}/api/v2/workflows/{workflow_id}/instances".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"), workflow_id=workflow_identifier
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["EXECUTEWORKFLOW_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def get_workflow_instance(
    context: ToolContext,
    workflow_id: Annotated[
        str, "The unique identifier of the workflow to retrieve its specific execution details."
    ],
    workflow_instance_id: Annotated[str, "The ID of the specific workflow instance to retrieve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetWorkflowInstance'."]:
    """Retrieve a specific workflow execution instance.

    Fetches details of a specific execution for a given Datadog workflow. Requires proper application key registration or permissions setup."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/workflows/{workflow_id}/instances/{instance_id}".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            workflow_id=workflow_id,
            instance_id=workflow_instance_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["DATADOG_API_KEY", "DATADOG_APPLICATION_KEY", "DATADOG_BASE_URL"])
async def cancel_workflow_instance(
    context: ToolContext,
    workflow_id: Annotated[
        str,
        "The unique ID of the workflow to cancel. It must be a valid string as per the API specifications.",  # noqa: E501
    ],
    workflow_instance_id: Annotated[
        str, "The unique identifier of the workflow instance to be canceled."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelWorkflowInstance'."]:
    """Cancel a specific execution of a workflow.

    Use this tool to cancel a specific execution of a given workflow in Datadog. Ensure you have the necessary application key or permissions configured to perform this action."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://{datadog_base_url}/api/v2/workflows/{workflow_id}/instances/{instance_id}/cancel".format(
            datadog_base_url=context.get_secret("DATADOG_BASE_URL"),
            workflow_id=workflow_id,
            instance_id=workflow_instance_id,
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "DD-API-KEY": context.get_secret("DATADOG_API_KEY"),
            "DD-APPLICATION-KEY": context.get_secret("DATADOG_APPLICATION_KEY"),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}
