"""Arcade Starter Tools for Vercel

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=["VERCEL_ACCESS_TOKEN"])
async def read_access_group(
    context: ToolContext,
    access_group_id_or_name: Annotated[
        str, "The ID or name of the access group to retrieve details for."
    ],
    team_identifier: Annotated[
        str | None, "The identifier of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The Team slug used to perform the request on behalf of the specified team."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'readAccessGroup'."]:
    """Retrieve details of a specific access group.

    Use this tool to fetch information about an access group by its ID or name. It helps in obtaining the detailed configuration and properties of the specific access group."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/access-groups/{idOrName}".format(  # noqa: UP032
            idOrName=access_group_id_or_name
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_access_group(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    access_group_id_or_name: Annotated[
        str | None,
        "The ID or name of the access group to update. Use either the unique identifier or the group's name to specify which access group you want to modify.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the team on whose behalf the request is performed.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The identifier for the team, used to perform the request on its behalf.  Only used when mode is 'execute'.",
    ] = 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 'updateAccessGroup'."]:
    """Update metadata for an access group.

    Use this tool to update the metadata of a specified access group by its ID or name. This is helpful when you need to modify access controls or group settings.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEACCESSGROUP_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 access_group_id_or_name:
        missing_params.append(("access_group_id_or_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["UPDATEACCESSGROUP_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["UPDATEACCESSGROUP_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/access-groups/{idOrName}".format(  # noqa: UP032
            idOrName=access_group_id_or_name
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEACCESSGROUP_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_access_group(
    context: ToolContext,
    group_id_or_name: Annotated[str, "The ID or name of the access group to be deleted."],
    team_identifier: Annotated[
        str | None, "The identifier for the team on whose behalf the request is made."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug for the team on whose behalf the request will be made. This identifies the team uniquely.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteAccessGroup'."]:
    """Deletes an access group by ID or name.

    Use this tool to delete an access group on Vercel by specifying the group's ID or name. It is useful for managing and updating your access control configurations."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/access-groups/{idOrName}".format(idOrName=group_id_or_name),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def list_access_group_members(
    context: ToolContext,
    access_group_id_or_name: Annotated[
        str, "Specify the ID or name of the access group to list its members."
    ],
    continuation_cursor_for_paging: Annotated[
        str | None, "Cursor used to retrieve the next page of access group members."
    ] = None,
    member_limit: Annotated[
        int | None, "Specify the maximum number of access group members to return."
    ] = None,
    member_search_query: Annotated[
        str | None, "Search for members using their name, username, or email."
    ] = None,
    team_identifier: Annotated[
        str | None, "The Team identifier for which to list access group members."
    ] = None,
    team_slug: Annotated[
        str | None, "The unique slug of the team for which you want to list access group members."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'listAccessGroupMembers'."]:
    """Retrieve members of a specific access group.

    This tool calls the API to list all members within a specified access group. Use it to retrieve member information for management or review purposes."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/access-groups/{idOrName}/members".format(  # noqa: UP032
            idOrName=access_group_id_or_name
        ),
        method="GET",
        params=remove_none_values({
            "limit": member_limit,
            "next": continuation_cursor_for_paging,
            "search": member_search_query,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def list_access_groups(
    context: ToolContext,
    access_groups_limit: Annotated[
        int | None, "Specify the maximum number of access groups to be returned in the response."
    ] = None,
    continuation_cursor_for_next_page: Annotated[
        str | None, "A string to retrieve the next page of results using a continuation cursor."
    ] = None,
    max_projects_in_response: Annotated[
        int | None, "Specify the maximum number of projects to include in the response list."
    ] = None,
    members_inclusion_limit: Annotated[
        int | None, "Specify the number of members to include in the response."
    ] = None,
    project_id: Annotated[
        str | None, "Filter access groups by the specified project ID in Vercel."
    ] = None,
    search_access_groups_by_name: Annotated[
        str | None, "Provide a name or keyword to search for specific access groups."
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The ID of the team for which to list access groups. Specify this to perform the request on behalf of a specific team.",
    ] = None,
    team_slug: Annotated[
        str | None, "A string representing the Team slug to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'listAccessGroups'."]:
    """Retrieve a list of access groups within Vercel.

    Use this tool to obtain a detailed list of all access groups available in your Vercel account."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/access-groups",
        method="GET",
        params=remove_none_values({
            "projectId": project_id,
            "search": search_access_groups_by_name,
            "membersLimit": members_inclusion_limit,
            "projectsLimit": max_projects_in_response,
            "limit": access_groups_limit,
            "next": continuation_cursor_for_next_page,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_access_group(
    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 identifier of the team on whose behalf the access group is being created.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The unique slug identifier for the team to create the access group for.  Only used when mode is 'execute'.",
    ] = 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 'createAccessGroup'."]:
    """Create a new access group on Vercel.

    This tool should be called when there is a need to create a new access group in Vercel. It facilitates setting up user permissions and organization in projects.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEACCESSGROUP_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["CREATEACCESSGROUP_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["CREATEACCESSGROUP_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/access-groups",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEACCESSGROUP_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def list_access_group_projects(
    context: ToolContext,
    access_group_identifier: Annotated[
        str, "The ID or name of the Access Group to list its projects."
    ],
    continuation_cursor: Annotated[
        str | None,
        "The continuation cursor used to retrieve the next page of results in a paginated response.",
    ] = None,
    max_project_count: Annotated[
        int | None, "Maximum number of access group projects to return. Must be an integer."
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the team to perform the request on behalf of. This specifies which team's access group projects to list.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team to perform the request on behalf of. Specify to filter projects by team.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'listAccessGroupProjects'."]:
    """Retrieve a list of projects for a given access group.

    This tool should be called to obtain a list of projects associated with a specific access group by providing the group ID or name."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/access-groups/{idOrName}/projects".format(  # noqa: UP032
            idOrName=access_group_identifier
        ),
        method="GET",
        params=remove_none_values({
            "limit": max_project_count,
            "next": continuation_cursor,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_access_group_project(
    context: ToolContext,
    access_group_id_or_name: Annotated[
        str,
        "Identifier or name of the access group to associate with the project. It helps specify which access group the new project will be part of.",
    ],
    project_id: Annotated[str, "The unique ID of the project to be added to the access group."],
    project_role: Annotated[
        str,
        "The role to be assigned to the project within the access group. Options: 'ADMIN', 'PROJECT_VIEWER', 'PROJECT_DEVELOPER'.",
    ],
    team_identifier: Annotated[
        str | None, "The unique identifier for the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The unique identifier slug for the team on whose behalf the request is made."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'createAccessGroupProject'."]:
    """Create a project within a specific access group."""
    request_data = remove_none_values({"projectId": project_id, "role": project_role})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/access-groups/{accessGroupIdOrName}/projects".format(  # noqa: UP032
            accessGroupIdOrName=access_group_id_or_name
        ),
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_access_group_project(
    context: ToolContext,
    access_group_id_or_name: Annotated[
        str, "The identifier or name of the access group for the project."
    ],
    project_id: Annotated[
        str,
        "The unique identifier for the project within the access group. It is required to fetch the project details.",
    ],
    team_identifier: Annotated[
        str | None,
        "The identifier of the team to perform the request on behalf of. This should be a string representing the team's unique ID.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug to perform the request on behalf of. Provide the specific slug associated with the team.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'readAccessGroupProject'."]:
    """Retrieve details of a specific access group project.

    Use this tool to obtain information about a specific project within an access group by providing the access group identifier or name and the project ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/access-groups/{accessGroupIdOrName}/projects/{projectId}".format(  # noqa: UP032
            accessGroupIdOrName=access_group_id_or_name, projectId=project_id
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_access_group_project(
    context: ToolContext,
    access_group_id_or_name: Annotated[
        str, "Specify the access group by its ID or name to target the update."
    ],
    project_id: Annotated[
        str, "The unique identifier for the project to update in the access group."
    ],
    project_role: Annotated[
        str,
        "Specify the project role to add to the access group. Choose from 'ADMIN', 'PROJECT_VIEWER', or 'PROJECT_DEVELOPER'.",
    ],
    team_identifier: Annotated[
        str | None, "The unique identifier for the team on whose behalf the request is made."
    ] = None,
    team_slug: Annotated[
        str | None, "The unique identifier for the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'updateAccessGroupProject'."]:
    """Update an access group project in Vercel.

    This tool allows updating the details of a specific access group project in Vercel. It should be called when modifications to an existing access group project are needed, such as changing roles or permissions."""
    request_data = remove_none_values({"role": project_role})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/access-groups/{accessGroupIdOrName}/projects/{projectId}".format(  # noqa: UP032
            accessGroupIdOrName=access_group_id_or_name, projectId=project_id
        ),
        method="PATCH",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_access_group_project(
    context: ToolContext,
    access_group_id_or_name: Annotated[
        str,
        "Enter the access group ID or name to identify the specific group for project deletion.",
    ],
    project_id: Annotated[str, "The ID of the project you want to delete from the access group."],
    team_identifier: Annotated[
        str | None, "The ID of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug of the team to perform the deletion on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteAccessGroupProject'."]:
    """Delete a specified access group project on Vercel.

    Use this tool to delete a specific access group project on Vercel by providing the access group ID or name and project ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/access-groups/{accessGroupIdOrName}/projects/{projectId}".format(  # noqa: UP032
            accessGroupIdOrName=access_group_id_or_name, projectId=project_id
        ),
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def record_cache_events(
    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 Team identifier to perform the request on behalf of. Used to specify which team's cache usage events are being recorded.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug to perform the request on behalf of. It identifies the specific team within Vercel.  Only used when mode is 'execute'.",
    ] = None,
    ci_environment: Annotated[
        str | None,
        "The continuous integration or delivery environment where this artifact is downloaded.  Only used when mode is 'execute'.",
    ] = None,
    is_interactive_shell: Annotated[
        int | None,
        "Set to 1 if the client is an interactive shell, otherwise set to 0.  Only used when mode is 'execute'.",
    ] = 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 'recordEvents'."]:
    """Record artifacts cache usage events for Vercel.

    Use this tool to log cache usage events, including types 'HIT' or 'MISS', and sources 'LOCAL' or 'REMOTE'. Useful for tracking cache performance and usage.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["RECORDCACHEEVENTS_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["RECORDCACHEEVENTS_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["RECORDCACHEEVENTS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v8/artifacts/events",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["RECORDCACHEEVENTS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "x-artifact-client-ci": ci_environment,
            "x-artifact-client-interactive": is_interactive_shell,
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def check_remote_caching_status(
    context: ToolContext,
    team_identifier: Annotated[
        str | None, "The unique identifier for the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The unique slug identifier for the team on whose behalf the request is made."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'status'."]:
    """Check the status of Remote Caching.

    This tool is used to determine if Remote Caching is currently enabled, disabled, or disabled due to usage limits. It provides a quick way to verify the status of caching in Vercel."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v8/artifacts/status",
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def download_cache_artifact(
    context: ToolContext,
    artifact_hash: Annotated[str, "The unique hash identifier for the cache artifact to download."],
    ci_environment: Annotated[
        str | None,
        "Specify the continuous integration or delivery environment from which the artifact is downloaded.",
    ] = None,
    interactive_shell_client: Annotated[
        int | None, "Set to 1 if the client is an interactive shell; otherwise set to 0."
    ] = None,
    team_identifier: Annotated[
        str | None, "The Team identifier to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team to perform the request on behalf of, identifying which team's artifact to download.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'downloadArtifact'."]:
    """Downloads a cache artifact using its hash identifier.

    Use this tool to retrieve a cache artifact from Vercel by specifying its hash. The artifact is returned as an octet-stream. Ensure to verify the content-length header and response body for correctness."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v8/artifacts/{hash}".format(hash=artifact_hash),  # noqa: UP032
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "x-artifact-client-ci": ci_environment,
            "x-artifact-client-interactive": interactive_shell_client,
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def query_artifacts_info(
    context: ToolContext,
    artifact_hashes: Annotated[list[str], "An array of artifact hashes to query information for."],
    team_identifier: Annotated[
        str | None, "The identifier of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug identifying the team for which the request is performed."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'artifactQuery'."]:
    """Retrieve detailed information about multiple artifacts.

    This tool queries detailed information about an array of artifacts. It should be called when you need specifics or insights regarding multiple artifacts from the Vercel platform."""
    request_data = remove_none_values({"hashes": artifact_hashes})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v8/artifacts",
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_new_deployment_check(
    context: ToolContext,
    block_deployment_on_failure: Annotated[
        bool, "Indicates if the check should block a deployment from succeeding."
    ],
    check_name: Annotated[
        str, "The name of the check being created. This is required to identify the check purpose."
    ],
    deployment_id: Annotated[
        str, "The unique identifier of the deployment to create the check for."
    ],
    allow_rerun_request: Annotated[
        bool | None, "Allow users to request a rerun of the check if it fails. Use a boolean value."
    ] = None,
    details_url: Annotated[
        str | None,
        "A URL that provides further details about the check. Expected format is a valid URL string.",
    ] = None,
    external_identifier: Annotated[
        str | None, "A unique identifier used as an external reference for the check."
    ] = None,
    page_path_to_check: Annotated[
        str | None,
        "Specify the path of the page that is being checked. This should be a string value representing the specific page path for the deployment check.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The unique identifier used to perform the request on behalf of a team."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug (unique identifier) of the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'createCheck'."]:
    """Create a new deployment check using Vercel API.

    Use this tool to initiate a new check for a specific deployment in Vercel. This requires providing a deployment ID and must be authorized with OAuth2 to avoid errors."""
    request_data = remove_none_values({
        "name": check_name,
        "path": page_path_to_check,
        "blocking": block_deployment_on_failure,
        "detailsUrl": details_url,
        "externalId": external_identifier,
        "rerequestable": allow_rerun_request,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/deployments/{deploymentId}/checks".format(  # noqa: UP032
            deploymentId=deployment_id
        ),
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def list_deployment_checks(
    context: ToolContext,
    deployment_id: Annotated[str, "The ID of the deployment to retrieve checks for."],
    team_identifier: Annotated[
        str | None,
        "The ID of the team to perform the request for. This identifies which team's context is used.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug used to perform the request. This identifies the team under which the deployment was made.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getAllChecks'."]:
    """List all checks for a specific deployment.

    Fetches and lists all checks created for a given deployment using its ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/deployments/{deploymentId}/checks".format(  # noqa: UP032
            deploymentId=deployment_id
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_deployment_check_details(
    context: ToolContext,
    check_identifier: Annotated[str, "The unique identifier of the check to fetch details for."],
    deployment_id: Annotated[
        str, "The ID of the deployment for which the check details are required."
    ],
    team_id: Annotated[
        str | None, "The identifier for the team on whose behalf the request is performed."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug identifier for the team to perform the request on behalf of. It uniquely represents the team within the system.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getCheck'."]:
    """Retrieve details for a specific deployment check.

    This tool is used to obtain detailed information about a single check within a specified deployment. Use it when you need insights or results from a specific deployment check."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/deployments/{deploymentId}/checks/{checkId}".format(  # noqa: UP032
            deploymentId=deployment_id, checkId=check_identifier
        ),
        method="GET",
        params=remove_none_values({"teamId": team_id, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_existing_check(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    deployment_id: Annotated[
        str | None,
        "The identifier for the deployment to update the check for. Ensure it is a valid string.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    check_identifier: Annotated[
        str | None,
        "The unique identifier of the check to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The identifier of the Team to perform the request on behalf of.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team to perform the request on behalf of. This identifies the team within Vercel.  Only used when mode is 'execute'.",
    ] = 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 'updateCheck'."]:
    """Updates an existing deployment check.

    Use this tool to update an existing check in a deployment on Vercel. Ensure that OAuth2 authentication is used to avoid errors.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEEXISTINGCHECK_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 deployment_id:
        missing_params.append(("deployment_id", "path"))
    if not check_identifier:
        missing_params.append(("check_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["UPDATEEXISTINGCHECK_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["UPDATEEXISTINGCHECK_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/deployments/{deploymentId}/checks/{checkId}".format(  # noqa: UP032
            deploymentId=deployment_id, checkId=check_identifier
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEEXISTINGCHECK_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def rerequest_check(
    context: ToolContext,
    check_to_rerun_id: Annotated[
        str,
        "The ID of the check you want to rerun. This identifies the specific failed check to retry.",
    ],
    deployment_id: Annotated[
        str,
        "The ID of the deployment for which the check needs to be rerun. This specifies the which specific deployment's check is to be retried.",
    ],
    mark_check_as_running: Annotated[
        bool | None, "Mark the check as running if set to true when re-requested."
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The Team identifier to perform the request on behalf of. It specifies which team's context the request should be executed in.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The identifier for the team to perform the check rerequest on behalf of. Use the team's slug format.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'rerequestCheck'."]:
    """Retries a failed deployment check.

    Use this tool to retry a specific deployment check that has failed. Ideal when you need to manually reinvoke a check due to a transient failure or updated conditions."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/deployments/{deploymentId}/checks/{checkId}/rerequest".format(  # noqa: UP032
            deploymentId=deployment_id, checkId=check_to_rerun_id
        ),
        method="POST",
        params=remove_none_values({
            "autoUpdate": mark_check_as_running,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_project_data_cache(
    context: ToolContext,
    project_id: Annotated[
        str, "The unique identifier for the Vercel project to update the data cache."
    ],
    disable_data_cache: Annotated[
        bool | None,
        "Set to true to disable the project's data cache, or false to enable it. Default is false.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique Team ID to perform the request on behalf of. Required for targeted updates.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug to perform the request on behalf of. Use this to specify which team's project to update.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'updateProjectDataCache'."]:
    """Update the data cache for a Vercel project.

    This tool updates the data cache feature on a specified Vercel project. Use it when you need to refresh or modify the data cache to ensure the project is up-to-date with the latest changes."""
    request_data = remove_none_values({"disabled": disable_data_cache})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/data-cache/projects/{projectId}".format(  # noqa: UP032
            projectId=project_id
        ),
        method="PATCH",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def fetch_deployment_logs(
    context: ToolContext,
    deployment_identifier_or_hostname: Annotated[
        str, "The unique identifier or hostname of the deployment to fetch logs for."
    ],
    build_event_delimiter: Annotated[
        float | None,
        "Specify the delimiter type for separating logged events. Use '0' for no delimiter and '1' for an alternative separation method.",
    ] = None,
    deployment_build_id: Annotated[
        str | None,
        "The unique identifier for the deployment build for which logs are to be retrieved.",
    ] = None,
    enable_live_streaming: Annotated[
        float | None,
        "Set to '1' to enable live streaming of events as they happen. Use '0' to disable live streaming.",
    ] = None,
    event_order: Annotated[
        str | None,
        "Specifies the order of returned events by timestamp. Use 'forward' for chronological order or 'backward' for reverse order.",
    ] = "forward",
    fetch_until_timestamp: Annotated[
        float | None, "Timestamp up to which the build logs should be retrieved."
    ] = None,
    filter_by_status_code: Annotated[
        str | None, "Specify the HTTP status code range to filter deployment events."
    ] = None,
    include_builds: Annotated[
        float | None, "Specify whether to include build events (1) or not (0) in the response."
    ] = None,
    maximum_events_to_return: Annotated[
        float | None, "Specify the max number of events to return. Use `-1` for all available logs."
    ] = None,
    start_timestamp_for_logs: Annotated[
        float | None,
        "Timestamp from which to start retrieving build logs. Provide in milliseconds.",
    ] = None,
    team_identifier: Annotated[
        str | None, "Identifies the team on whose behalf the request is performed."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The unique identifier (slug) of the Team for which the request is made. Used to specify the team context.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getDeploymentEvents'."]:
    """Retrieve build logs for a specific deployment by ID.

    Use this tool to access the build logs or events of a deployment using its deployment ID and build ID. It can stream logs continuously or return them in JSON format based on input parameters."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v3/deployments/{idOrUrl}/events".format(  # noqa: UP032
            idOrUrl=deployment_identifier_or_hostname
        ),
        method="GET",
        params=remove_none_values({
            "direction": event_order,
            "follow": enable_live_streaming,
            "limit": maximum_events_to_return,
            "name": deployment_build_id,
            "since": start_timestamp_for_logs,
            "until": fetch_until_timestamp,
            "statusCode": filter_by_status_code,
            "delimiter": build_event_delimiter,
            "builds": include_builds,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_integration_deployment(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    deployment_id: Annotated[
        str | None,
        "The unique identifier for the deployment to update. This is required to specify which deployment's integration action should be modified.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    integration_configuration_id: Annotated[
        str | None,
        "The ID of the integration configuration to update. This is required to specify which integration setup the action applies to.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    resource_id: Annotated[
        str | None,
        "The unique identifier for the resource to be updated in the deployment integration.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    deployment_action: Annotated[
        str | None,
        "Specifies the action to be taken for the deployment integration. Expected as a descriptive string indicating the action type.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'update-integration-deployment-action'."
]:
    """Update a deployment integration action.

    Use this tool to update the action of a deployment integration for a specific installation. It is ideal for modifying settings or actions related to deployment integrations.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEINTEGRATIONDEPLOYMENT_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 deployment_id:
        missing_params.append(("deployment_id", "path"))
    if not integration_configuration_id:
        missing_params.append(("integration_configuration_id", "path"))
    if not resource_id:
        missing_params.append(("resource_id", "path"))
    if not deployment_action:
        missing_params.append(("deployment_action", "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["UPDATEINTEGRATIONDEPLOYMENT_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["UPDATEINTEGRATIONDEPLOYMENT_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/deployments/{deploymentId}/integrations/{integrationConfigurationId}/resources/{resourceId}/actions/{action}".format(  # noqa: UP032
            deploymentId=deployment_id,
            integrationConfigurationId=integration_configuration_id,
            resourceId=resource_id,
            action=deployment_action,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEINTEGRATIONDEPLOYMENT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_deployment_info(
    context: ToolContext,
    deployment_id_or_url: Annotated[
        str, "The unique identifier or hostname of the deployment to retrieve details for."
    ],
    include_git_repo_information: Annotated[
        str | None, "Set to 'true' to include Git repository details or 'false' to exclude them."
    ] = None,
    team_identifier: Annotated[
        str | None, "The unique identifier for the team on whose behalf the request is made."
    ] = None,
    team_slug: Annotated[
        str | None, "The Team slug for performing the request on behalf of that team."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getDeployment'."]:
    """Retrieve deployment information by ID or URL.

    Use this tool to get details about a Vercel deployment using either its ID or URL. This is useful for obtaining deployment specifics, especially when the user or team owns the deployment."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v13/deployments/{idOrUrl}".format(idOrUrl=deployment_id_or_url),  # noqa: UP032
        method="GET",
        params=remove_none_values({
            "withGitRepoInfo": include_git_repo_information,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_vercel_deployment(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    force_new_deployment: Annotated[
        str | None,
        "Set to 'true' to force a new deployment even if a similar one exists.  Only used when mode is 'execute'.",
    ] = None,
    skip_framework_detection_confirmation: Annotated[
        str | None,
        "Set to 'true' to skip framework detection and avoid confirmation request failures.  Only used when mode is 'execute'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The Team identifier to perform the request on behalf of for creating a deployment on Vercel.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug identifying the team to perform the deployment on behalf of. This is essential for specifying the target team for the deployment request.  Only used when mode is 'execute'.",
    ] = 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 'createDeployment'."]:
    """Create a new deployment on Vercel.

    This tool helps create a new deployment on Vercel, ideal for deploying projects that aren't directly linked to a git repository. Use it to initiate a deployment by providing necessary files either as references or inlined content. It also allows redeployment of previous deployments by specifying a deployment ID.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEVERCELDEPLOYMENT_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["CREATEVERCELDEPLOYMENT_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["CREATEVERCELDEPLOYMENT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v13/deployments",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEVERCELDEPLOYMENT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({
            "forceNew": force_new_deployment,
            "skipAutoDetectionConfirmation": skip_framework_detection_confirmation,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def cancel_deployment(
    context: ToolContext,
    deployment_id: Annotated[str, "The unique identifier of the deployment to cancel."],
    team_identifier: Annotated[
        str | None,
        "The Team identifier to perform the request on behalf of when canceling a deployment.",
    ] = None,
    team_slug: Annotated[
        str | None, "The Team slug for which the deployment cancellation should be performed."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'cancelDeployment'."]:
    """Cancel a currently building deployment.

    Use this tool to cancel a deployment that is currently in progress 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://api.vercel.com/v12/deployments/{id}/cancel".format(id=deployment_id),  # noqa: UP032
        method="PATCH",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def purchase_domain(
    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 perform the domain purchase request.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug representing the team to perform the purchase on behalf of. This identifies the team within Vercel.  Only used when mode is 'execute'.",
    ] = 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 'buyDomain'."]:
    """Facilitates the purchase of a specified domain.

    Call this tool to purchase a domain through Vercel's new domain acquisition endpoints.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["PURCHASEDOMAIN_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["PURCHASEDOMAIN_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["PURCHASEDOMAIN_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v5/domains/buy",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["PURCHASEDOMAIN_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_domain_price(
    context: ToolContext,
    domain_name: Annotated[str, "The domain name to check the purchase price for."],
    domain_status_for_pricing: Annotated[
        str | None,
        "Specifies the domain status ('new', 'renewal', 'transfer', 'redemption') to check the price for.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The Team identifier for executing the request on behalf of a specific team. This is usually a unique string assigned to the team.",
    ] = None,
    team_slug_for_request: Annotated[
        str | None, "The slug identifier of the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'checkDomainPrice'."]:
    """Retrieve domain price and purchase period details.

    Use this tool to obtain the cost of purchasing a specific domain and the duration of a single purchase period, replacing the deprecated endpoint for domain price checking."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v4/domains/price",
        method="GET",
        params=remove_none_values({
            "name": domain_name,
            "type": domain_status_for_pricing,
            "teamId": team_identifier,
            "slug": team_slug_for_request,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def check_domain_availability(
    context: ToolContext,
    domain_name: Annotated[str, "The domain name you want to check for purchase availability."],
    team_identifier: Annotated[
        str | None, "The identifier of the Team on whose behalf the request is performed."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug for the team or organization on whose behalf the request is performed.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'checkDomainStatus'."]:
    """Check if a domain name is available for purchase.

    This tool checks the availability of a domain name for purchase, using the deprecated 'checkDomainStatus' endpoint from Vercel. Note that this endpoint has been replaced with 'Get availability for a domain.'"""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v4/domains/status",
        method="GET",
        params=remove_none_values({
            "name": domain_name,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_dns_records(
    context: ToolContext,
    domain_name: Annotated[
        str, "The domain name for which to retrieve DNS records. Must be a valid domain."
    ],
    maximum_records_to_list: Annotated[
        str | None, "Specify the maximum number of DNS records to retrieve in a single request."
    ] = None,
    records_created_after_timestamp: Annotated[
        str | None,
        "Get records created after this specified JavaScript timestamp. It filters DNS records based on the creation date.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The identifier of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug representing the team on whose behalf the request is made."
    ] = None,
    until_timestamp: Annotated[
        str | None, "Retrieve records created before the specified JavaScript timestamp."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getRecords'."]:
    """Retrieve DNS records for a specified domain name.

    This tool fetches DNS records for a given domain. It returns up to 20 records by default, with additional records available through pagination."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v4/domains/{domain}/records".format(domain=domain_name),  # noqa: UP032
        method="GET",
        params=remove_none_values({
            "limit": maximum_records_to_list,
            "since": records_created_after_timestamp,
            "until": until_timestamp,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_dns_record(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    domain_name_for_dns_record: Annotated[
        str | None,
        "The domain for which the DNS record will be created. Must be a valid and registered domain name.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The Team identifier for performing the request.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug identifier for the team performing the DNS record creation. It should be a string that represents the team's unique slug.  Only used when mode is 'execute'.",
    ] = 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 'createRecord'."]:
    """Creates a DNS record for a domain.

    Use this tool to create a DNS record for a specified domain. It helps manage DNS configurations by adding new records 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, 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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEDNSRECORD_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 domain_name_for_dns_record:
        missing_params.append(("domain_name_for_dns_record", "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["CREATEDNSRECORD_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["CREATEDNSRECORD_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v2/domains/{domain}/records".format(  # noqa: UP032
            domain=domain_name_for_dns_record
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEDNSRECORD_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_dns_record(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    dns_record_id: Annotated[
        str | None,
        "The unique identifier of the DNS record to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier of the team performing the request.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug representing the Team to perform the DNS update on behalf of. It is used to specify the team context for the request.  Only used when mode is 'execute'.",
    ] = 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 'updateRecord'."]:
    """Update an existing DNS record for a domain.

    This tool updates an existing DNS record for a specified domain name. It should be called when changes to a DNS record are needed, such as modifying the value or type. The tool returns details of the updated record after the operation is successful.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEDNSRECORD_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 dns_record_id:
        missing_params.append(("dns_record_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["UPDATEDNSRECORD_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["UPDATEDNSRECORD_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/domains/records/{recordId}".format(recordId=dns_record_id),  # noqa: UP032
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEDNSRECORD_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def remove_dns_record(
    context: ToolContext,
    dns_record_id: Annotated[
        str,
        "The unique identifier of the DNS record to be removed. Required for specifying which record to delete.",
    ],
    domain_name: Annotated[
        str, "The domain from which the DNS record will be removed. Provide the full domain name."
    ],
    team_identifier: Annotated[
        str | None, "The identifier for the team on whose behalf the request is performed."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug representing the team on whose behalf the DNS record is removed."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'removeRecord'."]:
    """Removes an existing DNS record from a domain.

    Use this tool to remove a DNS record from a specified domain. Useful when you need to delete outdated or incorrect DNS entries."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v2/domains/{domain}/records/{recordId}".format(  # noqa: UP032
            domain=domain_name, recordId=dns_record_id
        ),
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_supported_tlds(
    context: ToolContext,
    team_id: Annotated[
        str | None, "The ID of the team for which to retrieve supported TLDs."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getSupportedTlds'."]:
    """Retrieve a list of TLDs supported by Vercel.

    This tool provides a list of Top-Level Domains (TLDs) that are supported by Vercel. Use this tool to find out which TLDs can be managed or registered through Vercel's platform."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/registrar/tlds/supported",
        method="GET",
        params=remove_none_values({"teamId": team_id}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_tld_price(
    context: ToolContext,
    top_level_domain: Annotated[
        str,
        "The top-level domain (TLD) for which to retrieve the base price. Examples include 'com', 'net', 'org'.",
    ],
    registration_years: Annotated[
        str | None,
        "The number of years for which the TLD registration price should be calculated. Provide this as an integer representing the duration in years.",
    ] = None,
    team_id: Annotated[
        str | None, "The ID of the team for which the TLD price data is requested."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getTldPrice'."]:
    """Retrieve base price for a specific TLD.

    Use this tool to get the base price for a top-level domain (TLD). It does not account for premium domain pricing. For specific domain prices, refer to a different endpoint."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/registrar/tlds/{tld}/price".format(tld=top_level_domain),  # noqa: UP032
        method="GET",
        params=remove_none_values({"years": registration_years, "teamId": team_id}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def domain_availability_status(
    context: ToolContext,
    domain_name_to_check_availability: Annotated[
        str,
        "The domain name to check for availability. Must be a valid and complete domain name string.",
    ],
    team_identifier: Annotated[
        str | None,
        "A unique identifier for the team whose domain availability is being queried. This is a string value.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getDomainAvailability'."]:
    """Check if a domain is available for purchase.

    This tool checks the availability of a specified domain. If the domain is available, it can be purchased using the appropriate Vercel endpoints."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/registrar/domains/{domain}/availability".format(  # noqa: UP032
            domain=domain_name_to_check_availability
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def fetch_domain_price(
    context: ToolContext,
    domain_name: Annotated[
        str,
        "The domain name to check the pricing for. Provide a fully qualified domain like 'example.com'.",
    ],
    number_of_years: Annotated[
        str | None,
        "Specify the number of years for which the domain pricing is needed. Typically set to 1 or more.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "A string representing the unique identifier of the team associated with the domain. This is required for specifying which team's domain pricing information should be retrieved.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getDomainPrice'."]:
    """Retrieve price data for a specific domain from Vercel.

    Use this tool to get pricing information for a particular domain. It should be called when you need to verify or compare the cost of registering or renewing a domain via Vercel's registrar service."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/registrar/domains/{domain}/price".format(domain=domain_name),  # noqa: UP032
        method="GET",
        params=remove_none_values({"years": number_of_years, "teamId": team_identifier}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def check_domain_availability_bulk(
    context: ToolContext,
    domain_names: Annotated[
        list[str], "A list of domain names to check, with a maximum of 50 domains."
    ],
    team_identifier: Annotated[
        str | None,
        "Unique identifier for the team or organization associated with the request. It may be required to access specific domain availability data.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getBulkAvailability'."]:
    """Check availability for multiple domains.

    Use this tool to check if multiple domains are available for purchase. This can help in planning before acquiring domains via the buying endpoints."""
    request_data = remove_none_values({"domains": domain_names})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/registrar/domains/availability",
        method="POST",
        params=remove_none_values({"teamId": team_identifier}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_domain_auth_code(
    context: ToolContext,
    domain_name: Annotated[
        str,
        "The domain name for which the auth code is being requested. It should be a valid domain registered with Vercel.",
    ],
    team_id: Annotated[
        str | None,
        "The ID representing the Vercel team associated with the domain. Required for accessing team-specific domains.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getDomainAuthCode'."]:
    """Retrieve the auth code for transferring a domain from Vercel.

    This tool is used to obtain the auth code needed to transfer a domain from Vercel to another registrar. It should be called when a domain transfer from Vercel is intended."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/registrar/domains/{domain}/auth-code".format(  # noqa: UP032
            domain=domain_name
        ),
        method="GET",
        params=remove_none_values({"teamId": team_id}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def purchase_domain_vercel(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    domain_name: Annotated[
        str | None,
        "The domain name that you wish to purchase using Vercel's API.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_id: Annotated[
        str | None,
        "The unique identifier for the team under which the domain will be purchased. This is expected to be a string.  Only used when mode is 'execute'.",
    ] = 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 'buySingleDomain'."]:
    """Purchase a domain with Vercel's API.

    This tool allows users to purchase a single domain using Vercel's API. It should be called when a user wants to buy a domain name.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["PURCHASEDOMAINVERCEL_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 domain_name:
        missing_params.append(("domain_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["PURCHASEDOMAINVERCEL_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["PURCHASEDOMAINVERCEL_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/registrar/domains/{domain}/buy".format(domain=domain_name),  # noqa: UP032
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["PURCHASEDOMAINVERCEL_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_id}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def purchase_multiple_domains(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    team_id: Annotated[
        str | None,
        "The unique identifier for the team under which the domains will be purchased.  Only used when mode is 'execute'.",
    ] = 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 'buyDomains'."]:
    """Purchase multiple domains simultaneously.

    Use this tool to buy several domains at once through Vercel's service. It should be called when a user wants to acquire multiple domain names in a single request.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "PURCHASEMULTIPLEDOMAINS_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["PURCHASEMULTIPLEDOMAINS_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["PURCHASEMULTIPLEDOMAINS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/registrar/domains/buy",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["PURCHASEMULTIPLEDOMAINS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_id}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def transfer_domain_to_vercel(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    domain_name: Annotated[
        str | None,
        "The domain name to be transferred to Vercel. It should be a valid domain currently registered elsewhere.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the Vercel team requesting the domain transfer. It helps associate the domain transfer with the correct Vercel team.  Only used when mode is 'execute'.",
    ] = 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 'transferInDomain'."]:
    """Transfer a domain to Vercel from another registrar.

    Use this tool to initiate the transfer of a domain from its current registrar to Vercel. This is useful when consolidating domain management under Vercel's services.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "TRANSFERDOMAINTOVERCEL_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 domain_name:
        missing_params.append(("domain_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["TRANSFERDOMAINTOVERCEL_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["TRANSFERDOMAINTOVERCEL_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/registrar/domains/{domain}/transfer".format(  # noqa: UP032
            domain=domain_name
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["TRANSFERDOMAINTOVERCEL_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def check_domain_transfer_status(
    context: ToolContext,
    domain_name: Annotated[
        str,
        "Specifies the domain name to check the transfer status for. It should be a valid domain string.",
    ],
    team_id: Annotated[
        str | None, "The unique identifier of the team requesting the domain transfer status."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getDomainTransferIn'."]:
    """Retrieve the transfer status of a domain.

    Use this tool to check the current transfer status of a specific domain, providing insights into progress or any issues."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/registrar/domains/{domain}/transfer".format(  # noqa: UP032
            domain=domain_name
        ),
        method="GET",
        params=remove_none_values({"teamId": team_id}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def renew_domain(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    domain_name: Annotated[
        str | None,
        "The domain name to be renewed, in string format.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the team that owns the domain.  Only used when mode is 'execute'.",
    ] = 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 'renewDomain'."]:
    """Renews a domain registration through Vercel.



    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["RENEWDOMAIN_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 domain_name:
        missing_params.append(("domain_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["RENEWDOMAIN_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["RENEWDOMAIN_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/registrar/domains/{domain}/renew".format(domain=domain_name),  # noqa: UP032
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["RENEWDOMAIN_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_domain_auto_renew(
    context: ToolContext,
    domain_name: Annotated[
        str,
        "The domain for which you want to update the auto-renew setting. It should be a valid domain name, such as 'example.com'.",
    ],
    enable_auto_renew: Annotated[
        bool, "Set to true to enable auto-renewal of the domain, or false to disable it."
    ],
    team_id: Annotated[
        str | None, "The unique identifier for the team associated with the domain."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'updateDomainAutoRenew'."]:
    """Update the auto-renew setting for a domain.

    Use this tool to toggle the auto-renewal option for a specific domain. Useful for managing domain renewal preferences."""
    request_data = remove_none_values({"autoRenew": enable_auto_renew})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/registrar/domains/{domain}/auto-renew".format(  # noqa: UP032
            domain=domain_name
        ),
        method="PATCH",
        params=remove_none_values({"teamId": team_id}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_domain_nameservers(
    context: ToolContext,
    domain_name: Annotated[
        str,
        "The domain name to update the nameservers for. Provide the full domain, e.g., 'example.com'.",
    ],
    nameservers_list: Annotated[
        list[str],
        "A list of nameservers to set for the domain. Pass an empty list to revert to Vercel's default nameservers.",
    ],
    team_id: Annotated[
        str | None,
        "The unique identifier for the team to which the domain belongs. If not provided, the default team context is used.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'updateDomainNameservers'."]:
    """Update the nameservers for a domain.

    Use this tool to change the nameservers of a domain via Vercel. An empty array can be provided to revert to Vercel's default nameservers."""
    request_data = remove_none_values({"nameservers": nameservers_list})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/registrar/domains/{domain}/nameservers".format(  # noqa: UP032
            domain=domain_name
        ),
        method="PATCH",
        params=remove_none_values({"teamId": team_id}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_domain_contact_info_schema(
    context: ToolContext,
    domain_name: Annotated[
        str, "The domain name for which to retrieve the TLD-specific contact information schema."
    ],
    team_id: Annotated[
        str | None,
        "A unique identifier for the team within Vercel. Required to retrieve domain-specific contact info.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getContactInfoSchema'."]:
    """Retrieve the schema for TLD-specific contact information.

    Use this tool to get the necessary contact information schema required by specific TLDs for a domain. This is helpful when additional TLD-specific details are needed for domain registration or management."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/registrar/domains/{domain}/contact-info/schema".format(  # noqa: UP032
            domain=domain_name
        ),
        method="GET",
        params=remove_none_values({"teamId": team_id}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_domain_order_info(
    context: ToolContext,
    order_id: Annotated[str, "The unique ID of the domain order to retrieve information about."],
    team_id: Annotated[
        str | None,
        "The ID of the team associated with the domain order. This identifies the specific team within Vercel that the domain order belongs to.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getOrder'."]:
    """Retrieve information about a domain order by its ID.

    Use this tool to get detailed information about a specific domain order using its unique ID. It is useful for checking the status and details of a domain order processed through the registrar."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/registrar/orders/{orderId}".format(orderId=order_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({"teamId": team_id}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def fetch_domain_transfer_availability(
    context: ToolContext,
    domain_name: Annotated[str, "The domain name to check for transfer status or availability."],
    team_identifier: Annotated[
        str | None,
        "The Team identifier to perform the request on behalf of. It should be a string representing the team's unique ID.",
    ] = None,
    team_slug: Annotated[
        str | None, "The identifier slug of the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getDomainTransfer'."]:
    """Fetch a domain's transfer status or availability.

    Use this tool to check if a domain transfer is available or to obtain the transfer status if a transfer is in progress."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/domains/{domain}/registry".format(domain=domain_name),  # noqa: UP032
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_domain_configuration(
    context: ToolContext,
    domain_name: Annotated[str, "The name of the domain to retrieve configuration details for."],
    include_only_assigned_nameservers: Annotated[
        str | None,
        "When true, only nameservers assigned directly to the domain are included. When false, parent zone nameservers are included if no direct assignment exists.",
    ] = None,
    project_id_or_name: Annotated[
        str | None,
        "The project ID or name associated with the domain, used if not yet linked to a project.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The identifier for the team to perform the request on behalf of. It specifies which team's context to use when fetching domain configurations.",
    ] = None,
    team_slug: Annotated[
        str | None, "The Team slug used to perform the request on behalf of a specific team."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getDomainConfig'."]:
    """Retrieve configuration details for a specific domain.

    This tool calls the Vercel API to get the configuration settings for a given domain. It should be used when you need insights into the current setup or settings of a domain on Vercel."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v6/domains/{domain}/config".format(domain=domain_name),  # noqa: UP032
        method="GET",
        params=remove_none_values({
            "projectIdOrName": project_id_or_name,
            "strict": include_only_assigned_nameservers,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_domain_info(
    context: ToolContext,
    domain_name: Annotated[str, "The name of the domain to retrieve information for."],
    team_identifier: Annotated[
        str | None,
        "The identifier for the team to perform the request on behalf of. Typically a unique string.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug identifier for the team on behalf of whom the request is made. It uniquely identifies the team in Vercel.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getDomain'."]:
    """Retrieve information on a single domain from Vercel.

    Use this tool to get detailed information about a specific domain associated with a Vercel account or team. This can be useful for managing domains or checking their configuration."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v5/domains/{domain}".format(domain=domain_name),  # noqa: UP032
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_registered_domains(
    context: ToolContext,
    domains_created_since_timestamp: Annotated[
        float | None, "Get domains created after the specified JavaScript timestamp."
    ] = None,
    maximum_domains_to_list: Annotated[
        float | None,
        "The maximum number of domains to include in the list returned by the request.",
    ] = None,
    team_id: Annotated[
        str | None,
        "The unique Team identifier to retrieve domains for a specific team instead of the authenticated user.",
    ] = None,
    team_slug: Annotated[
        str | None, "The team slug used to perform the request on behalf of a specific team."
    ] = None,
    until_timestamp: Annotated[
        float | None, "Fetch domains created before this JavaScript timestamp."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getDomains'."]:
    """Retrieve a list of registered domains for the user or team.

    This tool fetches a list of domains registered under the authenticated user or team from Vercel. It is useful for managing or reviewing domain configurations."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v5/domains",
        method="GET",
        params=remove_none_values({
            "limit": maximum_domains_to_list,
            "since": domains_created_since_timestamp,
            "until": until_timestamp,
            "teamId": team_id,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def add_new_domain_vercel(
    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 identifier of the Team on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The identifier for the team to execute the request on behalf of.  Only used when mode is 'execute'.",
    ] = 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 'createOrTransferDomain'."]:
    """Add a new apex domain with Vercel for the user.

    Use this tool to add a new apex domain name with Vercel for the authenticating user. This tool is not meant for transferring domains from external registrars to Vercel.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["ADDNEWDOMAINVERCEL_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["ADDNEWDOMAINVERCEL_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["ADDNEWDOMAINVERCEL_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v7/domains",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ADDNEWDOMAINVERCEL_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_apex_domain(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    apex_domain_name: Annotated[
        str | None,
        "The apex domain to update or move. Accepts a string value representing the domain name (e.g., 'example.com').  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the team to perform the domain update on behalf of. This allows the request to be associated with a specific team.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug identifying the Team on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = 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 'patchDomain'."]:
    """Update or move the apex domain configuration.

    This tool updates or moves the apex domain configuration. It is not used for updating auto-renew or nameservers, for which separate endpoints should be used.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEAPEXDOMAIN_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 apex_domain_name:
        missing_params.append(("apex_domain_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["UPDATEAPEXDOMAIN_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["UPDATEAPEXDOMAIN_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v3/domains/{domain}".format(domain=apex_domain_name),  # noqa: UP032
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEAPEXDOMAIN_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_vercel_domain(
    context: ToolContext,
    domain_name: Annotated[
        str,
        "The domain name to be deleted from Vercel. This action will also remove any associated aliases.",
    ],
    team_identifier: Annotated[
        str | None,
        "The identifier of the team to perform the domain deletion request on behalf of.",
    ] = None,
    team_slug: Annotated[
        str | None, "The specific team slug to perform the deletion request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteDomain'."]:
    """Delete a domain from Vercel and remove associated aliases.

    Use this tool to delete a previously registered domain from Vercel. This action also removes any aliases linked to the domain."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v6/domains/{domain}".format(domain=domain_name),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def invalidate_cache_by_tags(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_id_or_name: Annotated[
        str | None,
        "Specify the project ID or name for which the cache tags should be marked as stale.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_id: Annotated[
        str | None,
        "The Team identifier to execute the request for. Provide the team's unique ID.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The identifier (slug) for the team to perform the request on their behalf.  Only used when mode is 'execute'.",
    ] = 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 'invalidateByTags'."]:
    """Mark cache tags as stale for revalidation in the background.

    Use this tool to mark specific cache tags as stale, prompting them to be revalidated during the next request. This is useful when data associated with these tags has changed and needs refreshing.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "INVALIDATECACHEBYTAGS_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 required parameters
    missing_params = []
    if not project_id_or_name:
        missing_params.append(("project_id_or_name", "query"))

    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["INVALIDATECACHEBYTAGS_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["INVALIDATECACHEBYTAGS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/edge-cache/invalidate-by-tags",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["INVALIDATECACHEBYTAGS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({
            "projectIdOrName": project_id_or_name,
            "teamId": team_id,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_cache_by_tags(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_id_or_name: Annotated[
        str | None,
        "The ID or name of the project associated with the cache tags to delete.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The identifier for the team on whose behalf the cache deletion request is performed.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "Specify the team slug to perform the cache deletion on behalf of a team.  Only used when mode is 'execute'.",
    ] = 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 'dangerouslyDeleteByTags'."]:
    """Marks cache tags as deleted to revalidate associated entries.

    Use this tool to delete cache entries marked by specific tags, causing associated entries to be revalidated. This should be employed cautiously due to potential cache stampede risks. It's useful when the origin returns status codes like 404 or 410.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["DELETECACHEBYTAGS_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 required parameters
    missing_params = []
    if not project_id_or_name:
        missing_params.append(("project_id_or_name", "query"))

    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["DELETECACHEBYTAGS_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["DELETECACHEBYTAGS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/edge-cache/dangerously-delete-by-tags",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["DELETECACHEBYTAGS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({
            "projectIdOrName": project_id_or_name,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_edge_configs(
    context: ToolContext,
    team_identifier: Annotated[
        str | None, "Specify the Team identifier for which the Edge Configs need to be fetched."
    ] = None,
    team_slug: Annotated[str | None, "The Team slug to perform the request on behalf of."] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getEdgeConfigs'."]:
    """Fetch all Edge Configs from Vercel's service.

    Use this tool to retrieve a comprehensive list of all Edge Configurations available in a Vercel account."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config",
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_edge_config(
    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 identifier of the team to perform the request for. This is required to specify on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug that specifies the team to perform the request on behalf of.  Only used when mode is 'execute'.",
    ] = 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 'createEdgeConfig'."]:
    """Create a new Edge Configuration.

    This tool is used to create a new Edge Config on Vercel. It should be called when there's a need to set up or initialize a configuration for edge functions.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEEDGECONFIG_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["CREATEEDGECONFIG_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["CREATEEDGECONFIG_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/edge-config",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEEDGECONFIG_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_edge_config(
    context: ToolContext,
    edge_config_id: Annotated[
        str, "The unique identifier for the Edge Config to retrieve details from Vercel."
    ],
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the team to perform the request on behalf of in Vercel.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team to perform the request on behalf of in Vercel. This identifier is needed to specify which team's settings are being accessed.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getEdgeConfig'."]:
    """Retrieve Edge Config details from Vercel.

    Use this tool to obtain configuration details for a specific Edge Config in Vercel. It should be called when you need to access or review settings for an Edge Config using its unique identifier."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}".format(  # noqa: UP032
            edgeConfigId=edge_config_id
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_edge_config(
    context: ToolContext,
    edge_config_identifier: Annotated[
        str,
        "The unique identifier of the Edge Config to be updated. This is required to specify which configuration should be modified.",
    ],
    edge_config_slug: Annotated[
        str, "The unique slug identifier for the Edge Config that needs updating."
    ],
    team_slug: Annotated[
        str, "The slug identifying the team on whose behalf the request is performed."
    ],
    team_identifier: Annotated[
        str | None,
        "The unique identifier of the team to perform the request for. Required for updating the Edge Config on behalf of a specific team.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'updateEdgeConfig'."]:
    """Update an existing Edge Config to apply changes.

    This tool updates an existing Edge Config with new settings. It should be called when there is a need to modify the settings of an Edge Config identified by its ID."""
    request_data = remove_none_values({"slug": edge_config_slug})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}".format(  # noqa: UP032
            edgeConfigId=edge_config_identifier
        ),
        method="PUT",
        params=remove_none_values({"teamId": team_identifier, "slug": edge_config_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_edge_config(
    context: ToolContext,
    edge_config_id: Annotated[str, "The unique identifier of the Edge Config to be deleted."],
    team_identifier: Annotated[
        str | None, "The unique identifier for the team on whose behalf the request is made."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug of the team on whose behalf the request is performed."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteEdgeConfig'."]:
    """Delete a Vercel Edge Config by ID.

    Use this tool to delete a specific Edge Config in Vercel by providing its unique ID. This will remove the configuration from the system."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}".format(  # noqa: UP032
            edgeConfigId=edge_config_id
        ),
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_edge_config_items(
    context: ToolContext,
    edge_config_id: Annotated[
        str,
        "The ID of the Edge Config to retrieve items from. This is required to specify which Edge Config data to access.",
    ],
    team_identifier: Annotated[
        str | None, "The Team identifier to perform the request for a specific Vercel team."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug representing the team on whose behalf the request is made. Each team has a unique slug identifier.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getEdgeConfigItems'."]:
    """Retrieve all items from an Edge Config.

    Call this tool to obtain all items within a specified Edge Config by providing the Edge Config ID. Useful for accessing configuration details stored in Vercel's Edge Config."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}/items".format(  # noqa: UP032
            edgeConfigId=edge_config_id
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_edge_config_items(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    edge_config_identifier: Annotated[
        str | None,
        "The identifier for the specific Edge Config to update in the batch request.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier of the Team on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The unique slug of the team to perform the request on behalf of. It identifies the team in a URL-friendly format.  Only used when mode is 'execute'.",
    ] = 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 'patchEdgeConfigItems'."]:
    """Batch update Edge Config Items efficiently.

    Use this tool to update multiple Edge Config Items in a single request. Ideal for making bulk modifications to your Vercel Edge Configurations.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEEDGECONFIGITEMS_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 edge_config_identifier:
        missing_params.append(("edge_config_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["UPDATEEDGECONFIGITEMS_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["UPDATEEDGECONFIGITEMS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}/items".format(  # noqa: UP032
            edgeConfigId=edge_config_identifier
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEEDGECONFIGITEMS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_edge_config_schema(
    context: ToolContext,
    edge_config_id: Annotated[
        str,
        "The identifier for the specific Edge Config to retrieve its schema. It is required to specify which configuration's schema you want to query.",
    ],
    team_identifier: Annotated[
        str | None, "The unique identifier for the team to make requests on their behalf."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug of the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getEdgeConfigSchema'."]:
    """Retrieve the schema of an Edge Config.

    This tool is used to obtain the schema details of a specific Edge Config by its ID. It should be called when there's a need to understand the structure or configuration of a given Edge Config."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}/schema".format(  # noqa: UP032
            edgeConfigId=edge_config_id
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_edge_config_schema(
    context: ToolContext,
    edge_config_identifier: Annotated[
        str, "The unique identifier for the Edge Config to update its schema."
    ],
    edge_config_schema_definition: Annotated[
        str, "JSON string defining the updated structure and settings of the Edge Config."
    ],
    enable_dry_run: Annotated[
        str | None,
        "Set to true to simulate the update without applying changes. Useful for testing.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The unique ID of the team on whose behalf the request will be made."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug to perform the request on behalf of. It identifies the specific team for the operation.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'patchEdgeConfigSchema'."]:
    """Update an Edge Config's schema to modify its structure.

    Use this tool to update the schema of an Edge Config in Vercel. It is useful when changes to the structure or configuration settings of an Edge Config are needed."""
    request_data = remove_none_values({"definition": edge_config_schema_definition})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}/schema".format(  # noqa: UP032
            edgeConfigId=edge_config_identifier
        ),
        method="POST",
        params=remove_none_values({
            "dryRun": enable_dry_run,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_edge_config_schema(
    context: ToolContext,
    edge_config_id: Annotated[
        str, "The unique identifier for the specific Edge Config schema to be deleted."
    ],
    team_identifier: Annotated[
        str | None, "The identifier of the team on whose behalf the request is made."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug representing the team on whose behalf the request is performed. It uniquely identifies the team within Vercel.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteEdgeConfigSchema'."]:
    """Deletes an existing Edge Config schema.

    Use this tool to delete the schema of an existing Edge Config in a Vercel project. This can be useful for managing and updating Edge Configurations efficiently."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}/schema".format(  # noqa: UP032
            edgeConfigId=edge_config_id
        ),
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_edge_config_item(
    context: ToolContext,
    edge_config_id: Annotated[str, "The ID of the Edge Config to retrieve a specific item from."],
    edge_config_item_key: Annotated[str, "The key of the specific Edge Config Item to retrieve."],
    team_identifier: Annotated[
        str | None, "The ID of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug of the team for which the Edge Config Item is requested."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getEdgeConfigItem'."]:
    """Retrieve a specific Edge Config Item by its identifiers.

    Use this tool to get details of a specific Edge Config Item using its configuration ID and item key."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}/item/{edgeConfigItemKey}".format(  # noqa: UP032
            edgeConfigId=edge_config_id, edgeConfigItemKey=edge_config_item_key
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_edge_config_tokens(
    context: ToolContext,
    edge_config_id: Annotated[
        str,
        "A string representing the unique identifier of the Edge Config to retrieve tokens for. This ID is necessary to specify which Edge Config's tokens are being accessed.",
    ],
    team_identifier: Annotated[
        str | None,
        "The ID of the team to perform the request on behalf of. It identifies which team's Edge Config tokens to retrieve.",
    ] = None,
    team_slug: Annotated[
        str | None, "Slug of the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getEdgeConfigTokens'."]:
    """Retrieve all tokens of a specific Edge Config.

    This tool retrieves all tokens related to a specified Edge Config. It should be called when you need to access or manage tokens for an Edge Config identified by its edgeConfigId."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}/tokens".format(  # noqa: UP032
            edgeConfigId=edge_config_id
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_edge_config_tokens(
    context: ToolContext,
    edge_config_id: Annotated[
        str,
        "The unique identifier for the Edge Config from which tokens will be deleted. Required for specifying the target Edge Config.",
    ],
    tokens_to_delete: Annotated[
        list[str],
        "A list of token identifiers to be deleted from the Edge Config. Each token should be a string.",
    ],
    team_identifier: Annotated[
        str | None, "The unique identifier of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The Team slug representing the team on whose behalf the request is performed."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteEdgeConfigTokens'."]:
    """Delete tokens from an existing Edge Config.

    Use this tool to delete one or more tokens associated with a specific Edge Config in Vercel. It is useful for managing and securing access by removing outdated or compromised tokens."""
    request_data = remove_none_values({"tokens": tokens_to_delete})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}/tokens".format(  # noqa: UP032
            edgeConfigId=edge_config_id
        ),
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_edge_config_token_info(
    context: ToolContext,
    edge_config_id: Annotated[
        str,
        "The identifier for the Edge Config to retrieve metadata for. This is required to specify which configuration token's information is needed.",
    ],
    edge_config_token: Annotated[
        str, "The token used to obtain metadata for a specific Edge Config."
    ],
    team_identifier: Annotated[
        str | None, "The identifier of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The Team slug for performing the request on behalf of a specific team."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getEdgeConfigToken'."]:
    """Retrieve metadata about an Edge Config token.

    Use this tool to obtain metadata for a specific Edge Config token by providing the edgeConfigId and token. Ideal for understanding token attributes and usage in Edge Config."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}/token/{token}".format(  # noqa: UP032
            edgeConfigId=edge_config_id, token=edge_config_token
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def add_edge_config_token(
    context: ToolContext,
    edge_config_id: Annotated[
        str, "The unique identifier for the Edge Config to which the token will be added."
    ],
    token_label: Annotated[
        str, "A descriptive label for the token being added to the Edge Config."
    ],
    team_identifier: Annotated[
        str | None, "The identifier for the team on whose behalf the request is performed."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug identifying the Team on whose behalf the request is made. This is used for specifying the target team within Vercel.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'createEdgeConfigToken'."]:
    """Adds a token to an existing Edge Config.

    This tool is used to add a new token to an existing Edge Config in Vercel. It should be called when you need to update the configuration with additional access or permissions via token."""
    request_data = remove_none_values({"label": token_label})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}/token".format(  # noqa: UP032
            edgeConfigId=edge_config_id
        ),
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_edge_config_backup(
    context: ToolContext,
    edge_config_backup_version_id: Annotated[
        str, "The unique identifier for the backup version of the Edge Config to retrieve."
    ],
    edge_config_id: Annotated[str, "The ID of the Edge Config to retrieve from backup storage."],
    team_identifier: Annotated[
        str | None, "The unique identifier of the Team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The team's unique slug to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getEdgeConfigBackup'."]:
    """Retrieve a specific Edge Config version from backup storage.

    Use this tool to fetch a particular version of an Edge Config that is stored as a backup. This is helpful for accessing historical configurations or restoring previous settings."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}/backups/{edgeConfigBackupVersionId}".format(  # noqa: UP032
            edgeConfigId=edge_config_id, edgeConfigBackupVersionId=edge_config_backup_version_id
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_edge_config_backups(
    context: ToolContext,
    edge_config_id: Annotated[
        str, "The unique identifier for the Edge Config whose backups you want to retrieve."
    ],
    backup_limit: Annotated[
        float | None,
        "The maximum number of Edge Config backups to return. This is useful for paginating results.",
    ] = None,
    include_metadata: Annotated[
        str | None,
        "Indicate if metadata should be included in the response. Use 'true' to include.",
    ] = None,
    next_page_token: Annotated[
        str | None, "A token for fetching the next page of results if pagination is needed."
    ] = None,
    team_identifier: Annotated[
        str | None, "The identifier for the team on whose behalf the request is made."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The team slug to perform the request on behalf of. It identifies the specific team in Vercel.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getEdgeConfigBackups'."]:
    """Retrieve backups of an Edge Config.

    This tool retrieves and returns backups for a specified Edge Config. It should be called when there is a need to access or review past configurations of an Edge Config managed by Vercel."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/edge-config/{edgeConfigId}/backups".format(  # noqa: UP032
            edgeConfigId=edge_config_id
        ),
        method="GET",
        params=remove_none_values({
            "next": next_page_token,
            "limit": backup_limit,
            "metadata": include_metadata,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def list_user_events(
    context: ToolContext,
    deprecated_user_id: Annotated[
        str | None,
        "Deprecated. Use 'principal_id' instead. If both 'principal_id' and 'deprecated_user_id' exist, 'principal_id' will be used.",
    ] = None,
    end_time_filter: Annotated[
        str | None, "Timestamp to filter events created until this time."
    ] = None,
    event_types_filter: Annotated[
        str | None, "Comma-delimited list of event types to filter the results by."
    ] = None,
    filter_by_principal_id: Annotated[
        str | None,
        "Filter events generated by a specific principal when retrieving events for a Team.",
    ] = None,
    include_event_payload: Annotated[
        str | None, "Set to 'true' to include the 'payload' field in each event response."
    ] = None,
    include_items_since_timestamp: Annotated[
        str | None, "Timestamp to only include items created since then. Use ISO 8601 format."
    ] = None,
    maximum_items_to_return: Annotated[
        float | None, "Maximum number of items that can be returned from the request."
    ] = None,
    project_ids_filter: Annotated[
        str | None, "Comma-separated list of project IDs to filter the events by."
    ] = None,
    team_identifier: Annotated[
        str | None, "Specify the Team ID to retrieve events related to that team."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug to perform the request on behalf of. Use this to specify which team's events to retrieve.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'listUserEvents'."]:
    """Fetches a list of user-generated events on Vercel.

    This tool retrieves events generated by user actions on Vercel, such as logging in, creating deployments, or joining teams. If a `teamId` is provided, events related to that specific team are returned."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v3/events",
        method="GET",
        params=remove_none_values({
            "limit": maximum_items_to_return,
            "since": include_items_since_timestamp,
            "until": end_time_filter,
            "types": event_types_filter,
            "userId": deprecated_user_id,
            "principalId": filter_by_principal_id,
            "projectIds": project_ids_filter,
            "withPayload": include_event_payload,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_billing_plans(
    context: ToolContext,
    integration_identifier_or_slug: Annotated[
        str,
        "The unique identifier or slug for the integration to retrieve billing plans for. Use the specific key related to the integration.",
    ],
    product_id_or_slug: Annotated[
        str, "The unique identifier or slug for the product to retrieve billing plans."
    ],
    additional_metadata: Annotated[
        str | None, "Optional metadata for the request, provided as a string."
    ] = None,
    team_identifier: Annotated[
        str | None, "The identifier for the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The unique team slug used to identify which team's context the request should be performed in.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getBillingPlans'."]:
    """Retrieve billing plans for a specific integration and product.

    Use this tool to obtain a list of billing plans associated with a specified integration and product. It retrieves the available plans using the integration and product identifiers."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/integrations/integration/{integrationIdOrSlug}/products/{productIdOrSlug}/plans".format(  # noqa: UP032
            integrationIdOrSlug=integration_identifier_or_slug, productIdOrSlug=product_id_or_slug
        ),
        method="GET",
        params=remove_none_values({
            "metadata": additional_metadata,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def connect_integration_resource_to_project(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "The identifier for the integration configuration to be connected to the Vercel project.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    resource_id: Annotated[
        str | None,
        "The ID of the integration resource to connect to the Vercel project. This is required to establish the link.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The identifier of the team to perform the request on behalf of.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The unique team slug used to perform the request on behalf of a specific team in Vercel.  Only used when mode is 'execute'.",
    ] = 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 'connectIntegrationResourceToProject'."
]:
    """Connect an integration resource to a Vercel project.

    This tool connects a provisioned integration resource to a specific Vercel project. It should be used when you need to establish a link between a resource and a project in Vercel.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CONNECTINTEGRATIONRESOURCETOPROJECT_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 integration_configuration_id:
        missing_params.append(("integration_configuration_id", "path"))
    if not resource_id:
        missing_params.append(("resource_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["CONNECTINTEGRATIONRESOURCETOPROJECT_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["CONNECTINTEGRATIONRESOURCETOPROJECT_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/integrations/installations/{integrationConfigurationId}/resources/{resourceId}/connections".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id, resourceId=resource_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CONNECTINTEGRATIONRESOURCETOPROJECT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_integration_installation(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "The ID of the integration configuration to update. This should be a string identifying the specific installation.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'update-installation'."]:
    """Updates an integration installation configuration.

    Use this tool to update the configuration of an existing integration installation. Call it when changes to the integration setup are necessary.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEINTEGRATIONINSTALLATION_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 integration_configuration_id:
        missing_params.append(("integration_configuration_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["UPDATEINTEGRATIONINSTALLATION_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["UPDATEINTEGRATIONINSTALLATION_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEINTEGRATIONINSTALLATION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def fetch_account_info(
    context: ToolContext,
    integration_configuration_id: Annotated[
        str,
        "The unique identifier for the integration configuration. Required to fetch the user's account info.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-account-info'."]:
    """Fetch the best account or user's contact info.

    Use this tool to retrieve detailed contact information for a user's account, ideal for scenarios where you need user engagement or account verification."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/account".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_member_role_info(
    context: ToolContext,
    integration_configuration_id: Annotated[
        str,
        "A unique identifier for the integration configuration. Required to specify which integration to retrieve member details from.",
    ],
    member_id: Annotated[
        str,
        'The ID of the member to retrieve role and details for. This corresponds to the "user_id" claim in the SSO OIDC token.',
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-member'."]:
    """Retrieve member role and details for a specific member ID.

    Use this tool to get the role and additional information for a member using their member ID. It is useful for verifying membership details and roles within installations."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/member/{memberId}".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id, memberId=member_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def notify_vercel_of_updates(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "The ID of the integration configuration. It links the notification to the specific Vercel installation or resource.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'create-event'."]:
    """Send update notifications to Vercel for installations or resources.

    Use this tool to notify Vercel of changes to installations or resources. Trigger 'resource.updated' events when a resource linked to Vercel is modified, such as renaming a database or suspending a resource. Trigger 'installation.updated' events when an installation's billing plan changes without Vercel's involvement.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "NOTIFYVERCELOFUPDATES_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 integration_configuration_id:
        missing_params.append(("integration_configuration_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["NOTIFYVERCELOFUPDATES_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["NOTIFYVERCELOFUPDATES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/events".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["NOTIFYVERCELOFUPDATES_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_integration_resources(
    context: ToolContext,
    installation_id: Annotated[
        str, "The unique identifier of the integration installation to fetch resources for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-integration-resources'."]:
    """Retrieve all resources for a given installation ID.

    Use this tool to get all resources associated with a specific integration installation. It requires the installation ID to fetch the relevant information."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/resources".format(  # noqa: UP032
            integrationConfigurationId=installation_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def fetch_integration_resource(
    context: ToolContext,
    integration_configuration_id: Annotated[
        str, "The ID of the specific installation to which the resource belongs."
    ],
    third_party_resource_id: Annotated[
        str, "The ID provided by the third-party provider for the specific resource."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-integration-resource'."]:
    """Fetch a resource using its partner ID.

    Use this tool to get detailed information about a specific resource by providing the integration configuration and resource IDs."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/resources/{resourceId}".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id,
            resourceId=third_party_resource_id,
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_integration_resource(
    context: ToolContext,
    integration_installation_id: Annotated[
        str, "The ID of the installation to delete the resource from."
    ],
    resource_id: Annotated[
        str,
        "The ID of the resource to be deleted. Required for identifying the specific resource within the integration.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'delete-integration-resource'."]:
    """Delete a resource from an integration by ID.

    Use this tool to delete a resource associated with a specific integration installation. You need the installation and resource IDs to successfully execute this operation."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/resources/{resourceId}".format(  # noqa: UP032
            integrationConfigurationId=integration_installation_id, resourceId=resource_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def import_resource_to_vercel(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "The unique identifier for the integration configuration in Vercel. Required to specify which configuration to use when importing the resource.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    resource_identifier: Annotated[
        str | None,
        "The unique identifier for the resource to be imported or synchronized with Vercel. This ID is used to match the resource between the partner's system and Vercel's system.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'import-resource'."]:
    """Import or synchronize a resource to Vercel.

    This tool is used to import or update a resource in Vercel's installation. It should be called when resources created externally need to be synchronized with Vercel.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "IMPORTRESOURCETOVERCEL_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 integration_configuration_id:
        missing_params.append(("integration_configuration_id", "path"))
    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["IMPORTRESOURCETOVERCEL_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["IMPORTRESOURCETOVERCEL_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/resources/{resourceId}".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id, resourceId=resource_identifier
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["IMPORTRESOURCETOVERCEL_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_resource(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "The unique identifier for the integration configuration to update. Required for identifying which integration configuration is being modified.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    resource_id: Annotated[
        str | None,
        "The unique identifier of the resource to be updated. This is required to specify which resource you are targeting for updates.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'update-resource'."]:
    """Update an existing resource with new information.

    Use this tool to update details of a specific resource in an installation. Supports partial updates, allowing changes to be made without modifying the entire resource.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATERESOURCE_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 integration_configuration_id:
        missing_params.append(("integration_configuration_id", "path"))
    if not resource_id:
        missing_params.append(("resource_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["UPDATERESOURCE_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["UPDATERESOURCE_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/resources/{resourceId}".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id, resourceId=resource_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATERESOURCE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def submit_billing_data(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "A string representing the unique identifier for the integration configuration. This is required to submit billing data.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'submit-billing-data'."]:
    """Submit billing and usage data to the server.

    This tool should be used to send billing and usage data at least once a day, ideally once per hour, using the provided access token for authorization.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["SUBMITBILLINGDATA_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 integration_configuration_id:
        missing_params.append(("integration_configuration_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["SUBMITBILLINGDATA_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["SUBMITBILLINGDATA_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/billing".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SUBMITBILLINGDATA_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def submit_invoice_to_vercel(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "The unique ID for the Vercel integration configuration. This links the invoice submission to the correct integration setup in Vercel.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'submit-invoice'."]:
    """Submit an invoice to Vercel's billing system.

    Use this tool to submit an invoice to Vercel, creating it in their billing system and sending it to the customer. Suitable for different billing plans at various stages of the billing period. Ensure compliance with billing limitations.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "SUBMITINVOICETOVERCEL_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 integration_configuration_id:
        missing_params.append(("integration_configuration_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["SUBMITINVOICETOVERCEL_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["SUBMITINVOICETOVERCEL_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/billing/invoices".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SUBMITINVOICETOVERCEL_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_invoice_details(
    context: ToolContext,
    integration_configuration_id: Annotated[
        str,
        "The unique identifier for the integration configuration. Required to specify the configuration context for the invoice retrieval.",
    ],
    invoice_id: Annotated[
        str, "The unique identifier of the invoice to retrieve details and status for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'get-invoice'."]:
    """Retrieve invoice details and status by ID.

    This tool retrieves the details and status of an invoice using its ID. Use this when you need specific information about a particular invoice, such as its current status or other details."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/billing/invoices/{invoiceId}".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id, invoiceId=invoice_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def request_vercel_invoice_refund(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    vercel_integration_configuration_id: Annotated[
        str | None,
        "The unique identifier for the Vercel integration configuration related to the invoice.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    invoice_id: Annotated[
        str | None,
        "The unique identifier for the invoice for which the refund is requested. This ID is obtained from the invoice creation process.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'update-invoice'."]:
    """Request a refund for an invoice in Vercel.

    Use this tool to request a refund for an invoice created via the Submit Invoice API in Vercel. Call this when a refund is necessary.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "REQUESTVERCELINVOICEREFUND_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 vercel_integration_configuration_id:
        missing_params.append(("vercel_integration_configuration_id", "path"))
    if not invoice_id:
        missing_params.append(("invoice_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["REQUESTVERCELINVOICEREFUND_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["REQUESTVERCELINVOICEREFUND_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/billing/invoices/{invoiceId}/actions".format(  # noqa: UP032
            integrationConfigurationId=vercel_integration_configuration_id, invoiceId=invoice_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["REQUESTVERCELINVOICEREFUND_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def submit_prepayment_balances(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "The unique identifier for the integration configuration. Use the ID provided during the integration setup.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'submit-prepayment-balances'."]:
    """Submit prepayment balances to Vercel for billing.

    This tool sends prepayment balances to Vercel, which should be done at least daily, but ideally hourly. It requires the access token provided during the installation process for authorization.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "SUBMITPREPAYMENTBALANCES_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 integration_configuration_id:
        missing_params.append(("integration_configuration_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["SUBMITPREPAYMENTBALANCES_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["SUBMITPREPAYMENTBALANCES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/billing/balance".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SUBMITPREPAYMENTBALANCES_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_resource_secrets(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "The unique identifier for the integration configuration associated with the resource.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    integration_product_id_or_slug: Annotated[
        str | None,
        "Specify the product ID or slug to identify the integration product for the resource update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    resource_id: Annotated[
        str | None,
        "The unique identifier for the resource whose secrets are being updated. This is required to specify which resource's secrets need modification.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'update-resource-secrets'."]:
    """Updates the secrets of a specified resource.

    This tool updates the secrets of a specified resource and connected projects. Old secrets may still be used by existing projects until manually redeployed. Useful for resetting resource credentials.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATERESOURCESECRETS_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 integration_configuration_id:
        missing_params.append(("integration_configuration_id", "path"))
    if not integration_product_id_or_slug:
        missing_params.append(("integration_product_id_or_slug", "path"))
    if not resource_id:
        missing_params.append(("resource_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["UPDATERESOURCESECRETS_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["UPDATERESOURCESECRETS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/products/{integrationProductIdOrSlug}/resources/{resourceId}/secrets".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id,
            integrationProductIdOrSlug=integration_product_id_or_slug,
            resourceId=resource_id,
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATERESOURCESECRETS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_secrets_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",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "The ID of the integration configuration. This identifies the specific configuration in Vercel to update secrets for.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    resource_id: Annotated[
        str | None,
        "The unique identifier of the Vercel resource whose secrets are to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'update-resource-secrets-by-id'."]:
    """Update the secrets of a Vercel resource by ID.

    This tool updates the secrets for a given resource in Vercel, affecting connected projects with the new secrets. Note that existing projects using old secrets require manual redeployment to apply changes.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATESECRETSBYID_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 integration_configuration_id:
        missing_params.append(("integration_configuration_id", "path"))
    if not resource_id:
        missing_params.append(("resource_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["UPDATESECRETSBYID_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["UPDATESECRETSBYID_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/resources/{resourceId}/secrets".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id, resourceId=resource_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATESECRETSBYID_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_integration_configurations(
    context: ToolContext,
    configuration_view_type: Annotated[
        str,
        "Specify 'account' to view all configurations or 'project' to exclude configurations generated from the authorization flow.",
    ],
    installation_type: Annotated[
        str | None, "Specifies the installation type. Options are 'marketplace' or 'external'."
    ] = None,
    integration_id: Annotated[
        str | None, "The ID or slug of the integration to retrieve configurations for."
    ] = None,
    team_identifier: Annotated[
        str | None, "Specifies the Team ID to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug to perform the request on behalf of when retrieving configurations.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getConfigurations'."]:
    """Retrieve all configurations for an authenticated integration.

    Use this tool to access configurations associated with an authenticated integration on Vercel, excluding those from the authorization flow when using the `project` view."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/integrations/configurations",
        method="GET",
        params=remove_none_values({
            "view": configuration_view_type,
            "installationType": installation_type,
            "integrationIdOrSlug": integration_id,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_integration_configuration(
    context: ToolContext,
    configuration_id: Annotated[
        str, "ID of the configuration to retrieve. The user or team must own this configuration."
    ],
    team_identifier: Annotated[
        str | None, "The identifier for the team on whose behalf the request is made."
    ] = None,
    team_slug: Annotated[
        str | None, "The unique team slug to perform the request on behalf of a specific team."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getConfiguration'."]:
    """Retrieve configuration details by ID.

    Use this tool to fetch the configuration details using a specific ID. The authenticated user or team must own the configuration to access it."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/integrations/configuration/{id}".format(id=configuration_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def remove_vercel_configuration(
    context: ToolContext,
    configuration_id: Annotated[
        str, "The unique identifier of the Vercel configuration to be deleted."
    ],
    team_identifier: Annotated[
        str | None, "The identifier for the team on behalf of which the configuration is removed."
    ] = None,
    team_slug: Annotated[
        str | None, "The team slug representing the Vercel team to perform the action on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteConfiguration'."]:
    """Delete a Vercel configuration by ID.

    Use this tool to remove a specific Vercel configuration and its associated resources by providing the configuration ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/integrations/configuration/{id}".format(id=configuration_id),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def list_integration_configuration_products(
    context: ToolContext,
    integration_configuration_id: Annotated[
        str, "ID of the specific integration configuration to list available products for."
    ],
    team_identifier: Annotated[
        str | None,
        "The Team identifier to perform the request on behalf of. It specifies which team's configuration products to list.",
    ] = None,
    team_slug: Annotated[
        str | None, "The slug of the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getConfigurationProducts'."]:
    """Retrieve products for a specific integration configuration.

    Use this tool to list all available products for a given integration configuration. It helps discover resources that can be provisioned for integrations. The response includes product IDs, slugs, names, supported protocols, and metadata requirements."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/integrations/configuration/{id}/products".format(  # noqa: UP032
            id=integration_configuration_id
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def exchange_sso_token(
    context: ToolContext,
    authorization_code: Annotated[
        str,
        "The sensitive OAuth authorization code received from Vercel for the SSO token exchange process.",
    ],
    integration_client_id: Annotated[
        str, "The unique client ID for the integration, required for authentication."
    ],
    integration_client_secret: Annotated[
        str, "The secret key for the integration client, used for authentication."
    ],
    authorization_state: Annotated[
        str | None, "The state received from the initialization request for security validation."
    ] = None,
    integration_redirect_uri: Annotated[
        str | None, "The URL where the user will be redirected after authentication."
    ] = None,
    sso_grant_type: Annotated[
        str | None, "Specifies the grant type as 'authorization_code' for OAuth process."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'exchange-sso-token'."]:
    """Exchange OAuth code for an OIDC token to authenticate users.

    Call this tool to exchange an OAuth authorization code for an OIDC token during the SSO process. This helps authenticate users in Vercel without persisting the token. Refer to the Vercel SSO documentation for more details."""
    request_data = remove_none_values({
        "code": authorization_code,
        "state": authorization_state,
        "client_id": integration_client_id,
        "client_secret": integration_client_secret,
        "redirect_uri": integration_redirect_uri,
        "grant_type": sso_grant_type,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/integrations/sso/token",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_integration_log_drains(
    context: ToolContext,
    team_identifier: Annotated[
        str | None, "The identifier for the Team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team to perform the request on behalf of. Used to specify which team's log drains to retrieve.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getIntegrationLogDrains'."]:
    """Retrieve all integration log drains for the user or team.

    This tool retrieves a list of all integration log drains defined for the authenticated user or team. It is especially useful when using an OAuth2 token, as the list is limited to log drains created by the authenticated integration."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v2/integrations/log-drains",
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_integration_log_drain(
    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 Team identifier for performing the request on behalf of a specific team in Vercel.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug representing the team to perform the request on behalf of.  Only used when mode is 'execute'.",
    ] = 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 'createLogDrain'."]:
    """Sets up an Integration log drain for Vercel.

    This tool creates an Integration log drain in Vercel, accessible via an OAuth2 client. It should be used when you need to tie log drains to specific integrations within Vercel.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEINTEGRATIONLOGDRAIN_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["CREATEINTEGRATIONLOGDRAIN_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["CREATEINTEGRATIONLOGDRAIN_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v2/integrations/log-drains",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEINTEGRATIONLOGDRAIN_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_integration_log_drain(
    context: ToolContext,
    log_drain_id: Annotated[
        str,
        "The ID of the log drain to be deleted. This identifies the specific log drain for removal.",
    ],
    team_identifier: Annotated[
        str | None, "The identifier of the Team on whose behalf the request is made."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug of the team on whose behalf the request is performed."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteIntegrationLogDrain'."]:
    """Delete an Integration log drain by ID.

    Use this tool to delete a specific Integration log drain by its ID. Applicable when you need to remove log drains associated with an integration, especially under OAuth2 constraints."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/integrations/log-drains/{id}".format(id=log_drain_id),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_deployment_runtime_logs(
    context: ToolContext,
    deployment_id: Annotated[
        str, "The unique identifier for the deployment whose runtime logs are to be retrieved."
    ],
    project_id: Annotated[
        str,
        "The unique identifier for the project related to the deployment. This is necessary to retrieve the correct runtime logs.",
    ],
    team_identifier: Annotated[
        str | None, "The unique identifier for the team to make the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The unique slug representing the team on whose behalf the request is made."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getRuntimeLogs'."]:
    """Get logs for a specific deployment's runtime.

    Use this tool to retrieve a stream of runtime logs for a given deployment in a project. This is useful for monitoring and debugging deployment processes."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{projectId}/deployments/{deploymentId}/runtime-logs".format(  # noqa: UP032
            projectId=project_id, deploymentId=deployment_id
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_experimentation_items(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "The ID of the integration configuration for which to create experimentation items. This ties the items to a specific setup or environment.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    resource_identifier: Annotated[
        str | None,
        "The unique identifier of the resource to associate with the experimentation items.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 ''."]:
    """Create one or multiple experimentation items.

    This tool is used to create one or multiple experimentation items for a specific installation. It should be called when you need to add experimentation data to a resource tied to an integration 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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEEXPERIMENTATIONITEMS_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 integration_configuration_id:
        missing_params.append(("integration_configuration_id", "path"))
    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["CREATEEXPERIMENTATIONITEMS_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["CREATEEXPERIMENTATIONITEMS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/resources/{resourceId}/experimentation/items".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id, resourceId=resource_identifier
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEEXPERIMENTATIONITEMS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_experimentation_item(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "The ID of the integration configuration to be updated. This identifies which configuration is being patched.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    resource_identifier: Annotated[
        str | None,
        "The unique identifier of the experimentation resource to update. Provides context for which item needs modification.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    experiment_item_id: Annotated[
        str | None,
        "The unique identifier for the experimentation item to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 ''."]:
    """Update an existing experimentation item.

    Use this tool to modify the details of an existing experimentation item in a Vercel installation. It's called when you need to update specific experimentation parameters or 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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEEXPERIMENTATIONITEM_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 integration_configuration_id:
        missing_params.append(("integration_configuration_id", "path"))
    if not resource_identifier:
        missing_params.append(("resource_identifier", "path"))
    if not experiment_item_id:
        missing_params.append(("experiment_item_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["UPDATEEXPERIMENTATIONITEM_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["UPDATEEXPERIMENTATIONITEM_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/resources/{resourceId}/experimentation/items/{itemId}".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id,
            resourceId=resource_identifier,
            itemId=experiment_item_id,
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEEXPERIMENTATIONITEM_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_experimentation_item(
    context: ToolContext,
    experiment_item_id: Annotated[
        str, "The unique identifier for the experimentation item to be deleted."
    ],
    integration_configuration_id: Annotated[
        str, "The ID of the integration configuration to identify the correct setup."
    ],
    resource_id: Annotated[
        str,
        "The unique identifier for the resource containing the item to be deleted. This is required to specify which resource the item belongs to.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint ''."]:
    """Delete an existing experimentation item.

    This tool deletes a specific experimentation item identified by its itemId within the given integration configuration and resource."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/resources/{resourceId}/experimentation/items/{itemId}".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id,
            resourceId=resource_id,
            itemId=experiment_item_id,
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def push_edge_config(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    integration_configuration_id: Annotated[
        str | None,
        "The unique identifier for the integration configuration. Use this to specify which configuration to push to Edge Config.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    resource_identifier: Annotated[
        str | None,
        "The ID of the resource for the configuration data to be pushed. Required for identifying the target Edge Config.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 ''."]:
    """Push configuration data to Edge Config for syncing.

    Use this tool to push configuration data into the Edge Config when Edge Config syncing is enabled. It is used to update the relevant configurations for experimentation through Vercel.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["PUSHEDGECONFIG_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 integration_configuration_id:
        missing_params.append(("integration_configuration_id", "path"))
    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["PUSHEDGECONFIG_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["PUSHEDGECONFIG_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/installations/{integrationConfigurationId}/resources/{resourceId}/experimentation/edge-config".format(  # noqa: UP032
            integrationConfigurationId=integration_configuration_id, resourceId=resource_identifier
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["PUSHEDGECONFIG_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def list_project_members(
    context: ToolContext,
    project_id_or_name: Annotated[str, "The ID or name of the project to list members for."],
    added_since_timestamp: Annotated[
        int | None, "Timestamp in milliseconds to include members added since this time."
    ] = None,
    end_time_timestamp: Annotated[
        int | None,
        "The timestamp in milliseconds to include project members added until this time.",
    ] = None,
    member_limit: Annotated[
        int | None,
        "Specify the maximum number of project members to return. Provide an integer value.",
    ] = None,
    search_project_members: Annotated[
        str | None, "Search for project members by name, username, or email."
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier of the team to perform the request on behalf of. This should be a string.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug to perform the request on behalf of, identifying the specific team associated with the project.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getProjectMembers'."]:
    """Retrieve all members of a specified project on Vercel.

    Use this tool to get a list of all team members associated with a particular project in Vercel. Call it when you need to know who is involved in a given project."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{idOrName}/members".format(  # noqa: UP032
            idOrName=project_id_or_name
        ),
        method="GET",
        params=remove_none_values({
            "limit": member_limit,
            "since": added_since_timestamp,
            "until": end_time_timestamp,
            "search": search_project_members,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def add_project_member(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_id_or_name: Annotated[
        str | None,
        "The ID or name of the Vercel project to which a new member will be added.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier of the Team to perform the request on behalf of.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug identifying the team for performing the request.  Only used when mode is 'execute'.",
    ] = 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 'addProjectMember'."]:
    """Add a new member to a Vercel project.

    Use this tool to add a new member to a Vercel project using the project's ID or name.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["ADDPROJECTMEMBER_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 project_id_or_name:
        missing_params.append(("project_id_or_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["ADDPROJECTMEMBER_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["ADDPROJECTMEMBER_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/projects/{idOrName}/members".format(  # noqa: UP032
            idOrName=project_id_or_name
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ADDPROJECTMEMBER_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def remove_project_member(
    context: ToolContext,
    project_id_or_name: Annotated[
        str, "The ID or name of the project from which the member will be removed."
    ],
    user_id: Annotated[str, "The unique user ID of the member to be removed from the project."],
    team_identifier: Annotated[
        str | None,
        "The identifier for the team on whose behalf the request is made. This should be a string.",
    ] = None,
    team_slug: Annotated[
        str | None, "The slug used to identify the Team for which the request is being made."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'removeProjectMember'."]:
    """Removes a member from a specific project.

    This tool removes a specified member from a project. It should be used when you need to revoke a member's access to a project."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{idOrName}/members/{uid}".format(  # noqa: UP032
            idOrName=project_id_or_name, uid=user_id
        ),
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_projects_list(
    context: ToolContext,
    exclude_repositories: Annotated[
        str | None, "Comma-separated list of repository names to exclude from the results."
    ] = None,
    filter_by_edge_config_id: Annotated[
        str | None,
        "Filter results by connected Edge Config ID. Provide the ID as a string to retrieve projects linked to this specific config.",
    ] = None,
    filter_by_edge_config_token_id: Annotated[
        str | None,
        "Filter results by the connected Edge Config Token ID. Provide the specific token ID to refine project search.",
    ] = None,
    filter_by_elastic_concurrency: Annotated[
        str | None,
        "Filter projects by elastic concurrency status. Use '1' for enabled or '0' for disabled.",
    ] = None,
    filter_by_repo: Annotated[
        str | None,
        "Filter the project results by the specified repository name, also used for project count.",
    ] = None,
    filter_by_repository_id: Annotated[
        str | None, "Filter the project results by specifying the Repository ID."
    ] = None,
    filter_by_static_ips_enabled: Annotated[
        str | None, "Set to '1' to filter projects with Static IPs enabled, '0' otherwise."
    ] = None,
    include_deprecated_projects: Annotated[
        bool | None, "Include deprecated projects in the results when set to True."
    ] = None,
    max_projects_returned: Annotated[
        str | None, "Specifies the maximum number of projects to return in the list."
    ] = None,
    repository_url_filter: Annotated[
        str | None, "URL to filter projects associated with a specific repository."
    ] = None,
    require_git_fork_authorization: Annotated[
        str | None,
        "Set to '1' to require authorization for Git fork PRs before deployment, or '0' to disable.",
    ] = None,
    search_by_project_name: Annotated[
        str | None, "Search for projects using a keyword or term in the name field."
    ] = None,
    team_identifier: Annotated[
        str | None, "The ID of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The team slug to perform the request on behalf of, representing a specific team within Vercel.",
    ] = None,
    updated_after: Annotated[
        str | None,
        "Filter projects updated after the specified timestamp or using a continuation token.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getProjects'."]:
    """Retrieve the list of user's or team's projects.

    This tool fetches the list of projects associated with the authenticated user or team. It supports pagination and allows filtering through query parameters."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v10/projects",
        method="GET",
        params=remove_none_values({
            "from": updated_after,
            "gitForkProtection": require_git_fork_authorization,
            "limit": max_projects_returned,
            "search": search_by_project_name,
            "repo": filter_by_repo,
            "repoId": filter_by_repository_id,
            "repoUrl": repository_url_filter,
            "excludeRepos": exclude_repositories,
            "edgeConfigId": filter_by_edge_config_id,
            "edgeConfigTokenId": filter_by_edge_config_token_id,
            "deprecated": include_deprecated_projects,
            "elasticConcurrencyEnabled": filter_by_elastic_concurrency,
            "staticIpsEnabled": filter_by_static_ips_enabled,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_new_project(
    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 identifier of the team on whose behalf the project will be created.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug representing the Team to perform the request on behalf of. It should be a string identifier.  Only used when mode is 'execute'.",
    ] = 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 'createProject'."]:
    """Create a new project with specified configurations.

    This tool creates a new project using the provided configurations. You can specify only the project name or add additional configurations to customize the project further. Call this tool when you need to initialize a new project.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATENEWPROJECT_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["CREATENEWPROJECT_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["CREATENEWPROJECT_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v11/projects",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATENEWPROJECT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_project_info(
    context: ToolContext,
    project_identifier_or_name: Annotated[
        str, "The unique project identifier or the project name to retrieve details."
    ],
    team_identifier: Annotated[
        str | None, "The unique identifier for the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The unique slug representing the team on whose behalf the request is performed.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getProject'."]:
    """Retrieve specific project details using project ID or name.

    This tool retrieves information about a specific project by using the project ID or name. It's useful for accessing project details from Vercel."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}".format(  # noqa: UP032
            idOrName=project_identifier_or_name
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_project_details(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_id_or_name: Annotated[
        str | None,
        "The unique identifier or name of the Vercel project to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier of the team on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = 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 'updateProject'."]:
    """Update a project's fields using its name or ID.

    Use this tool to modify specific details of a Vercel project by providing its name or ID. Ideal for updating project configurations 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, 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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEPROJECTDETAILS_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 project_id_or_name:
        missing_params.append(("project_id_or_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["UPDATEPROJECTDETAILS_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["UPDATEPROJECTDETAILS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v9/projects/{idOrName}".format(idOrName=project_id_or_name),  # noqa: UP032
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEPROJECTDETAILS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_project(
    context: ToolContext,
    project_identifier_or_name: Annotated[
        str, "The unique project identifier or the project name to specify which project to delete."
    ],
    team_identifier: Annotated[
        str | None, "The unique identifier of the team to perform the project deletion."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug representing the team to execute the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteProject'."]:
    """Delete a Vercel project by ID or name.

    Use this tool to delete a specific Vercel project by providing either the project's ID or name. Ideal for cleaning up unused projects or managing active projects through automation."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}".format(  # noqa: UP032
            idOrName=project_identifier_or_name
        ),
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_project_network_links(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_identifier_or_name: Annotated[
        str | None,
        "Specify the unique project identifier or project name for the network connection update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The identifier for the team on whose behalf the request is made. This is required to specify the context of the update.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The unique slug of the team that this request should be performed on behalf of.  Only used when mode is 'execute'.",
    ] = 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 'updateSharedConnectLinks'."]:
    """Update project connections to shared Secure Compute networks.

    Use this tool to update a project's connections to shared Secure Compute networks on Vercel. It should be called when there's a need to modify existing network connection settings for a particular project.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEPROJECTNETWORKLINKS_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 project_identifier_or_name:
        missing_params.append(("project_identifier_or_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["UPDATEPROJECTNETWORKLINKS_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["UPDATEPROJECTNETWORKLINKS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/projects/{idOrName}/shared-connect-links".format(  # noqa: UP032
            idOrName=project_identifier_or_name
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEPROJECTNETWORKLINKS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_custom_environment(
    context: ToolContext,
    project_unique_identifier_or_name: Annotated[
        str,
        "The unique project identifier or project name for which the custom environment is being created.",
    ],
    branch_matcher_type: Annotated[
        str | None, "Specifies the type of branch matcher: 'equals', 'startsWith', or 'endsWith'."
    ] = None,
    copy_environment_variables_from: Annotated[
        str | None, "Specify the source environment to copy variables from. This is optional."
    ] = None,
    custom_environment_slug: Annotated[
        str | None,
        "Specify the slug for the new custom environment. It cannot be 'Production' or 'Preview'.",
    ] = None,
    environment_description: Annotated[
        str | None, "Optional description for the custom environment being created."
    ] = None,
    git_branch_name_pattern: Annotated[
        str | None, "Git branch name or part of it to match with the custom environment."
    ] = None,
    team_identifier: Annotated[
        str | None, "The identifier of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team to perform the request on behalf of. It is required to specify the unique team for the custom environment.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'createCustomEnvironment'."]:
    """Create a custom environment for your Vercel project.

    This tool creates a custom environment in a specified Vercel project, excluding 'Production' or 'Preview' as names. Use it to set up tailored project environments."""
    request_data = remove_none_values({
        "slug": custom_environment_slug,
        "description": environment_description,
        "branchMatcher": {"type": branch_matcher_type, "pattern": git_branch_name_pattern},
        "copyEnvVarsFrom": copy_environment_variables_from,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}/custom-environments".format(  # noqa: UP032
            idOrName=project_unique_identifier_or_name
        ),
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": custom_environment_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_custom_project_environments(
    context: ToolContext,
    project_id_or_name: Annotated[
        str, "The unique identifier or name of the project to retrieve custom environments."
    ],
    git_branch_name: Annotated[
        str | None,
        "Specify the git branch to fetch custom environments from. This identifies the branch to retrieve environments for.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The identifier for the Team to perform the request on behalf of. Expected as a string.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team to perform the request on behalf of. This identifies the specific team related to the project.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint ''."]:
    """Retrieve custom environments for a specified project.

    Use this tool to get custom environments of a project on Vercel. These environments cannot be named 'Production' or 'Preview'."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}/custom-environments".format(  # noqa: UP032
            idOrName=project_id_or_name
        ),
        method="GET",
        params=remove_none_values({
            "gitBranch": git_branch_name,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_custom_environment(
    context: ToolContext,
    custom_environment_identifier: Annotated[
        str,
        "The unique identifier for a custom environment within the project, excluding 'Production' or 'Preview'.",
    ],
    project_identifier_or_name: Annotated[
        str, "The unique project identifier or the project's name to retrieve details for."
    ],
    team_identifier: Annotated[
        str | None,
        "The unique Team identifier used to perform the request on behalf of a specified team.",
    ] = None,
    team_slug: Annotated[str | None, "The Team slug used to perform the request."] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getCustomEnvironment'."]:
    """Retrieve custom environment details for a project.

    Use this tool to obtain information about a custom environment in a project, excluding 'Production' or 'Preview' environments."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}/custom-environments/{environmentSlugOrId}".format(  # noqa: UP032
            idOrName=project_identifier_or_name, environmentSlugOrId=custom_environment_identifier
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_custom_environment(
    context: ToolContext,
    custom_environment_id: Annotated[
        str, "The unique identifier for the custom environment within the project."
    ],
    project_identifier_or_name: Annotated[
        str, "The unique project identifier or project name for the custom environment."
    ],
    branch_matcher_type: Annotated[
        str | None, "Specifies the branch matcher type: 'equals', 'startsWith', or 'endsWith'."
    ] = None,
    branch_name_pattern: Annotated[
        str | None,
        "Specify a portion or full Git branch name for matching. Used to identify branches in custom environments.",
    ] = None,
    custom_environment_description: Annotated[
        str | None, "Optional description of the custom environment to be updated."
    ] = None,
    custom_environment_slug: Annotated[
        str | None,
        "Slug of the custom environment to update. Must not be 'Production' or 'Preview'.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The identifier of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug of the team to perform the update on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'updateCustomEnvironment'."]:
    """Update a custom environment for a Vercel project.

    Use this tool to update a custom environment in a Vercel project. The environment should not be named 'Production' or 'Preview'. Useful for managing project-specific settings."""
    request_data = remove_none_values({
        "slug": custom_environment_slug,
        "description": custom_environment_description,
        "branchMatcher": {"type": branch_matcher_type, "pattern": branch_name_pattern},
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}/custom-environments/{environmentSlugOrId}".format(  # noqa: UP032
            idOrName=project_identifier_or_name, environmentSlugOrId=custom_environment_id
        ),
        method="PATCH",
        params=remove_none_values({"teamId": team_identifier, "slug": custom_environment_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def remove_custom_environment(
    context: ToolContext,
    custom_environment_identifier: Annotated[
        str, "The unique identifier for the custom environment within the project to be removed."
    ],
    project_identifier_or_name: Annotated[
        str, "The unique project identifier or the project name to target the environment removal."
    ],
    delete_unassigned_environment_variables: Annotated[
        bool | None,
        "Delete environment variables that are not assigned to any environments when set to true.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The identifier for the team to make the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug representing the team on whose behalf to perform the request."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'removeCustomEnvironment'."]:
    """Remove a specified custom environment from a project.

    This tool removes a custom environment from a project, excluding 'Production' or 'Preview'. Use it when you need to delete a specific environment from a Vercel project."""
    request_data = remove_none_values({
        "deleteUnassignedEnvironmentVariables": delete_unassigned_environment_variables
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}/custom-environments/{environmentSlugOrId}".format(  # noqa: UP032
            idOrName=project_identifier_or_name, environmentSlugOrId=custom_environment_identifier
        ),
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_project_domains(
    context: ToolContext,
    project_id_or_name: Annotated[
        str, "Specify the unique project identifier or the project name."
    ],
    created_before_timestamp: Annotated[
        float | None, "Get domains created before this JavaScript timestamp for filtering results."
    ] = None,
    custom_environment_id: Annotated[
        str | None, "The unique custom environment identifier within the project."
    ] = None,
    domains_created_since_timestamp: Annotated[
        float | None, "Get domains created after this JavaScript timestamp."
    ] = None,
    domains_sort_order: Annotated[
        str | None, "Sort order for domains based on creation date."
    ] = "DESC",
    filter_by_git_branch: Annotated[
        str | None, "Specify the branch to filter domains associated with that branch."
    ] = None,
    filter_by_redirect_target: Annotated[
        str | None,
        "Specify the redirect target to filter domains. Useful for targeting specific redirections.",
    ] = None,
    filter_by_verification_status: Annotated[
        str | None, "Filter domains by their verification status (e.g., verified, unverified)."
    ] = None,
    filter_production_domains: Annotated[
        str | None, "Set to 'true' to filter only production domains; otherwise, returns all."
    ] = "false",
    filter_target_domain: Annotated[
        str | None,
        "Specify 'production' or 'preview' to filter domains based on their target environment.",
    ] = None,
    include_redirect_domains: Annotated[
        str | None,
        'Specify whether to include redirect project domains. Use "true" to include (default), "false" to exclude.',
    ] = "true",
    max_domains_to_list: Annotated[
        float | None,
        "The maximum number of domains to list in the response, with a maximum value of 100.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The unique identifier for the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug representing the team on whose behalf the request is made."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getProjectDomains'."]:
    """Retrieve domains linked to a specific project.

    Use this tool to get the domains associated with a project by providing the project's ID or name. This can help manage and review project-related domains efficiently."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}/domains".format(  # noqa: UP032
            idOrName=project_id_or_name
        ),
        method="GET",
        params=remove_none_values({
            "production": filter_production_domains,
            "target": filter_target_domain,
            "customEnvironmentId": custom_environment_id,
            "gitBranch": filter_by_git_branch,
            "redirects": include_redirect_domains,
            "redirect": filter_by_redirect_target,
            "verified": filter_by_verification_status,
            "limit": max_domains_to_list,
            "since": domains_created_since_timestamp,
            "until": created_before_timestamp,
            "order": domains_sort_order,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def fetch_project_domain(
    context: ToolContext,
    project_domain_name: Annotated[str, "The name of the project's domain to fetch details for."],
    project_id_or_name: Annotated[
        str, "The unique project identifier or the project name for fetching domain details."
    ],
    team_identifier: Annotated[
        str | None, "The Team identifier to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug of the team on whose behalf the request is made."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getProjectDomain'."]:
    """Fetch domain details for a specific project.

    Use this tool to obtain detailed information about a project's domain using the project's ID or name and the domain name."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}/domains/{domain}".format(  # noqa: UP032
            idOrName=project_id_or_name, domain=project_domain_name
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_project_domain_config(
    context: ToolContext,
    project_domain_name: Annotated[
        str, "The project domain name to be updated. Example: 'example.com'."
    ],
    project_identifier_or_name: Annotated[
        str,
        "The unique project identifier or the project name used to update domain configuration.",
    ],
    linked_git_branch: Annotated[
        str | None, "The Git branch to associate with the project domain."
    ] = None,
    redirect_status_code: Annotated[
        int | None,
        "HTTP status code for the domain redirect. Acceptable values are 301, 302, 307, 308, or None if no redirect is required.",
    ] = None,
    redirect_target_domain: Annotated[
        str | None, "Specify the target destination domain for the redirect of a project domain."
    ] = None,
    team_identifier: Annotated[
        str | None, "The identifier for the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug identifier for the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'updateProjectDomain'."]:
    """Update a project's domain configuration.

    Use this tool to update a project's domain settings, including the domain name, associated git branch, and redirect options."""
    request_data = remove_none_values({
        "gitBranch": linked_git_branch,
        "redirect": redirect_target_domain,
        "redirectStatusCode": redirect_status_code,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}/domains/{domain}".format(  # noqa: UP032
            idOrName=project_identifier_or_name, domain=project_domain_name
        ),
        method="PATCH",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def remove_project_domain(
    context: ToolContext,
    project_domain_name: Annotated[str, "The domain name of the project to be removed."],
    project_id_or_name: Annotated[
        str,
        "The unique project identifier or name to specify which project's domain is to be removed.",
    ],
    remove_redirects: Annotated[
        bool | None,
        "Set to true to remove all domains from the project that redirect to the domain being removed.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The identifier for the team on whose behalf the request is performed."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug identifier of the team to perform the request on behalf of. Used to specify which team's project domain should be removed.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'removeProjectDomain'."]:
    """Removes a domain from a specified project.

    Use this tool to remove a domain from a Vercel project by specifying the project's ID or name along with the domain name."""
    request_data = remove_none_values({"removeRedirects": remove_redirects})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}/domains/{domain}".format(  # noqa: UP032
            idOrName=project_id_or_name, domain=project_domain_name
        ),
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def add_project_domain(
    context: ToolContext,
    project_domain_name: Annotated[
        str, "The domain name to be added to the specified Vercel project."
    ],
    project_identifier_or_name: Annotated[
        str, "The unique identifier or name of the project to which the domain will be added."
    ],
    custom_environment_id: Annotated[
        str | None, "The unique custom environment identifier within the project."
    ] = None,
    git_branch_to_link_domain: Annotated[
        str | None,
        "The Git branch to associate with the project domain when adding it to a Vercel project. This allows the domain to be tied to a specific branch in the repository.",
    ] = None,
    redirect_status_code: Annotated[
        int | None,
        "HTTP status code for redirecting the domain. Options are: 301, 302, 307, 308, or None.",
    ] = None,
    redirect_target_domain: Annotated[
        str | None, "Specify the target destination domain to redirect to."
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The identifier of the team for which the request is made. This ensures the request is executed on behalf of the specified team.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team to perform the request on behalf of. This identifies the team context for the domain addition.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'addProjectDomain'."]:
    """Add a domain to a specified Vercel project.

    This tool adds a domain to a Vercel project by specifying the domain name and project identifier (id or name). If the domain is unverified, the response will indicate it needs verification. If the domain already exists, a 400 error will occur."""
    request_data = remove_none_values({
        "name": project_domain_name,
        "gitBranch": git_branch_to_link_domain,
        "customEnvironmentId": custom_environment_id,
        "redirect": redirect_target_domain,
        "redirectStatusCode": redirect_status_code,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v10/projects/{idOrName}/domains".format(  # noqa: UP032
            idOrName=project_identifier_or_name
        ),
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def move_project_domain(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_id_or_name: Annotated[
        str | None,
        "Provide the unique project identifier or the project name for the domain transfer.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    project_domain_name: Annotated[
        str | None,
        "The domain name of the project to be moved to another project.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The identifier for the team on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug for the team on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = 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 'moveProjectDomain'."]:
    """Transfer a domain from one project to another.

    Use this tool to move a project's domain to a different project, and optionally transfer redirects associated with that domain.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["MOVEPROJECTDOMAIN_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 project_id_or_name:
        missing_params.append(("project_id_or_name", "path"))
    if not project_domain_name:
        missing_params.append(("project_domain_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["MOVEPROJECTDOMAIN_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["MOVEPROJECTDOMAIN_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/projects/{idOrName}/domains/{domain}/move".format(  # noqa: UP032
            idOrName=project_id_or_name, domain=project_domain_name
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["MOVEPROJECTDOMAIN_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def verify_project_domain(
    context: ToolContext,
    project_id_or_name: Annotated[
        str, "The unique project identifier or the project name to verify the domain for."
    ],
    verify_domain_name: Annotated[str, "The domain name you want to verify for the project."],
    team_identifier: Annotated[
        str | None, "The Team identifier to perform the request on behalf of. Provide as a string."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug used to perform the verification request on behalf of a specific team.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'verifyProjectDomain'."]:
    """Verify the status of a project domain's verification challenge.

    This tool attempts to verify a project domain marked as `verified = false` by checking the correctness of its verification challenge. Use this to confirm domain ownership for a project."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}/domains/{domain}/verify".format(  # noqa: UP032
            idOrName=project_id_or_name, domain=verify_domain_name
        ),
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_project_environment_variables(
    context: ToolContext,
    project_id_or_name: Annotated[
        str, "The unique identifier or name of the project to retrieve environment variables for."
    ],
    caller_source: Annotated[str | None, "Specify the source making the API call."] = None,
    custom_environment_id: Annotated[
        str | None,
        "The unique custom environment identifier within the project. Use this to specify a specific custom environment.",
    ] = None,
    custom_environment_slug: Annotated[
        str | None,
        "The custom environment slug (name) within the project to filter specific settings.",
    ] = None,
    decrypt_values: Annotated[
        str | None,
        "Set to 'true' to decrypt environment variable values. Use 'false' to keep them encrypted.",
    ] = None,
    filter_by_git_branch: Annotated[
        str | None,
        "Specify the git branch to filter the environment variable results. Must have target set to 'preview'.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The unique identifier of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug to perform the request on behalf of. Use this to specify the team context for the request.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'filterProjectEnvs'."]:
    """Retrieve environment variables for a specified project.

    Use this tool to get the environment variables associated with a project by providing its `id` or `name`. Useful for managing and configuring project settings."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v10/projects/{idOrName}/env".format(  # noqa: UP032
            idOrName=project_id_or_name
        ),
        method="GET",
        params=remove_none_values({
            "gitBranch": filter_by_git_branch,
            "decrypt": decrypt_values,
            "source": caller_source,
            "customEnvironmentId": custom_environment_id,
            "customEnvironmentSlug": custom_environment_slug,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_project_environment_variables(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_identifier_or_name: Annotated[
        str | None,
        "The unique identifier or name of the Vercel project to create or update environment variables for.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    allow_override_existing_variable: Annotated[
        str | None,
        "Allows updating the value of an existing environment variable if set to true.  Only used when mode is 'execute'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The Team identifier to perform the request on behalf of, specified as a string.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug for the project. Used to perform the request on behalf of a specific team.  Only used when mode is 'execute'.",
    ] = 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 'createProjectEnv'."]:
    """Create or update environment variables for a Vercel project.

    This tool creates or updates one or more environment variables for a specified Vercel project. The project can be identified by its `id` or `name`. The tool allows specifying the `key`, `value`, `type`, and `target` of the environment variables. If `upsert=true` is included, existing variables will be updated instead of creating new ones.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEPROJECTENVIRONMENTVARIABLES_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 project_identifier_or_name:
        missing_params.append(("project_identifier_or_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["CREATEPROJECTENVIRONMENTVARIABLES_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["CREATEPROJECTENVIRONMENTVARIABLES_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v10/projects/{idOrName}/env".format(  # noqa: UP032
            idOrName=project_identifier_or_name
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEPROJECTENVIRONMENTVARIABLES_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({
            "upsert": allow_override_existing_variable,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_project_environment_variable(
    context: ToolContext,
    environment_variable_id: Annotated[
        str, "The unique ID for the environment variable to retrieve its decrypted value."
    ],
    project_identifier_or_name: Annotated[
        str, "The unique identifier or name of the project to retrieve its environment variable."
    ],
    team_identifier: Annotated[
        str | None, "The identifier for the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug for the team to perform the request on behalf of. This identifies the team in a user-friendly way.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getProjectEnv'."]:
    """Retrieve the environment variable for a given project.

    Call this tool to access the environment variable details for a specific project using its ID or name."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{idOrName}/env/{id}".format(  # noqa: UP032
            idOrName=project_identifier_or_name, id=environment_variable_id
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_project_env_variable(
    context: ToolContext,
    environment_variable_identifier: Annotated[
        str, "The unique identifier of the environment variable to be deleted."
    ],
    project_identifier_or_name: Annotated[
        str,
        "The unique project identifier or name to identify the target project for which the environment variable should be deleted.",
    ],
    custom_environment_identifier: Annotated[
        str | None, "The unique custom environment identifier within the project."
    ] = None,
    team_identifier: Annotated[
        str | None, "The Team identifier to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The Team slug used to perform the request on behalf of a team."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'removeProjectEnv'."]:
    """Delete a project's specific environment variable.

    This tool deletes a specified environment variable from a project by using the environment variable's identifier. You can specify the project using its ID or name."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v9/projects/{idOrName}/env/{id}".format(  # noqa: UP032
            idOrName=project_identifier_or_name, id=environment_variable_identifier
        ),
        method="DELETE",
        params=remove_none_values({
            "customEnvironmentId": custom_environment_identifier,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def edit_project_environment_variable(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_id_or_name: Annotated[
        str | None,
        "Specify the unique project identifier or the project name.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    environment_variable_id: Annotated[
        str | None,
        "The unique environment variable identifier for the Vercel project.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the team on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The identifier for the team on whose behalf the request is made. This is usually a URL-friendly name for the team.  Only used when mode is 'execute'.",
    ] = 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 'editProjectEnv'."]:
    """Edit a specific environment variable for a project.

    Use this tool to modify an environment variable of a Vercel project by providing the variable's ID and the project's ID or name.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "EDITPROJECTENVIRONMENTVARIABLE_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 project_id_or_name:
        missing_params.append(("project_id_or_name", "path"))
    if not environment_variable_id:
        missing_params.append(("environment_variable_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["EDITPROJECTENVIRONMENTVARIABLE_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["EDITPROJECTENVIRONMENTVARIABLE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v9/projects/{idOrName}/env/{id}".format(  # noqa: UP032
            idOrName=project_id_or_name, id=environment_variable_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["EDITPROJECTENVIRONMENTVARIABLE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_project_env_variables(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_id_or_name: Annotated[
        str | None,
        "The unique project identifier or the project name to specify which project to delete variables from.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The Team identifier to perform the request on behalf of when deleting environment variables.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug representing the team on whose behalf the request is performed.  Only used when mode is 'execute'.",
    ] = 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 'batchRemoveProjectEnv'."]:
    """Delete multiple environment variables from a Vercel project.

    Use this tool to delete several environment variables for a specific Vercel project at once. It's useful when managing project configurations and needing to remove obsolete or sensitive data efficiently.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "DELETEPROJECTENVVARIABLES_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 project_id_or_name:
        missing_params.append(("project_id_or_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["DELETEPROJECTENVVARIABLES_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["DELETEPROJECTENVVARIABLES_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/projects/{idOrName}/env".format(idOrName=project_id_or_name),  # noqa: UP032
        method="DELETE",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["DELETEPROJECTENVVARIABLES_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def upload_client_cert_to_project(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_id_or_name: Annotated[
        str | None,
        "The unique identifier or name of the Vercel project to upload the client certificate to. This is required to specify which project the mTLS certificate should be associated with.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The string identifier of the team to perform the request on behalf of.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug representing the team on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = 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 'uploadProjectClientCert'."]:
    """Upload a client certificate for mTLS authentication.

    Use this tool to upload a client certificate to a Vercel project for mutual TLS (mTLS) authentication with external origins.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPLOADCLIENTCERTTOPROJECT_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 project_id_or_name:
        missing_params.append(("project_id_or_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["UPLOADCLIENTCERTTOPROJECT_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["UPLOADCLIENTCERTTOPROJECT_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/projects/{idOrName}/client-cert".format(  # noqa: UP032
            idOrName=project_id_or_name
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPLOADCLIENTCERTTOPROJECT_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_rolling_release_billing_status(
    context: ToolContext,
    project_id_or_name: Annotated[
        str,
        "Project ID or name, URL-encoded, to identify the project for which to retrieve billing status.",
    ],
    team_identifier: Annotated[
        str | None, "The identifier for the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug representation of the team to perform the request for. Used to specify which team's billing status is being queried.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getRollingReleaseBillingStatus'."]:
    """Get the billing status for a project's rolling releases.

    This tool retrieves the billing status for a specific project's rolling releases from Vercel. It determines if the project is eligible for configuration under rolling releases based on the team's billing status."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{idOrName}/rolling-release/billing".format(  # noqa: UP032
            idOrName=project_id_or_name
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_rolling_release_config(
    context: ToolContext,
    project_identifier: Annotated[
        str,
        "The project ID or name, URL-encoded, to identify the project for the configuration request.",
    ],
    team_identifier: Annotated[
        str | None, "The unique identifier for the Team on whose behalf the request is made."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug representing the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getRollingReleaseConfig'."]:
    """Fetch the rolling releases configuration for a project.

    Use this tool to obtain the template configuration for future rolling releases in a given project on Vercel. It does not provide settings for already active releases."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{idOrName}/rolling-release/config".format(  # noqa: UP032
            idOrName=project_identifier
        ),
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def disable_rolling_releases(
    context: ToolContext,
    project_id_or_name: Annotated[
        str, "Project ID or URL-encoded name to specify the Vercel project."
    ],
    team_identifier: Annotated[
        str | None, "The identifier of the team to execute the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug identifying the team to perform the request on behalf of. This should be a string value.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteRollingReleaseConfig'."]:
    """Disable rolling releases for a Vercel project.

    Use this tool to disable future rolling releases for a specified Vercel project. Note that this will not affect any current rollouts in progress. For ongoing rollouts, additional actions like completing or aborting may be required."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{idOrName}/rolling-release/config".format(  # noqa: UP032
            idOrName=project_id_or_name
        ),
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_rolling_release_config(
    context: ToolContext,
    project_id_or_name: Annotated[
        str, "Project ID or name (URL-encoded) for updating rolling release settings."
    ],
    team_identifier: Annotated[
        str | None, "The identifier for the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug identifying the team for which the request is made."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'updateRollingReleaseConfig'."]:
    """Update or disable rolling releases for a Vercel project.

    Use this tool to update or disable rolling releases for a specific Vercel project. It changes the configuration for future deployments without affecting in-progress rollouts. Disabling the feature requires additional API calls to complete or abort current rollouts."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{idOrName}/rolling-release/config".format(  # noqa: UP032
            idOrName=project_id_or_name
        ),
        method="PATCH",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_rolling_release(
    context: ToolContext,
    project_id_or_name: Annotated[
        str, "The project ID or URL-encoded project name to identify the specific project."
    ],
    filter_by_release_state: Annotated[
        str | None, "Filter the rolling release by its state: ACTIVE, COMPLETE, or ABORTED."
    ] = None,
    team_identifier: Annotated[
        str | None, "The unique identifier of the team on whose behalf the request is made."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug that identifies the team on whose behalf the request is made. This is required for team-specific data access.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getRollingRelease'."]:
    """Retrieve the rolling release for a specific project.

    Use this tool to get details about a project's rolling release, including its current status (active, aborted, or completed). Useful when monitoring or managing project deployments."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{idOrName}/rolling-release".format(  # noqa: UP032
            idOrName=project_id_or_name
        ),
        method="GET",
        params=remove_none_values({
            "state": filter_by_release_state,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def advance_rollout_stage(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_identifier: Annotated[
        str | None,
        "Project ID or URL-encoded project name to identify the project.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The Team identifier used for performing the request on behalf of the specified team.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug representing the team for which the rollout action will be performed.  Only used when mode is 'execute'.",
    ] = 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 'approveRollingReleaseStage'."]:
    """Advance a rollout to the next stage when manual approval is required.

    Use this tool to advance a release rollout to the next stage when manual approval is enabled in Vercel's rolling releases configuration.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["ADVANCEROLLOUTSTAGE_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 project_identifier:
        missing_params.append(("project_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["ADVANCEROLLOUTSTAGE_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["ADVANCEROLLOUTSTAGE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/projects/{idOrName}/rolling-release/approve-stage".format(  # noqa: UP032
            idOrName=project_identifier
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ADVANCEROLLOUTSTAGE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def force_complete_rolling_release(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_identifier: Annotated[
        str | None,
        "The project ID or URL-encoded project name in Vercel.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the team on whose behalf the request is performed.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team for which the request is performed.  Only used when mode is 'execute'.",
    ] = 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 'completeRollingRelease'."]:
    """Complete a rolling release to serve 100% traffic from canary.

    This tool should be used to force-complete a rolling release in Vercel projects, ensuring that the canary deployment is now serving 100% of the traffic. Call this tool when you need to finalize a rolling release deployment.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "FORCECOMPLETEROLLINGRELEASE_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 project_identifier:
        missing_params.append(("project_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["FORCECOMPLETEROLLINGRELEASE_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["FORCECOMPLETEROLLINGRELEASE_REQUEST_BODY_SCHEMA"],
                    indent=2,
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/projects/{idOrName}/rolling-release/complete".format(  # noqa: UP032
            idOrName=project_identifier
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["FORCECOMPLETEROLLINGRELEASE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def initiate_project_transfer(
    context: ToolContext,
    project_id_or_name: Annotated[str, "The ID or name of the project to transfer between teams."],
    team_identifier: Annotated[
        str | None, "The unique identifier of the team initiating the project transfer."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug to perform the project transfer request on behalf of. This is a unique identifier for the team on Vercel.",
    ] = None,
    webhook_callback_url: Annotated[
        str | None, "The URL to send a webhook to when the project transfer is accepted."
    ] = None,
    webhook_signing_secret: Annotated[
        str | None,
        "The secret key used to sign the webhook payload with HMAC-SHA256 for security verification.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'createProjectTransferRequest'."]:
    """Initiate a project transfer request between teams.

    This tool initiates a project transfer request from one team to another on Vercel. It returns a code valid for 24 hours, which can be used to accept the transfer via a specified endpoint or claim URL."""
    request_data = remove_none_values({
        "callbackUrl": webhook_callback_url,
        "callbackSecret": webhook_signing_secret,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/projects/{idOrName}/transfer-request".format(  # noqa: UP032
            idOrName=project_id_or_name
        ),
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def accept_project_transfer(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_transfer_code: Annotated[
        str | None,
        "The unique code of the project transfer request, required to accept the transfer.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier of the team to perform the request on behalf of.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The team slug used to perform the project transfer request on behalf of a specific team.  Only used when mode is 'execute'.",
    ] = 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 'acceptProjectTransferRequest'."]:
    """Accept a project transfer request on Vercel.

    Use this tool to accept a project transfer request that has been initiated by another team on Vercel. This process requires a transfer code, which is generated by the initiating team. Call this tool when you need to finalize the transfer of a project to your team.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "ACCEPTPROJECTTRANSFER_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 project_transfer_code:
        missing_params.append(("project_transfer_code", "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["ACCEPTPROJECTTRANSFER_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["ACCEPTPROJECTTRANSFER_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/projects/transfer-request/{code}".format(  # noqa: UP032
            code=project_transfer_code
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ACCEPTPROJECTTRANSFER_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_project_protection_bypass(
    context: ToolContext,
    project_id_or_name: Annotated[
        str,
        "The unique Vercel project identifier or project name to update the protection bypass for.",
    ],
    create_new_automation_bypass: Annotated[
        bool | None, "Create a new automation bypass after revoking the current secret."
    ] = None,
    optional_secret_value: Annotated[
        str | None, "Optional value of the secret to generate; omit for OAuth2 tokens."
    ] = None,
    revoke_automation_bypass: Annotated[
        str | None, "Secret value of the automation bypass to be revoked for a Vercel project."
    ] = None,
    team_identifier: Annotated[
        str | None, "The identifier for the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug of the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'updateProjectProtectionBypass'."]:
    """Update the deployment protection bypass for a Vercel project.

    Use this tool to modify the deployment protection automation bypass settings for a specific Vercel project."""
    request_data = remove_none_values({
        "revoke": {"secret": revoke_automation_bypass, "regenerate": create_new_automation_bypass},
        "generate": {"secret": optional_secret_value},
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{idOrName}/protection-bypass".format(  # noqa: UP032
            idOrName=project_id_or_name
        ),
        method="PATCH",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def promote_deployment_to_production(
    context: ToolContext,
    deployment_identifier: Annotated[
        str,
        "The ID of the deployment to be promoted to production. It should be a valid string representing the deployment ID.",
    ],
    project_id: Annotated[
        str, "The unique identifier of the project associated with the deployment to promote."
    ],
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the team on whose behalf the request is made. It should be a string value.",
    ] = None,
    team_slug: Annotated[
        str | None, "The slug of the team to perform the promotion request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'requestPromote'."]:
    """Promotes a deployment to production without rebuilding it.

    Use this tool to promote an existing deployment to production on Vercel. This action does not rebuild the deployment. To rebuild, use the create-deployments endpoint instead."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v10/projects/{projectId}/promote/{deploymentId}".format(  # noqa: UP032
            projectId=project_id, deploymentId=deployment_identifier
        ),
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_last_promote_aliases_status(
    context: ToolContext,
    project_id: Annotated[
        str,
        "Specify the Project ID to filter the promote aliases related to that specific project.",
    ],
    aliases_created_before_timestamp: Annotated[
        float | None, "Get aliases created before this epoch timestamp."
    ] = None,
    filter_failed_aliases: Annotated[
        bool | None,
        "Set to true to filter results to only include aliases that failed to map to the requested deployment.",
    ] = None,
    get_aliases_created_after_epoch: Annotated[
        float | None, "Get aliases created after the specified epoch timestamp."
    ] = None,
    max_aliases_to_list: Annotated[
        float | None,
        "Specify the maximum number of aliases to list from the request. The maximum allowed is 100.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The Team identifier to perform the request on behalf of. Must be a string."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug representing the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'listPromoteAliases'."]:
    """Retrieve aliases and their mapping status from last promote request.

    Call this tool to obtain a list of aliases related to the most recent promote request, along with their current mapping status for a specified project."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{projectId}/promote/aliases".format(  # noqa: UP032
            projectId=project_id
        ),
        method="GET",
        params=remove_none_values({
            "limit": max_aliases_to_list,
            "since": get_aliases_created_after_epoch,
            "until": aliases_created_before_timestamp,
            "failedOnly": filter_failed_aliases,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def pause_project(
    context: ToolContext,
    project_id: Annotated[str, "The unique identifier for the Vercel project you wish to pause."],
    team_identifier: Annotated[
        str | None, "The identifier of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[str | None, "The Team slug to perform the request on behalf of."] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'pauseProject'."]:
    """Pause a Vercel project by its ID.

    Use this tool to pause a Vercel project by providing its project ID. This will disable auto-assigning custom production domains and block active Production Deployments if applicable."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{projectId}/pause".format(projectId=project_id),  # noqa: UP032
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def unpause_project(
    context: ToolContext,
    project_id: Annotated[str, "The unique identifier for the Vercel project to be unpaused."],
    team_identifier: Annotated[
        str | None,
        "The identifier for the team on behalf of which the request is performed. Used to specify the target team in Vercel.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug identifying the team to perform the request on behalf of. Required for targeting the correct team's project.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'unpauseProject'."]:
    """Unpause a Vercel project using its project ID.

    Use this tool to unpause a Vercel project by providing its project ID. The tool confirms successful unpausing or indicates failure if the project ID is invalid."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/projects/{projectId}/unpause".format(projectId=project_id),  # noqa: UP032
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_attack_challenge_mode(
    context: ToolContext,
    enable_attack_challenge_mode: Annotated[
        bool, "Set to true to enable Attack Challenge mode; false to disable it."
    ],
    project_id: Annotated[
        str, "The unique identifier of the project to update the Attack Challenge mode for."
    ],
    attack_mode_active_until: Annotated[
        float | None,
        "The UNIX timestamp indicating when the Attack Challenge mode should be active until. Specify this to control the duration of the mode being enabled.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The unique identifier for the team on whose behalf the request is made."
    ] = None,
    team_slug: Annotated[
        str | None, "The unique slug of the team on behalf of which the request is made."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'updateAttackChallengeMode'."]:
    """Updates Attack Challenge mode setting for a project.

    This tool updates the Attack Challenge mode setting for a project in Vercel's security settings. Use it when you need to enable or disable the Attack Challenge mode."""
    request_data = remove_none_values({
        "projectId": project_id,
        "attackModeEnabled": enable_attack_challenge_mode,
        "attackModeActiveUntil": attack_mode_active_until,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/security/attack-mode",
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def set_firewall_configuration(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_id: Annotated[
        str | None,
        "The unique identifier for the project to configure the firewall settings.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier of the team to perform the request on behalf of.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug representing the team for which the firewall configuration will be updated.  Only used when mode is 'execute'.",
    ] = 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 'putFirewallConfig'."]:
    """Update firewall configuration with specified rules.

    This tool sets or overwrites the firewall configuration with the provided rules and settings on Vercel.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "SETFIREWALLCONFIGURATION_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 required parameters
    missing_params = []
    if not project_id:
        missing_params.append(("project_id", "query"))

    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["SETFIREWALLCONFIGURATION_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["SETFIREWALLCONFIGURATION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/security/firewall/config",
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SETFIREWALLCONFIGURATION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({
            "projectId": project_id,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_firewall_config(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_identifier: Annotated[
        str | None,
        "The unique identifier for the project to modify the firewall config.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the team to perform the request on behalf of.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug used to identify the team for the request.  Only used when mode is 'execute'.",
    ] = 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 'updateFirewallConfig'."]:
    """Modify the existing firewall config for a project.



    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEFIREWALLCONFIG_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 required parameters
    missing_params = []
    if not project_identifier:
        missing_params.append(("project_identifier", "query"))

    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["UPDATEFIREWALLCONFIG_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["UPDATEFIREWALLCONFIG_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/security/firewall/config",
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEFIREWALLCONFIG_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({
            "projectId": project_identifier,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_firewall_config(
    context: ToolContext,
    firewall_configuration_version: Annotated[
        str, "The deployed version of the firewall configuration to retrieve."
    ],
    project_id: Annotated[
        str,
        "The unique identifier for the project whose firewall configuration is being retrieved.",
    ],
    team_identifier: Annotated[
        str | None, "The identifier for the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The team slug to perform the request on behalf of. It identifies the specific team by its slug name.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getFirewallConfig'."]:
    """Retrieve the active firewall configuration for a project.

    This tool is used to obtain the active firewall configuration for a specific project by retrieving the specified version of the configuration."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/security/firewall/config/{configVersion}".format(  # noqa: UP032
            configVersion=firewall_configuration_version
        ),
        method="GET",
        params=remove_none_values({
            "projectId": project_id,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_active_attack_status(
    context: ToolContext,
    project_id: Annotated[str, "The unique identifier of the project to retrieve attack data for."],
    active_days_since: Annotated[
        float | None,
        "Number of days in the past to look for active attack data. Defaults to 1 day if not specified.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The unique identifier of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team to perform the request on behalf of. This identifies which team to target for the retrieval of attack data within Vercel's system.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getActiveAttackStatus'."]:
    """Retrieve active attack data from the Vercel firewall.

    Use this tool to obtain information about any active attacks detected by Vercel's firewall within the specified number of days."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/security/firewall/attack-status",
        method="GET",
        params=remove_none_values({
            "projectId": project_id,
            "since": active_days_since,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_project_bypass_rules(
    context: ToolContext,
    project_id: Annotated[
        str, "The unique identifier of the project for which to retrieve bypass rules."
    ],
    filter_by_domain: Annotated[
        str | None,
        "Specify the domain to filter bypass rules. This filters rules related to the given domain.",
    ] = None,
    filter_by_project_scope: Annotated[
        bool | None, "Set to true to filter results by project-scoped rules."
    ] = None,
    filter_by_source_ip: Annotated[
        str | None, "Specify a source IP to filter the system bypass rules for a project."
    ] = None,
    pagination_offset: Annotated[
        str | None, "Pagination offset, retrieving results after the specified ID."
    ] = None,
    result_limit: Annotated[
        float | None,
        "The maximum number of rules to retrieve. Specify as a number. This is useful for controlling the volume of data returned.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The unique identifier of the team on whose behalf the request is performed."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug representing the team to make the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getBypassIp'."]:
    """Retrieve the bypass rules for a specified project.

    Use this tool to access the system bypass rules configured for a specific project on Vercel. It is useful for security management and monitoring of firewall settings."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/security/firewall/bypass",
        method="GET",
        params=remove_none_values({
            "projectId": project_id,
            "limit": result_limit,
            "sourceIp": filter_by_source_ip,
            "domain": filter_by_domain,
            "projectScope": filter_by_project_scope,
            "offset": pagination_offset,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_firewall_bypass_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_id: Annotated[
        str | None,
        "The identifier for the project to create a bypass rule for.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The identifier for the team on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug identifier of the team for which the bypass rule is created.  Only used when mode is 'execute'.",
    ] = 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 'addBypassIp'."]:
    """Create a new firewall bypass rule.

    This tool should be called to create new system bypass rules in the firewall. Useful for configuring security exceptions.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEFIREWALLBYPASSRULE_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 required parameters
    missing_params = []
    if not project_id:
        missing_params.append(("project_id", "query"))

    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["CREATEFIREWALLBYPASSRULE_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["CREATEFIREWALLBYPASSRULE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/security/firewall/bypass",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEFIREWALLBYPASSRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({
            "projectId": project_id,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def remove_bypass_rule(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    project_id: Annotated[
        str | None,
        "The unique identifier for the project from which to remove the bypass rule.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier for the team on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug indicating which team to perform the request on behalf of.  Only used when mode is 'execute'.",
    ] = 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 'removeBypassIp'."]:
    """Removes a bypass rule from the firewall.

    Use this tool to remove a system bypass rule from the Vercel firewall, ensuring that specific IPs can no longer bypass the security restrictions.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["REMOVEBYPASSRULE_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 required parameters
    missing_params = []
    if not project_id:
        missing_params.append(("project_id", "query"))

    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["REMOVEBYPASSRULE_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["REMOVEBYPASSRULE_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/security/firewall/bypass",
        method="DELETE",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["REMOVEBYPASSRULE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({
            "projectId": project_id,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_integration_store(
    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 of the team on whose behalf the request is being made.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The unique slug of the team for which the integration store is being created. This identifies the team on behalf of which the request is made.  Only used when mode is 'execute'.",
    ] = 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 'createIntegrationStoreDirect'."]:
    """Create integration stores for FREE and PAID billing plans.

    This tool creates integration stores on Vercel for both free and paid plans, handling billing automatically. It validates configurations, discovers free plans, and creates billing authorizations for paid resources. It should be called when you need to provision integration storage resources while managing billing. Requires admin access and, for paid plans, a valid payment method.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "CREATEINTEGRATIONSTORE_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["CREATEINTEGRATIONSTORE_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["CREATEINTEGRATIONSTORE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/storage/stores/integration/direct",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEINTEGRATIONSTORE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_team_members(
    context: ToolContext,
    added_since_timestamp: Annotated[
        float | None, "Include team members added since this timestamp in milliseconds."
    ] = None,
    exclude_project_id: Annotated[
        str | None, "Exclude members belonging to the specified project using the project ID."
    ] = None,
    filter_by_team_role: Annotated[
        str | None,
        "Return members with the specified team role. Valid roles include OWNER, MEMBER, DEVELOPER, SECURITY, BILLING, VIEWER, VIEWER_FOR_PLUS, and CONTRIBUTOR.",
    ] = None,
    include_members_until: Annotated[
        float | None, "Timestamp in milliseconds to include members added until this time."
    ] = None,
    member_limit: Annotated[
        float | None, "Specify the maximum number of team members to return in a single request."
    ] = None,
    project_id_for_eligible_members: Annotated[
        str | None,
        "Include team members eligible for the specified project by providing the project ID.",
    ] = None,
    search_team_members: Annotated[
        str | None, "Search for team members by their name, username, or email."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getTeamMembers'."]:
    """Retrieve a list of team members for a specified team.

    Use this tool to get a paginated list of members belonging to a specific team by providing the team ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v3/teams/{teamId}/members",
        method="GET",
        params=remove_none_values({
            "limit": member_limit,
            "since": added_since_timestamp,
            "until": include_members_until,
            "search": search_team_members,
            "role": filter_by_team_role,
            "excludeProject": exclude_project_id,
            "eligibleMembersForProjectId": project_id_for_eligible_members,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def invite_user_to_team(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    team_id: Annotated[
        str | None,
        "The unique identifier for the Vercel team to which the user is being invited.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'inviteUserToTeam'."]:
    """Invite a user to join a Vercel team.

    This tool invites a user to join a specified Vercel team. The user issuing the command must be an OWNER of the team. Specify the user with an email or ID; ID takes precedence if both are provided.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["INVITEUSERTOTEAM_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_id:
        missing_params.append(("team_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["INVITEUSERTOTEAM_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["INVITEUSERTOTEAM_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/teams/{teamId}/members".format(teamId=team_id),  # noqa: UP032
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["INVITEUSERTOTEAM_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def request_team_access(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    team_id: Annotated[
        str | None,
        "The unique identifier of the Vercel team you want to join.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'requestAccessToTeam'."]:
    """Request to join a team on Vercel.

    This tool allows users to request access to a specific team on Vercel as a member. The request needs to be approved by a team owner, and only 10 users can have pending requests for a team at the same time.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["REQUESTTEAMACCESS_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_id:
        missing_params.append(("team_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["REQUESTTEAMACCESS_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["REQUESTTEAMACCESS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/teams/{teamId}/request".format(teamId=team_id),  # noqa: UP032
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["REQUESTTEAMACCESS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def check_team_access_status(
    context: ToolContext,
    team_id: Annotated[
        str, "The unique identifier for the team whose access request status is being checked."
    ],
    user_id: Annotated[
        str,
        "The ID of the user whose team access request status is being checked. Leave empty to use the authenticated user.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getTeamAccessRequest'."]:
    """Check the status of a team access request.

    Use this tool to check the status of a user's request to join a team on Vercel. The tool will return the status of the request or a 404 error if the request was declined. If no user ID is provided, it returns the status for the authenticated user."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/teams/{teamId}/request/{userId}".format(  # noqa: UP032
            userId=user_id, teamId=team_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def join_vercel_team(
    context: ToolContext,
    team_id: Annotated[
        str,
        "The unique ID of the Vercel team to join. Use this if you have the team ID instead of an invite code.",
    ],
    team_invite_code: Annotated[
        str | None,
        "The invite code used to join a specific Vercel team. This is a string value provided to new members for team access.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'joinTeam'."]:
    """Join a Vercel team using invite code or team ID.

    This tool allows a user to join a Vercel team by providing an invite code or team ID. It should be called when a user wants to become a member of a specified team on Vercel."""
    request_data = remove_none_values({"inviteCode": team_invite_code})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/teams/{teamId}/members/teams/join".format(teamId=team_id),  # noqa: UP032
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_team_member(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    member_id: Annotated[
        str | None,
        "The unique identifier for the team member to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_id: Annotated[
        str | None,
        "The unique ID of the team where the member's role or membership status will be updated. It is required to identify the specific team.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = 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 'updateTeamMember'."]:
    """Update a team member's role or confirm membership.

    This tool updates a team member's details within a specified team, such as modifying their role or confirming an unconfirmed member's request to join. It requires the authenticated user to have 'OWNER' privileges in the team.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATETEAMMEMBER_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 member_id:
        missing_params.append(("member_id", "path"))
    if not team_id:
        missing_params.append(("team_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["UPDATETEAMMEMBER_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["UPDATETEAMMEMBER_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/teams/{teamId}/members/{uid}".format(  # noqa: UP032
            uid=member_id, teamId=team_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATETEAMMEMBER_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def remove_team_member(
    context: ToolContext,
    team_id: Annotated[
        str, "The ID of the team from which to remove or dismiss a member, or leave."
    ],
    user_id: Annotated[
        str, "The unique identifier of the user to be removed or dismissed from the team."
    ],
    new_default_team_id: Annotated[
        str | None,
        "The ID of the team to set as the new default team for the Northstar user when removing another team member.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'removeTeamMember'."]:
    """Remove or dismiss a team member or leave a team.

    This tool removes a team member from a team, dismisses a user who requested access, or allows a user to leave a team on Vercel."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/teams/{teamId}/members/{uid}".format(  # noqa: UP032
            uid=user_id, teamId=team_id
        ),
        method="DELETE",
        params=remove_none_values({"newDefaultTeamId": new_default_team_id}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_team_info(
    context: ToolContext,
    team_identifier: Annotated[
        str, "The unique identifier for the team to retrieve information about."
    ],
    team_slug: Annotated[
        str | None,
        "A string representing the unique slug of the team. Used to specify which team's data to retrieve.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getTeam'."]:
    """Retrieve information for a specified team using teamId.

    Use this tool to get detailed information about a team by specifying the teamId. It retrieves the data related to the team, which can include various details pertinent to the team specified."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v2/teams/{teamId}".format(teamId=team_identifier),  # noqa: UP032
        method="GET",
        params=remove_none_values({"slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
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 whose information you want to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The unique slug for the team used to perform the request.  Only used when mode is 'execute'.",
    ] = 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 'patchTeam'."]:
    """Update information of a specified team.

    Use this tool to modify details of an existing team by providing the team ID and the updated information.

    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.
    """
    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, 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["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://api.vercel.com/v2/teams/{teamId}".format(teamId=team_identifier),  # noqa: UP032
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATETEAMINFO_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_user_teams(
    context: ToolContext,
    max_number_of_teams: Annotated[
        float | None, "Maximum number of teams to return in the response."
    ] = None,
    teams_created_since_timestamp: Annotated[
        float | None, "Timestamp in milliseconds to include only teams created since this time."
    ] = None,
    teams_created_until: Annotated[
        float | None, "Timestamp in milliseconds to filter Teams created until the specified time."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getTeams'."]:
    """Retrieve all teams for the authenticated user.

    Call this tool to get a list of all teams the authenticated user is a member of, with pagination support."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v2/teams",
        method="GET",
        params=remove_none_values({
            "limit": max_number_of_teams,
            "since": teams_created_since_timestamp,
            "until": teams_created_until,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_vercel_team(
    context: ToolContext,
    team_slug: Annotated[str, "The desired slug for the new team, used as a team identifier."],
    session_landing_page: Annotated[
        str | None,
        "The URL of the landing page where the session started. This is used for session attribution on Vercel.",
    ] = None,
    session_referrer: Annotated[
        str | None, "Referrer URL for the session initiating the team creation process."
    ] = None,
    signup_referrer_page: Annotated[
        str | None,
        "The referrer URL of the page before the signup page, used for tracking attribution.",
    ] = None,
    team_name: Annotated[
        str | None,
        "The desired name for the Team. If not provided, it will be generated from the slug.",
    ] = None,
    utm_campaign_name: Annotated[
        str | None, "Specifies the UTM campaign name for tracking purposes when creating a team."
    ] = None,
    utm_medium: Annotated[
        str | None, "The medium through which the user arrived, such as email, social, or cpc."
    ] = None,
    utm_source: Annotated[
        str | None,
        "The UTM source identifier, indicating where the traffic originates from, such as a search engine or newsletter.",
    ] = None,
    utm_term: Annotated[
        str | None, "The UTM term used for tracking specific keywords in marketing campaigns."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'createTeam'."]:
    """Create a new team in your Vercel account.

    Use this tool to create a new team under your Vercel account by specifying a team slug, and optionally a team name. This is useful for organizing projects and members under a unified group."""
    request_data = remove_none_values({
        "slug": team_slug,
        "name": team_name,
        "attribution": {
            "sessionReferrer": session_referrer,
            "landingPage": session_landing_page,
            "pageBeforeConversionPage": signup_referrer_page,
            "utm": {
                "utmSource": utm_source,
                "utmMedium": utm_medium,
                "utmCampaign": utm_campaign_name,
                "utmTerm": utm_term,
            },
        },
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/teams",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_team(
    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 of the team to be deleted in your Vercel account.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    new_default_team_id: Annotated[
        str | None,
        "Specify the team ID to set as the new default after deletion.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team you want to delete. Used to identify the team for the delete operation.  Only used when mode is 'execute'.",
    ] = 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 'deleteTeam'."]:
    """Delete a team from your Vercel account.

    Use this tool to send a request to delete a specific team under your Vercel account by providing the team ID. Optional reasons for deletion can be included.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["DELETETEAM_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["DELETETEAM_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["DELETETEAM_REQUEST_BODY_SCHEMA"], indent=2)
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/teams/{teamId}".format(teamId=team_identifier),  # noqa: UP032
        method="DELETE",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["DELETETEAM_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"newDefaultTeamId": new_default_team_id, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_team_invite_code(
    context: ToolContext,
    team_identifier: Annotated[
        str, "The unique identifier of the team to perform the operation for in Vercel."
    ],
    team_invite_code_id: Annotated[str, "The ID of the team invite code to be deleted in Vercel."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteTeamInviteCode'."]:
    """Delete an active team invite code in Vercel.

    Use this tool to remove an active team invite code for a specified team on Vercel. Call this tool when you need to cancel or invalidate an existing invite."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/teams/{teamId}/invites/{inviteId}".format(  # noqa: UP032
            inviteId=team_invite_code_id, teamId=team_identifier
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_auth_tokens(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'listAuthTokens'."]:
    """Retrieve a list of the current user's authentication tokens.

    Use this tool to get a list of authentication tokens for the current user. It is useful for managing and reviewing user access tokens."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v5/user/tokens",
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_auth_token(
    context: ToolContext,
    token_name: Annotated[
        str,
        "A descriptive name for the authentication token. This helps in identifying the token's purpose or context.",
    ],
    expiration_timestamp: Annotated[
        float | None,
        "The expiration time for the token, specified as a Unix timestamp. This defines when the token will no longer be valid.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The unique identifier for the Team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the Team to perform the request on behalf of. This identifies the specific team within your Vercel account.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'createAuthToken'."]:
    """Create a new authentication token for the user.

    Use this tool to generate and retrieve a new authentication token for the currently authenticated user. Ensure to save the token for subsequent API requests, as it is only provided once."""
    request_data = remove_none_values({"name": token_name, "expiresAt": expiration_timestamp})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v3/user/tokens",
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_auth_token_metadata(
    context: ToolContext,
    authentication_token_identifier: Annotated[
        str,
        'The ID of the token to retrieve metadata for. Use "current" for the token that the current request is authenticated with.',
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getAuthToken'."]:
    """Retrieve metadata about an authentication token.

    This tool retrieves metadata about an authentication token for the currently authenticated user. It should be called when details about a specific token are needed, such as checking token permissions or validity."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v5/user/tokens/{tokenId}".format(  # noqa: UP032
            tokenId=authentication_token_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def invalidate_auth_token(
    context: ToolContext,
    token_id: Annotated[
        str,
        "The ID of the token to invalidate. Use 'current' to invalidate the token used for this request.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteAuthToken'."]:
    """Invalidate an authentication token to revoke access.

    This tool is used to invalidate an authentication token, ensuring it is no longer valid for any future HTTP requests. Call this tool when you need to revoke access granted by a specific token."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v3/user/tokens/{tokenId}".format(tokenId=token_id),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_authenticated_user_info(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getAuthUser'."]:
    """Retrieve current authenticated user's information.

    Use this tool to obtain details about the currently authenticated user from Vercel."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v2/user",
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def initiate_user_deletion(
    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 'requestDelete'."]:
    """Initiates user deletion and sends a confirmation email.

    Use this tool to start the deletion process for a user by emailing a confirmation link. The user has to follow the link to complete the deletion.

    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["INITIATEUSERDELETION_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["INITIATEUSERDELETION_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["INITIATEUSERDELETION_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v1/user",
        method="DELETE",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["INITIATEUSERDELETION_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def create_vercel_webhook(
    context: ToolContext,
    events_list: Annotated[
        list[str],
        "A list of event types that trigger the webhook. Must be an array of strings, each representing an event.",
    ],
    webhook_url: Annotated[
        str,
        "The target URL where the webhook will send POST requests. It should be a valid and publicly accessible URL.",
    ],
    project_ids: Annotated[
        list[str] | None,
        "List of project IDs for which the webhook is being created. Each ID should be a string.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The identifier for the team on whose behalf the request is performed."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug representing the Vercel team to target the request for. It identifies the team on whose behalf the webhook is created.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'createWebhook'."]:
    """Create a new webhook in Vercel projects.

    This tool is used to create a new webhook in Vercel. It should be called when you need to set up automated notifications or integrations by creating a webhook within your Vercel projects."""
    request_data = remove_none_values({
        "url": webhook_url,
        "events": events_list,
        "projectIds": project_ids,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/webhooks",
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def list_vercel_webhooks(
    context: ToolContext,
    project_id: Annotated[
        str | None, "The unique identifier for the project to retrieve webhooks from."
    ] = None,
    team_identifier: Annotated[
        str | None, "The identifier for the Vercel team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug identifier for the team to perform the request on behalf of. This is used to specify which team's webhooks you want to retrieve.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getWebhooks'."]:
    """Retrieve a list of webhooks from Vercel.

    This tool is used to get all the webhooks associated with a Vercel account. Use this to monitor or manage integrations and callbacks configured in a Vercel environment."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/webhooks",
        method="GET",
        params=remove_none_values({
            "projectId": project_id,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_webhook(
    context: ToolContext,
    webhook_id: Annotated[str, "The unique identifier of the webhook to retrieve details for."],
    team_identifier: Annotated[
        str | None,
        "The identifier of the team on whose behalf the request is made. Required to specify the team context.",
    ] = None,
    team_slug: Annotated[
        str | None, "The slug representing the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getWebhook'."]:
    """Retrieve details of a specific webhook using its ID.

    Use this tool to obtain information about a specific webhook by providing its ID. It should be called when details are needed for a webhook managed by Vercel."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/webhooks/{id}".format(id=webhook_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_vercel_webhook(
    context: ToolContext,
    webhook_id: Annotated[str, "The unique identifier of the webhook to be deleted."],
    team_identifier: Annotated[
        str | None,
        "The Team identifier used to perform the request on behalf of the specified team.",
    ] = None,
    team_slug: Annotated[
        str | None, "The slug representing the Vercel team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteWebhook'."]:
    """Delete a specific webhook from Vercel.

    This tool deletes a specified webhook from a Vercel project using its unique ID. It should be called when you need to remove an existing webhook integration."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v1/webhooks/{id}".format(id=webhook_id),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_deployment_aliases(
    context: ToolContext,
    deployment_id: Annotated[str, "The ID of the deployment for which to list the aliases."],
    team_identifier: Annotated[
        str | None,
        "The identifier of the team to perform the request on behalf of. It is required to retrieve deployment aliases.",
    ] = None,
    team_slug: Annotated[
        str | None, "The slug representing the team on whose behalf the request is made."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'listDeploymentAliases'."]:
    """Fetch aliases for a specific deployment by ID.

    Retrieves all aliases associated with a given deployment ID. Use this to find or verify domain aliases linked to a specific Vercel deployment."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v2/deployments/{id}/aliases".format(id=deployment_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def set_deployment_alias(
    context: ToolContext,
    deployment_id: Annotated[
        str,
        "The ID of the deployment to assign the alias to. This identifier is crucial for specifying which deployment will receive the new alias.",
    ],
    deployment_alias: Annotated[
        str | None, "The alias to assign to the specified Vercel deployment."
    ] = None,
    redirect_hostname: Annotated[
        str | None,
        "Hostname to redirect the alias to, using status code 307. This will override the deployment ID from the URL.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The identifier of the team to perform the alias assignment on behalf of. Required for team-based operations.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug to perform the request on behalf of. This identifies the team for the deployment operation.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'assignAlias'."]:
    """Assigns a new alias to a Vercel deployment.

    Use this tool to create or update an alias for a specific Vercel deployment. If the alias is currently linked to another deployment, it will be reassigned to the specified deployment."""
    request_data = remove_none_values({"alias": deployment_alias, "redirect": redirect_hostname})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v2/deployments/{id}/aliases".format(id=deployment_id),  # noqa: UP032
        method="POST",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def list_aliases(
    context: ToolContext,
    aliases_created_after_timestamp: Annotated[
        float | None,
        "Get aliases created after this JavaScript timestamp. Use a timestamp in milliseconds since the epoch.",
    ] = None,
    created_after_timestamp: Annotated[
        float | None, "Return aliases created after this UNIX timestamp."
    ] = None,
    filter_by_domain: Annotated[
        str | None, "Return only aliases associated with the specified domain name."
    ] = None,
    get_aliases_before_timestamp: Annotated[
        float | None, "Retrieve aliases created before the specified JavaScript timestamp."
    ] = None,
    maximum_aliases_to_list: Annotated[
        float | None, "Specifies the maximum number of aliases to retrieve in the request."
    ] = None,
    project_id_filter: Annotated[
        str | None, "Filter to list aliases associated with the specified project ID."
    ] = None,
    rollback_deployment_id: Annotated[
        str | None,
        "Specify the deployment ID to get aliases that would be rolled back for that deployment.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The identifier for the team to perform the request on behalf of. Use this to specify the team whose aliases should be listed.",
    ] = None,
    team_slug: Annotated[
        str | None, "The slug identifier for the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'listAliases'."]:
    """Retrieve a list of Vercel aliases for a user or team.

    This tool retrieves a list of aliases for the authenticated Vercel user or team. Optionally, it can filter aliases by a specific domain or project."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v4/aliases",
        method="GET",
        params=remove_none_values({
            "domain": filter_by_domain,
            "from": created_after_timestamp,
            "limit": maximum_aliases_to_list,
            "projectId": project_id_filter,
            "since": aliases_created_after_timestamp,
            "until": get_aliases_before_timestamp,
            "rollbackDeploymentId": rollback_deployment_id,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_vercel_alias(
    context: ToolContext,
    alias_identifier: Annotated[str, "The alias or alias ID of the Vercel entity to retrieve."],
    after_timestamp: Annotated[
        float | None,
        "Get the alias only if it was created after this JavaScript timestamp (milliseconds since epoch).",
    ] = None,
    created_after_timestamp: Annotated[
        float | None,
        "Retrieve the alias only if it was created after the specified timestamp (in milliseconds).",
    ] = None,
    created_before_timestamp: Annotated[
        float | None, "Retrieve the alias only if it was created before this JavaScript timestamp."
    ] = None,
    project_id: Annotated[
        str | None, "Fetch the alias only if it is associated with this project ID in Vercel."
    ] = None,
    team_identifier: Annotated[
        str | None, "The Team identifier to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug to perform the request on behalf of. This specifies the team context for the alias retrieval.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getAlias'."]:
    """Retrieve Vercel alias information for a host name or alias ID.

    Use this tool to fetch alias details for a specified host name or alias ID on Vercel."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v4/aliases/{idOrAlias}".format(idOrAlias=alias_identifier),  # noqa: UP032
        method="GET",
        params=remove_none_values({
            "from": created_after_timestamp,
            "projectId": project_id,
            "since": after_timestamp,
            "until": created_before_timestamp,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_alias_by_id(
    context: ToolContext,
    alias_id_to_remove: Annotated[
        str, "The unique ID or alias of the item to be removed from Vercel."
    ],
    team_identifier: Annotated[
        str | None, "The unique identifier for the team to execute the request."
    ] = None,
    team_slug: Annotated[
        str | None, "The unique slug of the team for which the alias will be deleted."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteAlias'."]:
    """Delete a specific alias by its ID.

    Use this tool to delete an alias from Vercel using its unique ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v2/aliases/{aliasId}".format(aliasId=alias_id_to_remove),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def update_url_protection_bypass(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    alias_or_deployment_id: Annotated[
        str | None,
        "The ID of the alias or deployment for which to update the protection bypass.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The unique identifier of the team on whose behalf the request is made.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The team slug representing which team the request should be performed for in Vercel.  Only used when mode is 'execute'.",
    ] = 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 'patchUrlProtectionBypass'."]:
    """Update the protection bypass for a Vercel URL.

    Use this tool to update the protection bypass for an alias or deployment URL in Vercel, enabling user or comment access on preview deployments.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS[
                "UPDATEURLPROTECTIONBYPASS_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 alias_or_deployment_id:
        missing_params.append(("alias_or_deployment_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["UPDATEURLPROTECTIONBYPASS_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["UPDATEURLPROTECTIONBYPASS_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/aliases/{id}/protection-bypass".format(  # noqa: UP032
            id=alias_or_deployment_id
        ),
        method="PATCH",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEURLPROTECTIONBYPASS_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_certificate_by_id(
    context: ToolContext,
    certificate_id: Annotated[
        str, "The unique identifier of the certificate to be retrieved from Vercel."
    ],
    team_identifier: Annotated[
        str | None, "The identifier of the team to perform the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug identifying the team to perform the request on behalf of."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getCertById'."]:
    """Retrieve a Vercel certificate using its ID.

    Use this tool to get information about a specific Vercel certificate 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://api.vercel.com/v8/certs/{id}".format(id=certificate_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def remove_certificate(
    context: ToolContext,
    certificate_id: Annotated[str, "The unique identifier of the certificate to remove."],
    team_identifier: Annotated[
        str | None, "The identifier for the team on whose behalf the removal request is made."
    ] = None,
    team_slug: Annotated[
        str | None, "The slug of the team to perform the request on behalf of in Vercel."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'removeCert'."]:
    """Remove a certificate from Vercel using its ID.

    This tool removes a specific certificate from a Vercel account identified by its ID. It should be called when you need to delete an existing certificate to manage SSL/TLS configurations."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v8/certs/{id}".format(id=certificate_id),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def issue_vercel_certificate(
    context: ToolContext,
    common_names_for_certificate: Annotated[
        list[str] | None,
        "List of common names (domains) that the SSL certificate should be issued for.",
    ] = None,
    team_id: Annotated[
        str | None,
        "The identifier for the Vercel team on whose behalf the certificate request is made.",
    ] = None,
    team_slug: Annotated[
        str | None, "The team slug identifier for cert request on behalf of a specific team."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'issueCert'."]:
    """Request a new SSL certificate from Vercel.

    This tool is used to issue a new SSL certificate through Vercel's API. It's called when you need to obtain a certificate for secure communication with your Vercel-hosted applications."""
    request_data = remove_none_values({"cns": common_names_for_certificate})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v8/certs",
        method="POST",
        params=remove_none_values({"teamId": team_id, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def upload_certificate(
    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 perform the request on behalf of.  Only used when mode is 'execute'.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "Specify the team slug to perform the request on behalf of in Vercel.  Only used when mode is 'execute'.",
    ] = 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 'uploadCert'."]:
    """Uploads a certificate to Vercel.

    Use this tool to upload a new certificate to the Vercel platform. This is needed when adding or updating SSL certificates for your domains.

    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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPLOADCERTIFICATE_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["UPLOADCERTIFICATE_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["UPLOADCERTIFICATE_REQUEST_BODY_SCHEMA"], indent=2
                )
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.vercel.com/v8/certs",
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPLOADCERTIFICATE_REQUEST_BODY_SCHEMA"],
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def get_vercel_deployment_files(
    context: ToolContext,
    deployment_identifier: Annotated[
        str, "The unique identifier for the deployment to retrieve its file structure."
    ],
    team_identifier: Annotated[
        str | None, "The identifier of the team on whose behalf the request is made."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team performing the request. It is required for team-scoped requests.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'listDeploymentFiles'."]:
    """Retrieve the file structure of a Vercel deployment.

    Use this tool to get the file structure of a Vercel deployment by providing the unique deployment identifier. Useful for analyzing the contents and structure of deployed source code."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v6/deployments/{id}/files".format(id=deployment_identifier),  # noqa: UP032
        method="GET",
        params=remove_none_values({"teamId": team_identifier, "slug": team_slug}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def retrieve_deployment_file_contents(
    context: ToolContext,
    deployment_unique_identifier: Annotated[
        str, "The unique identifier for the deployment from which to retrieve the file."
    ],
    file_identifier: Annotated[
        str, "The unique identifier for the file you want to retrieve from the deployment."
    ],
    file_path: Annotated[
        str | None, "Path to the file to fetch, applicable only for Git-based deployments."
    ] = None,
    team_identifier: Annotated[
        str | None, "The identifier of the team to make the request on behalf of."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The slug of the team to perform the request on behalf of. Specify which team's context to use.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getDeploymentFileContents'."]:
    """Retrieve the contents of a file from a Vercel deployment.

    Use this tool to get the content of a specific file from a Vercel deployment by providing the deployment and file identifiers. The response provides the file contents encoded in base64."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v8/deployments/{id}/files/{fileId}".format(  # noqa: UP032
            id=deployment_unique_identifier, fileId=file_identifier
        ),
        method="GET",
        params=remove_none_values({
            "path": file_path,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def list_vercel_deployments(
    context: ToolContext,
    branch_name_filter: Annotated[
        str | None, "Specify the branch name to filter deployments."
    ] = None,
    created_after_timestamp: Annotated[
        float | None,
        "Retrieve deployments created after this Date timestamp. Defaults to current time if not specified.",
    ] = None,
    deployment_name: Annotated[str | None, "The name of the deployment to filter results."] = None,
    deployment_since_timestamp: Annotated[
        float | None, "Retrieve deployments created after this JavaScript timestamp."
    ] = None,
    deployment_state_filter: Annotated[
        str | None,
        "Filter deployments by their state, such as `BUILDING`, `ERROR`, `INITIALIZING`, `QUEUED`, `READY`, or `CANCELED`.",
    ] = None,
    fetch_deployments_before_timestamp: Annotated[
        float | None,
        "Specify a JavaScript timestamp to retrieve deployments created before this time.",
    ] = None,
    filter_by_environment: Annotated[
        str | None, "Specify the environment to filter deployments (e.g., 'production', 'staging')."
    ] = None,
    filter_by_project_id: Annotated[
        str | None, "Filter deployments using a specific project ID or name."
    ] = None,
    filter_by_project_ids: Annotated[
        list[str] | None,
        "Filter deployments from specified project IDs. Cannot be used with the 'filter_by_project_id' argument.",
    ] = None,
    filter_by_rollback_candidacy: Annotated[
        bool | None, "Set to true to filter and include only rollback candidate deployments."
    ] = None,
    filter_by_sha: Annotated[
        str | None, "Filter deployments based on the specific SHA value."
    ] = None,
    get_deployments_before_timestamp: Annotated[
        float | None,
        "A timestamp to get deployments created before a specific date. Useful for filtering older deployments. Defaults to the current time if not specified.",
    ] = None,
    maximum_deployments_to_list: Annotated[
        float | None, "Sets the maximum number of deployments to retrieve in one request."
    ] = None,
    team_identifier: Annotated[
        str | None,
        "The identifier for the team to perform the request on behalf of. Use when filtering deployments by team.",
    ] = None,
    team_slug: Annotated[
        str | None,
        "The team slug to perform the request on behalf of. Specify which team's deployments to retrieve.",
    ] = None,
    user_filter: Annotated[
        str | None,
        "Filter deployments by the user who created them. Provide a username or user ID.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getDeployments'."]:
    """Retrieve deployments from Vercel for a user or team.

    This tool fetches the list of deployments for the authenticated user or team from Vercel. It should be called when users need to view their recent deployment history. If a deployment is incomplete, the URL will be null."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v6/deployments",
        method="GET",
        params=remove_none_values({
            "app": deployment_name,
            "from": created_after_timestamp,
            "limit": maximum_deployments_to_list,
            "projectId": filter_by_project_id,
            "projectIds": filter_by_project_ids,
            "target": filter_by_environment,
            "to": get_deployments_before_timestamp,
            "users": user_filter,
            "since": deployment_since_timestamp,
            "until": fetch_deployments_before_timestamp,
            "state": deployment_state_filter,
            "rollbackCandidate": filter_by_rollback_candidacy,
            "branch": branch_name_filter,
            "sha": filter_by_sha,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_secrets=["VERCEL_ACCESS_TOKEN"])
async def delete_deployment(
    context: ToolContext,
    deployment_id: Annotated[
        str, "The ID of the deployment to be deleted. Use this if not providing a URL."
    ],
    deployment_url: Annotated[
        str | None,
        "A Deployment or Alias URL for identifying the deployment to delete. The ID will be ignored if this is provided.",
    ] = None,
    team_identifier: Annotated[
        str | None, "The unique identifier for the team on whose behalf the request is executed."
    ] = None,
    team_slug: Annotated[
        str | None,
        "The Team slug to perform the deletion on behalf of. Specify which team's deployment to delete.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'deleteDeployment'."]:
    """Delete a Vercel deployment using its ID or URL.

    Use this tool to delete a Vercel deployment by providing the deployment's `id` or `url`. Useful for managing and cleaning up deployments."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.vercel.com/v13/deployments/{id}".format(id=deployment_id),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({
            "url": deployment_url,
            "teamId": team_identifier,
            "slug": team_slug,
        }),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(
                authorization=context.get_secret("VERCEL_ACCESS_TOKEN")
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}
