"""Arcade Starter Tools for Squareup

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.auth import OAuth2
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] | str,
    auth: tuple[str, str] | None = None,
    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."""
    # Parse schema if it's a string, skip validation if parsing fails
    parsed_schema = None
    if isinstance(schema, str):
        try:
            parsed_schema = json.loads(schema)
        except Exception:
            # If schema parsing fails, just skip validation
            parsed_schema = None
    else:
        parsed_schema = schema

    try:
        response = await make_request(
            url=url,
            auth=auth,
            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):
            api_error_details = f"API returned {e.response.status_code}: {e.response.text}"

            # Only run validation if we have a valid parsed schema
            if parsed_schema is not None:
                # Run validation to provide additional context
                is_valid, validation_error = validate_json_against_schema(
                    request_data, parsed_schema
                )

                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
            else:
                # No valid schema - 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_auth=OAuth2(id="arcade-squareup"))
async def revoke_oauth_access_token(
    context: ToolContext,
    application_client_id: Annotated[
        str | None,
        "The Square-issued ID for your application, found on the OAuth page in the Developer Dashboard.",  # noqa: E501
    ] = None,
    merchant_access_token: Annotated[
        str | None,
        "The access token of the merchant whose token you want to revoke. Cannot be used with `merchant_id`.",  # noqa: E501
    ] = None,
    merchant_id_to_revoke: Annotated[
        str | None,
        "The merchant ID whose token you want to revoke. Do not use if 'access_token' is provided.",
    ] = None,
    terminate_single_access_token: Annotated[
        bool | None,
        "If true, terminate only the specified access token without revoking the entire authorization. Defaults to false.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RevokeToken'."]:
    """Revoke all OAuth access tokens for an account.

    This tool revokes all OAuth access tokens for a given account associated with your application. It should be called when you need to invalidate the access tokens generated through the OAuth flow. Note that all tokens will be revoked regardless of which one is specified."""  # noqa: E501
    request_data = remove_none_values({
        "client_id": application_client_id,
        "access_token": merchant_access_token,
        "merchant_id": merchant_id_to_revoke,
        "revoke_only_access_token": terminate_single_access_token,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/oauth2/revoke",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def obtain_oauth_token(
    context: ToolContext,
    application_id: Annotated[
        str,
        "The Square-issued ID of your application, available as the Application ID in the Developer Console. Required for code and PKCE flows.",  # noqa: E501
    ],
    oauth_grant_type: Annotated[
        str,
        "Specifies the method for obtaining an OAuth access token. Choose from 'authorization_code', 'refresh_token', or 'migration_token'.",  # noqa: E501
    ],
    application_client_secret: Annotated[
        str | None,
        "The application's secret key from the Developer Console, required for code flow. Distinct from a personal access token.",  # noqa: E501
    ] = None,
    application_redirect_url: Annotated[
        str | None,
        "The registered redirect URL for your application. Required for code flow and PKCE flow if `grant_type` is `authorization_code`.",  # noqa: E501
    ] = None,
    authorization_code: Annotated[
        str | None,
        "The authorization code for exchanging an OAuth access token, required for code flow and PKCE flow if `grant_type` is `authorization_code`.",  # noqa: E501
    ] = None,
    expire_token_in_24_hours: Annotated[
        bool | None,
        "Set to true to make the access token expire in 24 hours. Optional for any grant type.",
    ] = None,
    legacy_migration_token: Annotated[
        str | None,
        "A valid legacy access token for generating a new OAuth access token, required if `grant_type` is `migration_token`.",  # noqa: E501
    ] = None,
    pkce_code_verifier: Annotated[
        str | None,
        "The secret your application generated for the authorization request, required for PKCE flow when `grant_type` is `authorization_code`.",  # noqa: E501
    ] = None,
    requested_scopes: Annotated[
        list[str] | None,
        'List of permissions for the access token, like ["MERCHANT_PROFILE_READ", "PAYMENTS_READ"]. Optional for certain flows.',  # noqa: E501
    ] = None,
    valid_refresh_token: Annotated[
        str | None,
        "A valid refresh token used to generate a new OAuth access token, returned in a previous ObtainToken response. Required for the code and PKCE flow if grant_type is refresh_token.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ObtainToken'."]:
    """Obtain OAuth access and refresh tokens.

    This tool is used to obtain an OAuth access token and refresh token using either the authorization_code or refresh_token grant type. It's applicable for both code flow and PKCE flow scenarios, depending on the credentials and additional parameters provided."""  # noqa: E501
    request_data = remove_none_values({
        "client_id": application_id,
        "client_secret": application_client_secret,
        "code": authorization_code,
        "redirect_uri": application_redirect_url,
        "grant_type": oauth_grant_type,
        "refresh_token": valid_refresh_token,
        "migration_token": legacy_migration_token,
        "scopes": requested_scopes,
        "short_lived": expire_token_in_24_hours,
        "code_verifier": pkce_code_verifier,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/oauth2/token",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def retrieve_token_status(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveTokenStatus'."]:
    """Retrieve the status of an OAuth or personal access token.

    Use this tool to get information on the validity and status of an OAuth access token or a personal access token. Call this tool to check if a token is expired or unauthorized."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/oauth2/token/status",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def activate_domain_for_apple_pay(
    context: ToolContext,
    apple_pay_domain_name: Annotated[
        str, "Enter the domain name, as per RFC-1034, to register it with Apple Pay."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RegisterDomain'."]:
    """Activates a domain for use with Apple Pay and Square.

    Use this tool to activate a domain for Apple Pay on the Web with Square. It performs a validation to ensure the domain is Apple Pay enabled, allowing platforms to bulk activate Apple Pay for their merchants. Ensure a valid domain verification file is hosted as per the guidelines."""  # noqa: E501
    request_data = remove_none_values({"domain_name": apple_pay_domain_name})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/apple-pay/domains",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["BANK_ACCOUNTS_READ"]))
async def list_bank_accounts(
    context: ToolContext,
    location_id_filter: Annotated[
        str | None,
        "Specify this optional filter to retrieve bank accounts linked to a specific location.",
    ] = None,
    max_bank_accounts: Annotated[
        int | None,
        "Specify the maximum number of bank accounts to return. The limit can be up to 1000, which is also the default.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "The pagination cursor from a previous `ListBankAccounts` call to retrieve the next set of results.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListBankAccounts'."]:
    """Fetches bank accounts linked to a Square account.

    Use this tool to get a list of all bank accounts associated with a Square account. It retrieves relevant bank account details."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bank-accounts",
        method="GET",
        params=remove_none_values({
            "cursor": pagination_cursor,
            "limit": max_bank_accounts,
            "location_id": location_id_filter,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["BANK_ACCOUNTS_READ"]))
async def get_bank_account_details_by_v1_id(
    context: ToolContext,
    v1_bank_account_id: Annotated[
        str,
        "The V1 ID of the bank account to retrieve details for. This ID is used to fetch specific account information from the Squareup service.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetBankAccountByV1Id'."]:
    """Fetches bank account details using a V1 ID.

    Use this tool to retrieve details of a bank account by providing the V1 bank account ID. Useful for obtaining specific bank account information from the Squareup service."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bank-accounts/by-v1-id/{v1_bank_account_id}".format(  # noqa: UP032
            v1_bank_account_id=v1_bank_account_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["BANK_ACCOUNTS_READ"]))
async def get_bank_account_details(
    context: ToolContext,
    bank_account_id: Annotated[
        str, "Square-issued ID of the desired bank account to retrieve its details."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetBankAccount'."]:
    """Retrieve details of a bank account linked to a Square account.

    Use this tool to obtain detailed information about a specific bank account associated with a Square account by providing the bank account ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bank-accounts/{bank_account_id}".format(  # noqa: UP032
            bank_account_id=bank_account_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_READ"]))
async def retrieve_bookings(
    context: ToolContext,
    customer_id_for_bookings: Annotated[
        str | None,
        "The ID of the customer for whom to retrieve bookings. If not provided, retrieves bookings for all customers.",  # noqa: E501
    ] = None,
    earliest_start_time: Annotated[
        str | None,
        "The RFC 3339 timestamp specifying the earliest start time for bookings. Defaults to current time if not set.",  # noqa: E501
    ] = None,
    latest_start_time: Annotated[
        str | None,
        "The latest possible start time of bookings in RFC 3339 format. Defaults to 31 days after `start_at_min` if not set.",  # noqa: E501
    ] = None,
    max_results_per_page: Annotated[
        int | None, "The maximum number of results per page to return in a paged response."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "The pagination cursor for the next page of results. Leave empty for the first page.",
    ] = None,
    specific_location_id: Annotated[
        str | None,
        "Retrieve bookings for a specific location by its ID. If not set, retrieves bookings for all locations.",  # noqa: E501
    ] = None,
    team_member_id: Annotated[
        str | None,
        "The ID of the team member to retrieve bookings for. Leave unset to retrieve bookings for all members.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListBookings'."]:
    """Retrieve a collection of bookings.

    Call this tool to get a list of bookings. Ensure appropriate OAuth scopes are set depending on buyer-level or seller-level permissions."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings",
        method="GET",
        params=remove_none_values({
            "limit": max_results_per_page,
            "cursor": pagination_cursor,
            "customer_id": customer_id_for_bookings,
            "team_member_id": team_member_id,
            "location_id": specific_location_id,
            "start_at_min": earliest_start_time,
            "start_at_max": latest_start_time,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_WRITE"]))
async def create_booking(
    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 'CreateBooking'."]:
    """Create a new booking for a service.

    This tool creates a booking using specified details like location, start time, team member, and service variation. Ideal for scheduling appointments with buyer or seller permissions, ensuring necessary subscriptions are met.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEBOOKING"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATEBOOKING"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATEBOOKING"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/bookings",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEBOOKING"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_READ"]))
async def search_booking_availability(
    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 'SearchAvailability'."]:
    """Find available booking slots for appointments.

    Use this tool to search for available booking slots. It requires appropriate permissions depending on user level (buyer or seller).

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["SEARCHBOOKINGAVAILABILITY"],
            "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\n" + REQUEST_BODY_SCHEMAS["SEARCHBOOKINGAVAILABILITY"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["SEARCHBOOKINGAVAILABILITY"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/bookings/availability/search",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SEARCHBOOKINGAVAILABILITY"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_READ"]))
async def bulk_retrieve_bookings(
    context: ToolContext,
    booking_ids: Annotated[
        list[str], "A non-empty list of booking IDs to retrieve the corresponding bookings."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BulkRetrieveBookings'."]:
    """Retrieve multiple bookings using booking IDs.

    This tool retrieves a list of bookings by their IDs. It requires appropriate OAuth scopes for buyer or seller level permissions."""  # noqa: E501
    request_data = remove_none_values({"booking_ids": booking_ids})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/bulk-retrieve",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_BUSINESS_SETTINGS_READ"]))
async def retrieve_booking_profile(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveBusinessBookingProfile'."]:
    """Retrieve a seller's booking profile information.

    Use this tool to obtain details about a seller's booking profile. Ideal for acquiring booking-related information for businesses."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/business-booking-profile",
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_READ"]))
async def list_booking_custom_attribute_definitions(
    context: ToolContext,
    pagination_cursor: Annotated[
        str | None, "Cursor from the previous response to get the next page of results."
    ] = None,
    result_limit: Annotated[
        int | None, "Maximum results to return per page. Must be between 1 and 100, default is 20."
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ListBookingCustomAttributeDefinitions'."
]:
    """Retrieve all custom attribute definitions for bookings.

    Use this tool to get all custom attribute definitions associated with bookings. It requires appropriate OAuth permissions based on buyer or seller level access. This tool is ideal for managing and understanding the custom attributes tied to booking data."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/custom-attribute-definitions",
        method="GET",
        params=remove_none_values({"limit": result_limit, "cursor": pagination_cursor}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_WRITE"]))
async def create_booking_custom_attribute(
    context: ToolContext,
    attribute_schema_json: Annotated[
        dict[str, str] | None,
        "The JSON schema defining data type for booking custom attributes. Required for attribute creation. For more, visit Squareup Custom Attributes Overview.",  # noqa: E501
    ] = None,
    current_attribute_definition_version: Annotated[
        int | None,
        "The current version of the custom attribute definition. Must be set to the latest version for updates and strong consistency.",  # noqa: E501
    ] = None,
    custom_attribute_created_timestamp: Annotated[
        str | None, "The creation timestamp of the custom attribute definition in RFC 3339 format."
    ] = None,
    custom_attribute_definition_name: Annotated[
        str | None,
        "The name for the custom attribute definition. Must be unique within the seller and application pair and required if visibility is 'VISIBILITY_READ_ONLY' or 'VISIBILITY_READ_WRITE_VALUES'.",  # noqa: E501
    ] = None,
    custom_attribute_description: Annotated[
        str | None,
        "A description for the custom attribute, required if visibility is read-only or read-write. Displayed as a tooltip in UIs.",  # noqa: E501
    ] = None,
    custom_attribute_key: Annotated[
        str | None,
        "Identifier for the custom attribute definition. Use a simple or qualified key. Must be unique and cannot be changed later.",  # noqa: E501
    ] = None,
    custom_attribute_visibility: Annotated[
        str | None,
        "Defines the permission level required to view the custom attribute. Options: VISIBILITY_HIDDEN, VISIBILITY_READ_ONLY, VISIBILITY_READ_WRITE_VALUES.",  # noqa: E501
    ] = None,
    idempotency_key: Annotated[
        str | None,
        "A unique string identifier for the request to ensure idempotency. This prevents duplicate operations in case of retries.",  # noqa: E501
    ] = None,
    updated_at_timestamp: Annotated[
        str | None,
        "The timestamp in RFC 3339 format indicating when the custom attribute definition was last updated or created.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateBookingCustomAttributeDefinition'."
]:
    """Creates a custom attribute definition for bookings.

    This tool creates a custom attribute definition for bookings in Squareup. It requires specific OAuth permissions depending on the level (buyer or seller) and seller subscriptions to certain plans. Call this when you need to define new custom attributes for bookings."""  # noqa: E501
    request_data = remove_none_values({
        "custom_attribute_definition": {
            "key": custom_attribute_key,
            "schema": attribute_schema_json,
            "name": custom_attribute_definition_name,
            "description": custom_attribute_description,
            "visibility": custom_attribute_visibility,
            "version": current_attribute_definition_version,
            "updated_at": updated_at_timestamp,
            "created_at": custom_attribute_created_timestamp,
        },
        "idempotency_key": idempotency_key,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/custom-attribute-definitions",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_WRITE"]))
async def delete_booking_custom_attribute_definition(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str, "The key of the custom attribute definition to delete for bookings."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'DeleteBookingCustomAttributeDefinition'."
]:
    """Deletes a booking's custom attribute definition.

    Use this tool to delete a custom attribute definition for bookings. Ensure appropriate OAuth scopes are set: 'APPOINTMENTS_WRITE' for buyer-level permissions, or 'APPOINTMENTS_ALL_WRITE' and 'APPOINTMENTS_WRITE' for seller-level permissions. Seller-level permissions require a subscription to Appointments Plus or Premium."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_READ"]))
async def retrieve_booking_custom_attribute_definition(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key for the custom attribute definition to retrieve. Use a qualified key if not the definition owner.",  # noqa: E501
    ],
    current_version_of_custom_attribute_definition: Annotated[
        int | None,
        "The current version of the custom attribute definition for consistent reads. Provides the specified version or higher if available. Returns `BAD_REQUEST` if the version is higher than current.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'RetrieveBookingCustomAttributeDefinition'."
]:
    """Retrieve a booking's custom attribute definition.

    Use this tool to retrieve detailed information about a specific booking's custom attribute definition. Ensure proper OAuth scope: `APPOINTMENTS_READ` for buyer-level or `APPOINTMENTS_ALL_READ` and `APPOINTMENTS_READ` for seller-level permissions."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="GET",
        params=remove_none_values({"version": current_version_of_custom_attribute_definition}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_WRITE"]))
async def update_booking_custom_attribute(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute definition to update. This key can be a simple key or a qualified key consisting of the application ID and the simple key, formatted as 'application_id:simple_key'.",  # noqa: E501
    ],
    created_at_timestamp: Annotated[
        str | None,
        "The timestamp indicating when the custom attribute definition was created, in RFC 3339 format. This is used to track the original creation time of the attribute definition.",  # noqa: E501
    ] = None,
    current_version_of_custom_attribute_definition: Annotated[
        int | None,
        "Specify the current version of the custom attribute definition to enable optimistic concurrency. Must match the latest version; stale writes are rejected.",  # noqa: E501
    ] = None,
    custom_attribute_definition_key: Annotated[
        str | None,
        "The key of the custom attribute definition, either as a simple key or in the format 'application_id:simple key'. Unique per application, seller, and resource type.",  # noqa: E501
    ] = None,
    custom_attribute_definition_name: Annotated[
        str | None,
        "The unique name for the booking's custom attribute definition, required if visibility is set to read-only or read-write.",  # noqa: E501
    ] = None,
    custom_attribute_description: Annotated[
        str | None,
        "A seller-oriented description of the custom attribute, required if visibility is read-only or read-write.",  # noqa: E501
    ] = None,
    custom_attribute_schema: Annotated[
        dict[str, str] | None,
        "A JSON object defining the schema for the custom attribute, determining its data type. Required for creating a definition.",  # noqa: E501
    ] = None,
    idempotency_token: Annotated[
        str | None,
        "A unique string identifier for ensuring the request is idempotent. Refer to Square's Idempotency guidelines for details.",  # noqa: E501
    ] = None,
    updated_timestamp: Annotated[
        str | None,
        "The RFC 3339 formatted timestamp of the last update for the custom attribute definition.",
    ] = None,
    visibility_level: Annotated[
        str | None,
        "Specifies permission level for viewing the custom attribute definition. Options: VISIBILITY_HIDDEN, VISIBILITY_READ_ONLY, VISIBILITY_READ_WRITE_VALUES.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'UpdateBookingCustomAttributeDefinition'."
]:
    """Update a booking's custom attribute definition.

    Use this tool to update the custom attribute definition for a booking. It requires appropriate permissions, either buyer-level or seller-level, and the seller must be subscribed to Appointments Plus or Appointments Premium for seller-level calls."""  # noqa: E501
    request_data = remove_none_values({
        "custom_attribute_definition": {
            "key": custom_attribute_definition_key,
            "schema": custom_attribute_schema,
            "name": custom_attribute_definition_name,
            "description": custom_attribute_description,
            "visibility": visibility_level,
            "version": current_version_of_custom_attribute_definition,
            "updated_at": updated_timestamp,
            "created_at": created_at_timestamp,
        },
        "idempotency_key": idempotency_token,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_WRITE"]))
async def bulk_delete_booking_custom_attributes(
    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 'BulkDeleteBookingCustomAttributes'."
]:
    """Bulk delete custom attributes for bookings.

    Use this tool to delete multiple custom attributes associated with bookings in bulk. Ensure the proper OAuth permissions are set: 'APPOINTMENTS_WRITE' for buyer-level and both 'APPOINTMENTS_ALL_WRITE' and 'APPOINTMENTS_WRITE' for seller-level. Requires an active subscription to 'Appointments Plus' or 'Appointments Premium' for seller-level actions.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKDELETEBOOKINGCUSTOMATTRIBUTES"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["BULKDELETEBOOKINGCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["BULKDELETEBOOKINGCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/bookings/custom-attributes/bulk-delete",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKDELETEBOOKINGCUSTOMATTRIBUTES"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_WRITE"]))
async def bulk_upsert_booking_custom_attributes(
    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 'BulkUpsertBookingCustomAttributes'."
]:
    """Bulk upserts custom attributes for bookings.

    Use this tool to update or insert multiple custom attributes for bookings at once. Requires specific OAuth permissions depending on whether you operate at the buyer or seller level. Ensure sellers are subscribed to necessary service tiers.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKUPSERTBOOKINGCUSTOMATTRIBUTES"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["BULKUPSERTBOOKINGCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["BULKUPSERTBOOKINGCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/bookings/custom-attributes/bulk-upsert",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKUPSERTBOOKINGCUSTOMATTRIBUTES"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_BUSINESS_SETTINGS_READ"]))
async def list_seller_booking_profiles(
    context: ToolContext,
    max_results_per_page: Annotated[
        int | None, "The maximum number of booking profiles to return in a single response page."
    ] = None,
    pagination_cursor: Annotated[
        str | None, "Use this to fetch the next page of results. Leave blank for the first page."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListLocationBookingProfiles'."]:
    """Retrieve booking profiles for seller locations.

    Call this tool to obtain a list of booking profiles for a seller's locations. Useful for understanding how bookings are managed across different locations of a seller."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/location-booking-profiles",
        method="GET",
        params=remove_none_values({"limit": max_results_per_page, "cursor": pagination_cursor}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_BUSINESS_SETTINGS_READ"]))
async def retrieve_seller_location_booking_profile(
    context: ToolContext,
    location_id: Annotated[
        str, "The ID of the location for which to retrieve the booking profile."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveLocationBookingProfile'."]:
    """Retrieve a seller's location booking profile.

    Use this tool to get details about a seller's location booking profile by providing the location ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/location-booking-profiles/{location_id}".format(  # noqa: UP032
            location_id=location_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_BUSINESS_SETTINGS_READ"]))
async def list_booking_profiles(
    context: ToolContext,
    filter_by_location_id: Annotated[
        str | None, "Filter to return only team members enabled at the specified location ID."
    ] = None,
    include_only_bookable_members: Annotated[
        bool | None,
        "Set to true to include only team members who are bookable. False includes all members.",
    ] = False,
    maximum_results_limit: Annotated[
        int | None, "Specify the maximum number of results to return in a paged response."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "The cursor for pagination to retrieve the next page of results. Omit for the first page of results.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListTeamMemberBookingProfiles'."]:
    """Retrieve booking profiles for team members.

    Use this tool to obtain a list of booking profiles associated with team members. Ideal for viewing or managing team schedules and bookings."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/team-member-booking-profiles",
        method="GET",
        params=remove_none_values({
            "bookable_only": include_only_bookable_members,
            "limit": maximum_results_limit,
            "cursor": pagination_cursor,
            "location_id": filter_by_location_id,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_BUSINESS_SETTINGS_READ"]))
async def retrieve_team_booking_profiles(
    context: ToolContext,
    team_member_ids: Annotated[
        list[str], "A non-empty list of team member IDs to retrieve booking profiles for."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'BulkRetrieveTeamMemberBookingProfiles'."
]:
    """Retrieve booking profiles for one or more team members.

    Use this tool to get booking profiles of specified team members. Useful for accessing and managing team members' booking information."""  # noqa: E501
    request_data = remove_none_values({"team_member_ids": team_member_ids})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/team-member-booking-profiles/bulk-retrieve",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_BUSINESS_SETTINGS_READ"]))
async def get_team_member_booking_profile(
    context: ToolContext,
    team_member_unique_id: Annotated[
        str, "The unique identifier for the team member whose booking profile is being retrieved."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'RetrieveTeamMemberBookingProfile'."
]:
    """Retrieve a team member's booking profile from Square.

    Use this tool to get specific booking information for a team member by their unique ID. Ideal for checking availability or scheduling details."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/team-member-booking-profiles/{team_member_id}".format(  # noqa: UP032
            team_member_id=team_member_unique_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_READ"]))
async def retrieve_booking(
    context: ToolContext,
    booking_id: Annotated[str, "The ID of the Booking object to retrieve details for."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveBooking'."]:
    """Retrieve detailed information about a booking.

    Use this tool to get comprehensive details of a specific booking by providing the booking ID. It requires appropriate OAuth permissions to access booking data."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/{booking_id}".format(booking_id=booking_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_WRITE"]))
async def update_booking(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    booking_identifier: Annotated[
        str | None,
        "The unique identifier of the booking to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateBooking'."]:
    """Update an existing booking with new details.

    Use this tool to modify an existing booking in the Squareup system. Ensure you have the correct permissions: buyer-level permissions require 'APPOINTMENTS_WRITE', while seller-level permissions need 'APPOINTMENTS_ALL_WRITE' and 'APPOINTMENTS_WRITE'. Seller-level updates require a subscription to Appointments Plus or Premium.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEBOOKING"],
            "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 booking_identifier:
        missing_params.append(("booking_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\n" + REQUEST_BODY_SCHEMAS["UPDATEBOOKING"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATEBOOKING"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/bookings/{booking_id}".format(  # noqa: UP032
            booking_id=booking_identifier
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEBOOKING"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_WRITE"]))
async def cancel_booking(
    context: ToolContext,
    booking_id: Annotated[
        str,
        "The unique ID of the booking to be canceled. This ID is required to identify which booking to cancel.",  # noqa: E501
    ],
    booking_revision_number: Annotated[
        int | None, "The current revision number of the booking for optimistic concurrency control."
    ] = None,
    idempotency_key: Annotated[
        str | None,
        "Unique key to ensure the request is idempotent and prevents duplicate operations.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelBooking'."]:
    """Cancel an existing booking.

    Call this tool to cancel a booking using its unique ID. Requires specific OAuth permissions depending on user level. For seller-level cancellations, an Appointments Plus or Premium subscription is necessary."""  # noqa: E501
    request_data = remove_none_values({
        "idempotency_key": idempotency_key,
        "booking_version": booking_revision_number,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/{booking_id}/cancel".format(  # noqa: UP032
            booking_id=booking_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_READ"]))
async def list_booking_custom_attributes(
    context: ToolContext,
    booking_id: Annotated[
        str,
        "The unique identifier for the target booking to list custom attributes for. This is required to specify which booking's attributes to retrieve.",  # noqa: E501
    ],
    include_custom_attribute_definitions: Annotated[
        bool | None,
        "Set to true to include custom attribute definitions, providing names, descriptions, data types, and other details. Default is false.",  # noqa: E501
    ] = False,
    maximum_results_limit: Annotated[
        int | None,
        "The maximum number of results to return in a single response (1-100). Default is 20.",
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "The cursor from the previous API response used to retrieve the next page of results.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListBookingCustomAttributes'."]:
    """Retrieve a booking's custom attributes.

    Use this tool to list custom attributes associated with a specific booking. Ensure proper OAuth scope with 'APPOINTMENTS_READ' for buyer-level permissions or both 'APPOINTMENTS_ALL_READ' and 'APPOINTMENTS_READ' for seller-level permissions."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/{booking_id}/custom-attributes".format(  # noqa: UP032
            booking_id=booking_id
        ),
        method="GET",
        params=remove_none_values({
            "limit": maximum_results_limit,
            "cursor": pagination_cursor,
            "with_definitions": include_custom_attribute_definitions,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_WRITE"]))
async def delete_booking_custom_attribute(
    context: ToolContext,
    booking_id: Annotated[
        str,
        "The unique identifier for the booking from which the custom attribute will be deleted.",
    ],
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute to delete. Must match the key in the Square seller account. Use a qualified key if not the definition owner.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteBookingCustomAttribute'."]:
    """Deletes a custom attribute from a booking.

    Use this tool to delete a specific custom attribute from a booking. Requires appropriate OAuth permissions depending on user level. Seller-level usage requires a subscription to Appointments Plus or Appointments Premium."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/{booking_id}/custom-attributes/{key}".format(  # noqa: UP032
            booking_id=booking_id, key=custom_attribute_key
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_READ"]))
async def retrieve_booking_custom_attribute(
    context: ToolContext,
    booking_id: Annotated[
        str,
        "The unique identifier for the target booking. This ID is required to retrieve the specific custom attribute for the booking.",  # noqa: E501
    ],
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute to retrieve. Must match the `key` of a custom attribute definition in the Square seller account. Use the qualified key if not the definition owner.",  # noqa: E501
    ],
    custom_attribute_version: Annotated[
        int | None,
        "The desired version for strong consistency reads of the custom attribute. Uses the specified version or a higher one if available.",  # noqa: E501
    ] = None,
    include_custom_attribute_definition: Annotated[
        bool | None,
        "Set to true to include the custom attribute definition, providing name, description, and data type details. Default is false.",  # noqa: E501
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveBookingCustomAttribute'."]:
    """Retrieve custom attributes of a booking.

    This tool retrieves a specific custom attribute for a booking using the booking ID and custom attribute key. Ensure proper OAuth permissions are set: `APPOINTMENTS_READ` for buyer-level, and `APPOINTMENTS_ALL_READ` with `APPOINTMENTS_READ` for seller-level."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/bookings/{booking_id}/custom-attributes/{key}".format(  # noqa: UP032
            booking_id=booking_id, key=custom_attribute_key
        ),
        method="GET",
        params=remove_none_values({
            "with_definition": include_custom_attribute_definition,
            "version": custom_attribute_version,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["APPOINTMENTS_WRITE"]))
async def set_booking_custom_attribute(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    booking_id: Annotated[
        str | None,
        "The ID of the target booking to update or insert a custom attribute for.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    custom_attribute_key: Annotated[
        str | None,
        "The key for the custom attribute to create or update, matching the existing key in the Square seller account. Use the qualified key if not the definition owner.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpsertBookingCustomAttribute'."]:
    """Upserts a custom attribute for a booking.

    Use this tool to update or insert a custom attribute for a specific booking. Requires appropriate OAuth scopes and seller subscriptions to Appointments Plus or Premium.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["SETBOOKINGCUSTOMATTRIBUTE"],
            "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 booking_id:
        missing_params.append(("booking_id", "path"))
    if not custom_attribute_key:
        missing_params.append(("custom_attribute_key", "path"))

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

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

    # 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\n" + REQUEST_BODY_SCHEMAS["SETBOOKINGCUSTOMATTRIBUTE"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/bookings/{booking_id}/custom-attributes/{key}".format(  # noqa: UP032
            booking_id=booking_id, key=custom_attribute_key
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SETBOOKINGCUSTOMATTRIBUTE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def list_user_cards(
    context: ToolContext,
    filter_by_customer_id: Annotated[
        str | None,
        "Limit results to cards associated with a specific customer ID. By default, all cards owned by the merchant are returned.",  # noqa: E501
    ] = None,
    include_disabled_cards: Annotated[
        bool | None,
        "Include disabled cards in the results. By default, only enabled cards are returned.",
    ] = False,
    limit_to_reference_id: Annotated[
        str | None,
        "Limit results to cards associated with the given reference ID. Use this to filter cards matching a specific reference.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None, "A string token to retrieve the next set of card results for pagination."
    ] = None,
    sort_order: Annotated[
        str | None,
        "Specifies the sort order of the list by card creation date. Options are 'ASC' (ascending) or 'DESC' (descending). Defaults to 'ASC'.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCards'."]:
    """Retrieve a list of cards owned by the account.

    This tool fetches up to 25 cards associated with the user's account. It should be called when there is a need to view all cards linked to the account making the request."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/cards",
        method="GET",
        params=remove_none_values({
            "cursor": pagination_cursor,
            "customer_id": filter_by_customer_id,
            "include_disabled": include_disabled_cards,
            "reference_id": limit_to_reference_id,
            "sort_order": sort_order,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def add_card_to_merchant(
    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 'CreateCard'."]:
    """Adds a card on file to an existing merchant.

    This tool is used to store a new card on file for an existing merchant, facilitating future transactions. It provides confirmation once the card is successfully added.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["ADDCARDTOMERCHANT"],
            "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\n" + REQUEST_BODY_SCHEMAS["ADDCARDTOMERCHANT"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["ADDCARDTOMERCHANT"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/cards",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ADDCARDTOMERCHANT"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def retrieve_card_details(
    context: ToolContext,
    card_id: Annotated[str, "Unique ID for the desired card to retrieve its details."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveCard'."]:
    """Retrieve details for a specific card.

    Use this tool to get information about a specific card by providing the card ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/cards/{card_id}".format(card_id=card_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def disable_credit_card(
    context: ToolContext,
    credit_card_id: Annotated[
        str,
        "Unique ID of the credit card to be disabled. This is required to specify the card to be deactivated.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DisableCard'."]:
    """Disable a credit card to prevent further charges.

    Use this tool to disable a credit card, which will prevent any further updates or charges on the card. Attempting to disable an already disabled card is allowed but will have no effect."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/cards/{card_id}/disable".format(  # noqa: UP032
            card_id=credit_card_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CASH_DRAWER_READ"]))
async def list_cash_drawer_shifts(
    context: ToolContext,
    location_id: Annotated[
        str, "The unique identifier of the location to retrieve cash drawer shifts for."
    ],
    exclusive_query_end_time: Annotated[
        str | None,
        "The exclusive end time for the query on opened_at, provided in ISO 8601 format.",
    ] = None,
    pagination_cursor: Annotated[
        str | None, "Opaque cursor used for fetching the next page of results from the API."
    ] = None,
    results_per_page: Annotated[
        int | None,
        "Number of cash drawer shift events per page. Default is 200, with a maximum of 1000.",
    ] = None,
    sort_order: Annotated[
        str | None,
        "Specifies the order of cash drawer shifts based on their opened_at field. Options are 'ASC' for ascending and 'DESC' for descending. Default is 'ASC'.",  # noqa: E501
    ] = None,
    start_time: Annotated[
        str | None, "The inclusive start time for the query in ISO 8601 format."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCashDrawerShifts'."]:
    """Retrieve cash drawer shift details for a location and date range.

    Use this tool to obtain detailed information about cash drawer shifts for a specific location during a specified date range. Ideal for managing and auditing cash flows in business operations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/cash-drawers/shifts",
        method="GET",
        params=remove_none_values({
            "location_id": location_id,
            "sort_order": sort_order,
            "begin_time": start_time,
            "end_time": exclusive_query_end_time,
            "limit": results_per_page,
            "cursor": pagination_cursor,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CASH_DRAWER_READ"]))
async def retrieve_cash_drawer_shift_summary(
    context: ToolContext,
    location_identifier: Annotated[
        str, "The unique ID of the location to retrieve cash drawer shifts from."
    ],
    shift_identifier: Annotated[
        str, "The unique ID of the specific cash drawer shift to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveCashDrawerShift'."]:
    """Retrieve summary details for a specific cash drawer shift.

    This tool fetches summary details for a single cash drawer shift. It should be used when you need to know the specifics of a particular shift, such as its total cash movements and status. Ideal for financial reconciliations and auditing tasks."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/cash-drawers/shifts/{shift_id}".format(  # noqa: UP032
            shift_id=shift_identifier
        ),
        method="GET",
        params=remove_none_values({"location_id": location_identifier}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CASH_DRAWER_READ"]))
async def list_cash_drawer_shift_events(
    context: ToolContext,
    location_id: Annotated[
        str, "The unique identifier for the location to retrieve cash drawer shift events from."
    ],
    shift_id: Annotated[str, "The ID of the cash drawer shift to retrieve events for."],
    max_results_per_page: Annotated[
        int | None, "Number of results to return per page (default is 200, maximum is 1000)."
    ] = None,
    pagination_cursor: Annotated[
        str | None, "Opaque cursor for fetching the next page of cash drawer shift event results."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCashDrawerShiftEvents'."]:
    """Retrieve events for a specific cash drawer shift.

    Use this tool to get a list of events related to a specific cash drawer shift. This is useful for tracking activities during the shift."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/cash-drawers/shifts/{shift_id}/events".format(  # noqa: UP032
            shift_id=shift_id
        ),
        method="GET",
        params=remove_none_values({
            "location_id": location_id,
            "limit": max_results_per_page,
            "cursor": pagination_cursor,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ITEMS_WRITE"]))
async def delete_catalog_items(
    context: ToolContext,
    catalog_object_ids_to_delete: Annotated[
        list[str],
        "The IDs of the catalog objects to be deleted. Deletion is cascading, removing the item and dependent objects.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BatchDeleteCatalogObjects'."]:
    """Deletes catalog items and their children by IDs.

    Use this tool to delete a set of catalog items by providing their IDs. Deletion is cascading, removing both the items and their associated variations. The tool succeeds even if only part of the requested deletions can be processed, returning only the successfully deleted item IDs."""  # noqa: E501
    request_data = remove_none_values({"object_ids": catalog_object_ids_to_delete})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/catalog/batch-delete",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ITEMS_READ"]))
async def retrieve_catalog_objects(
    context: ToolContext,
    catalog_object_ids: Annotated[
        list[str],
        "An array of IDs representing the CatalogObjects to retrieve. Each ID must be a string.",
    ],
    catalog_version: Annotated[
        int | None,
        "The specific version of catalog objects to retrieve, allowing access to historical data. If omitted, the current version is used.",  # noqa: E501
    ] = None,
    include_category_path_to_root: Annotated[
        bool | None,
        "Include the `path_to_root` list for each returned category instance if set to true. Shows the path from parent categories to root.",  # noqa: E501
    ] = None,
    include_deleted_objects: Annotated[
        bool | None,
        "Set to `true` to include deleted objects (`is_deleted` attribute) in the response.",
    ] = None,
    include_related_objects: Annotated[
        bool | None,
        "If true, include additional related objects in the response. These are objects referenced by ID, included one level deep. Defaults to false.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BatchRetrieveCatalogObjects'."]:
    """Retrieve detailed catalog objects by provided IDs.

    This tool retrieves a set of catalog objects based on provided IDs, including comprehensive details like item variations, modifier lists, and applicable tax IDs."""  # noqa: E501
    request_data = remove_none_values({
        "object_ids": catalog_object_ids,
        "include_related_objects": include_related_objects,
        "catalog_version": catalog_version,
        "include_deleted_objects": include_deleted_objects,
        "include_category_path_to_root": include_category_path_to_root,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/catalog/batch-retrieve",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ITEMS_WRITE"]))
async def batch_upsert_catalog_objects(
    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 'BatchUpsertCatalogObjects'."]:
    """Batch create or update up to 10,000 catalog objects.

    The tool creates or updates catalog objects in batches, each containing up to 1,000 objects. It's an all-or-nothing operation for each batch, meaning if one object in a batch is invalid, the entire batch fails. The tool ensures consistency by processing one update request at a time per seller account.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BATCHUPSERTCATALOGOBJECTS"],
            "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\n" + REQUEST_BODY_SCHEMAS["BATCHUPSERTCATALOGOBJECTS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["BATCHUPSERTCATALOGOBJECTS"] + "\n```"
            ),
        ) from e

    response = await make_request(
        url="https://connect.squareup.com/v2/catalog/batch-upsert",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=json.dumps(request_data),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ITEMS_READ"]))
async def get_square_catalog_info(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CatalogInfo'."]:
    """Retrieve Square Catalog API information and batch size limits.

    This tool fetches details about the Square Catalog API, including information on batch size limits for the `BatchUpsertCatalogObjects` endpoint. Use this to understand API constraints and capabilities."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/catalog/info",
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ITEMS_READ"]))
async def list_catalog_items(
    context: ToolContext,
    catalog_object_types: Annotated[
        str | None,
        "A case-insensitive, comma-separated list of object types to retrieve. Valid types include ITEM, ITEM_VARIATION, CATEGORY, DISCOUNT, TAX, MODIFIER, MODIFIER_LIST, IMAGE, etc. Defaults to top-level types if unspecified.",  # noqa: E501
    ] = None,
    catalog_version_number: Annotated[
        int | None,
        "Specify the catalog version number to retrieve historical objects. If omitted, retrieves current version.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "The cursor for pagination from a previous response. Leave unset for the initial request. Page size is 100.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCatalog'."]:
    """Retrieve a list of catalog objects by type from Square catalog.

    Use this tool to get a list of catalog objects from Square, specifying desired types such as ITEM, ITEM_VARIATION, MODIFIER, etc. Note: It does not include deleted items."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/catalog/list",
        method="GET",
        params=remove_none_values({
            "cursor": pagination_cursor,
            "types": catalog_object_types,
            "catalog_version": catalog_version_number,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ITEMS_WRITE"]))
async def upsert_catalog_object(
    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 'UpsertCatalogObject'."]:
    """Create or update a catalog object in Squareup.

    Use this tool to create a new or update an existing catalog object in Squareup. It is designed to handle requests one at a time per seller account for consistency. If another update is in process, further requests will be rejected with a `429` error.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPSERTCATALOGOBJECT"],
            "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\n" + REQUEST_BODY_SCHEMAS["UPSERTCATALOGOBJECT"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPSERTCATALOGOBJECT"] + "\n```"
            ),
        ) from e

    response = await make_request(
        url="https://connect.squareup.com/v2/catalog/object",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=json.dumps(request_data),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ITEMS_WRITE"]))
async def delete_catalog_object(
    context: ToolContext,
    catalog_object_id: Annotated[
        str,
        "The unique ID of the catalog object to delete. Deletion is cascading, removing all dependent objects.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCatalogObject'."]:
    """Delete a catalog object and its children by ID.

    This tool deletes a catalog object in the Squareup catalog using the provided ID. The deletion is cascading, removing all child objects as well. Only one delete request is processed at a time per seller account."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/catalog/object/{object_id}".format(  # noqa: UP032
            object_id=catalog_object_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ITEMS_READ"]))
async def get_catalog_item_info(
    context: ToolContext,
    catalog_object_id: Annotated[
        str, "The object ID of the catalog item to retrieve detailed information for."
    ],
    catalog_version: Annotated[
        int | None,
        "Specify a catalog version to retrieve historical object data. If not provided, the current catalog version is used.",  # noqa: E501
    ] = None,
    include_category_path_to_root: Annotated[
        bool | None,
        "Include the category's path to the root in the response to show its hierarchy. Returns an empty list for top-level categories.",  # noqa: E501
    ] = False,
    include_related_objects: Annotated[
        bool | None,
        "Set to `true` to include additional related objects, like associated categories, taxes, and images, in the response. Ideal for immediate user display.",  # noqa: E501
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveCatalogObject'."]:
    """Retrieve detailed information for a specific catalog item.

    This tool retrieves a single catalog item as a CatalogObject based on the provided ID. It includes all relevant information, such as CatalogItemVariation children, references to CatalogModifierList objects, and applicable CatalogTax IDs."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/catalog/object/{object_id}".format(  # noqa: UP032
            object_id=catalog_object_id
        ),
        method="GET",
        params=remove_none_values({
            "include_related_objects": include_related_objects,
            "catalog_version": catalog_version,
            "include_category_path_to_root": include_category_path_to_root,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ITEMS_READ"]))
async def search_catalog_objects(
    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 'SearchCatalogObjects'."]:
    """Search for catalog objects using specified query filters.

    Use this tool to search for any type of catalog object through the Squareup API by matching supported search attribute values. This tool is ideal for retrieving catalog objects, including deleted ones, based on provided query filters. It's useful for comprehensive catalog searches excluding custom attribute values.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["SEARCHCATALOGOBJECTS"],
            "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\n" + REQUEST_BODY_SCHEMAS["SEARCHCATALOGOBJECTS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["SEARCHCATALOGOBJECTS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/catalog/search",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SEARCHCATALOGOBJECTS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ITEMS_READ"]))
async def search_catalog_items(
    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 'SearchCatalogItems'."]:
    """Find catalog items or variations based on search filters.

    Use this tool to search for catalog items or item variations by matching supported search attributes and custom attribute values against specified query filters. This tool is specifically for searching items or item variations, unlike the broader SearchCatalogObjects endpoint.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["SEARCHCATALOGITEMS"],
            "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\n" + REQUEST_BODY_SCHEMAS["SEARCHCATALOGITEMS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["SEARCHCATALOGITEMS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/catalog/search-catalog-items",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SEARCHCATALOGITEMS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ITEMS_WRITE"]))
async def update_item_modifier_lists(
    context: ToolContext,
    catalog_item_ids: Annotated[
        list[str],
        "A list of catalog item IDs associated with the CatalogModifierList objects to update.",
    ],
    modifier_list_ids_to_disable: Annotated[
        list[str] | None,
        "The IDs of CatalogModifierList objects to disable for the CatalogItem. At least one of this or modifier_lists_to_enable must be specified.",  # noqa: E501
    ] = None,
    modifier_list_ids_to_enable: Annotated[
        list[str] | None,
        "The IDs of the CatalogModifierList objects to enable for the CatalogItem. At least one of `modifier_lists_to_enable` or `modifier_lists_to_disable` must be specified.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateItemModifierLists'."]:
    """Update modifier lists for a catalog item.

    This tool updates the CatalogModifierList objects for a specific CatalogItem without needing a complete item upsert. Call this tool when you need to modify which modifier lists apply to an item in your catalog."""  # noqa: E501
    request_data = remove_none_values({
        "item_ids": catalog_item_ids,
        "modifier_lists_to_enable": modifier_list_ids_to_enable,
        "modifier_lists_to_disable": modifier_list_ids_to_disable,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/catalog/update-item-modifier-lists",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ITEMS_WRITE"]))
async def update_item_taxes(
    context: ToolContext,
    catalog_item_ids: Annotated[
        list[str],
        "List of IDs for the CatalogItems associated with the CatalogTax objects being updated. Maximum of 1,000 IDs.",  # noqa: E501
    ],
    catalog_tax_ids_to_disable: Annotated[
        list[str] | None,
        "List of CatalogTax object IDs to disable. Specify either this or taxes_to_enable.",
    ] = None,
    tax_ids_to_enable: Annotated[
        list[str] | None,
        "List of CatalogTax object IDs to enable. Must specify at least one if 'tax_ids_to_disable' is not provided.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateItemTaxes'."]:
    """Update tax settings for specified catalog items.

    Use this tool to update the CatalogTax objects applied to specific CatalogItems without performing a full upsert. Ideal for making quick tax configuration changes to existing items in the catalog."""  # noqa: E501
    request_data = remove_none_values({
        "item_ids": catalog_item_ids,
        "taxes_to_enable": tax_ids_to_enable,
        "taxes_to_disable": catalog_tax_ids_to_disable,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/catalog/update-item-taxes",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CHANNELS_READ"]))
async def list_sales_channels(
    context: ToolContext,
    channel_reference_type: Annotated[
        str | None,
        "Type of reference associated with the sales channel, such as LOCATION or ONLINE_SITE.",
    ] = None,
    channel_status: Annotated[
        str | None, "Specify the status of the channel. Options are 'ACTIVE' or 'INACTIVE'."
    ] = None,
    maximum_results_limit: Annotated[
        int | None,
        "Specify the maximum number of sales channels to return. Defaults to 100 if not provided.",
    ] = None,
    next_page_cursor: Annotated[
        str | None, "Provide the cursor to fetch the next set of results if pagination is needed."
    ] = None,
    reference_id: Annotated[
        str | None, "ID of the reference associated with the sales channel."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListChannels'."]:
    """Retrieve a list of available sales channels.

    Use this tool to obtain a list of all available sales channels through which transactions can be processed. Useful for managing sales operations and integrations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/channels",
        method="GET",
        params=remove_none_values({
            "reference_type": channel_reference_type,
            "reference_id": reference_id,
            "status": channel_status,
            "cursor": next_page_cursor,
            "limit": maximum_results_limit,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CHANNELS_READ"]))
async def retrieve_sales_channels(
    context: ToolContext,
    channel_identifiers: Annotated[
        list[str], "A list of IDs representing sales channels to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BulkRetrieveChannels'."]:
    """Retrieve bulk information about sales channels.

    Use this tool to fetch detailed information about multiple sales channels for business operations."""  # noqa: E501
    request_data = remove_none_values({"channel_ids": channel_identifiers})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/channels/bulk-retrieve",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CHANNELS_READ"]))
async def retrieve_channel_info(
    context: ToolContext,
    channel_id: Annotated[
        str, "The unique identifier for the channel to retrieve information about."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveChannel'."]:
    """Retrieve detailed information about a specific channel.

    Use this tool to obtain detailed information about a channel using its channel ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/channels/{channel_id}".format(channel_id=channel_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_READ"]))
async def list_customer_profiles(
    context: ToolContext,
    customer_sort_field: Annotated[
        str | None,
        "Specifies the field by which customers should be sorted. Options: 'DEFAULT' or 'CREATED_AT'. Default is 'DEFAULT'.",  # noqa: E501
    ] = None,
    customer_sort_order: Annotated[
        str | None,
        "Specify sorting order for customers: 'ASC' for ascending or 'DESC' for descending. Default is 'ASC'.",  # noqa: E501
    ] = None,
    include_total_customer_count: Annotated[
        bool | None,
        "Set to true to include the total customer count in the response. Default is false.",
    ] = False,
    max_results_per_page: Annotated[
        int | None,
        "Specify the maximum number of customer profiles to retrieve in a single page. Must be between 1 and 100. Default is 100.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A pagination cursor from a previous request to retrieve the next set of results.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCustomers'."]:
    """Retrieve customer profiles from a Square account.

    Use this tool to list customer profiles linked to a Square account. It is useful when you need to access or manage customer data stored on Square. Newly created or updated profiles may take up to a minute to be available for listing during network incidents."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers",
        method="GET",
        params=remove_none_values({
            "cursor": pagination_cursor,
            "limit": max_results_per_page,
            "sort_field": customer_sort_field,
            "sort_order": customer_sort_order,
            "count": include_total_customer_count,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def create_customer(
    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 'CreateCustomer'."]:
    """Creates a new customer for a business in Square.

    This tool is used to create a new customer profile for a business in Square. You must provide at least one of the following details: given name, family name, company name, email address, or phone number.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATECUSTOMER"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATECUSTOMER"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATECUSTOMER"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/customers",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATECUSTOMER"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def bulk_create_customers(
    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 'BulkCreateCustomers'."]:
    """Create multiple customer profiles in bulk.

    Use this tool to create multiple customer profiles for a business at once. You need to provide at least one identifying value for each customer, such as given name, family name, company name, email address, or phone number.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKCREATECUSTOMERS"],
            "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\n" + REQUEST_BODY_SCHEMAS["BULKCREATECUSTOMERS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["BULKCREATECUSTOMERS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/customers/bulk-create",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKCREATECUSTOMERS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def bulk_delete_customers(
    context: ToolContext,
    customer_profile_ids: Annotated[list[str], "Array of customer profile IDs to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BulkDeleteCustomers'."]:
    """Deletes multiple customer profiles at once.

    Use this tool to remove several customer profiles in a single action by providing their IDs. It returns a map of responses detailing the outcome for each profile."""  # noqa: E501
    request_data = remove_none_values({"customer_ids": customer_profile_ids})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/bulk-delete",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_READ"]))
async def bulk_retrieve_customers(
    context: ToolContext,
    customer_ids: Annotated[list[str], "List of customer profile IDs to retrieve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BulkRetrieveCustomers'."]:
    """Retrieve multiple customer profiles using IDs.

    Use this tool to fetch detailed customer profiles by providing a list of customer IDs. It is useful for applications needing to access multiple customer data at once."""  # noqa: E501
    request_data = remove_none_values({"customer_ids": customer_ids})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/bulk-retrieve",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def update_multiple_customer_profiles(
    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 'BulkUpdateCustomers'."]:
    """Update multiple customer profiles in one request.

    This tool updates several customer profiles simultaneously by taking a map of individual update requests, and returns a map of responses detailing the outcome of each request.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEMULTIPLECUSTOMERPROFILES"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["UPDATEMULTIPLECUSTOMERPROFILES"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["UPDATEMULTIPLECUSTOMERPROFILES"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/customers/bulk-update",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEMULTIPLECUSTOMERPROFILES"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_READ"]))
async def list_customer_custom_attribute_definitions(
    context: ToolContext,
    max_results_per_page: Annotated[
        int | None,
        "Maximum number of results to return per page. It ranges from 1 to 100, defaulting to 20.",
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "Cursor from the previous response for pagination. Use it to get the next page of results.",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ListCustomerCustomAttributeDefinitions'."
]:
    """Retrieve customer custom attribute definitions for a Square seller.

    Lists customer-related custom attribute definitions of a Square seller account, including those visible to the requesting app. This includes custom attributes that are read-only or read-write."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/custom-attribute-definitions",
        method="GET",
        params=remove_none_values({"limit": max_results_per_page, "cursor": pagination_cursor}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def create_customer_attribute_definition(
    context: ToolContext,
    attribute_definition_name: Annotated[
        str | None,
        "The unique name of the custom attribute definition for both API and UI purposes. Required if visibility is set to 'VISIBILITY_READ_ONLY' or 'VISIBILITY_READ_WRITE_VALUES'.",  # noqa: E501
    ] = None,
    created_at_timestamp: Annotated[
        str | None,
        "The timestamp indicating when the custom attribute definition was created, in RFC 3339 format.",  # noqa: E501
    ] = None,
    current_custom_attribute_version: Annotated[
        int | None,
        "The current version of the custom attribute definition. Incremental value used for optimistic concurrency control. Must be the latest version on writes to avoid rejection of stale updates.",  # noqa: E501
    ] = None,
    custom_attribute_key: Annotated[
        str | None,
        "Unique identifier for the custom attribute definition, either a simple or qualified key. Up to 60 characters, including alphanumeric, periods (.), underscores (_), and hyphens (-).",  # noqa: E501
    ] = None,
    custom_attribute_schema: Annotated[
        dict[str, str] | None,
        "The JSON schema defining the data type for the custom attribute. This is required when creating a definition.",  # noqa: E501
    ] = None,
    custom_attribute_visibility: Annotated[
        str | None,
        "Specify the level of permission required to view the custom attribute definition. Options include 'VISIBILITY_HIDDEN', 'VISIBILITY_READ_ONLY', and 'VISIBILITY_READ_WRITE_VALUES'.",  # noqa: E501
    ] = None,
    customer_attribute_description: Annotated[
        str | None,
        "A description for the custom attribute definition, required if visibility is set to 'VISIBILITY_READ_ONLY' or 'VISIBILITY_READ_WRITE_VALUES'. This serves as a seller-oriented tooltip.",  # noqa: E501
    ] = None,
    idempotency_key: Annotated[
        str | None,
        "A unique identifier for the request to prevent duplicate executions in case of retries. This is used for ensuring idempotency.",  # noqa: E501
    ] = None,
    timestamp_updated_at: Annotated[
        str | None,
        "Timestamp indicating when the custom attribute definition was created or updated, in RFC 3339 format.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateCustomerCustomAttributeDefinition'."
]:
    """Create a custom attribute for customer profiles.

    This tool creates a custom attribute definition that can be associated with customer profiles for a Square seller account. Use it to specify the key, visibility, schema, and other properties for the custom attribute."""  # noqa: E501
    request_data = remove_none_values({
        "custom_attribute_definition": {
            "key": custom_attribute_key,
            "schema": custom_attribute_schema,
            "name": attribute_definition_name,
            "description": customer_attribute_description,
            "visibility": custom_attribute_visibility,
            "version": current_custom_attribute_version,
            "updated_at": timestamp_updated_at,
            "created_at": created_at_timestamp,
        },
        "idempotency_key": idempotency_key,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/custom-attribute-definitions",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def delete_customer_attribute_definition(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str, "The key of the custom attribute definition to delete from a Square seller account."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'DeleteCustomerCustomAttributeDefinition'."
]:
    """Delete a customer custom attribute definition from Square.

    Deletes a custom attribute definition linked to customer profiles from a Square seller account, removing it from all associated customer profiles. Only the owner of the definition can perform this action."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_READ"]))
async def get_customer_custom_attribute_definition(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute definition to retrieve. Use the qualified key if not the owner.",  # noqa: E501
    ],
    custom_attribute_version: Annotated[
        int | None,
        "The current version of the custom attribute definition to ensure consistent reads. Use to check for the most up-to-date data.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'RetrieveCustomerCustomAttributeDefinition'."
]:
    """Retrieve a customer's custom attribute definition from Square.

    Use this tool to get a specific customer-related custom attribute definition from a Square seller account. It retrieves details about the attribute, provided it has the correct visibility settings."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="GET",
        params=remove_none_values({"version": custom_attribute_version}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def update_customer_attribute_definition(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute definition to update. This should be either a simple or qualified key.",  # noqa: E501
    ],
    attribute_description: Annotated[
        str | None,
        "Provide a seller-oriented description for the custom attribute. This may include constraints and will be displayed as a tooltip. Required if visibility is set to `VISIBILITY_READ_ONLY` or `VISIBILITY_READ_WRITE_VALUES`.",  # noqa: E501
    ] = None,
    attribute_visibility: Annotated[
        str | None,
        "Defines who can read and write the custom attribute values and definition. Options are 'VISIBILITY_HIDDEN', 'VISIBILITY_READ_ONLY', or 'VISIBILITY_READ_WRITE_VALUES'.",  # noqa: E501
    ] = None,
    creation_timestamp: Annotated[
        str | None,
        "The timestamp indicating when the custom attribute definition was created, in RFC 3339 format.",  # noqa: E501
    ] = None,
    current_version: Annotated[
        int | None,
        "The current version of the custom attribute definition, required for optimistic concurrency control. Must be set to the latest version to ensure updates are not stale. This helps enforce strong consistency during updates.",  # noqa: E501
    ] = None,
    custom_attribute_definition_key: Annotated[
        str | None,
        "The identifier for the custom attribute definition. It can be a simple key or a qualified key. The format is either 'key' or 'application_id:key'. It must be unique and cannot be changed after creation.",  # noqa: E501
    ] = None,
    custom_attribute_definition_name: Annotated[
        str | None,
        "The name of the custom attribute definition for API and seller UI, unique within the seller and application pair. Required if visibility is `VISIBILITY_READ_ONLY` or `VISIBILITY_READ_WRITE_VALUES`.",  # noqa: E501
    ] = None,
    custom_attribute_definition_schema: Annotated[
        dict[str, str] | None,
        "The JSON schema defining the data type of custom attributes. This is mandatory when creating a definition.",  # noqa: E501
    ] = None,
    unique_request_identifier: Annotated[
        str | None,
        "A unique identifier for this request to ensure idempotency. Prevents duplicate operations by using the same identifier for retries.",  # noqa: E501
    ] = None,
    updated_timestamp: Annotated[
        str | None,
        "The RFC 3339 format timestamp indicating when the custom attribute definition was last updated.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'UpdateCustomerCustomAttributeDefinition'."
]:
    """Update customer custom attribute definitions for a Square seller.

    Use this tool to modify fields like `name`, `description`, `visibility`, or the `schema` for a `Selection` data type of customer-related custom attribute definitions in a Square seller account. Only accessible by the definition owner, and all attributes are visible in exported data."""  # noqa: E501
    request_data = remove_none_values({
        "custom_attribute_definition": {
            "key": custom_attribute_definition_key,
            "schema": custom_attribute_definition_schema,
            "name": custom_attribute_definition_name,
            "description": attribute_description,
            "visibility": attribute_visibility,
            "version": current_version,
            "updated_at": updated_timestamp,
            "created_at": creation_timestamp,
        },
        "idempotency_key": unique_request_identifier,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def bulk_upsert_customer_attributes(
    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 'BulkUpsertCustomerCustomAttributes'."
]:
    """Bulk create or update custom attributes for customer profiles.

    Use this tool to set or update the value of custom attributes for multiple customer profiles in a Square seller account. It handles up to 25 upsert requests, returning responses mapped to their request IDs. Useful for managing multiple custom attributes at once.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKUPSERTCUSTOMERATTRIBUTES"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["BULKUPSERTCUSTOMERATTRIBUTES"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["BULKUPSERTCUSTOMERATTRIBUTES"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/customers/custom-attributes/bulk-upsert",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKUPSERTCUSTOMERATTRIBUTES"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_READ"]))
async def list_customer_groups(
    context: ToolContext,
    max_results_per_page: Annotated[
        int | None,
        "The maximum number of customer groups to return per page. Must be between 1 and 50.",
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A pagination cursor to retrieve the next set of results from a previous query. Useful for handling paginated responses.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCustomerGroups'."]:
    """Retrieve a list of customer groups for a business.

    Call this tool to get the customer groups associated with a business. Useful for understanding customer segments or managing customer-related operations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/groups",
        method="GET",
        params=remove_none_values({"cursor": pagination_cursor, "limit": max_results_per_page}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def create_customer_group(
    context: ToolContext,
    customer_group_name: Annotated[
        str,
        "The name of the customer group to be created. It is required for organizing customers into the group.",  # noqa: E501
    ],
    customer_group_id: Annotated[
        str | None,
        "A unique Square-generated ID for the customer group. This is automatically generated by Square and is used to identify the customer group.",  # noqa: E501
    ] = None,
    group_created_timestamp: Annotated[
        str | None,
        "Specify the timestamp for when the customer group was created, in RFC 3339 format.",
    ] = None,
    group_last_updated_time: Annotated[
        str | None, "The timestamp when the customer group was last updated, in RFC 3339 format."
    ] = None,
    idempotency_key: Annotated[
        str | None,
        "A unique string to ensure the request is processed only once. See [Idempotency](https://developer.squareup.com/docs/build-basics/common-api-patterns/idempotency).",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateCustomerGroup'."]:
    """Creates a new customer group for a business.

    Use this tool to create a new customer group by specifying the group's name. It should be called when a business needs to organize customers into a new group for targeted marketing or management purposes."""  # noqa: E501
    request_data = remove_none_values({
        "idempotency_key": idempotency_key,
        "group": {
            "id": customer_group_id,
            "name": customer_group_name,
            "created_at": group_created_timestamp,
            "updated_at": group_last_updated_time,
        },
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/groups",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def delete_customer_group(
    context: ToolContext,
    customer_group_id: Annotated[str, "The ID of the customer group you want to delete."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCustomerGroup'."]:
    """Deletes a customer group by its ID.

    Use this tool to delete a customer group in your database by specifying the group ID. It confirms the successful deletion of the group."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/groups/{group_id}".format(  # noqa: UP032
            group_id=customer_group_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_READ"]))
async def get_customer_group(
    context: ToolContext,
    customer_group_id: Annotated[
        str, "The unique ID of the customer group to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveCustomerGroup'."]:
    """Retrieve details of a specific customer group by group ID.

    Use this tool to get information about a specific customer group using its unique group ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/groups/{group_id}".format(  # noqa: UP032
            group_id=customer_group_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def update_customer_group(
    context: ToolContext,
    customer_group_id: Annotated[str, "The ID of the customer group to update."],
    customer_group_name: Annotated[str, "The new name for the customer group to be updated."],
    customer_group_unique_id: Annotated[
        str | None, "A unique Square-generated ID for the customer group to be updated."
    ] = None,
    group_creation_timestamp: Annotated[
        str | None,
        "The timestamp of when the customer group was created in RFC 3339 format. Required to track creation time.",  # noqa: E501
    ] = None,
    group_last_updated_timestamp: Annotated[
        str | None, "The timestamp when the customer group was last updated, in RFC 3339 format."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCustomerGroup'."]:
    """Updates a customer group by its ID.

    This tool updates the details of a specified customer group using its group ID. It should be called when you need to modify the attributes of an existing customer group."""  # noqa: E501
    request_data = remove_none_values({
        "group": {
            "id": customer_group_unique_id,
            "name": customer_group_name,
            "created_at": group_creation_timestamp,
            "updated_at": group_last_updated_timestamp,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/groups/{group_id}".format(  # noqa: UP032
            group_id=customer_group_id
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_READ"]))
async def search_square_customers(
    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 'SearchCustomers'."]:
    """Search customer profiles in a Square account.

    Use this tool to search for customer profiles in a Square account by applying various query filters. If no filters are provided, it returns all profiles sorted alphabetically by name.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["SEARCHSQUARECUSTOMERS"],
            "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\n" + REQUEST_BODY_SCHEMAS["SEARCHSQUARECUSTOMERS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["SEARCHSQUARECUSTOMERS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/customers/search",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SEARCHSQUARECUSTOMERS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_READ"]))
async def list_customer_segments(
    context: ToolContext,
    max_results_per_page: Annotated[
        int | None,
        "The maximum number of results to return in a single page. Value must be between 1 and 50. Default is 50.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A pagination cursor for retrieving the next set of customer segment results. Use a cursor returned from a previous call to continue listing.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCustomerSegments'."]:
    """Retrieve customer segments for a business.

    Use this tool to get the current list of customer segments associated with a business. Ideal for understanding customer categorization and segmentation."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/segments",
        method="GET",
        params=remove_none_values({"cursor": pagination_cursor, "limit": max_results_per_page}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_READ"]))
async def get_customer_segment(
    context: ToolContext,
    customer_segment_id: Annotated[
        str, "The Square-issued ID of the customer segment to retrieve."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveCustomerSegment'."]:
    """Retrieve specific customer segment information.

    Use this tool to get information about a specific customer segment using the segment ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/segments/{segment_id}".format(  # noqa: UP032
            segment_id=customer_segment_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def delete_customer_profile(
    context: ToolContext,
    customer_id: Annotated[
        str,
        "The ID of the customer to delete from the business system. Required for identifying the customer profile to remove.",  # noqa: E501
    ],
    customer_profile_version: Annotated[
        int | None,
        "The current version of the customer profile for optimistic concurrency control.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCustomer'."]:
    """Delete a customer profile from a business system.

    Use this tool to delete a customer profile from a business's records. It requires the customer ID, especially if the profile was created by merging existing profiles."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/{customer_id}".format(  # noqa: UP032
            customer_id=customer_id
        ),
        method="DELETE",
        params=remove_none_values({"version": customer_profile_version}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_READ"]))
async def retrieve_customer_details(
    context: ToolContext,
    customer_id: Annotated[str, "The unique identifier of the customer to retrieve details for."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveCustomer'."]:
    """Retrieve detailed information for a specific customer.

    Use this tool to obtain detailed information about a single customer by their ID. Ideal for cases where customer-specific information is needed, such as contact details, purchase history, or personalized service."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/{customer_id}".format(  # noqa: UP032
            customer_id=customer_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def update_customer_profile(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    customer_id: Annotated[
        str | None,
        "The unique identifier of the customer to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateCustomer'."]:
    """Update a customer's profile with new or changed details.

    Use this tool to update a customer profile by specifying only the new or changed fields. You can also remove fields by setting them to null. For profiles created by merging, use the ID of the new profile.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATECUSTOMERPROFILE"],
            "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 customer_id:
        missing_params.append(("customer_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\n" + REQUEST_BODY_SCHEMAS["UPDATECUSTOMERPROFILE"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATECUSTOMERPROFILE"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/customers/{customer_id}".format(  # noqa: UP032
            customer_id=customer_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATECUSTOMERPROFILE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_READ"]))
async def list_customer_custom_attributes(
    context: ToolContext,
    customer_profile_id: Annotated[
        str,
        "The unique identifier of the customer profile whose custom attributes are to be retrieved.",  # noqa: E501
    ],
    include_attribute_definitions: Annotated[
        bool | None,
        "Set to true to include custom attribute definitions in each custom attribute's definition field. Default is false.",  # noqa: E501
    ] = False,
    maximum_results_per_page: Annotated[
        int | None,
        "The maximum number of results to return in a single response. Valid values are 1 to 100, default is 20.",  # noqa: E501
    ] = None,
    paging_cursor: Annotated[
        str | None,
        "Cursor from the previous response to get the next page of results. Used for pagination.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListCustomerCustomAttributes'."]:
    """Retrieve custom attributes of a customer profile.

    This tool lists the custom attributes associated with a customer profile, with an option to include custom attribute definitions. It retrieves all custom attributes visible to the requesting application, including those owned by other applications with shared visibility settings."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/{customer_id}/custom-attributes".format(  # noqa: UP032
            customer_id=customer_profile_id
        ),
        method="GET",
        params=remove_none_values({
            "limit": maximum_results_per_page,
            "cursor": paging_cursor,
            "with_definitions": include_attribute_definitions,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def delete_customer_custom_attribute(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute to delete. Must match the key of a custom attribute definition in the Square seller account. Use the qualified key if not the definition owner.",  # noqa: E501
    ],
    customer_profile_id: Annotated[
        str, "The ID of the target customer profile for which the custom attribute will be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteCustomerCustomAttribute'."]:
    """Deletes a custom attribute from a customer profile.

    Use this tool to delete a custom attribute associated with a customer profile. Applicable when removing attributes with visibility set to READ_WRITE."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/{customer_id}/custom-attributes/{key}".format(  # noqa: UP032
            customer_id=customer_profile_id, key=custom_attribute_key
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_READ"]))
async def get_customer_custom_attribute(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute to retrieve. This must match an existing custom attribute key in the Square seller account. Use a qualified key if not the definition owner.",  # noqa: E501
    ],
    customer_profile_id: Annotated[
        str, "The ID of the customer profile to retrieve the custom attribute from."
    ],
    attribute_version: Annotated[
        int | None,
        "The version of the custom attribute for consistent reads. A higher version will return a BAD_REQUEST error.",  # noqa: E501
    ] = None,
    include_custom_attribute_definition: Annotated[
        bool | None,
        "Set to true to include the custom attribute definition with name, description, data type, and other details. Default is false.",  # noqa: E501
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveCustomerCustomAttribute'."]:
    """Retrieve a custom attribute from a customer profile.

    Retrieves a custom attribute associated with a customer profile in Squareup. Optionally, also retrieves the custom attribute definition using a query parameter. Useful for accessing specific customer data fields set by sellers or other applications."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/{customer_id}/custom-attributes/{key}".format(  # noqa: UP032
            customer_id=customer_profile_id, key=custom_attribute_key
        ),
        method="GET",
        params=remove_none_values({
            "with_definition": include_custom_attribute_definition,
            "version": attribute_version,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def update_customer_custom_attribute(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    customer_identifier: Annotated[
        str | None,
        "The ID of the target customer profile for which the custom attribute will be created or updated. This ID should match the customer profile in the Square system.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    custom_attribute_key: Annotated[
        str | None,
        "The key identifying the custom attribute to create or update. Must match a key in the Square seller account's custom attribute definition. Use a qualified key if the application is not the definition owner.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpsertCustomerCustomAttribute'."]:
    """Create or update a custom attribute for a customer profile.

    This tool allows you to create or update a custom attribute for a specified customer profile in a Square seller account. Use it when you need to manage customer-specific details that are defined by custom attribute definitions.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATECUSTOMERCUSTOMATTRIBUTE"],
            "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 customer_identifier:
        missing_params.append(("customer_identifier", "path"))
    if not custom_attribute_key:
        missing_params.append(("custom_attribute_key", "path"))

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

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

    # 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\n"
                + REQUEST_BODY_SCHEMAS["UPDATECUSTOMERCUSTOMATTRIBUTE"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/customers/{customer_id}/custom-attributes/{key}".format(  # noqa: UP032
            customer_id=customer_identifier, key=custom_attribute_key
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATECUSTOMERCUSTOMATTRIBUTE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def remove_customer_group(
    context: ToolContext,
    customer_id: Annotated[
        str,
        "The ID of the customer to remove from the group. Provide the unique identifier associated with the customer.",  # noqa: E501
    ],
    group_id: Annotated[
        str, "The unique identifier of the customer group to remove the customer from."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveGroupFromCustomer'."]:
    """Remove a group membership from a customer.

    Call this tool to remove a specific group association from a customer using their customer ID and group ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/{customer_id}/groups/{group_id}".format(  # noqa: UP032
            customer_id=customer_id, group_id=group_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["CUSTOMERS_WRITE"]))
async def add_group_to_customer(
    context: ToolContext,
    customer_group_id: Annotated[
        str,
        "The unique identifier of the group to which the customer will be added. This is necessary to specify the exact group for categorization purposes.",  # noqa: E501
    ],
    customer_id: Annotated[
        str, "The unique identifier of the customer to be added to the specified group."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddGroupToCustomer'."]:
    """Adds a customer to a specified group.

    Use this tool to assign a customer to a specific group using their customer ID and the group ID. This helps in categorizing customers into different groups for better management."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/customers/{customer_id}/groups/{group_id}".format(  # noqa: UP032
            customer_id=customer_id, group_id=customer_group_id
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DEVICES_READ"]))
async def list_merchant_devices(
    context: ToolContext,
    device_listing_order: Annotated[
        str | None,
        "Specifies whether to list devices from oldest to newest ('ASC') or newest to oldest ('DESC').",  # noqa: E501
    ] = None,
    filter_by_location_id: Annotated[
        str | None, "Return devices only at the specified location ID, if provided."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A string used to fetch the next set of device results. Obtained from a prior API call, it facilitates pagination.",  # noqa: E501
    ] = None,
    results_page_limit: Annotated[
        int | None,
        "The number of results to return in a single page. Use this to control pagination of the device list.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListDevices'."]:
    """Retrieve a list of devices for a merchant's terminal API.

    This tool is used to list all devices associated with a merchant, specifically for the Terminal API. It is helpful for businesses needing an overview of their registered devices."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/devices",
        method="GET",
        params=remove_none_values({
            "cursor": pagination_cursor,
            "sort_order": device_listing_order,
            "limit": results_page_limit,
            "location_id": filter_by_location_id,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DEVICE_CREDENTIAL_MANAGEMENT"]))
async def list_device_codes(
    context: ToolContext,
    device_code_status: Annotated[
        str | None,
        "Filter DeviceCodes by statuses: 'UNKNOWN', 'UNPAIRED', 'PAIRED', or 'EXPIRED'. Defaults to 'PAIRED' and 'UNPAIRED' if empty.",  # noqa: E501
    ] = None,
    location_id_filter: Annotated[
        str | None,
        "Filter to return only DeviceCodes from the specified location. Returns data from all locations if not provided.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None, "A pagination cursor from a previous call. Use to get the next set of results."
    ] = None,
    product_type_filter: Annotated[
        str | None,
        "Specify the product type to filter DeviceCodes. Defaults to all if empty. Options include 'TERMINAL_API'.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListDeviceCodes'."]:
    """List all device codes for a merchant.

    Fetches all device codes linked to a merchant, providing access to their associated data."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/devices/codes",
        method="GET",
        params=remove_none_values({
            "cursor": pagination_cursor,
            "location_id": location_id_filter,
            "product_type": product_type_filter,
            "status": device_code_status,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DEVICE_CREDENTIAL_MANAGEMENT"]))
async def create_square_terminal_device_code(
    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 'CreateDeviceCode'."]:
    """Generate a DeviceCode for Square Terminal login.

    Creates a code for logging into a Square Terminal device in connected terminal mode. Useful for setting up devices for point-of-sale operations.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATESQUARETERMINALDEVICECODE"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["CREATESQUARETERMINALDEVICECODE"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["CREATESQUARETERMINALDEVICECODE"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/devices/codes",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATESQUARETERMINALDEVICECODE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DEVICE_CREDENTIAL_MANAGEMENT"]))
async def retrieve_device_code(
    context: ToolContext,
    device_code_id: Annotated[
        str, "The unique identifier for the device code to retrieve its details."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetDeviceCode'."]:
    """Retrieve device code details by ID.

    Use this tool to obtain device code details when given a specific ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/devices/codes/{id}".format(id=device_code_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DEVICES_READ"]))
async def retrieve_device_by_id(
    context: ToolContext,
    device_identifier: Annotated[
        str, "The unique identifier for the device to retrieve information from Square's system."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetDevice'."]:
    """Retrieve specific device information using its ID.

    Call this tool to obtain details about a specific device using its unique ID. Useful for accessing device-related information in Square's system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/devices/{device_id}".format(  # noqa: UP032
            device_id=device_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DISPUTES_READ"]))
async def get_account_disputes(
    context: ToolContext,
    dispute_states_filter: Annotated[
        str | None,
        "Specify dispute states to filter the results. Options: INQUIRY_EVIDENCE_REQUIRED, INQUIRY_PROCESSING, INQUIRY_CLOSED, EVIDENCE_REQUIRED, PROCESSING, WON, LOST, ACCEPTED. Defaults to all states if not provided.",  # noqa: E501
    ] = None,
    location_id: Annotated[
        str | None,
        "The unique ID of the location to filter disputes for. If omitted, disputes from all locations are returned.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A string cursor from a previous call to retrieve the next set of dispute results. See [Pagination](https://developer.squareup.com/docs/build-basics/common-api-patterns/pagination).",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListDisputes'."]:
    """Retrieve a list of disputes for an account.

    Use this tool to obtain a list of disputes related to a specific account. It is useful for managing and reviewing disputes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/disputes",
        method="GET",
        params=remove_none_values({
            "cursor": pagination_cursor,
            "states": dispute_states_filter,
            "location_id": location_id,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DISPUTES_READ"]))
async def retrieve_dispute_details(
    context: ToolContext,
    dispute_id: Annotated[
        str, "The unique identifier for the dispute you want to retrieve details about."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveDispute'."]:
    """Retrieve details about a specific dispute using its ID.

    Call this tool to obtain information about a particular dispute, including its details, by providing the dispute ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/disputes/{dispute_id}".format(dispute_id=dispute_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DISPUTES_WRITE"]))
async def accept_dispute_loss(
    context: ToolContext,
    dispute_id: Annotated[str, "The unique ID of the dispute to accept and resolve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AcceptDispute'."]:
    """Accept the loss on a dispute and update its status to ACCEPTED.

    This tool should be called when you need to accept the loss on a dispute for a transaction. It updates the dispute state to ACCEPTED and returns the disputed amount to the cardholder. Square debits the corresponding amount from the seller's account. Use this when a dispute resolution is finalized, and the seller agrees to the loss."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/disputes/{dispute_id}/accept".format(  # noqa: UP032
            dispute_id=dispute_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DISPUTES_READ"]))
async def get_dispute_evidence(
    context: ToolContext,
    dispute_id: Annotated[
        str, "Specify the ID of the dispute to retrieve its associated evidence."
    ],
    pagination_cursor: Annotated[
        str | None,
        "A cursor for pagination. Provide this to retrieve the next set of results for the original query.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListDisputeEvidence'."]:
    """Retrieve evidence for a specific dispute.

    Use this tool to obtain a list of all evidence linked to a particular dispute, identified by its dispute ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/disputes/{dispute_id}/evidence".format(  # noqa: UP032
            dispute_id=dispute_id
        ),
        method="GET",
        params=remove_none_values({"cursor": pagination_cursor}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DISPUTES_WRITE"]))
async def upload_dispute_evidence_text(
    context: ToolContext,
    dispute_id: Annotated[str, "The unique ID of the dispute to upload text evidence for."],
    evidence_text_string: Annotated[
        str, "Provide the textual evidence string to be used in the dispute challenge."
    ],
    unique_request_key: Annotated[
        str,
        "A unique key for identifying and ensuring the idempotency of the request. For additional details, see Square's idempotency documentation.",  # noqa: E501
    ],
    dispute_evidence_type: Annotated[
        str | None,
        "Specify the type of evidence for the dispute. Options include: GENERIC_EVIDENCE, ONLINE_OR_APP_ACCESS_LOG, AUTHORIZATION_DOCUMENTATION, etc.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateDisputeEvidenceText'."]:
    """Upload text evidence for a dispute challenge.

    Use this tool to upload textual evidence to support a dispute challenge. Call this tool when you need to provide written information or arguments for a dispute with a unique dispute ID."""  # noqa: E501
    request_data = remove_none_values({
        "idempotency_key": unique_request_key,
        "evidence_type": dispute_evidence_type,
        "evidence_text": evidence_text_string,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/disputes/{dispute_id}/evidence-text".format(  # noqa: UP032
            dispute_id=dispute_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DISPUTES_WRITE"]))
async def remove_dispute_evidence(
    context: ToolContext,
    dispute_id: Annotated[
        str,
        "The ID of the dispute to remove evidence from. Provide a valid string ID to specify the dispute.",  # noqa: E501
    ],
    evidence_id: Annotated[str, "The ID of the evidence to be removed from the dispute."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteDisputeEvidence'."]:
    """Removes specified evidence from a dispute in Square.

    This tool should be called when there's a need to remove specific evidence from a dispute in the Square platform. It ensures the evidence is not sent to the bank by Square."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/disputes/{dispute_id}/evidence/{evidence_id}".format(  # noqa: UP032
            dispute_id=dispute_id, evidence_id=evidence_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DISPUTES_READ"]))
async def get_dispute_evidence_metadata(
    context: ToolContext,
    dispute_id: Annotated[str, "The unique ID of the dispute to retrieve evidence metadata from."],
    evidence_id: Annotated[
        str,
        "The unique identifier for the evidence to retrieve metadata for. Required to specify which evidence's metadata is needed in the dispute.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveDisputeEvidence'."]:
    """Get metadata for specified dispute evidence.

    Use this tool to retrieve the metadata of a specific piece of evidence related to a dispute by providing the dispute ID and evidence ID. Useful for managing and reviewing evidence details."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/disputes/{dispute_id}/evidence/{evidence_id}".format(  # noqa: UP032
            dispute_id=dispute_id, evidence_id=evidence_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["DISPUTES_WRITE"]))
async def submit_evidence_to_bank(
    context: ToolContext,
    dispute_identifier: Annotated[
        str, "The unique ID of the dispute for which evidence is being submitted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SubmitEvidence'."]:
    """Submit evidence for a dispute to the cardholder's bank.

    Use this tool to submit evidence related to a cardholder dispute to their bank. It includes evidence from files, text submissions, and any automatically provided by Square. Evidence can't be removed once submitted."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/disputes/{dispute_id}/submit-evidence".format(  # noqa: UP032
            dispute_id=dispute_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def search_square_events(
    context: ToolContext,
    end_time_range: Annotated[
        str | None,
        "A datetime in RFC 3339 format marking the end of the time range for the event search.",
    ] = None,
    filter_by_location_ids: Annotated[
        list[str] | None, "An array of location IDs to filter events by specific locations."
    ] = None,
    filter_event_types: Annotated[
        list[str] | None,
        "A list of event types to filter the search results by. Each event type should be a string.",  # noqa: E501
    ] = None,
    maximum_events_per_page: Annotated[
        int | None, "Specify the maximum number of events to return per page, up to 100."
    ] = None,
    merchant_ids_filter: Annotated[
        list[str] | None, "An array of merchant IDs used to filter events by merchant."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A pagination cursor for retrieving the next set of events from a previous search query.",
    ] = None,
    sort_key_for_event_search: Annotated[
        str | None, "Set the key by which to sort the returned events. Options include 'DEFAULT'."
    ] = None,
    sort_order: Annotated[
        str | None,
        "Specify the order (chronological or alphabetical) for sorting results. Choose 'ASC' for ascending or 'DESC' for descending order.",  # noqa: E501
    ] = None,
    time_range_start: Annotated[
        str | None, "Datetime value in RFC 3339 format indicating the start of the event timeframe."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchEvents'."]:
    """Search for Square API events within a specified timeframe.

    Use this tool to find events from the Square API that have occurred within a 28-day period. Ideal for tracking event history and obtaining specific event details."""  # noqa: E501
    request_data = remove_none_values({
        "cursor": pagination_cursor,
        "limit": maximum_events_per_page,
        "query": {
            "filter": {
                "event_types": filter_event_types,
                "merchant_ids": merchant_ids_filter,
                "location_ids": filter_by_location_ids,
                "created_at": {"start_at": time_range_start, "end_at": end_time_range},
            },
            "sort": {"field": sort_key_for_event_search, "order": sort_order},
        },
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/events",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def disable_searchable_events(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DisableEvents'."]:
    """Disable events to prevent them from being searchable.

    Use this tool to disable events, ensuring they cannot be searched. Useful for controlling event visibility during specific time periods."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/events/disable",
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def enable_events_search(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'EnableEvents'."]:
    """Enable events to make them searchable.

    This tool should be called to enable events, allowing them to be searchable from this point onward. Only events that occur while in the enabled state will be searchable."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/events/enable",
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def list_event_types(
    context: ToolContext,
    api_version: Annotated[
        str | None,
        "Specify the API version to list event types, overriding the application's default version.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListEventTypes'."]:
    """Retrieve available event types for webhooks and API queries.

    Fetches all event types that can be subscribed to as webhooks or queried via the Events API using Squareup."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/events/types",
        method="GET",
        params=remove_none_values({"api_version": api_version}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["GIFTCARDS_READ"]))
async def list_gift_cards(
    context: ToolContext,
    filter_by_customer_id: Annotated[
        str | None,
        "Provide a customer ID to return only the gift cards linked to that specific customer.",
    ] = None,
    gift_card_state: Annotated[
        str | None,
        "Specify the state of the gift cards to filter the results by their current status. If not provided, all states are included.",  # noqa: E501
    ] = None,
    gift_card_type: Annotated[
        str | None, "Filter gift cards by a specified type, or return all types if not provided."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A pagination cursor from a previous call to retrieve the next set of results. If not provided, returns the first page.",  # noqa: E501
    ] = None,
    results_per_page_limit: Annotated[
        int | None,
        "Specify the number of gift cards to return per page. Maximum is 200; default is 30.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListGiftCards'."]:
    """Retrieve and filter a list of gift cards.

    This tool calls the API to list all available gift cards, with the option to apply filters for a more specific subset. Results are returned sorted by their creation date in ascending order."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/gift-cards",
        method="GET",
        params=remove_none_values({
            "type": gift_card_type,
            "state": gift_card_state,
            "limit": results_per_page_limit,
            "cursor": pagination_cursor,
            "customer_id": filter_by_customer_id,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["GIFTCARDS_WRITE"]))
async def create_gift_card(
    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 'CreateGiftCard'."]:
    """Create and register digital or physical gift cards.

    This tool creates a digital or registers a physical gift card in a pending state. To activate the gift card for purchases, further action is needed using CreateGiftCardActivity for activation or RefundPayment for refunds.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEGIFTCARD"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATEGIFTCARD"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATEGIFTCARD"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/gift-cards",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEGIFTCARD"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["GIFTCARDS_READ"]))
async def list_gift_card_activities(
    context: ToolContext,
    activity_sort_order: Annotated[
        str | None,
        "Specify the order to return gift card activities: 'ASC' for oldest to newest, 'DESC' for newest to oldest (default).",  # noqa: E501
    ] = None,
    end_time_rfc3339: Annotated[
        str | None,
        "The inclusive end timestamp for the reporting period in RFC 3339 format. Defaults to current time.",  # noqa: E501
    ] = None,
    filter_by_activity_type: Annotated[
        str | None,
        "Specify a type of gift card activity to filter the results. If not provided, all activity types are returned.",  # noqa: E501
    ] = None,
    filter_by_location_id: Annotated[
        str | None,
        "Specify a location ID to filter gift card activities for that location. Leave empty for activities across all locations.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A cursor returned by a previous call to paginate results. Use to retrieve the next set of results.",  # noqa: E501
    ] = None,
    reporting_period_start_time: Annotated[
        str | None,
        "The starting timestamp for filtering gift card activities, in RFC 3339 format. Inclusive of the provided time; defaults to one year ago.",  # noqa: E501
    ] = None,
    results_limit_per_page: Annotated[
        int | None, "Specify the number of results per page. Maximum is 100; default is 50."
    ] = None,
    specific_gift_card_id: Annotated[
        str | None,
        "Specify a gift card ID to retrieve activities related to that specific card. If not provided, activities for all gift cards will be returned.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListGiftCardActivities'."]:
    """Retrieve and filter gift card activities.

    Fetches gift card activities for the seller's account, allowing optional filters for specific cards, regions, or timeframes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/gift-cards/activities",
        method="GET",
        params=remove_none_values({
            "gift_card_id": specific_gift_card_id,
            "type": filter_by_activity_type,
            "location_id": filter_by_location_id,
            "begin_time": reporting_period_start_time,
            "end_time": end_time_rfc3339,
            "limit": results_limit_per_page,
            "cursor": pagination_cursor,
            "sort_order": activity_sort_order,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["GIFTCARDS_WRITE"]))
async def create_gift_card_activity(
    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 'CreateGiftCardActivity'."]:
    """Creates a gift card activity to manage gift card balance or state.

    Use this tool to create activities such as activation for gift cards, to manage their balance or state.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEGIFTCARDACTIVITY"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATEGIFTCARDACTIVITY"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATEGIFTCARDACTIVITY"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/gift-cards/activities",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEGIFTCARDACTIVITY"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["GIFTCARDS_READ"]))
async def retrieve_gift_card(
    context: ToolContext,
    gift_card_account_number: Annotated[
        str,
        "The account number (GAN) of the gift card to retrieve. Max length 255 digits; Square-issued GANs have 16 digits.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveGiftCardFromGAN'."]:
    """Retrieve a gift card using its account number.

    This tool retrieves details of a gift card by using the gift card account number (GAN). It should be called when you need to access information about a specific gift card."""  # noqa: E501
    request_data = remove_none_values({"gan": gift_card_account_number})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/gift-cards/from-gan",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["GIFTCARDS_READ"]))
async def retrieve_gift_card_from_token(
    context: ToolContext,
    secure_payment_token: Annotated[
        str,
        "The secure payment token used to retrieve the gift card. Generated by the Web Payments SDK or In-App Payments SDK.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveGiftCardFromNonce'."]:
    """Retrieve a gift card using a secure token.

    Call this tool to obtain gift card details using a secure payment token."""
    request_data = remove_none_values({"nonce": secure_payment_token})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/gift-cards/from-nonce",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["GIFTCARDS_WRITE"]))
async def link_customer_to_gift_card(
    context: ToolContext,
    customer_id: Annotated[
        str,
        "The ID of the customer to link to the gift card. This should be a unique identifier representing the customer in the system.",  # noqa: E501
    ],
    gift_card_identifier: Annotated[str, "The unique ID of the gift card to link to the customer."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'LinkCustomerToGiftCard'."]:
    """Link a customer to a gift card for future use.

    This tool is used to link a specific customer to a gift card, which allows storing the card on file for the customer. Use it when you want to associate a gift card with a customer for tracking and usage purposes."""  # noqa: E501
    request_data = remove_none_values({"customer_id": customer_id})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/gift-cards/{gift_card_id}/link-customer".format(  # noqa: UP032
            gift_card_id=gift_card_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["GIFTCARDS_WRITE"]))
async def unlink_customer_from_gift_card(
    context: ToolContext,
    customer_id_to_unlink: Annotated[
        str,
        "The unique identifier of the customer to unlink from the gift card. Ensure it matches the correct customer.",  # noqa: E501
    ],
    gift_card_id_to_unlink: Annotated[
        str,
        "The ID of the gift card to be unlinked from the customer. Required for unlinking process.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UnlinkCustomerFromGiftCard'."]:
    """Unlink a customer from a gift card.

    This tool is used to unlink or remove a customer from a gift card. Call this tool when you need to detach a customer's association with a specific gift card, also known as removing a card on file."""  # noqa: E501
    request_data = remove_none_values({"customer_id": customer_id_to_unlink})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/gift-cards/{gift_card_id}/unlink-customer".format(  # noqa: UP032
            gift_card_id=gift_card_id_to_unlink
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["GIFTCARDS_READ"]))
async def retrieve_gift_card_squareup(
    context: ToolContext,
    gift_card_id: Annotated[str, "The unique ID of the gift card to retrieve information."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveGiftCard'."]:
    """Retrieve gift card details using a gift card ID.

    Use this tool to obtain information about a specific gift card by providing its ID. Ideal for checking balance, status, or other gift card details."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/gift-cards/{id}".format(id=gift_card_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ"]))
async def retrieve_inventory_adjustment(
    context: ToolContext,
    inventory_adjustment_id: Annotated[str, "ID of the InventoryAdjustment to retrieve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveInventoryAdjustment'."]:
    """Fetches inventory adjustment details by ID.

    Use this tool to get details of a specific inventory adjustment by its ID. Ideal for tracking changes in inventory levels."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/inventory/adjustments/{adjustment_id}".format(  # noqa: UP032
            adjustment_id=inventory_adjustment_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_WRITE"]))
async def apply_inventory_adjustments(
    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 'BatchChangeInventory'."]:
    """Apply batch adjustments to inventory quantities.

    Use this tool to apply inventory adjustments and counts to item quantities. Returns updated quantities on success, or a list of errors if the operation fails.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["APPLYINVENTORYADJUSTMENTS"],
            "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\n" + REQUEST_BODY_SCHEMAS["APPLYINVENTORYADJUSTMENTS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["APPLYINVENTORYADJUSTMENTS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/inventory/changes/batch-create",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["APPLYINVENTORYADJUSTMENTS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ"]))
async def fetch_inventory_changes(
    context: ToolContext,
    filter_by_catalog_object_ids: Annotated[
        list[str] | None,
        "Filter results by providing an array of CatalogObject IDs. Only applicable when set.",
    ] = None,
    filter_by_location_ids: Annotated[
        list[str] | None,
        "Return results filtered by specific `Location` IDs. This is optional and defaults to null.",  # noqa: E501
    ] = None,
    filter_by_updated_before_timestamp: Annotated[
        str | None,
        "Return results with `created_at` or `calculated_at` before this RFC 3339 timestamp.",
    ] = None,
    filter_inventory_change_types: Annotated[
        list[str] | None,
        "A list of `InventoryChangeType` values to filter results, excluding `TRANSFER`. Default is `[PHYSICAL_COUNT, ADJUSTMENT]`.",  # noqa: E501
    ] = None,
    filter_updated_after_timestamp: Annotated[
        str | None,
        "Return results with `calculated_at` after specified time in RFC 3339 format. Default is the UNIX epoch (`1970-01-01T00:00:00Z`).",  # noqa: E501
    ] = None,
    inventory_states_filter: Annotated[
        list[str] | None,
        "Filter to return `ADJUSTMENT` query results by `InventoryState`. Only applies if set. Accepts a list of states.",  # noqa: E501
    ] = None,
    number_of_records_to_return: Annotated[
        int | None, "The number of inventory change records to return in the response."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A cursor for pagination to retrieve the next set of inventory results. Use this from a previous call to continue fetching data.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BatchRetrieveInventoryChanges'."]:
    """Retrieve historical inventory changes and adjustments.

    Use this tool to access historical physical counts and inventory adjustments based on specific filter criteria. The results are paginated and sorted by the `occurred_at` timestamp in ascending order, providing a comprehensive log of inventory changes."""  # noqa: E501
    request_data = remove_none_values({
        "catalog_object_ids": filter_by_catalog_object_ids,
        "location_ids": filter_by_location_ids,
        "types": filter_inventory_change_types,
        "states": inventory_states_filter,
        "updated_after": filter_updated_after_timestamp,
        "updated_before": filter_by_updated_before_timestamp,
        "cursor": pagination_cursor,
        "limit": number_of_records_to_return,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/inventory/changes/batch-retrieve",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ"]))
async def retrieve_inventory_counts(
    context: ToolContext,
    filter_by_catalog_object_ids: Annotated[
        list[str] | None,
        "A list of `CatalogObject` IDs to filter the inventory results. Only applicable when set.",
    ] = None,
    filter_by_location_ids: Annotated[
        list[str] | None,
        "An array of Location IDs to filter inventory counts. Only applicable when set.",
    ] = None,
    filter_by_updated_after_timestamp: Annotated[
        str | None,
        "Filter results to include only those with a `calculated_at` timestamp after the specified RFC 3339 time. Default is the UNIX epoch.",  # noqa: E501
    ] = None,
    inventory_state_filters: Annotated[
        list[str] | None,
        "Filter results by specific inventory states. Ignored states: NONE, SOLD, UNLINKED_RETURN. Provide as an array of strings.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A pagination cursor to retrieve the next set of results for the original query.",
    ] = None,
    record_limit: Annotated[
        int | None, "Specify the number of inventory count records to return."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BatchRetrieveInventoryCounts'."]:
    """Retrieve current inventory counts for specific items and locations.

    Call this tool to get current inventory counts for specified catalog objects at specific locations. Results are sorted by newest first and can be filtered by changes since a given timestamp to facilitate synchronization."""  # noqa: E501
    request_data = remove_none_values({
        "catalog_object_ids": filter_by_catalog_object_ids,
        "location_ids": filter_by_location_ids,
        "updated_after": filter_by_updated_after_timestamp,
        "cursor": pagination_cursor,
        "states": inventory_state_filters,
        "limit": record_limit,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/inventory/counts/batch-retrieve",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ"]))
async def get_inventory_physical_count(
    context: ToolContext,
    inventory_physical_count_id: Annotated[
        str, "ID of the InventoryPhysicalCount to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveInventoryPhysicalCount'."]:
    """Retrieve details of a specific inventory physical count.

    Use this tool to get details of an inventory physical count using the provided physical count ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/inventory/physical-counts/{physical_count_id}".format(  # noqa: UP032
            physical_count_id=inventory_physical_count_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ"]))
async def retrieve_inventory_transfer(
    context: ToolContext,
    inventory_transfer_id: Annotated[
        str, "The unique identifier for the InventoryTransfer to retrieve."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveInventoryTransfer'."]:
    """Retrieve detailed inventory transfer information.

    Use this tool to obtain information about a specific inventory transfer using the transfer ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/inventory/transfers/{transfer_id}".format(  # noqa: UP032
            transfer_id=inventory_transfer_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ"]))
async def get_inventory_count(
    context: ToolContext,
    catalog_object_id: Annotated[
        str, "The ID of the CatalogObject to retrieve inventory count for."
    ],
    location_ids: Annotated[
        str | None,
        "Comma-separated list of Location IDs to query. Use an empty list to query all locations.",
    ] = None,
    pagination_cursor: Annotated[
        str | None, "A cursor for paginating results, from previous call, to fetch next page."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveInventoryCount'."]:
    """Retrieve current stock count for a specific catalog item.

    Use this tool to get the current calculated inventory count for a specific CatalogObject at specified locations. This is useful for tracking stock levels in real-time."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/inventory/{catalog_object_id}".format(  # noqa: UP032
            catalog_object_id=catalog_object_id
        ),
        method="GET",
        params=remove_none_values({"location_ids": location_ids, "cursor": pagination_cursor}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVOICES_READ"]))
async def list_invoices(
    context: ToolContext,
    location_identifier: Annotated[
        str, "The unique identifier for the location to fetch invoices from."
    ],
    maximum_invoices_to_return: Annotated[
        int | None,
        "Specify the maximum number of invoices to return. The limit is 200; default is 100 if not set.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None, "A cursor for pagination to retrieve the next set of invoice results."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListInvoices'."]:
    """Retrieve a list of invoices for a specified location.

    This tool retrieves a list of invoices associated with a particular location. It supports pagination, so if the response is too large, a cursor is provided for subsequent requests to fetch the remaining invoices."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/invoices",
        method="GET",
        params=remove_none_values({
            "location_id": location_identifier,
            "cursor": pagination_cursor,
            "limit": maximum_invoices_to_return,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVOICES_WRITE", "ORDERS_WRITE"]))
async def create_draft_invoice(
    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 'CreateInvoice'."]:
    """Create a draft invoice for an order using Squareup.

    This tool creates a draft invoice for an order via Squareup. The invoice remains in draft status until published, at which point it can be emailed to the customer or a card on file can be charged.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEDRAFTINVOICE"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATEDRAFTINVOICE"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATEDRAFTINVOICE"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/invoices",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEDRAFTINVOICE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVOICES_READ"]))
async def search_invoices(
    context: ToolContext,
    location_id: Annotated[
        list[str],
        "Specify the location ID to limit the invoice search. Only one location ID can be provided.",  # noqa: E501
    ],
    customer_id: Annotated[
        list[str] | None,
        "Specify the customer ID to limit the search to invoices for that customer. Only one customer can be specified.",  # noqa: E501
    ] = None,
    maximum_invoice_count: Annotated[
        int | None,
        "The maximum number of invoices to return, up to 200. Defaults to 100 if not specified.",
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A cursor to retrieve the next set of results for pagination. Use the cursor from the previous response.",  # noqa: E501
    ] = None,
    sort_by_field: Annotated[
        str | None, "Specify the field for sorting results. Default is 'INVOICE_SORT_DATE'."
    ] = None,
    sort_order: Annotated[
        str | None,
        "The order in which results are returned, either 'DESC' for descending or 'ASC' for ascending.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchInvoices'."]:
    """Search for invoices based on location and optional customer.

    This tool searches for invoices from a specified location and can filter by customer. It supports pagination; if results are truncated, a cursor is provided for retrieving more invoices."""  # noqa: E501
    request_data = remove_none_values({
        "query": {
            "filter": {"location_ids": location_id, "customer_ids": customer_id},
            "sort": {"field": sort_by_field, "order": sort_order},
        },
        "limit": maximum_invoice_count,
        "cursor": pagination_cursor,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/invoices/search",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVOICES_WRITE", "ORDERS_WRITE"]))
async def delete_invoice(
    context: ToolContext,
    invoice_id: Annotated[str, "The ID of the draft invoice to delete."],
    invoice_version: Annotated[
        int | None,
        "The version number of the invoice to delete. Use GetInvoice or ListInvoices if unknown.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteInvoice'."]:
    """Delete a draft invoice and change order status to CANCELED.

    This tool deletes a specified draft invoice. Once deleted, the associated order status changes to CANCELED. It cannot delete a published invoice, including those scheduled for processing."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/invoices/{invoice_id}".format(invoice_id=invoice_id),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({"version": invoice_version}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVOICES_READ"]))
async def retrieve_invoice_by_id(
    context: ToolContext,
    invoice_id: Annotated[
        str,
        "The ID of the invoice to retrieve. This should be a unique string representing a specific invoice.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetInvoice'."]:
    """Retrieve invoice details using an invoice ID.

    Use this tool to get detailed information of an invoice by providing a specific invoice ID."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/invoices/{invoice_id}".format(invoice_id=invoice_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVOICES_WRITE", "ORDERS_WRITE"]))
async def update_invoice(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    invoice_id: Annotated[
        str | None,
        "The unique ID of the invoice you wish to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateInvoice'."]:
    """Updates invoice details with specified changes.

    Use this tool to update fields in an existing invoice. Only the fields you wish to change need to be specified, along with the required `version` field. Note that some restrictions apply, such as the inability to change fields like `order_id` or `location_id`.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEINVOICE"],
            "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 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\n" + REQUEST_BODY_SCHEMAS["UPDATEINVOICE"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATEINVOICE"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/invoices/{invoice_id}".format(invoice_id=invoice_id),  # noqa: UP032
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEINVOICE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVOICES_WRITE"]))
async def remove_invoice_attachment(
    context: ToolContext,
    attachment_id: Annotated[
        str,
        "The unique identifier for the invoice attachment to be deleted. Required for removing the specified file from the invoice.",  # noqa: E501
    ],
    invoice_identifier: Annotated[
        str,
        "The unique ID of the invoice from which to delete the attachment. Applicable for specific invoice states.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteInvoiceAttachment'."]:
    """Removes an attachment from an invoice.

    Use this tool to permanently delete an attachment from an invoice. Applicable only for invoices in 'DRAFT', 'SCHEDULED', 'UNPAID', or 'PARTIALLY_PAID' states."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/invoices/{invoice_id}/attachments/{attachment_id}".format(  # noqa: UP032
            invoice_id=invoice_identifier, attachment_id=attachment_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVOICES_WRITE", "ORDERS_WRITE"]))
async def cancel_invoice(
    context: ToolContext,
    invoice_id: Annotated[
        str,
        "The ID of the invoice you want to cancel. This is required to specify which invoice will be affected.",  # noqa: E501
    ],
    invoice_version_to_cancel: Annotated[
        int,
        "The version number of the invoice to be canceled. Use GetInvoice or ListInvoices to find it if unknown.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelInvoice'."]:
    """Cancel an invoice to prevent further transactions.

    This tool cancels an invoice, ensuring that no payments can be collected. It cannot be used for invoices in the `DRAFT`, `PAID`, `REFUNDED`, `CANCELED`, or `FAILED` states."""  # noqa: E501
    request_data = remove_none_values({"version": invoice_version_to_cancel})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/invoices/{invoice_id}/cancel".format(  # noqa: UP032
            invoice_id=invoice_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVOICES_WRITE", "ORDERS_WRITE"]))
async def publish_invoice(
    context: ToolContext,
    invoice_id: Annotated[
        str,
        "The ID of the draft invoice that you wish to publish. This ID uniquely identifies the invoice within the system.",  # noqa: E501
    ],
    invoice_version: Annotated[
        int,
        "Specify the current version number of the invoice to publish. This must match the existing invoice version to avoid rejection.",  # noqa: E501
    ],
    unique_request_identifier: Annotated[
        str | None,
        "A unique string to identify the `PublishInvoice` request. If omitted or empty, each request is treated as independent. Refer to the Square documentation on idempotency for more information.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'PublishInvoice'."]:
    """Publish a draft invoice with Square, updating its status.

    This tool publishes a specified draft invoice using Square's service, updating its status and triggering configured actions such as sending emails or charging stored cards. The tool should be called when an invoice needs to be finalized and sent or processed according to its settings."""  # noqa: E501
    request_data = remove_none_values({
        "version": invoice_version,
        "idempotency_key": unique_request_identifier,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/invoices/{invoice_id}/publish".format(  # noqa: UP032
            invoice_id=invoice_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_SETTINGS_READ"]))
async def list_break_types(
    context: ToolContext,
    filter_by_location_id: Annotated[
        str | None, "Filter results to break types associated with the specified location ID."
    ] = None,
    max_results_per_page: Annotated[
        int | None,
        "Specify the maximum number of BreakType results to return per page. Must be between 1 and 200. Default is 200.",  # noqa: E501
    ] = None,
    next_page_cursor: Annotated[
        str | None,
        "A pointer to the next page of `BreakType` results to fetch for continued listing of break types.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListBreakTypes'."]:
    """Retrieve a paginated list of break types for a business.

    Use this tool to obtain a list of different break types defined in a business. Ideal for accessing and managing labor-related break policies."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/break-types",
        method="GET",
        params=remove_none_values({
            "location_id": filter_by_location_id,
            "limit": max_results_per_page,
            "cursor": next_page_cursor,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_SETTINGS_WRITE"]))
async def create_break_type(
    context: ToolContext,
    break_expected_duration: Annotated[
        str,
        "The expected length of the break in RFC-3339 duration format (e.g., PT15M for 15 minutes).",  # noqa: E501
    ],
    break_name: Annotated[
        str, "A human-readable name for the break type, displayed to team members."
    ],
    is_break_paid: Annotated[
        bool,
        "Indicates if the break counts towards compensated work time. Use true for a paid break, false for an unpaid break.",  # noqa: E501
    ],
    location_id: Annotated[
        str,
        "The ID of the business location where this break type will apply. It is required to associate the break type with a specific location.",  # noqa: E501
    ],
    break_type_uuid: Annotated[
        str | None, "The UUID for the BreakType object. It uniquely identifies the break type."
    ] = None,
    concurrency_version: Annotated[
        int | None,
        "Integer for resolving concurrency issues; fails if it doesn't match server version.",
    ] = None,
    created_at_timestamp: Annotated[
        str | None,
        "A read-only timestamp in RFC 3339 format, automatically populated and not required for input.",  # noqa: E501
    ] = None,
    idempotency_key: Annotated[
        str | None,
        "A unique string to ensure the operation is idempotent, avoiding duplicate actions.",
    ] = None,
    updated_at_timestamp: Annotated[
        str | None,
        "A read-only timestamp in RFC 3339 format indicating when the BreakType was last updated.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateBreakType'."]:
    """Create a new BreakType template for a location.

    This tool creates a new BreakType, which is a template for establishing Break objects. Required inputs include location ID, break name, expected duration, and payment status. Note that each location can have a maximum of three BreakType instances."""  # noqa: E501
    request_data = remove_none_values({
        "idempotency_key": idempotency_key,
        "break_type": {
            "id": break_type_uuid,
            "location_id": location_id,
            "break_name": break_name,
            "expected_duration": break_expected_duration,
            "is_paid": is_break_paid,
            "version": concurrency_version,
            "created_at": created_at_timestamp,
            "updated_at": updated_at_timestamp,
        },
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/break-types",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_SETTINGS_WRITE"]))
async def delete_break_type(
    context: ToolContext,
    break_type_uuid: Annotated[str, "The UUID of the BreakType to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteBreakType'."]:
    """Deletes an existing BreakType.

    Use this tool to delete an existing BreakType, even if it is referenced in a Shift."""
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/break-types/{id}".format(id=break_type_uuid),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_SETTINGS_READ"]))
async def get_break_type_by_id(
    context: ToolContext,
    break_type_uuid: Annotated[str, "The UUID of the BreakType to retrieve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetBreakType'."]:
    """Retrieve details of a specific BreakType by ID.

    Use this tool to get information about a specific BreakType using its unique ID. It should be called when details about a particular break type in the system are required."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/break-types/{id}".format(id=break_type_uuid),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup", scopes=["TIMECARDS_SETTINGS_READ", "TIMECARDS_SETTINGS_WRITE"]
    )
)
async def update_break_type(
    context: ToolContext,
    break_counts_for_compensation: Annotated[
        bool, "Set to true if this break counts towards time worked for compensation purposes."
    ],
    break_duration_rfc3339: Annotated[
        str, "The expected duration of the break in RFC-3339 format (e.g., PT15M for 15 minutes)."
    ],
    break_name: Annotated[
        str, "A human-readable name for the break type, shown to team members in Square products."
    ],
    break_type_uuid: Annotated[str, "The UUID of the BreakType to be updated."],
    business_location_id: Annotated[
        str,
        "The ID of the business location this type of break applies to. Required for updating break settings.",  # noqa: E501
    ],
    break_type_creation_timestamp: Annotated[
        str | None,
        "A read-only timestamp in RFC 3339 format indicating when the BreakType was created. This is not modifiable.",  # noqa: E501
    ] = None,
    break_type_id: Annotated[
        str | None,
        "The UUID for the BreakType object being updated. This is required for identifying which BreakType to modify.",  # noqa: E501
    ] = None,
    readonly_updated_at_timestamp: Annotated[
        str | None,
        "The read-only timestamp in RFC 3339 format indicating the last update time for BreakType. It's not modifiable.",  # noqa: E501
    ] = None,
    version_for_concurrency: Annotated[
        int | None,
        "The version number for concurrency control. Helps resolve conflicts by matching the server's current version.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateBreakType'."]:
    """Update an existing BreakType configuration.

    Utilize this tool to modify the settings of an existing BreakType in the Squareup labor management system. Useful for updating rules related to employee break periods."""  # noqa: E501
    request_data = remove_none_values({
        "break_type": {
            "id": break_type_id,
            "location_id": business_location_id,
            "break_name": break_name,
            "expected_duration": break_duration_rfc3339,
            "is_paid": break_counts_for_compensation,
            "version": version_for_concurrency,
            "created_at": break_type_creation_timestamp,
            "updated_at": readonly_updated_at_timestamp,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/break-types/{id}".format(id=break_type_uuid),  # noqa: UP032
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_WRITE"]))
async def create_scheduled_shift(
    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 'CreateScheduledShift'."]:
    """Create a scheduled shift with draft shift details.

    Use this tool to create a scheduled shift by providing necessary draft shift details such as location ID, job ID, and start and end times. It returns the details of the created shift.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATESCHEDULEDSHIFT"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATESCHEDULEDSHIFT"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATESCHEDULEDSHIFT"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/labor/scheduled-shifts",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATESCHEDULEDSHIFT"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_WRITE"]))
async def bulk_publish_scheduled_shifts(
    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 'BulkPublishScheduledShifts'."]:
    """Publish multiple scheduled shifts in bulk.

    Use this tool to publish between 1 to 100 scheduled shifts at once. It processes a map of individual publish requests and provides corresponding responses. Ensure all shifts have start and end times within a two-week window.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKPUBLISHSCHEDULEDSHIFTS"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["BULKPUBLISHSCHEDULEDSHIFTS"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["BULKPUBLISHSCHEDULEDSHIFTS"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/labor/scheduled-shifts/bulk-publish",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKPUBLISHSCHEDULEDSHIFTS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_READ"]))
async def list_scheduled_shifts(
    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 'SearchScheduledShifts'."]:
    """Retrieve a list of scheduled shifts with filtering options.

    Fetch a paginated list of scheduled work shifts. You can apply filters and sort the results, which are by default ordered by start time in ascending order.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["LISTSCHEDULEDSHIFTS"],
            "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\n" + REQUEST_BODY_SCHEMAS["LISTSCHEDULEDSHIFTS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["LISTSCHEDULEDSHIFTS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/labor/scheduled-shifts/search",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["LISTSCHEDULEDSHIFTS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_READ"]))
async def retrieve_scheduled_shift(
    context: ToolContext,
    scheduled_shift_id: Annotated[
        str, "The unique identifier of the scheduled shift to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveScheduledShift'."]:
    """Retrieve details of a scheduled shift by ID.

    Use this tool to obtain information about a specific scheduled shift using its ID. This is useful for accessing shift details in scheduling and labor management systems."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/scheduled-shifts/{id}".format(  # noqa: UP032
            id=scheduled_shift_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_WRITE"]))
async def update_scheduled_shift(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    scheduled_shift_id: Annotated[
        str | None,
        "The unique identifier of the scheduled shift to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateScheduledShift'."]:
    """Updates draft shift details for a scheduled shift.

    Use this tool to make updates to draft shift details such as location, job, start and end times, team member, and notes. To finalize and make updates public, the shift must be published. You can also mark a shift as deleted.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATESCHEDULEDSHIFT"],
            "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 scheduled_shift_id:
        missing_params.append(("scheduled_shift_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\n" + REQUEST_BODY_SCHEMAS["UPDATESCHEDULEDSHIFT"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATESCHEDULEDSHIFT"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/labor/scheduled-shifts/{id}".format(  # noqa: UP032
            id=scheduled_shift_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATESCHEDULEDSHIFT"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_WRITE"]))
async def publish_scheduled_shift(
    context: ToolContext,
    scheduled_shift_id: Annotated[str, "The ID of the scheduled shift to publish."],
    unique_idempotency_key: Annotated[
        str,
        "A unique identifier for ensuring the idempotency of the publish request. Prevents duplicate operations if the request is retried.",  # noqa: E501
    ],
    current_shift_version: Annotated[
        int | None,
        "The current version of the scheduled shift for optimistic concurrency control. If it doesn't match the server version, the request fails.",  # noqa: E501
    ] = None,
    notification_audience: Annotated[
        str | None,
        "Specify who receives email notifications when a scheduled shift is published. Options are: ALL, AFFECTED, NONE.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'PublishScheduledShift'."]:
    """Publish a scheduled shift to make it official.

    Use this tool to publish a scheduled shift, confirming its details. The draft shift details are finalized and moved to the published shift details, marking the shift as official."""  # noqa: E501
    request_data = remove_none_values({
        "idempotency_key": unique_idempotency_key,
        "version": current_shift_version,
        "scheduled_shift_notification_audience": notification_audience,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/scheduled-shifts/{id}/publish".format(  # noqa: UP032
            id=scheduled_shift_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_READ"]))
async def list_team_member_wages(
    context: ToolContext,
    filter_by_team_member_id: Annotated[
        str | None, "Filter wages to only those associated with the specified team member by ID."
    ] = None,
    max_results_per_page: Annotated[
        int | None,
        "The maximum number of TeamMemberWage results to return per page, ranging from 1 to 200. Default is 200.",  # noqa: E501
    ] = None,
    next_page_cursor: Annotated[
        str | None, "A pointer to the next page of team member wage results to fetch."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListTeamMemberWages'."]:
    """Retrieve paginated list of team member wages for a business.

    This tool retrieves a paginated list of wage details for team members in a business. It can be called when specific information about team member wages is needed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/team-member-wages",
        method="GET",
        params=remove_none_values({
            "team_member_id": filter_by_team_member_id,
            "limit": max_results_per_page,
            "cursor": next_page_cursor,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_READ"]))
async def get_team_member_wage(
    context: ToolContext,
    team_member_wage_id: Annotated[
        str, "The unique identifier (UUID) for retrieving the specific TeamMemberWage record."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTeamMemberWage'."]:
    """Retrieve wage details for a specific team member.

    Use this tool to obtain wage information for a specific team member by their ID. Useful for managing payroll or reviewing individual compensation."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/team-member-wages/{id}".format(  # noqa: UP032
            id=team_member_wage_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_WRITE"]))
async def create_team_member_timecard(
    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 'CreateTimecard'."]:
    """Create a timecard for a team member's workday.

    This tool creates a new timecard representing a full workday for a specified team member. Use it when you need to log work hours for employees. Ensure no existing open timecard for the member, that the start time isn't future-dated, and that it doesn't overlap with other timecards.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATETEAMMEMBERTIMECARD"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATETEAMMEMBERTIMECARD"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATETEAMMEMBERTIMECARD"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/labor/timecards",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATETEAMMEMBERTIMECARD"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_READ"]))
async def search_timecards(
    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 'SearchTimecards'."]:
    """Retrieve filtered and sorted timecard records for a business.

    Use to get a list of timecard records filtered by location IDs, team member IDs, status, start and end times, or workday details. The records can also be sorted by start, end, creation, or update times.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["SEARCHTIMECARDS"],
            "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\n" + REQUEST_BODY_SCHEMAS["SEARCHTIMECARDS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["SEARCHTIMECARDS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/labor/timecards/search",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SEARCHTIMECARDS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_WRITE"]))
async def delete_timecard(
    context: ToolContext,
    timecard_uuid: Annotated[str, "The UUID for the Timecard being deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteTimecard'."]:
    """Delete a specific timecard entry.

    This tool deletes a specified timecard entry using its unique identifier. Use it when a timecard needs to be permanently removed from the system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/timecards/{id}".format(id=timecard_uuid),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_READ"]))
async def get_timecard_by_id(
    context: ToolContext,
    timecard_id: Annotated[str, "The unique UUID identifying the timecard you want to retrieve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveTimecard'."]:
    """Fetch details of a specific timecard by ID.

    Use this tool to retrieve information about a specific timecard using its unique ID. This is useful for accessing detailed timecard records for employees."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/timecards/{id}".format(id=timecard_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_READ", "TIMECARDS_WRITE"]))
async def update_timecard(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    timecard_id: Annotated[
        str | None,
        "The unique identifier of the timecard to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateTimecard'."]:
    """Update an existing timecard with new details.

    Use this tool to update an existing timecard, including adding or closing breaks. Ensure all break instances have the `end_at` property set before closing a timecard.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATETIMECARD"],
            "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 timecard_id:
        missing_params.append(("timecard_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\n" + REQUEST_BODY_SCHEMAS["UPDATETIMECARD"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATETIMECARD"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/labor/timecards/{id}".format(id=timecard_id),  # noqa: UP032
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATETIMECARD"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["TIMECARDS_SETTINGS_READ"]))
async def list_workweek_configs(
    context: ToolContext,
    maximum_results_per_page: Annotated[
        int | None, "Maximum number of WorkweekConfig results to return per page."
    ] = None,
    pagination_cursor: Annotated[
        str | None, "Pointer to the next page of WorkweekConfig results to fetch."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListWorkweekConfigs'."]:
    """Retrieve workweek configurations for a business.

    This tool retrieves a list of workweek configurations, providing details about the workweek setup for a business."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/workweek-configs",
        method="GET",
        params=remove_none_values({"limit": maximum_results_per_page, "cursor": pagination_cursor}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup", scopes=["TIMECARDS_SETTINGS_READ", "TIMECARDS_SETTINGS_WRITE"]
    )
)
async def update_workweek_configuration(
    context: ToolContext,
    workweek_config_id: Annotated[str, "The UUID for the `WorkweekConfig` object to be updated."],
    workweek_start_day: Annotated[
        str,
        "Specifies the start day of the workweek. Acceptable values are: MON, TUE, WED, THU, FRI, SAT, SUN.",  # noqa: E501
    ],
    workweek_start_time_local: Annotated[
        str,
        "The local time a business week starts, represented as a string in `HH:MM` format (`HH:MM:SS` is accepted, but seconds are truncated).",  # noqa: E501
    ],
    read_only_updated_at: Annotated[
        str | None,
        "A read-only timestamp in RFC 3339 format; presented in UTC, to indicate the last update time of the workweek configuration.",  # noqa: E501
    ] = None,
    workweek_config_uuid: Annotated[
        str | None, "The UUID of the workweek configuration object to be updated."
    ] = None,
    workweek_config_version: Annotated[
        int | None,
        "Version number to resolve concurrency issues. If it doesn't match the server, the update fails. If omitted, a blind write occurs.",  # noqa: E501
    ] = None,
    workweek_creation_timestamp: Annotated[
        str | None, "Read-only UTC timestamp of workweek config creation in RFC 3339 format."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateWorkweekConfig'."]:
    """Update workweek configuration settings.

    Use this tool to update the settings for a specific workweek configuration in the system."""
    request_data = remove_none_values({
        "workweek_config": {
            "id": workweek_config_uuid,
            "start_of_week": workweek_start_day,
            "start_of_day_local_time": workweek_start_time_local,
            "version": workweek_config_version,
            "created_at": workweek_creation_timestamp,
            "updated_at": read_only_updated_at,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/labor/workweek-configs/{id}".format(  # noqa: UP032
            id=workweek_config_id
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def list_all_locations(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListLocations'."]:
    """Fetch details of all seller's locations, including inactive ones.

    This tool retrieves and provides information about all of the seller's locations from SquareUp, including those with inactive status. Locations are listed alphabetically by name and can be used to gain insights into the seller's business locations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/locations",
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def create_new_location(
    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 'CreateLocation'."]:
    """Create a new location for sales and configuration.

    This tool creates a new location using the Square Locations API. It's useful for setting up separate configurations for receipt layouts, item prices, and sales reports. Ideal for developers integrating Square with their applications to manage distinct sales activities.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATENEWLOCATION"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATENEWLOCATION"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATENEWLOCATION"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/locations",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATENEWLOCATION"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def list_location_custom_attribute_definitions(
    context: ToolContext,
    filter_by_visibility: Annotated[
        str | None,
        "Filter results by visibility values: 'ALL', 'READ', or 'READ_WRITE'. Determines the visibility of custom attribute definitions returned.",  # noqa: E501
    ] = None,
    maximum_results_per_page: Annotated[
        int | None,
        "Sets the maximum number of results to return in a single response page. Accepts values between 1 and 100, default is 20.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None, "The cursor from the previous response to fetch the next page of results."
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ListLocationCustomAttributeDefinitions'."
]:
    """Get location-related custom attribute definitions for a Square account.

    Use this tool to retrieve all location-related custom attribute definitions associated with a Square seller account, including those with different visibility settings."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/locations/custom-attribute-definitions",
        method="GET",
        params=remove_none_values({
            "visibility_filter": filter_by_visibility,
            "limit": maximum_results_per_page,
            "cursor": pagination_cursor,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def create_location_custom_attribute(
    context: ToolContext,
    current_definition_version: Annotated[
        int | None,
        "The current version of the custom attribute definition. Must match the latest version for updates to ensure optimistic concurrency and avoid stale writes.",  # noqa: E501
    ] = None,
    custom_attribute_definition_creation_timestamp: Annotated[
        str | None,
        "The creation timestamp for the custom attribute definition in RFC 3339 format. Required for consistently tracking when the definition was created.",  # noqa: E501
    ] = None,
    custom_attribute_description: Annotated[
        str | None,
        "A seller-oriented description of the custom attribute definition. Required if visibility is `VISIBILITY_READ_ONLY` or `VISIBILITY_READ_WRITE_VALUES`. It may be shown as a tooltip in the Square UI.",  # noqa: E501
    ] = None,
    custom_attribute_key: Annotated[
        str | None,
        "The unique identifier for the custom attribute definition, formatted as 'application_id:simple_key' or a simple key with up to 60 characters including alphanumeric, periods, underscores, and hyphens.",  # noqa: E501
    ] = None,
    custom_attribute_name: Annotated[
        str | None,
        "The unique name for the custom attribute, used in API and UI. Required if visibility is not private.",  # noqa: E501
    ] = None,
    custom_attribute_schema: Annotated[
        dict[str, str] | None,
        "A JSON schema defining the data type of the custom attribute. It's required for creating a definition.",  # noqa: E501
    ] = None,
    custom_attribute_visibility: Annotated[
        str | None,
        "Sets the permission level to view and edit the custom attribute. Options: VISIBILITY_HIDDEN, VISIBILITY_READ_ONLY, VISIBILITY_READ_WRITE_VALUES.",  # noqa: E501
    ] = None,
    unique_request_id: Annotated[
        str | None,
        "A unique identifier for this request to ensure idempotency. It helps prevent duplicate operations by distinguishing each request as unique.",  # noqa: E501
    ] = None,
    updated_timestamp: Annotated[
        str | None,
        "The timestamp indicating when the custom attribute definition was created or last updated, in RFC 3339 format.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateLocationCustomAttributeDefinition'."
]:
    """Create a custom attribute for a Square location.

    Use this tool to define a custom attribute for a Square seller's location. Once defined, attributes like `key`, `visibility`, and `schema` can be set. This tool facilitates the creation of custom attributes that can later be linked to locations."""  # noqa: E501
    request_data = remove_none_values({
        "custom_attribute_definition": {
            "key": custom_attribute_key,
            "schema": custom_attribute_schema,
            "name": custom_attribute_name,
            "description": custom_attribute_description,
            "visibility": custom_attribute_visibility,
            "version": current_definition_version,
            "updated_at": updated_timestamp,
            "created_at": custom_attribute_definition_creation_timestamp,
        },
        "idempotency_key": unique_request_id,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/locations/custom-attribute-definitions",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def delete_location_custom_attribute(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str, "The identifier for the custom attribute definition to remove."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'DeleteLocationCustomAttributeDefinition'."
]:
    """Delete a custom attribute definition from a location.

    This tool deletes a location-related custom attribute definition from a Square seller account. It also removes the corresponding custom attribute from all locations. Only the definition owner can perform this action."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/locations/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def retrieve_location_custom_attribute_definition(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key for the custom attribute definition to retrieve, using the qualified key if not the owner.",  # noqa: E501
    ],
    current_custom_attribute_version: Annotated[
        int | None,
        "The version number of the custom attribute definition for consistent reads. Must match the current or higher version.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'RetrieveLocationCustomAttributeDefinition'."
]:
    """Retrieve a location's custom attribute definition.

    Use this tool to obtain the custom attribute definition for a specified location in a Square seller account. Ensure the attribute's visibility is set appropriately to access it."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/locations/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="GET",
        params=remove_none_values({"version": current_custom_attribute_version}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def update_location_custom_attribute(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute definition to update. It should match the key provided when the definition was created.",  # noqa: E501
    ],
    current_version: Annotated[
        int | None,
        "The current version of the custom attribute definition. Must be set to the latest version for updates to ensure optimistic concurrency.",  # noqa: E501
    ] = None,
    custom_attribute_creation_timestamp: Annotated[
        str | None, "The creation timestamp for the custom attribute definition in RFC 3339 format."
    ] = None,
    custom_attribute_definition_identifier: Annotated[
        str | None,
        "The unique identifier for the custom attribute definition. This can be a simple key or a qualified key (format: application_id:simple_key). It supports up to 60 alphanumeric characters, periods (.), underscores (_), and hyphens (-).",  # noqa: E501
    ] = None,
    custom_attribute_description: Annotated[
        str | None,
        "A description for the custom attribute definition, including any constraints. Required if the visibility is `VISIBILITY_READ_ONLY` or `VISIBILITY_READ_WRITE_VALUES`.",  # noqa: E501
    ] = None,
    custom_attribute_name: Annotated[
        str | None,
        "The unique name for the custom attribute definition. Required if visibility is set to specific levels.",  # noqa: E501
    ] = None,
    custom_attribute_schema: Annotated[
        dict[str, str] | None,
        "The JSON schema defining the data type of the custom attributes. Required when creating a definition. Refer to the Square Custom Attributes Overview for details.",  # noqa: E501
    ] = None,
    custom_attribute_visibility_level: Annotated[
        str | None,
        "Specifies permission level required to view the custom attribute definition. Options: VISIBILITY_HIDDEN, VISIBILITY_READ_ONLY, VISIBILITY_READ_WRITE_VALUES.",  # noqa: E501
    ] = None,
    unique_idempotency_key: Annotated[
        str | None,
        "A unique identifier for the request to ensure idempotency. Prevents duplicate processing of requests.",  # noqa: E501
    ] = None,
    update_timestamp: Annotated[
        str | None,
        "The timestamp indicating when the custom attribute definition was last updated, in RFC 3339 format.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'UpdateLocationCustomAttributeDefinition'."
]:
    """Updates a custom attribute definition for a location.

    Use this tool to update fields like `name`, `description`, `visibility`, or `schema` of a location-related custom attribute definition in a Square seller account. Only the definition owner can perform this update."""  # noqa: E501
    request_data = remove_none_values({
        "custom_attribute_definition": {
            "key": custom_attribute_definition_identifier,
            "schema": custom_attribute_schema,
            "name": custom_attribute_name,
            "description": custom_attribute_description,
            "visibility": custom_attribute_visibility_level,
            "version": current_version,
            "updated_at": update_timestamp,
            "created_at": custom_attribute_creation_timestamp,
        },
        "idempotency_key": unique_idempotency_key,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/locations/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def bulk_delete_location_custom_attributes(
    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 'BulkDeleteLocationCustomAttributes'."
]:
    """Delete custom attributes for multiple locations at once.

    This tool deletes custom attributes for locations in bulk. It is used when the visibility setting is 'VISIBILITY_READ_WRITE_VALUES', allowing the deletion of attributes owned by other applications.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKDELETELOCATIONCUSTOMATTRIBUTES"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["BULKDELETELOCATIONCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["BULKDELETELOCATIONCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/locations/custom-attributes/bulk-delete",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKDELETELOCATIONCUSTOMATTRIBUTES"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def bulk_upsert_location_custom_attributes(
    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 'BulkUpsertLocationCustomAttributes'."
]:
    """Bulk create or update custom attributes for multiple locations.

    This tool is used to create or update custom attributes for multiple locations in a Square seller account. It handles multiple upsert requests simultaneously, allowing you to efficiently manage location-specific data.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKUPSERTLOCATIONCUSTOMATTRIBUTES"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["BULKUPSERTLOCATIONCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["BULKUPSERTLOCATIONCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/locations/custom-attributes/bulk-upsert",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKUPSERTLOCATIONCUSTOMATTRIBUTES"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def retrieve_location_details(
    context: ToolContext,
    location_id: Annotated[
        str, 'The ID of the location to retrieve. Use "main" to return the main location details.'
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveLocation'."]:
    """Retrieve details of a specific business location.

    Use this tool to get information about a specific location by providing its ID. Specify 'main' to get details of the main location."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/locations/{location_id}".format(  # noqa: UP032
            location_id=location_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def update_location_squareup(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    location_id: Annotated[
        str | None,
        "The ID of the location to update on Square.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateLocation'."]:
    """Updates a business location on Square.

    Call this tool to update details of a specific business location using Square's Locations API. Provide the necessary location ID and data to modify.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATELOCATIONSQUAREUP"],
            "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 location_id:
        missing_params.append(("location_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\n" + REQUEST_BODY_SCHEMAS["UPDATELOCATIONSQUAREUP"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATELOCATIONSQUAREUP"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/locations/{location_id}".format(  # noqa: UP032
            location_id=location_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATELOCATIONSQUAREUP"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def list_location_custom_attributes(
    context: ToolContext,
    location_id: Annotated[str, "The ID of the target location to retrieve custom attributes for."],
    filter_by_visibility: Annotated[
        str | None,
        "Filters custom attribute definitions by visibility values. Options include 'ALL', 'READ', or 'READ_WRITE'.",  # noqa: E501
    ] = None,
    include_custom_attribute_definitions: Annotated[
        bool | None,
        "Set to true to include custom attribute definitions, providing name, description, and data type details. Defaults to false.",  # noqa: E501
    ] = False,
    maximum_results_limit: Annotated[
        int | None,
        "The maximum number of results to return in a single response. Valid range: 1 to 100. Default is 20.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None, "The cursor for fetching the next page of results in a paginated response."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListLocationCustomAttributes'."]:
    """Retrieve custom attributes for a specific location.

    This tool retrieves custom attributes associated with a specific location from Square. Optionally, custom attribute definitions can be included. It returns attributes visible to the requester, including those with varying visibility settings."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/locations/{location_id}/custom-attributes".format(  # noqa: UP032
            location_id=location_id
        ),
        method="GET",
        params=remove_none_values({
            "visibility_filter": filter_by_visibility,
            "limit": maximum_results_limit,
            "cursor": pagination_cursor,
            "with_definitions": include_custom_attribute_definitions,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def remove_location_custom_attribute(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute to delete, matching the key of a custom attribute definition in the Square seller account. Use the qualified key if not the definition owner.",  # noqa: E501
    ],
    location_id: Annotated[
        str,
        "The unique identifier for the target location where the custom attribute will be deleted.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteLocationCustomAttribute'."]:
    """Delete a custom attribute from a location.

    Use this tool to delete a custom attribute associated with a specific location. The attribute must have a visibility setting of `VISIBILITY_READ_WRITE_VALUES` if owned by another application."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/locations/{location_id}/custom-attributes/{key}".format(  # noqa: UP032
            location_id=location_id, key=custom_attribute_key
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def get_location_custom_attribute(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key for the custom attribute to retrieve. Must match the key in Square's custom attribute definition. Use qualified key if needed.",  # noqa: E501
    ],
    target_location_id: Annotated[
        str, "The ID of the target location to retrieve its custom attribute."
    ],
    custom_attribute_version: Annotated[
        int | None,
        "Specify the current version of the custom attribute for consistent data retrieval. A BAD_REQUEST error is returned if this version exceeds the current version.",  # noqa: E501
    ] = None,
    include_custom_attribute_definition: Annotated[
        bool | None,
        "Set to true to include details like name, description, and data type of the custom attribute definition. Default is false.",  # noqa: E501
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveLocationCustomAttribute'."]:
    """Retrieve a custom attribute for a specific location.

    This tool fetches a custom attribute linked with a specific location. It can also include the attribute definition if specified. Use this when you need details about a location's custom attributes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/locations/{location_id}/custom-attributes/{key}".format(  # noqa: UP032
            location_id=target_location_id, key=custom_attribute_key
        ),
        method="GET",
        params=remove_none_values({
            "with_definition": include_custom_attribute_definition,
            "version": custom_attribute_version,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def upsert_location_custom_attribute(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    location_id: Annotated[
        str | None,
        "The ID of the target location for which the custom attribute is to be created or updated. It should be a string that uniquely identifies the location within the Square seller account.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    custom_attribute_key: Annotated[
        str | None,
        "The key for the custom attribute to create or update. Must match an existing attribute definition key in the Square account. Use a qualified key if not the owner.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpsertLocationCustomAttribute'."]:
    """Create or update a custom attribute for a location.

    Use this tool to set or modify the value of a custom attribute for a specific location. Ensure the custom attribute is defined in the Square seller account. To work with attributes owned by other applications, the visibility must be set to 'VISIBILITY_READ_WRITE_VALUES'.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPSERTLOCATIONCUSTOMATTRIBUTE"],
            "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 location_id:
        missing_params.append(("location_id", "path"))
    if not custom_attribute_key:
        missing_params.append(("custom_attribute_key", "path"))

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

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

    # 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\n"
                + REQUEST_BODY_SCHEMAS["UPSERTLOCATIONCUSTOMATTRIBUTE"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/locations/{location_id}/custom-attributes/{key}".format(  # noqa: UP032
            location_id=location_id, key=custom_attribute_key
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPSERTLOCATIONCUSTOMATTRIBUTE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_WRITE"]))
async def create_loyalty_account(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateLoyaltyAccount'."]:
    """Create a loyalty account for a buyer.

    Create a loyalty account by providing a program ID and buyer's phone number mapping.

    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["CREATELOYALTYACCOUNT"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATELOYALTYACCOUNT"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATELOYALTYACCOUNT"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/loyalty/accounts",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATELOYALTYACCOUNT"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_READ"]))
async def search_loyalty_accounts(
    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 'SearchLoyaltyAccounts'."]:
    """Search for loyalty accounts by phone number or customer ID.

    This tool searches for loyalty accounts within a loyalty program. Use it when you need to find accounts based on a phone number or customer ID. To retrieve all accounts, you can leave the query empty. The results are sorted by the account creation date.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["SEARCHLOYALTYACCOUNTS"],
            "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\n" + REQUEST_BODY_SCHEMAS["SEARCHLOYALTYACCOUNTS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["SEARCHLOYALTYACCOUNTS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/loyalty/accounts/search",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SEARCHLOYALTYACCOUNTS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_READ"]))
async def retrieve_loyalty_account(
    context: ToolContext,
    loyalty_account_id: Annotated[str, "The unique identifier of the loyalty account to retrieve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveLoyaltyAccount'."]:
    """Retrieve details of a specific loyalty account.

    This tool fetches the information of a specific loyalty account using the account ID. Use it to access a member's loyalty details and activities."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/accounts/{account_id}".format(  # noqa: UP032
            account_id=loyalty_account_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_WRITE"]))
async def accumulate_loyalty_points(
    context: ToolContext,
    loyalty_account_id: Annotated[
        str, "The unique identifier for the target loyalty account to which points will be added."
    ],
    purchase_location_id: Annotated[
        str,
        "The ID of the location where the purchase was made, necessary for loyalty point accumulation.",  # noqa: E501
    ],
    unique_request_id: Annotated[
        str,
        "A unique string identifier for the `AccumulateLoyaltyPoints` request. Must be unique for each request to ensure idempotency.",  # noqa: E501
    ],
    loyalty_program_id: Annotated[
        str | None,
        "The ID of the loyalty program to add points to. Required to identify the correct program.",
    ] = None,
    order_id_for_accumulation: Annotated[
        str | None,
        "The ID of the order for which points are accumulated. Required if using the Orders API.",
    ] = None,
    points_to_accumulate: Annotated[
        int | None,
        "Specify the number of points to add to the loyalty account from the purchase event if not using the Orders API.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AccumulateLoyaltyPoints'."]:
    """Add points to a loyalty account for a purchase.

    This tool adds loyalty points to a customer's account following a purchase. It can be used with or without the Orders API. If using the Orders API, provide the `order_id` for automatic point calculation based on the program and promotions. If not, manually compute and provide the `points` to add."""  # noqa: E501
    request_data = remove_none_values({
        "accumulate_points": {
            "loyalty_program_id": loyalty_program_id,
            "points": points_to_accumulate,
            "order_id": order_id_for_accumulation,
        },
        "idempotency_key": unique_request_id,
        "location_id": purchase_location_id,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/accounts/{account_id}/accumulate".format(  # noqa: UP032
            account_id=loyalty_account_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_WRITE"]))
async def adjust_loyalty_points(
    context: ToolContext,
    loyalty_account_id: Annotated[str, "The unique ID of the buyer's loyalty account to adjust."],
    points_adjustment_amount: Annotated[
        int, "The number of points to add or remove from the buyer's account."
    ],
    unique_request_identifier: Annotated[
        str,
        "A unique string to identify this loyalty points adjustment request. Must be unique for each request to prevent duplicates.",  # noqa: E501
    ],
    adjustment_reason: Annotated[
        str | None,
        "The reason for adjusting the loyalty points, such as 'customer courtesy' or 'error correction'.",  # noqa: E501
    ] = None,
    allow_negative_balance: Annotated[
        bool | None,
        "Set to true to allow a negative balance after point subtraction. Defaults to false if not specified.",  # noqa: E501
    ] = None,
    loyalty_program_id: Annotated[
        str | None, "The Square-assigned ID of the loyalty program to adjust points for."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AdjustLoyaltyPoints'."]:
    """Manually adjust loyalty points for a buyer's account.

    Use this tool to manually add or subtract loyalty points from a buyer's account when automatic accumulation is not applied. Ideal for scenarios requiring manual point corrections."""  # noqa: E501
    request_data = remove_none_values({
        "idempotency_key": unique_request_identifier,
        "adjust_points": {
            "loyalty_program_id": loyalty_program_id,
            "points": points_adjustment_amount,
            "reason": adjustment_reason,
        },
        "allow_negative_balance": allow_negative_balance,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/accounts/{account_id}/adjust".format(  # noqa: UP032
            account_id=loyalty_account_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_READ"]))
async def search_loyalty_events(
    context: ToolContext,
    end_time: Annotated[
        str | None,
        "Datetime in RFC 3339 format indicating when the time range ends for the search.",
    ] = None,
    location_ids_for_events_query: Annotated[
        list[str] | None,
        "List of Location IDs for querying loyalty events. Multiple IDs use OR logic.",
    ] = None,
    loyalty_account_id: Annotated[
        str | None, "The ID of the loyalty account associated with the events to filter."
    ] = None,
    loyalty_event_types: Annotated[
        list[str] | None,
        "Array of loyalty event types to filter results. Multiple values are combined using OR logic. Refer to LoyaltyEventType for options.",  # noqa: E501
    ] = None,
    max_results_count: Annotated[
        int | None,
        "The maximum number of loyalty events to include in the response. Defaults to 30.",
    ] = None,
    order_id_filter: Annotated[
        str | None, "The ID of the order associated with the event to filter results."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "Provide a pagination cursor to retrieve the next set of results from a previous query.",
    ] = None,
    start_datetime: Annotated[
        str | None,
        "A datetime value in RFC 3339 format indicating when the time range starts for the search query.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchLoyaltyEvents'."]:
    """Retrieve and search for Square loyalty events.

    This tool allows you to search for loyalty events within a Square loyalty program, which logs events such as points earned, redeemed, or expired. Use this tool to get details of these events sorted by creation date."""  # noqa: E501
    request_data = remove_none_values({
        "query": {
            "filter": {
                "loyalty_account_filter": {"loyalty_account_id": loyalty_account_id},
                "type_filter": {"types": loyalty_event_types},
                "date_time_filter": {
                    "created_at": {"start_at": start_datetime, "end_at": end_time}
                },
                "location_filter": {"location_ids": location_ids_for_events_query},
                "order_filter": {"order_id": order_id_filter},
            }
        },
        "limit": max_results_count,
        "cursor": pagination_cursor,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/events/search",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_READ"]))
async def get_loyalty_program(
    context: ToolContext,
    loyalty_program_identifier: Annotated[
        str,
        "The ID of the loyalty program or the keyword 'main'. Use to retrieve the loyalty program of a seller.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveLoyaltyProgram'."]:
    """Retrieve the loyalty program details for a seller.

    Use this tool to get information about the loyalty program in a seller's account. You can specify the program by its ID or use the keyword 'main' to access the primary program. This is useful for understanding how buyers earn points and redeem rewards in the loyalty program."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/programs/{program_id}".format(  # noqa: UP032
            program_id=loyalty_program_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_READ"]))
async def calculate_loyalty_points(
    context: ToolContext,
    loyalty_program_id: Annotated[
        str, "The ID of the loyalty program, defining the rules for accruing points."
    ],
    currency_code: Annotated[
        str | None,
        "Indicates the associated currency for an amount of money using ISO 4217 codes. For example, 'USD' for United States Dollar.",  # noqa: E501
    ] = None,
    loyalty_account_id: Annotated[
        str | None,
        "The ID of the target loyalty account. Optionally specify if using the Orders API. Determines promotion point eligibility based on trigger limits.",  # noqa: E501
    ] = None,
    order_id: Annotated[
        str | None,
        "The Order ID used to calculate points. Provide this if using the Orders API. Otherwise, use transaction_amount_money.",  # noqa: E501
    ] = None,
    transaction_amount_in_smallest_denomination: Annotated[
        int | None,
        "The amount of money for the transaction, given in the smallest denomination of the currency (e.g., cents for USD).",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CalculateLoyaltyPoints'."]:
    """Calculate loyalty points a buyer can earn from a purchase.

    Use this tool to determine the number of loyalty points to reward a buyer after a purchase. Provide either the `order_id` or `transaction_amount_money` to calculate points based on the order or purchase amount, respectively. Points calculation considers the base loyalty program, and possibly a loyalty promotion if applicable."""  # noqa: E501
    request_data = remove_none_values({
        "order_id": order_id,
        "transaction_amount_money": {
            "amount": transaction_amount_in_smallest_denomination,
            "currency": currency_code,
        },
        "loyalty_account_id": loyalty_account_id,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/programs/{program_id}/calculate".format(  # noqa: UP032
            program_id=loyalty_program_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_READ"]))
async def list_loyalty_promotions(
    context: ToolContext,
    loyalty_program_id: Annotated[
        str,
        "The ID of the loyalty program to list promotions for. Obtain via `RetrieveLoyaltyProgram` using `main`.",  # noqa: E501
    ],
    max_results_per_page: Annotated[
        int | None,
        "Specify the maximum number of promotions to return in a single response. Must be between 1 and 30, defaults to 30.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None, "The cursor for retrieving the next page of results from the previous call."
    ] = None,
    promotion_status_filter: Annotated[
        str | None,
        "Specify the status to filter loyalty promotions. Options include ACTIVE, ENDED, CANCELED, SCHEDULED. Returns promotions with the specified status.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListLoyaltyPromotions'."]:
    """Retrieve promotions from a specific loyalty program.

    Call this tool to list loyalty promotions associated with a specific loyalty program, sorted by the most recent creation date."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/programs/{program_id}/promotions".format(  # noqa: UP032
            program_id=loyalty_program_id
        ),
        method="GET",
        params=remove_none_values({
            "status": promotion_status_filter,
            "cursor": pagination_cursor,
            "limit": max_results_per_page,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_WRITE"]))
async def create_loyalty_promotion(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    loyalty_program_id: Annotated[
        str | None,
        "The ID of the loyalty program to associate with the promotion. Use the RetrieveLoyaltyProgram endpoint with the 'main' keyword to obtain it.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateLoyaltyPromotion'."]:
    """Create a new loyalty promotion for a program.

    This tool creates a loyalty promotion within a specified loyalty program, allowing buyers to earn additional points. It sets the promotion to either 'ACTIVE' or 'SCHEDULED' based on the available time setting. A program can have up to 10 active or scheduled promotions.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATELOYALTYPROMOTION"],
            "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 loyalty_program_id:
        missing_params.append(("loyalty_program_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\n" + REQUEST_BODY_SCHEMAS["CREATELOYALTYPROMOTION"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATELOYALTYPROMOTION"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/loyalty/programs/{program_id}/promotions".format(  # noqa: UP032
            program_id=loyalty_program_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATELOYALTYPROMOTION"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_READ"]))
async def retrieve_loyalty_promotion(
    context: ToolContext,
    loyalty_program_id: Annotated[
        str,
        "The ID of the base loyalty program. Obtain this by calling the RetrieveLoyaltyProgram API using the 'main' keyword.",  # noqa: E501
    ],
    promotion_id: Annotated[
        str,
        "The ID of the loyalty promotion to retrieve. This ID is necessary to specify which promotion details to access.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveLoyaltyPromotion'."]:
    """Retrieve details of a specific loyalty promotion.

    Use this tool to obtain information about a particular loyalty promotion by providing the program and promotion IDs. It should be called when you need to view the specifics of a loyalty promotion."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/programs/{program_id}/promotions/{promotion_id}".format(  # noqa: UP032
            promotion_id=promotion_id, program_id=loyalty_program_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_WRITE"]))
async def cancel_loyalty_promotion(
    context: ToolContext,
    loyalty_program_id: Annotated[
        str,
        "The unique ID of the base loyalty program to identify which program the promotion is associated with.",  # noqa: E501
    ],
    loyalty_promotion_id: Annotated[
        str, "The ID of the loyalty promotion to cancel, which can be 'ACTIVE' or 'SCHEDULED'."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelLoyaltyPromotion'."]:
    """Cancels an active or scheduled loyalty promotion early.

    Use this tool to cancel a loyalty promotion that is currently active or scheduled, either earlier than its end date or if no end date is specified."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/programs/{program_id}/promotions/{promotion_id}/cancel".format(  # noqa: UP032
            promotion_id=loyalty_promotion_id, program_id=loyalty_program_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_WRITE"]))
async def create_loyalty_reward(
    context: ToolContext,
    loyalty_account_id: Annotated[
        str, "The Square-assigned ID of the loyalty account to which the reward belongs."
    ],
    reward_tier_id: Annotated[
        str, "The Square-assigned ID of the reward tier used to create the loyalty reward."
    ],
    unique_request_key: Annotated[
        str,
        "A unique string that identifies this CreateLoyaltyReward request. Must be unique for each request to prevent duplication.",  # noqa: E501
    ],
    loyalty_reward_id: Annotated[
        str | None, "The Square-assigned ID of the loyalty reward to be created or referenced."
    ] = None,
    loyalty_reward_status: Annotated[
        str | None,
        "The status of the loyalty reward. Possible values: 'ISSUED', 'REDEEMED', 'DELETED'.",
    ] = None,
    number_of_loyalty_points: Annotated[
        int | None, "The number of loyalty points to use for creating the reward."
    ] = None,
    order_id: Annotated[
        str | None,
        "The Square-assigned ID of the order to which the reward is attached. Optional if no order is involved.",  # noqa: E501
    ] = None,
    reward_creation_timestamp: Annotated[
        str | None,
        "The timestamp when the reward was created, in RFC 3339 format. This indicates the date and time of reward creation.",  # noqa: E501
    ] = None,
    reward_last_updated_timestamp: Annotated[
        str | None, "The timestamp when the reward was last updated, in RFC 3339 format."
    ] = None,
    reward_redeemed_timestamp: Annotated[
        str | None,
        "The timestamp when the reward was redeemed, in RFC 3339 format. This value indicates the moment the reward was used.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateLoyaltyReward'."]:
    """Create a loyalty reward by locking points for a customer.

    This tool creates a loyalty reward by using a specified reward tier ID to lock points. Optionally, it can add the reward and related discount to an existing order if an order ID is provided. After creation, the points are locked and unavailable for redemption."""  # noqa: E501
    request_data = remove_none_values({
        "reward": {
            "id": loyalty_reward_id,
            "status": loyalty_reward_status,
            "loyalty_account_id": loyalty_account_id,
            "reward_tier_id": reward_tier_id,
            "points": number_of_loyalty_points,
            "order_id": order_id,
            "created_at": reward_creation_timestamp,
            "updated_at": reward_last_updated_timestamp,
            "redeemed_at": reward_redeemed_timestamp,
        },
        "idempotency_key": unique_request_key,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/rewards",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_READ"]))
async def search_loyalty_rewards(
    context: ToolContext,
    loyalty_account_id: Annotated[
        str | None,
        "The ID of the loyalty account to which the loyalty reward belongs. Required if using a query object.",  # noqa: E501
    ] = None,
    maximum_results: Annotated[
        int | None, "The maximum number of loyalty reward results to return. Default is 30."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A cursor for pagination to retrieve the next set of results from a previous call.",
    ] = None,
    reward_status: Annotated[
        str | None, "The status of the loyalty reward. Options: ISSUED, REDEEMED, DELETED."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchLoyaltyRewards'."]:
    """Search for loyalty rewards with optional filters.

    This tool searches for loyalty rewards and can retrieve results for all loyalty accounts when no filters are applied. If a query object is used, the `loyalty_account_id` is required, while `status` is optional. Results are sorted by `updated_at` in descending order."""  # noqa: E501
    request_data = remove_none_values({
        "query": {"loyalty_account_id": loyalty_account_id, "status": reward_status},
        "limit": maximum_results,
        "cursor": pagination_cursor,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/rewards/search",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_WRITE"]))
async def delete_loyalty_reward(
    context: ToolContext,
    loyalty_reward_id: Annotated[
        str,
        "The ID of the loyalty reward to delete, returning points to the account. Cannot delete redeemed rewards.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteLoyaltyReward'."]:
    """Deletes a loyalty reward and restores points to the account.

    This tool deletes a specified loyalty reward, restoring the corresponding points to the user's account. It also updates any associated order to remove the reward and related discounts, if an order ID was specified upon reward creation. Cannot be used on rewards that have been redeemed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/rewards/{reward_id}".format(  # noqa: UP032
            reward_id=loyalty_reward_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_READ"]))
async def get_loyalty_reward(
    context: ToolContext,
    loyalty_reward_id: Annotated[
        str,
        "The ID of the loyalty reward to retrieve. This is required to fetch details about a specific reward for a customer.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveLoyaltyReward'."]:
    """Retrieve details of a specific loyalty reward.

    Use this tool to fetch details about a particular loyalty reward by providing the reward ID. This can help understand customer incentives or verify reward status."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/rewards/{reward_id}".format(  # noqa: UP032
            reward_id=loyalty_reward_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["LOYALTY_WRITE"]))
async def redeem_loyalty_reward(
    context: ToolContext,
    idempotency_key: Annotated[
        str,
        "A unique string to identify this `RedeemLoyaltyReward` request, ensuring uniqueness for each request.",  # noqa: E501
    ],
    location_id: Annotated[str, "The ID of the location where the loyalty reward is redeemed."],
    loyalty_reward_id: Annotated[
        str,
        "The unique ID of the loyalty reward to redeem. Required for specifying which reward to process.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RedeemLoyaltyReward'."]:
    """Redeem a loyalty reward for a customer purchase.

    Use this tool to mark a loyalty reward as redeemed after a customer has completed a purchase. Suitable for use when processing orders outside the Orders API. Once redeemed, rewards cannot be reversed."""  # noqa: E501
    request_data = remove_none_values({
        "idempotency_key": idempotency_key,
        "location_id": location_id,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/loyalty/rewards/{reward_id}/redeem".format(  # noqa: UP032
            reward_id=loyalty_reward_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def get_merchant_details(
    context: ToolContext,
    previous_response_cursor: Annotated[
        int | None, "The cursor generated by the previous response for fetching subsequent pages."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListMerchants'."]:
    """Retrieve details about a specific merchant.

    This tool provides information about the merchant associated with a given access token, returning a list with a single merchant object. Useful for acquiring merchant details when the merchant ID is unknown."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/merchants",
        method="GET",
        params=remove_none_values({"cursor": previous_response_cursor}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def list_merchant_custom_attribute_definitions(
    context: ToolContext,
    max_results_per_page: Annotated[
        int | None,
        "The maximum number of results to return in a single response, ranging from 1 to 100. Default is 20.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None, "The cursor for retrieving the next page of results from a previous response."
    ] = None,
    visibility_filter_option: Annotated[
        str | None,
        "Specify the visibility level of the CustomAttributeDefinition results. Options: ALL, READ, READ_WRITE.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ListMerchantCustomAttributeDefinitions'."
]:
    """Retrieve merchant custom attribute definitions.

    Fetch all merchant-related custom attribute definitions for a Square seller account, including those visible to the requesting application, even if created by other applications."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/merchants/custom-attribute-definitions",
        method="GET",
        params=remove_none_values({
            "visibility_filter": visibility_filter_option,
            "limit": max_results_per_page,
            "cursor": pagination_cursor,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def create_merchant_custom_attribute(
    context: ToolContext,
    attribute_visibility_level: Annotated[
        str | None,
        "Specify the permission level required to view and edit the custom attribute: VISIBILITY_HIDDEN, VISIBILITY_READ_ONLY, or VISIBILITY_READ_WRITE_VALUES.",  # noqa: E501
    ] = None,
    creation_timestamp: Annotated[
        str | None, "The creation timestamp of the custom attribute definition in RFC 3339 format."
    ] = None,
    custom_attribute_definition_description: Annotated[
        str | None,
        "The seller-oriented description for the custom attribute definition, including any constraints to observe. Required if visibility is 'VISIBILITY_READ_ONLY' or 'VISIBILITY_READ_WRITE_VALUES'.",  # noqa: E501
    ] = None,
    custom_attribute_definition_version: Annotated[
        int | None,
        "Read-only version of the custom attribute definition, required for updates to ensure optimistic concurrency control.",  # noqa: E501
    ] = None,
    custom_attribute_json_schema: Annotated[
        dict[str, str] | None,
        "The JSON schema for the custom attribute definition, which determines the data type of the corresponding custom attributes. Required for creation.",  # noqa: E501
    ] = None,
    custom_attribute_key: Annotated[
        str | None,
        "The unique identifier for the custom attribute. Format: application_id:simple_key. Up to 60 characters, including '.', '_', '-'. Must be unique per application, seller, and resource type.",  # noqa: E501
    ] = None,
    custom_attribute_name: Annotated[
        str | None,
        "The name of the custom attribute definition. It must be unique within the seller/application pair and is required if visibility is set to read-only or read-write.",  # noqa: E501
    ] = None,
    idempotency_key: Annotated[
        str | None,
        "A unique string identifier for the request to ensure it can be retried safely without duplication. Useful in scenarios with network issues or retries.",  # noqa: E501
    ] = None,
    updated_timestamp: Annotated[
        str | None,
        "The timestamp of the last update or creation of the custom attribute definition, in RFC 3339 format.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateMerchantCustomAttributeDefinition'."
]:
    """Create a custom attribute for a Square merchant account.

    Use this tool to define a custom attribute for a Square seller account, specifying its key, visibility, schema, and other properties. Once defined, it allows associating custom attributes with merchants."""  # noqa: E501
    request_data = remove_none_values({
        "custom_attribute_definition": {
            "key": custom_attribute_key,
            "schema": custom_attribute_json_schema,
            "name": custom_attribute_name,
            "description": custom_attribute_definition_description,
            "visibility": attribute_visibility_level,
            "version": custom_attribute_definition_version,
            "updated_at": updated_timestamp,
            "created_at": creation_timestamp,
        },
        "idempotency_key": idempotency_key,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/merchants/custom-attribute-definitions",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def delete_merchant_custom_attribute(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str, "The key of the custom attribute definition to be deleted from the Square account."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'DeleteMerchantCustomAttributeDefinition'."
]:
    """Delete a custom attribute definition for a Square merchant.

    Deletes a merchant-specific custom attribute definition from a Square seller account, including the associated custom attribute. Only the definition owner can perform this deletion."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/merchants/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def get_merchant_custom_attribute_definition(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute definition to retrieve. Use the qualified key if not the definition owner.",  # noqa: E501
    ],
    custom_attribute_version: Annotated[
        int | None,
        "Specifies the current version of the custom attribute definition for retrieving the most up-to-date data. If the specified version is higher than the current, a 'BAD_REQUEST' error is returned.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'RetrieveMerchantCustomAttributeDefinition'."
]:
    """Retrieves custom attribute definition for a Square seller account.

    This tool retrieves a custom attribute definition related to a merchant from a Square seller account. It is useful for obtaining detailed attribute information created by other applications, provided the visibility settings are set to allow access."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/merchants/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="GET",
        params=remove_none_values({"version": custom_attribute_version}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def update_merchant_custom_attribute_definition(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute definition to update. A string value that identifies the specific custom attribute definition.",  # noqa: E501
    ],
    creation_timestamp: Annotated[
        str | None,
        "The timestamp indicating when the custom attribute definition was created, in RFC 3339 format.",  # noqa: E501
    ] = None,
    current_definition_version: Annotated[
        int | None,
        "Specify the current version of the custom attribute definition to ensure optimistic concurrency. Must match the latest version as stale versions are rejected.",  # noqa: E501
    ] = None,
    custom_attribute_definition_identifier: Annotated[
        str | None,
        "The identifier for the custom attribute definition. It may be a simple key or a qualified key formatted as application_id:simple_key with up to 60 alphanumeric characters, periods, underscores, and hyphens.",  # noqa: E501
    ] = None,
    custom_attribute_definition_name: Annotated[
        str | None,
        "The unique name for the custom attribute definition, required if the visibility is 'VISIBILITY_READ_ONLY' or 'VISIBILITY_READ_WRITE_VALUES'.",  # noqa: E501
    ] = None,
    custom_attribute_description: Annotated[
        str | None,
        "A seller-oriented description for the custom attribute definition, detailing any constraints. Required for certain visibility settings.",  # noqa: E501
    ] = None,
    custom_attribute_json_schema: Annotated[
        dict[str, str] | None,
        "The JSON schema defining data type and structure for the custom attribute definition. Necessary when creating a definition.",  # noqa: E501
    ] = None,
    custom_attribute_visibility: Annotated[
        str | None,
        "The permission level required to view this custom attribute definition. Options: 'VISIBILITY_HIDDEN', 'VISIBILITY_READ_ONLY', 'VISIBILITY_READ_WRITE_VALUES'.",  # noqa: E501
    ] = None,
    idempotency_key: Annotated[
        str | None,
        "A unique identifier for the request to ensure idempotency. It prevents duplicate processing.",  # noqa: E501
    ] = None,
    last_updated_timestamp: Annotated[
        str | None,
        "The timestamp of the last update to the custom attribute definition in RFC 3339 format.",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'UpdateMerchantCustomAttributeDefinition'."
]:
    """Updates a merchant's custom attribute definition.

    Use this tool to update fields such as `name`, `description`, `visibility`, or the `schema` of a `Selection` type for a Square seller's custom attribute definition. Only the definition owner is authorized to make these updates."""  # noqa: E501
    request_data = remove_none_values({
        "custom_attribute_definition": {
            "key": custom_attribute_definition_identifier,
            "schema": custom_attribute_json_schema,
            "name": custom_attribute_definition_name,
            "description": custom_attribute_description,
            "visibility": custom_attribute_visibility,
            "version": current_definition_version,
            "updated_at": last_updated_timestamp,
            "created_at": creation_timestamp,
        },
        "idempotency_key": idempotency_key,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/merchants/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def bulk_delete_merchant_custom_attributes(
    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 'BulkDeleteMerchantCustomAttributes'."
]:
    """Bulk delete custom attributes for a merchant.

    Use this tool to delete custom attributes for a merchant in bulk. It can only delete attributes with 'VISIBILITY_READ_WRITE_VALUES' if owned by another application.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKDELETEMERCHANTCUSTOMATTRIBUTES"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["BULKDELETEMERCHANTCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["BULKDELETEMERCHANTCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/merchants/custom-attributes/bulk-delete",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKDELETEMERCHANTCUSTOMATTRIBUTES"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def upsert_merchant_custom_attributes_bulk(
    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 'BulkUpsertMerchantCustomAttributes'."
]:
    """Bulk creates or updates custom attributes for a merchant.

    Use this tool to set multiple custom attributes for a merchant based on predefined definitions in a Square seller account. It processes 1 to 25 upsert requests, each providing a unique ID, merchant ID, and custom attribute data. Returns mapped responses with the ID of each request.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPSERTMERCHANTCUSTOMATTRIBUTESBULK"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["UPSERTMERCHANTCUSTOMATTRIBUTESBULK"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["UPSERTMERCHANTCUSTOMATTRIBUTESBULK"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/merchants/custom-attributes/bulk-upsert",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPSERTMERCHANTCUSTOMATTRIBUTESBULK"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def retrieve_merchant_info(
    context: ToolContext,
    merchant_id: Annotated[
        str,
        "The ID of the merchant to retrieve. Use 'me' to get the merchant accessible to this call.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveMerchant'."]:
    """Retrieve merchant details using their ID.

    Use this tool to get comprehensive information about a merchant by providing the merchant ID. This is useful for obtaining merchant-specific data, such as business details or other relevant merchant information."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/merchants/{merchant_id}".format(  # noqa: UP032
            merchant_id=merchant_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def list_merchant_custom_attributes(
    context: ToolContext,
    merchant_identifier: Annotated[
        str,
        "The unique identifier for the target merchant whose custom attributes are to be listed.",
    ],
    filter_custom_attribute_visibility: Annotated[
        str | None,
        "Filters custom attribute definition results by visibility values. Valid options are 'ALL', 'READ', or 'READ_WRITE'.",  # noqa: E501
    ] = None,
    include_custom_attribute_definitions: Annotated[
        bool | None,
        "Set to true to include custom attribute definitions, providing details like name, description, and data type.",  # noqa: E501
    ] = False,
    maximum_results_limit: Annotated[
        int | None,
        "The maximum number of results to return in a single response. Minimum is 1, maximum is 100, defaults to 20.",  # noqa: E501
    ] = None,
    paging_cursor: Annotated[
        str | None,
        "The cursor from the previous paged response to retrieve the next set of results.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListMerchantCustomAttributes'."]:
    """Retrieve custom attributes for a specified merchant.

    This tool retrieves the custom attributes associated with a specific merchant, including those visible to the requesting application and owned by other applications. Useful for managing and analyzing merchant-specific data."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/merchants/{merchant_id}/custom-attributes".format(  # noqa: UP032
            merchant_id=merchant_identifier
        ),
        method="GET",
        params=remove_none_values({
            "visibility_filter": filter_custom_attribute_visibility,
            "limit": maximum_results_limit,
            "cursor": paging_cursor,
            "with_definitions": include_custom_attribute_definitions,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def remove_merchant_attribute(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute to delete. Use the qualified key if the attribute is owned by another application.",  # noqa: E501
    ],
    merchant_id: Annotated[
        str, "The unique identifier of the merchant whose custom attribute is being deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteMerchantCustomAttribute'."]:
    """Delete a custom attribute from a merchant.

    Use this tool to remove a custom attribute associated with a merchant in Squareup. Deletion is possible if the attribute's visibility setting is 'VISIBILITY_READ_WRITE_VALUES' when owned by another application."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/merchants/{merchant_id}/custom-attributes/{key}".format(  # noqa: UP032
            merchant_id=merchant_id, key=custom_attribute_key
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def retrieve_merchant_custom_attribute(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute to retrieve, matching the `key` of a custom attribute definition in the Square seller account.",  # noqa: E501
    ],
    merchant_identifier: Annotated[
        str, "The unique identifier of the target merchant to retrieve the custom attribute for."
    ],
    custom_attribute_version: Annotated[
        int | None,
        "Integer value representing the custom attribute's current version for consistent reads. If specified version exceeds current, an error is returned.",  # noqa: E501
    ] = None,
    include_custom_attribute_definition: Annotated[
        bool | None,
        "Set to true to return the custom attribute definition, including name, description, and data type. Defaults to false.",  # noqa: E501
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveMerchantCustomAttribute'."]:
    """Retrieve a custom attribute associated with a merchant.

    Use this tool to get a specific custom attribute of a merchant, optionally including the attribute's definition. This can be used when you need to access merchant-specific settings or details defined by custom attributes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/merchants/{merchant_id}/custom-attributes/{key}".format(  # noqa: UP032
            merchant_id=merchant_identifier, key=custom_attribute_key
        ),
        method="GET",
        params=remove_none_values({
            "with_definition": include_custom_attribute_definition,
            "version": custom_attribute_version,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_WRITE"]))
async def upsert_merchant_custom_attribute(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    merchant_id: Annotated[
        str | None,
        "The unique ID of the target merchant for whom the custom attribute is being created or updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    custom_attribute_key: Annotated[
        str | None,
        "The key of the custom attribute to create or update. This key must match the key of a custom attribute definition in the Square seller account. If the requester is not the definition owner, use the qualified key.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpsertMerchantCustomAttribute'."]:
    """Create or update a custom attribute for a merchant.

    This tool creates or updates a custom attribute for a specified merchant using the Square API. It's used to set the value of a custom attribute defined in a Square seller account. The attribute can be owned by another application if the visibility setting allows read-write operations.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPSERTMERCHANTCUSTOMATTRIBUTE"],
            "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 merchant_id:
        missing_params.append(("merchant_id", "path"))
    if not custom_attribute_key:
        missing_params.append(("custom_attribute_key", "path"))

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

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

    # 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\n"
                + REQUEST_BODY_SCHEMAS["UPSERTMERCHANTCUSTOMATTRIBUTE"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/merchants/{merchant_id}/custom-attributes/{key}".format(  # noqa: UP032
            merchant_id=merchant_id, key=custom_attribute_key
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPSERTMERCHANTCUSTOMATTRIBUTE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ"]))
async def retrieve_location_settings(
    context: ToolContext,
    location_id: Annotated[
        str, "The unique ID of the location for which to retrieve the checkout page settings."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveLocationSettings'."]:
    """Retrieve settings for a Square-hosted checkout page location.

    Call this tool to get the specific settings configured for a location's Square-hosted checkout page. Useful for understanding location-specific checkout configurations."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/online-checkout/location-settings/{location_id}".format(  # noqa: UP032
            location_id=location_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup", scopes=["MERCHANT_PROFILE_READ", "MERCHANT_PROFILE_WRITE"]
    )
)
async def update_checkout_location_settings(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    location_id: Annotated[
        str | None,
        "The unique identifier of the location to update settings for the Square-hosted checkout page.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateLocationSettings'."]:
    """Update location settings for a Square-hosted checkout page.

    This tool updates the settings for a specific location's checkout page hosted by Square. It should be called when there is a need to make changes to the location-specific configuration for an online checkout.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATECHECKOUTLOCATIONSETTINGS"],
            "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 location_id:
        missing_params.append(("location_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\n"
                + REQUEST_BODY_SCHEMAS["UPDATECHECKOUTLOCATIONSETTINGS"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["UPDATECHECKOUTLOCATIONSETTINGS"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/online-checkout/location-settings/{location_id}".format(  # noqa: UP032
            location_id=location_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATECHECKOUTLOCATIONSETTINGS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup", scopes=["PAYMENT_METHODS_READ", "MERCHANT_PROFILE_READ"]
    )
)
async def retrieve_square_merchant_settings(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveMerchantSettings'."]:
    """Retrieve Square merchant settings for checkout pages.

    Use this tool to get the settings for a merchant's Square-hosted checkout page, which could be useful for understanding or managing the checkout configuration."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/online-checkout/merchant-settings",
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup",
        scopes=["PAYMENT_METHODS_READ", "MERCHANT_PROFILE_READ", "MERCHANT_PROFILE_WRITE"],
    )
)
async def update_merchant_settings(
    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 'UpdateMerchantSettings'."]:
    """Updates Square-hosted checkout page settings for a merchant.

    Use this tool to update the settings of a Square-hosted checkout page at the merchant level, ensuring the checkout page reflects the desired configurations.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEMERCHANTSETTINGS"],
            "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\n" + REQUEST_BODY_SCHEMAS["UPDATEMERCHANTSETTINGS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATEMERCHANTSETTINGS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/online-checkout/merchant-settings",
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEMERCHANTSETTINGS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_READ"]))
async def list_payment_links(
    context: ToolContext,
    pagination_cursor: Annotated[
        str | None,
        "A pagination cursor from a previous call to fetch the next set of results. If not provided, returns the first page.",  # noqa: E501
    ] = None,
    results_per_page_limit: Annotated[
        int | None,
        "Advisory limit on number of results per page. Ignored if negative, zero, or over 1000. Defaults to 100.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListPaymentLinks'."]:
    """Lists all online payment links for Squareup.

    Use this tool to retrieve a list of all payment links created in Squareup's online checkout system."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/online-checkout/payment-links",
        method="GET",
        params=remove_none_values({"cursor": pagination_cursor, "limit": results_per_page_limit}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup", scopes=["PAYMENTS_WRITE", "ORDERS_READ", "ORDERS_WRITE"]
    )
)
async def create_payment_link(
    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 'CreatePaymentLink'."]:
    """Create a Square-hosted checkout page for payments.

    Use this tool to generate a Square-hosted payment link which can be shared with buyers to facilitate payments for goods and services. Ideal for businesses needing a simple online payment solution.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEPAYMENTLINK"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATEPAYMENTLINK"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATEPAYMENTLINK"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/online-checkout/payment-links",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEPAYMENTLINK"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_READ", "ORDERS_WRITE"]))
async def delete_payment_link(
    context: ToolContext,
    payment_link_id: Annotated[
        str,
        "The unique identifier of the payment link to delete. This ID specifies which payment link should be removed.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeletePaymentLink'."]:
    """Deletes a specified payment link.

    Use this tool to delete a payment link by its ID when it is no longer needed or was created in error."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/online-checkout/payment-links/{id}".format(  # noqa: UP032
            id=payment_link_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_READ"]))
async def retrieve_payment_link(
    context: ToolContext,
    payment_link_id: Annotated[str, "The unique ID of the payment link to retrieve."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrievePaymentLink'."]:
    """Retrieve a payment link using its ID.

    This tool retrieves details of a payment link by its unique ID. It's useful for getting information about a specific payment link set up in your Square account."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/online-checkout/payment-links/{id}".format(  # noqa: UP032
            id=payment_link_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup", scopes=["PAYMENTS_WRITE", "ORDERS_READ", "ORDERS_WRITE"]
    )
)
async def update_payment_link(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    payment_link_id: Annotated[
        str | None,
        "The unique identifier of the payment link to update. Required to specify which link will be modified.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdatePaymentLink'."]:
    """Update details of an existing payment link.

    Use this tool to update fields like `description`, `checkout_options`, and `pre_populated_data` of a payment link. You cannot update fields such as `order_id`, `version`, `URL`, or `timestamp`.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEPAYMENTLINK"],
            "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 payment_link_id:
        missing_params.append(("payment_link_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\n" + REQUEST_BODY_SCHEMAS["UPDATEPAYMENTLINK"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATEPAYMENTLINK"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/online-checkout/payment-links/{id}".format(  # noqa: UP032
            id=payment_link_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEPAYMENTLINK"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_WRITE"]))
async def create_order_for_purchase(
    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 'CreateOrder'."]:
    """Creates a new order for purchase with product details.

    Use this tool to create a new order, specifying products and settings for purchase. This tool is ideal for initiating a purchase order that includes specific product information.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEORDERFORPURCHASE"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATEORDERFORPURCHASE"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATEORDERFORPURCHASE"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/orders",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEORDERFORPURCHASE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_READ"]))
async def retrieve_multiple_orders(
    context: ToolContext,
    order_ids_list: Annotated[
        list[str], "An array of order IDs to retrieve, with a maximum of 100 per request."
    ],
    location_id: Annotated[
        str | None, "Optional location ID for the orders. Omit to use the current merchant's ID."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BatchRetrieveOrders'."]:
    """Retrieve multiple orders using their IDs.

    This tool retrieves details of multiple orders from Square using their IDs. It ignores any non-existent order IDs, ensuring no errors are returned for invalid IDs."""  # noqa: E501
    request_data = remove_none_values({"location_id": location_id, "order_ids": order_ids_list})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/orders/batch-retrieve",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def preview_order_pricing(
    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 'CalculateOrder'."]:
    """Preview order pricing without creating an order.

    Call this tool to obtain an estimate of the pricing for an order without the need to actually create the order. Useful for displaying potential costs to customers before finalizing the transaction.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["PREVIEWORDERPRICING"],
            "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\n" + REQUEST_BODY_SCHEMAS["PREVIEWORDERPRICING"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["PREVIEWORDERPRICING"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/orders/calculate",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["PREVIEWORDERPRICING"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_WRITE"]))
async def clone_order_draft(
    context: ToolContext,
    order_id_to_clone: Annotated[str, "The ID of the order you want to clone."],
    clone_request_idempotency_key: Annotated[
        str | None,
        "A unique string to identify the clone request. Allows safe retries without duplicating cloned orders.",  # noqa: E501
    ] = None,
    order_version: Annotated[
        int | None,
        "An optional integer specifying the order version for concurrency protection. If omitted, the latest version is used.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CloneOrder'."]:
    """Clone an existing order as a draft.

    This tool creates a new order in the DRAFT state by duplicating an existing order. It copies core fields such as line items, taxes, and discounts from the original order."""  # noqa: E501
    request_data = remove_none_values({
        "order_id": order_id_to_clone,
        "version": order_version,
        "idempotency_key": clone_request_idempotency_key,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/orders/clone",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_READ"]))
async def list_order_custom_attribute_definitions(
    context: ToolContext,
    custom_attribute_visibility_filter: Annotated[
        str | None,
        "Specify whether to return all custom attributes, or only those that are read-only ('READ') or read-write ('READ_WRITE'). Valid options are 'ALL', 'READ', or 'READ_WRITE'.",  # noqa: E501
    ] = None,
    maximum_results_per_page: Annotated[
        int | None,
        "The maximum number of results to return in a single response. Accepts values 1 to 100, default is 20.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "The cursor for fetching the next page of results in a multi-page response. It should be used as returned from a previous call.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'ListOrderCustomAttributeDefinitions'."
]:
    """Retrieve order-related custom attribute definitions for a Square seller.

    Lists all order-related custom attribute definitions for a Square seller account, including those with read-only or read-write visibility set by other applications."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/orders/custom-attribute-definitions",
        method="GET",
        params=remove_none_values({
            "visibility_filter": custom_attribute_visibility_filter,
            "cursor": pagination_cursor,
            "limit": maximum_results_per_page,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_WRITE"]))
async def create_order_custom_attribute(
    context: ToolContext,
    creation_timestamp: Annotated[
        str | None,
        "The timestamp indicating when the custom attribute definition was created, in RFC 3339 format.",  # noqa: E501
    ] = None,
    current_version_of_custom_attribute_definition: Annotated[
        int | None,
        "Read-only. Specifies the current version of the custom attribute definition for optimistic concurrency and consistency.",  # noqa: E501
    ] = None,
    custom_attribute_definition_key: Annotated[
        str | None,
        "A unique identifier for the custom attribute definition. Can be a simple or qualified key. Must be unique per application, seller, and resource type. Cannot exceed 60 characters.",  # noqa: E501
    ] = None,
    custom_attribute_definition_name: Annotated[
        str | None,
        "The name for the custom attribute definition, unique within the seller and application. Required if visibility is read-only or read-write.",  # noqa: E501
    ] = None,
    custom_attribute_description: Annotated[
        str | None,
        "Provide a detailed seller-oriented description for the custom attribute definition. Required if visibility is 'READ_ONLY' or 'READ_WRITE_VALUES'.",  # noqa: E501
    ] = None,
    custom_attribute_schema: Annotated[
        dict[str, str] | None,
        "The JSON schema defining the data type of the custom attribute. Required when creating a definition.",  # noqa: E501
    ] = None,
    custom_attribute_visibility: Annotated[
        str | None,
        "Specifies who can read and write the custom attribute values. Options are: 'VISIBILITY_HIDDEN', 'VISIBILITY_READ_ONLY', 'VISIBILITY_READ_WRITE_VALUES'.",  # noqa: E501
    ] = None,
    unique_request_id: Annotated[
        str | None,
        "A unique identifier for this request to ensure idempotency. Use a UUID or similar unique string.",  # noqa: E501
    ] = None,
    updated_timestamp: Annotated[
        str | None,
        "The timestamp in RFC 3339 format indicating when the custom attribute definition was last updated or created.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'CreateOrderCustomAttributeDefinition'."
]:
    """Define a custom attribute for Square orders.

    Use this tool to create a custom attribute definition that can be associated with orders in a Square seller account. This allows for additional customization and data management for orders."""  # noqa: E501
    request_data = remove_none_values({
        "custom_attribute_definition": {
            "key": custom_attribute_definition_key,
            "schema": custom_attribute_schema,
            "name": custom_attribute_definition_name,
            "description": custom_attribute_description,
            "visibility": custom_attribute_visibility,
            "version": current_version_of_custom_attribute_definition,
            "updated_at": updated_timestamp,
            "created_at": creation_timestamp,
        },
        "idempotency_key": unique_request_id,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/orders/custom-attribute-definitions",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_WRITE"]))
async def delete_order_custom_attribute_definition(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str, "The key of the custom attribute definition to delete from an order."
    ],
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'DeleteOrderCustomAttributeDefinition'."
]:
    """Delete a custom attribute definition from an order.

    This tool deletes an order-related custom attribute definition from a Square seller account. Only the definition owner has the permission to perform this action."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/orders/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_READ"]))
async def retrieve_order_custom_attribute_definition(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute definition you want to retrieve. This should match the key set in Square seller account.",  # noqa: E501
    ],
    current_custom_attribute_version: Annotated[
        int | None,
        "Specify the current version of the custom attribute for optimistic concurrency control.",
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'RetrieveOrderCustomAttributeDefinition'."
]:
    """Retrieve a custom attribute definition for an order.

    This tool retrieves an order-related custom attribute definition from a Square seller account. It should be called when you need to access specific custom attribute information related to orders. Custom attributes must have visibility settings of VISIBILITY_READ_ONLY or VISIBILITY_READ_WRITE_VALUES unless defined by the seller."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/orders/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="GET",
        params=remove_none_values({"version": current_custom_attribute_version}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_WRITE"]))
async def update_order_custom_attribute_definition(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key for the custom attribute definition to update. Must be unique per application, seller, and resource type.",  # noqa: E501
    ],
    creation_timestamp: Annotated[
        str | None,
        "The timestamp indicating when the custom attribute definition was created, in RFC 3339 format. This field is read-only and used for informational purposes.",  # noqa: E501
    ] = None,
    current_version_of_custom_attribute_definition: Annotated[
        int | None,
        "The current version of the custom attribute definition. Provide this to enable optimistic concurrency.",  # noqa: E501
    ] = None,
    custom_attribute_definition_key: Annotated[
        str | None,
        "The identifier of the custom attribute definition. Can be a simple or qualified key. Must be unique per application, seller, and resource type.",  # noqa: E501
    ] = None,
    custom_attribute_description: Annotated[
        str | None,
        "Description for the custom attribute definition, viewable as a Square UI tooltip. Required if visibility is READ_ONLY or READ_WRITE_VALUES.",  # noqa: E501
    ] = None,
    custom_attribute_name: Annotated[
        str | None,
        "The unique name for the custom attribute definition within the seller and application pair. Required if visibility is `VISIBILITY_READ_ONLY` or `VISIBILITY_READ_WRITE_VALUES`.",  # noqa: E501
    ] = None,
    custom_attribute_schema: Annotated[
        dict[str, str] | None,
        "Provide the JSON schema for the custom attribute definition, determining the data type of associated attributes. This is mandatory when creating a definition.",  # noqa: E501
    ] = None,
    custom_attribute_updated_timestamp: Annotated[
        str | None,
        "The timestamp in RFC 3339 format showing when the custom attribute definition was created or last updated.",  # noqa: E501
    ] = None,
    custom_attribute_visibility: Annotated[
        str | None,
        "Sets the visibility level for the custom attribute definition. Options: 'VISIBILITY_HIDDEN', 'VISIBILITY_READ_ONLY', 'VISIBILITY_READ_WRITE_VALUES'.",  # noqa: E501
    ] = None,
    idempotency_identifier: Annotated[
        str | None,
        "A unique string identifier for the request to ensure idempotency. Refer to Square's idempotency guidelines for more details.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'UpdateOrderCustomAttributeDefinition'."
]:
    """Modify order-related custom attribute definitions.

    Use this tool to update an order-related custom attribute definition for a Square seller account. Only the definition owner can perform updates. This tool is useful when you need to revise custom attributes that are part of an order."""  # noqa: E501
    request_data = remove_none_values({
        "custom_attribute_definition": {
            "key": custom_attribute_definition_key,
            "schema": custom_attribute_schema,
            "name": custom_attribute_name,
            "description": custom_attribute_description,
            "visibility": custom_attribute_visibility,
            "version": current_version_of_custom_attribute_definition,
            "updated_at": custom_attribute_updated_timestamp,
            "created_at": creation_timestamp,
        },
        "idempotency_key": idempotency_identifier,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/orders/custom-attribute-definitions/{key}".format(  # noqa: UP032
            key=custom_attribute_key
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_WRITE"]))
async def bulk_delete_order_custom_attributes(
    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 'BulkDeleteOrderCustomAttributes'."]:
    """Perform bulk deletion of custom attributes from orders.

    Use this tool to delete multiple custom attributes from one or more orders in bulk. It processes a map of delete requests and returns a map of responses, each paired with a request ID.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKDELETEORDERCUSTOMATTRIBUTES"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["BULKDELETEORDERCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["BULKDELETEORDERCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/orders/custom-attributes/bulk-delete",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKDELETEORDERCUSTOMATTRIBUTES"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_WRITE"]))
async def bulk_upsert_order_custom_attributes(
    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 'BulkUpsertOrderCustomAttributes'."]:
    """Perform bulk create or update of order custom attributes.

    This tool allows for creating or updating order custom attributes in bulk. It can handle up to 25 requests at once, each identified uniquely, and provides responses for each request. It is used to manage custom attributes based on definitions in a Square seller account, and may also delete attributes. Use this when multiple order custom attributes need simultaneous updating or creation.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKUPSERTORDERCUSTOMATTRIBUTES"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["BULKUPSERTORDERCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["BULKUPSERTORDERCUSTOMATTRIBUTES"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/orders/custom-attributes/bulk-upsert",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKUPSERTORDERCUSTOMATTRIBUTES"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_READ"]))
async def search_square_orders(
    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 'SearchOrders'."]:
    """Search and retrieve orders from Square locations.

    Use this tool to search for all orders across specified Square locations, including sales, returns, and exchanges. The search can include filters, sorting, and specify whether to return detailed order information or a summary.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["SEARCHSQUAREORDERS"],
            "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\n" + REQUEST_BODY_SCHEMAS["SEARCHSQUAREORDERS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["SEARCHSQUAREORDERS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/orders/search",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SEARCHSQUAREORDERS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_READ"]))
async def retrieve_order_by_id(
    context: ToolContext,
    order_id: Annotated[str, "The unique identifier of the order to retrieve from the Square API."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveOrder'."]:
    """Retrieve an order's details using its ID.

    Use this tool to fetch the details of a specific order by providing its ID. It simplifies the process of obtaining order information from the Square API."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/orders/{order_id}".format(order_id=order_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_WRITE"]))
async def update_order_square(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    order_id: Annotated[
        str | None,
        "The unique identifier for the order that needs to be updated. This ID specifies which order will have its fields modified.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateOrder'."]:
    """Update fields of an open Square order.

    This tool updates fields of an open order in Square by adding, replacing, or deleting details. It requires the order ID, the latest version of the order, and a sparse order containing only the fields to update. Use this tool to manage order details while in 'OPEN' state when changes are needed.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEORDERSQUARE"],
            "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 order_id:
        missing_params.append(("order_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\n" + REQUEST_BODY_SCHEMAS["UPDATEORDERSQUARE"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATEORDERSQUARE"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/orders/{order_id}".format(order_id=order_id),  # noqa: UP032
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEORDERSQUARE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_READ"]))
async def list_order_custom_attributes(
    context: ToolContext,
    target_order_id: Annotated[
        str, "The unique identifier of the target order to retrieve custom attributes for."
    ],
    custom_attributes_visibility: Annotated[
        str | None, "Specify which custom attributes to return: 'ALL', 'READ', or 'READ_WRITE'."
    ] = None,
    include_custom_attribute_definitions: Annotated[
        bool | None,
        "Set to true to include custom attribute definition details such as name, description, and data type. Defaults to false.",  # noqa: E501
    ] = False,
    max_results_per_page: Annotated[
        int | None,
        "Specifies the maximum number of custom attribute results returned per page. Accepts values from 1 to 100, default is 20.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None, "The cursor used to retrieve the next page of results in a paginated response."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListOrderCustomAttributes'."]:
    """Retrieve custom attributes associated with an order.

    This tool fetches the custom attributes linked to a specific order, optionally including their definitions. It provides all visible custom attributes, including those owned by other applications with specific visibility settings."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/orders/{order_id}/custom-attributes".format(  # noqa: UP032
            order_id=target_order_id
        ),
        method="GET",
        params=remove_none_values({
            "visibility_filter": custom_attributes_visibility,
            "cursor": pagination_cursor,
            "limit": max_results_per_page,
            "with_definitions": include_custom_attribute_definitions,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_WRITE"]))
async def delete_order_custom_attribute(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key of the custom attribute to delete. Must match an existing custom attribute definition key.",  # noqa: E501
    ],
    order_id: Annotated[
        str, "The unique identifier of the order from which the custom attribute will be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteOrderCustomAttribute'."]:
    """Delete a custom attribute from an order profile.

    Use this tool to delete a custom attribute associated with an order. Ensure that the visibility setting is 'VISIBILITY_READ_WRITE_VALUES' for attributes owned by other applications."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/orders/{order_id}/custom-attributes/{custom_attribute_key}".format(  # noqa: UP032
            order_id=order_id, custom_attribute_key=custom_attribute_key
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_READ"]))
async def retrieve_order_custom_attribute(
    context: ToolContext,
    custom_attribute_key: Annotated[
        str,
        "The key for the custom attribute to retrieve, matching an existing attribute definition key.",  # noqa: E501
    ],
    order_id: Annotated[
        str, "The unique ID of the target order to retrieve the custom attribute for."
    ],
    custom_attribute_version: Annotated[
        int | None,
        "Specify the current version of the custom attribute for optimistic concurrency control.",
    ] = None,
    include_custom_attribute_definition: Annotated[
        bool | None,
        "Set to true to include custom attribute definition details such as name, description, and data type. Defaults to false.",  # noqa: E501
    ] = False,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveOrderCustomAttribute'."]:
    """Retrieve a custom attribute for a specified order.

    Use this tool to get a custom attribute linked to an order. It can also retrieve the custom attribute definition if needed. Visibility settings determine if attributes from other applications can be accessed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/orders/{order_id}/custom-attributes/{custom_attribute_key}".format(  # noqa: UP032
            order_id=order_id, custom_attribute_key=custom_attribute_key
        ),
        method="GET",
        params=remove_none_values({
            "version": custom_attribute_version,
            "with_definition": include_custom_attribute_definition,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ORDERS_WRITE"]))
async def update_order_custom_attribute(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    order_id: Annotated[
        str | None,
        "The ID of the target order for which the custom attribute is being created or updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    custom_attribute_key: Annotated[
        str | None,
        "The key of the custom attribute to be created or updated. Must match an existing custom attribute definition key.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpsertOrderCustomAttribute'."]:
    """Create or update a custom attribute for an order.

    Call this tool to set or update the value of a custom attribute for a specific order. This is useful for customizing orders based on predefined custom attribute definitions in a Square seller account. Ensure the visibility setting is 'VISIBILITY_READ_WRITE_VALUES' for attributes owned by another application.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEORDERCUSTOMATTRIBUTE"],
            "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 order_id:
        missing_params.append(("order_id", "path"))
    if not custom_attribute_key:
        missing_params.append(("custom_attribute_key", "path"))

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

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

    # 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\n"
                + REQUEST_BODY_SCHEMAS["UPDATEORDERCUSTOMATTRIBUTE"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/orders/{order_id}/custom-attributes/{custom_attribute_key}".format(  # noqa: UP032
            order_id=order_id, custom_attribute_key=custom_attribute_key
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEORDERCUSTOMATTRIBUTE"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE", "ORDERS_WRITE"]))
async def pay_order(
    context: ToolContext,
    order_id: Annotated[str, "The unique identifier of the order to be paid."],
    unique_transaction_identifier: Annotated[
        str,
        "A unique string to identify the request and ensure the prevention of duplicate payments. Reuse the same key for retrying the same request.",  # noqa: E501
    ],
    order_version: Annotated[
        int | None,
        "Specify the version of the order to be paid. Defaults to latest if not provided.",
    ] = None,
    payment_ids_to_collect: Annotated[
        list[str] | None, "Array of payment IDs to collect; total must match the order total."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'PayOrder'."]:
    """Settle an order using approved payments.

    This tool is used to pay for an order with approved payments, or to mark an order with a total of 0 as paid. Ensure all payments reference the order correctly. Useful for completing transactions with delayed capture payments."""  # noqa: E501
    request_data = remove_none_values({
        "idempotency_key": unique_transaction_identifier,
        "order_version": order_version,
        "payment_ids": payment_ids_to_collect,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/orders/{order_id}/pay".format(order_id=order_id),  # noqa: UP032
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_READ"]))
async def retrieve_payments_list(
    context: ToolContext,
    card_last_four_digits: Annotated[
        str | None, "Filter payments by the last four digits of the payment card used."
    ] = None,
    end_time: Annotated[
        str | None,
        "End of the time range for retrieving payments, in RFC 3339 format. Defaults to the current time.",  # noqa: E501
    ] = None,
    end_time_updated_for_payments: Annotated[
        str | None,
        "The end time for retrieving payments, in RFC 3339 format, based on `updated_at`.",
    ] = None,
    is_offline_payment: Annotated[
        bool | None, "Set to true to include offline payments, or false to exclude them."
    ] = False,
    max_results_per_page: Annotated[
        int | None,
        "The maximum number of payment results to return per page (up to 100). Defaults to 100 if more is specified.",  # noqa: E501
    ] = None,
    offline_end_time: Annotated[
        str | None,
        "The end time in RFC 3339 format for retrieving offline payments, based on the `offline_payment_details.client_created_at` field. Default is the current time.",  # noqa: E501
    ] = None,
    offline_payment_start_time: Annotated[
        str | None,
        "Start of the time range for retrieving offline payments in RFC 3339 format. Uses 'offline_payment_details.client_created_at'.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A pagination cursor for fetching the next set of payment results from the ListPayments endpoint.",  # noqa: E501
    ] = None,
    payment_card_brand: Annotated[
        str | None, "The brand of the payment card to filter payments (e.g., VISA, MasterCard)."
    ] = None,
    results_sort_order: Annotated[
        str | None,
        "Specify the order of the results: `ASC` for oldest to newest, `DESC` for newest to oldest (default).",  # noqa: E501
    ] = None,
    sort_by_field: Annotated[
        str | None,
        "Choose the field to sort results by. Options: `CREATED_AT`, `OFFLINE_CREATED_AT`, `UPDATED_AT`. Default is `CREATED_AT`.",  # noqa: E501
    ] = None,
    specific_location_id: Annotated[
        str | None,
        "Limit results to the specified location ID. Defaults to the main location associated with the seller.",  # noqa: E501
    ] = None,
    start_time_range: Annotated[
        str | None,
        "Start time to retrieve payments, in RFC 3339 format. Defaults to one year ago if not specified.",  # noqa: E501
    ] = None,
    total_payment_amount: Annotated[
        int | None,
        "The exact amount in the total_money field for a payment. Use an integer to specify the amount in cents.",  # noqa: E501
    ] = None,
    updated_at_start_time: Annotated[
        str | None,
        "Start of the time range for retrieving payments based on the `updated_at` field, in RFC 3339 format.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListPayments'."]:
    """Retrieve a list of payments from your account.

    Use this tool to access a list of payments made through your account. It provides a consistent overview of payment transactions, with a maximum of 100 results per page. Note that changes to payments may take a few seconds to reflect."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/payments",
        method="GET",
        params=remove_none_values({
            "begin_time": start_time_range,
            "end_time": end_time,
            "sort_order": results_sort_order,
            "cursor": pagination_cursor,
            "location_id": specific_location_id,
            "total": total_payment_amount,
            "last_4": card_last_four_digits,
            "card_brand": payment_card_brand,
            "limit": max_results_per_page,
            "is_offline_payment": is_offline_payment,
            "offline_begin_time": offline_payment_start_time,
            "offline_end_time": offline_end_time,
            "updated_at_begin_time": updated_at_start_time,
            "updated_at_end_time": end_time_updated_for_payments,
            "sort_field": sort_by_field,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE"]))
async def create_payment(
    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 'CreatePayment'."]:
    """Create a payment using credit/debit card or other sources.

    This tool is used to create a payment through a specified source, such as a credit/debit card or to record an external payment. It returns the details of the payment created.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEPAYMENT"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATEPAYMENT"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATEPAYMENT"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/payments",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEPAYMENT"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE"]))
async def cancel_payment_by_idempotency(
    context: ToolContext,
    payment_idempotency_key: Annotated[
        str,
        "The idempotency key used to identify the payment to cancel. It should match the key used in the original CreatePayment request.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelPaymentByIdempotencyKey'."]:
    """Cancel a payment by idempotency key when status is unknown.

    Use this tool to cancel a payment when a CreatePayment request status is uncertain, typically due to network issues. Provide the same idempotency key used in the original CreatePayment request. If no payment is found, the tool confirms no action was taken."""  # noqa: E501
    request_data = remove_none_values({"idempotency_key": payment_idempotency_key})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/payments/cancel",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_READ"]))
async def get_payment_details(
    context: ToolContext,
    payment_id: Annotated[str, "Unique ID to retrieve specific payment details."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetPayment'."]:
    """Retrieve detailed information about a specific payment.

    This tool retrieves details for a specific payment using its ID. It should be called when information about a particular payment is needed, such as transaction details, status, and other relevant data."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/payments/{payment_id}".format(payment_id=payment_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE"]))
async def update_payment_status(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    payment_identifier: Annotated[
        str | None,
        "The unique ID of the payment you want to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdatePayment'."]:
    """Update a payment's approved status and details.

    Use this tool to update a payment with an APPROVED status, modifying fields like `amount_money` and `tip_money` as 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.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEPAYMENTSTATUS"],
            "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 payment_identifier:
        missing_params.append(("payment_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\n" + REQUEST_BODY_SCHEMAS["UPDATEPAYMENTSTATUS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATEPAYMENTSTATUS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/payments/{payment_id}".format(  # noqa: UP032
            payment_id=payment_identifier
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEPAYMENTSTATUS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE"]))
async def cancel_payment(
    context: ToolContext,
    payment_id_to_cancel: Annotated[
        str, "The unique identifier of the payment to be canceled. Must have an APPROVED status."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelPayment'."]:
    """Cancel or void an approved payment.

    Use this tool to cancel a payment that has an APPROVED status. Ideal for reversing payments that should not proceed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/payments/{payment_id}/cancel".format(  # noqa: UP032
            payment_id=payment_id_to_cancel
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE"]))
async def complete_payment(
    context: ToolContext,
    payment_identifier: Annotated[
        str,
        "The unique ID identifying the payment to be completed. This is required for completing a payment with an 'APPROVED' status.",  # noqa: E501
    ],
    current_payment_version_token: Annotated[
        str | None,
        "The token identifying the current Payment version for optimistic concurrency. It ensures the Payment version matches the caller's expectations to prevent mismatches.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CompletePayment'."]:
    """Complete an approved payment using Squareup.

    Use this tool to complete or capture a payment with an 'APPROVED' status, finalizing the transaction."""  # noqa: E501
    request_data = remove_none_values({"version_token": current_payment_version_token})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/payments/{payment_id}/complete".format(  # noqa: UP032
            payment_id=payment_identifier
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def list_payouts(
    context: ToolContext,
    begin_timestamp: Annotated[
        str | None,
        "The starting timestamp for the payout creation time, in RFC 3339 format. Defaults to one year ago if not provided.",  # noqa: E501
    ] = None,
    end_time_rfc3339: Annotated[
        str | None,
        "RFC 3339 timestamp marking the end of the payout creation time. Defaults to current time if not specified.",  # noqa: E501
    ] = None,
    filter_payout_status: Annotated[
        str | None,
        "If provided, only payouts with the specified status ('SENT', 'FAILED', 'PAID') are returned.",  # noqa: E501
    ] = None,
    location_identifier: Annotated[
        str | None,
        "The ID of the location for which to list the payouts. Defaults to the main location associated with the seller if not specified.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A cursor for pagination, returned by a previous call. Use it to retrieve the next set of results. Be aware of changes in request parameters between calls.",  # noqa: E501
    ] = None,
    payout_sort_order: Annotated[
        str | None,
        "Specifies the order for listing payouts. Use 'DESC' for descending or 'ASC' for ascending order.",  # noqa: E501
    ] = None,
    results_per_page: Annotated[
        int | None, "Maximum number of results per page. Defaults to 100 and cannot exceed 100."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListPayouts'."]:
    """Retrieve a list of payouts for the default location.

    This tool retrieves all payouts for the default location. It allows filtering by location ID, status, time range, and ordering results. Ensure that `PAYOUTS_READ` is set for the OAuth scope."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/payouts",
        method="GET",
        params=remove_none_values({
            "location_id": location_identifier,
            "status": filter_payout_status,
            "begin_time": begin_timestamp,
            "end_time": end_time_rfc3339,
            "sort_order": payout_sort_order,
            "cursor": pagination_cursor,
            "limit": results_per_page,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def get_payout_details(
    context: ToolContext,
    payout_id: Annotated[
        str,
        "The unique identifier of the payout to retrieve details for. This ID is required to fetch the specific payout information.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetPayout'."]:
    """Retrieve details of a specific payout using payout ID.

    Use this tool to obtain detailed information about a specific payout by providing its payout ID. Ensure that the required OAuth scope `PAYOUTS_READ` is set before calling this tool."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/payouts/{payout_id}".format(payout_id=payout_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def list_payout_entries(
    context: ToolContext,
    payout_id: Annotated[
        str, "The unique string identifier for the specific payout to retrieve information for."
    ],
    maximum_results_per_page: Annotated[
        int | None,
        "Specifies the maximum number of results to return on a single page (max 100). Default is 100.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A cursor to retrieve the next set of results in a paginated response. Use a cursor from a previous response for continuity.",  # noqa: E501
    ] = None,
    payout_entries_sort_order: Annotated[
        str | None, "Specify the order (ASC or DESC) in which payout entries are listed."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListPayoutEntries'."]:
    """Retrieve all payout entries for a specific payout.

    Use this tool to get a list of payout entries related to a specific payout ID. Ensure the `PAYOUTS_READ` OAuth scope is set before calling this tool."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/payouts/{payout_id}/payout-entries".format(  # noqa: UP032
            payout_id=payout_id
        ),
        method="GET",
        params=remove_none_values({
            "sort_order": payout_entries_sort_order,
            "cursor": pagination_cursor,
            "limit": maximum_results_per_page,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_READ"]))
async def list_payment_refunds(
    context: ToolContext,
    end_time_rfc3339: Annotated[
        str | None,
        "Specifies the end time in RFC 3339 format to retrieve `PaymentRefunds` based on `created_at`. Defaults to the current time.",  # noqa: E501
    ] = None,
    limit_results_to_location_id: Annotated[
        str | None,
        "Limit results to refunds from the specified location. By default, returns all locations.",
    ] = None,
    maximum_results_per_page: Annotated[
        int | None,
        "Specifies the max number of refund results per page. Max value is 100; defaults to 100.",
    ] = None,
    pagination_cursor: Annotated[
        str | None, "A pagination cursor from a previous response to fetch the next set of results."
    ] = None,
    refund_status_filter: Annotated[
        str | None,
        "Specify a refund status to filter the results. If omitted, refunds of all statuses are returned.",  # noqa: E501
    ] = None,
    refunds_start_time: Annotated[
        str | None,
        "The start time in RFC 3339 format to retrieve PaymentRefunds based on the created_at field. Default is the current time minus one year.",  # noqa: E501
    ] = None,
    results_sort_order: Annotated[
        str | None,
        "The order in which results are listed by their creation date: `ASC` for oldest to newest, `DESC` for newest to oldest (default).",  # noqa: E501
    ] = None,
    sort_results_by_field: Annotated[
        str | None,
        "The field used to sort payment refund results. Options: 'CREATED_AT' (default) or 'UPDATED_AT'.",  # noqa: E501
    ] = None,
    source_payment_type: Annotated[
        str | None,
        "Specify to only return refunds for payments with the indicated source type (e.g., CARD, BANK_ACCOUNT).",  # noqa: E501
    ] = None,
    updated_at_end_time: Annotated[
        str | None,
        "The end of the time range for retrieving refunds, in RFC 3339 format. Default is the current time.",  # noqa: E501
    ] = None,
    updated_at_start_time: Annotated[
        str | None,
        "Start of time range for retrieving each PaymentRefund, in RFC 3339 format. Defaults to begin_time if omitted.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListPaymentRefunds'."]:
    """Retrieve a list of payment refunds for the account.

    Use this tool to get a list of refunds associated with the account. This is useful for checking all refund transactions. Note that results are eventually consistent and may take a few seconds to update."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/refunds",
        method="GET",
        params=remove_none_values({
            "begin_time": refunds_start_time,
            "end_time": end_time_rfc3339,
            "sort_order": results_sort_order,
            "cursor": pagination_cursor,
            "location_id": limit_results_to_location_id,
            "status": refund_status_filter,
            "source_type": source_payment_type,
            "limit": maximum_results_per_page,
            "updated_at_begin_time": updated_at_start_time,
            "updated_at_end_time": updated_at_end_time,
            "sort_field": sort_results_by_field,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE"]))
async def refund_payment(
    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 'RefundPayment'."]:
    """Refund a payment partially or fully using Square.

    Use this tool to refund the entire amount or a portion of a payment. It supports card payments as well as cash or external payments. Relevant for reversing transactions in case of errors or customer returns.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["REFUNDPAYMENT"],
            "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\n" + REQUEST_BODY_SCHEMAS["REFUNDPAYMENT"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["REFUNDPAYMENT"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/refunds",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["REFUNDPAYMENT"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_READ"]))
async def retrieve_refund_details(
    context: ToolContext,
    refund_unique_id: Annotated[
        str, "The unique ID for the specific refund to retrieve details for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetPaymentRefund'."]:
    """Retrieve details of a specific refund using the refund ID.

    Use this tool to get detailed information about a refund by providing the refund ID. It's useful for checking the status and details of a refund processed through Square."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/refunds/{refund_id}".format(  # noqa: UP032
            refund_id=refund_unique_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ONLINE_STORE_SITE_READ"]))
async def list_square_online_sites(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListSites'."]:
    """List all Square Online sites for a seller.

    Fetches a list of Square Online sites associated with a seller account, ordered by creation date. Useful for managing or reviewing existing sites."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/sites",
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ONLINE_STORE_SNIPPETS_WRITE"]))
async def delete_square_snippet(
    context: ToolContext,
    site_id: Annotated[
        str, "The ID of the Square Online site containing the snippet to be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteSnippet'."]:
    """Delete a snippet from a Square Online site.

    Use this tool to remove a snippet from a specified Square Online site. Useful when you need to update or clean up content on a site. Make sure to retrieve the site IDs using the ListSites API beforehand."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/sites/{site_id}/snippet".format(site_id=site_id),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ONLINE_STORE_SNIPPETS_READ"]))
async def retrieve_online_site_snippet(
    context: ToolContext,
    site_identifier: Annotated[
        str, "The unique ID of the Square Online site containing the snippet to retrieve."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveSnippet'."]:
    """Retrieve a specific snippet from a Square Online site.

    Use this tool to get the snippet added by your application to a specific Square Online site. Ensure you have the site ID, which can be obtained by calling the ListSites API."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/sites/{site_id}/snippet".format(  # noqa: UP032
            site_id=site_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["ONLINE_STORE_SNIPPETS_WRITE"]))
async def add_or_update_square_online_snippet(
    context: ToolContext,
    site_id_for_snippet: Annotated[
        str,
        "The ID of the site where you want to add or update the snippet. Obtained through ListSites function.",  # noqa: E501
    ],
    snippet_code_content: Annotated[
        str,
        "The code snippet to add or update, which can include valid HTML, JavaScript, or both. This will be appended to the head element of all site pages except checkout pages.",  # noqa: E501
    ],
    snippet_id: Annotated[
        str | None, "The Square-assigned ID for the snippet to add or update."
    ] = None,
    snippet_initial_creation_timestamp: Annotated[
        str | None,
        "The timestamp indicating when the snippet was initially added to the site, in RFC 3339 format.",  # noqa: E501
    ] = None,
    snippet_last_updated_timestamp: Annotated[
        str | None,
        "The timestamp for when the snippet was last updated on the site, in RFC 3339 format.",
    ] = None,
    snippet_site_id: Annotated[
        str | None, "The ID of the site that contains the snippet to be updated or added."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpsertSnippet'."]:
    """Add or update a snippet on a Square Online site.

    This tool adds a new snippet to or updates an existing snippet on a Square Online site's `head` element. It excludes checkout pages and requires the site's ID. Use ListSites to find site IDs."""  # noqa: E501
    request_data = remove_none_values({
        "snippet": {
            "id": snippet_id,
            "site_id": snippet_site_id,
            "content": snippet_code_content,
            "created_at": snippet_initial_creation_timestamp,
            "updated_at": snippet_last_updated_timestamp,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/sites/{site_id}/snippet".format(  # noqa: UP032
            site_id=site_id_for_snippet
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup",
        scopes=[
            "ITEMS_READ",
            "INVOICES_WRITE",
            "CUSTOMERS_READ",
            "SUBSCRIPTIONS_WRITE",
            "ORDERS_WRITE",
            "PAYMENTS_WRITE",
        ],
    )
)
async def create_customer_subscription(
    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 'CreateSubscription'."]:
    """Enroll a customer in a subscription plan.

    This tool enrolls a customer in a subscription, either charging a card on file or sending an invoice to the customer's email. The subscription can start immediately or on a specified start date and is linked to a specific location.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATECUSTOMERSUBSCRIPTION"],
            "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\n"
                + REQUEST_BODY_SCHEMAS["CREATECUSTOMERSUBSCRIPTION"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["CREATECUSTOMERSUBSCRIPTION"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/subscriptions",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATECUSTOMERSUBSCRIPTION"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup", scopes=["SUBSCRIPTIONS_READ", "ITEMS_READ", "SUBSCRIPTIONS_WRITE"]
    )
)
async def bulk_swap_subscription_plan(
    context: ToolContext,
    location_id_association: Annotated[
        str,
        "The ID of the location to associate with the swapped subscriptions. This specifies where the subscription changes are applied.",  # noqa: E501
    ],
    new_plan_variation_id: Annotated[
        str, "The ID of the new subscription plan variation for the swap. This field is required."
    ],
    old_plan_variation_id: Annotated[
        str,
        "The ID of the current plan variation to swap. Active subscriptions using this plan will switch to the new variation on their next billing day.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BulkSwapPlan'."]:
    """Schedule a plan variation swap for multiple subscriptions.

    This tool schedules a swap of plan variations for all active subscriptions under a specified plan variation. It is used when there is a need to change the plan variation for multiple subscriptions simultaneously."""  # noqa: E501
    request_data = remove_none_values({
        "new_plan_variation_id": new_plan_variation_id,
        "old_plan_variation_id": old_plan_variation_id,
        "location_id": location_id_association,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/subscriptions/bulk-swap-plan",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["SUBSCRIPTIONS_READ"]))
async def search_subscriptions(
    context: ToolContext,
    customer_ids_to_filter: Annotated[
        list[str] | None,
        "A list of customer IDs to filter subscriptions by. Leave empty to include all customers.",
    ] = None,
    filter_by_location_ids: Annotated[
        list[str] | None, "An array of location IDs to filter subscriptions by location."
    ] = None,
    filter_by_source_applications: Annotated[
        list[str] | None, "A list of source application names to filter subscriptions by."
    ] = None,
    include_related_info: Annotated[
        list[str] | None,
        "Specify related information to include in the response, such as 'actions' for scheduled actions on subscriptions.",  # noqa: E501
    ] = None,
    max_subscriptions_returned: Annotated[
        int | None, "Defines the maximum number of subscriptions to return in a single response."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "Cursor for fetching the next set of subscription results if previous results exceeded the limit. If not set, returns the last page of results.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchSubscriptions'."]:
    """Search for subscriptions by location and customer IDs.

    This tool searches for subscriptions, ordering them by creation date, location, and optionally by customer IDs. If no location or customer IDs are provided, it searches across all available locations and customers."""  # noqa: E501
    request_data = remove_none_values({
        "cursor": pagination_cursor,
        "limit": max_subscriptions_returned,
        "query": {
            "filter": {
                "customer_ids": customer_ids_to_filter,
                "location_ids": filter_by_location_ids,
                "source_names": filter_by_source_applications,
            }
        },
        "include": include_related_info,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/subscriptions/search",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["SUBSCRIPTIONS_READ"]))
async def retrieve_subscription_details(
    context: ToolContext,
    subscription_id: Annotated[
        str,
        "The unique ID of the subscription to retrieve details for. Necessary to specify which subscription to access.",  # noqa: E501
    ],
    include_related_info: Annotated[
        str | None,
        "Specify related info to include in the response. Use 'actions' to include scheduled actions on the subscription.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveSubscription'."]:
    """Retrieve details of a specific subscription using its ID.

    Use this tool to obtain information about a subscription by providing its unique subscription ID. Useful for accessing current subscription status, billing, and service details."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/subscriptions/{subscription_id}".format(  # noqa: UP032
            subscription_id=subscription_id
        ),
        method="GET",
        params=remove_none_values({"include": include_related_info}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup",
        scopes=[
            "ITEMS_READ",
            "INVOICES_WRITE",
            "CUSTOMERS_READ",
            "SUBSCRIPTIONS_WRITE",
            "ORDERS_WRITE",
            "PAYMENTS_WRITE",
        ],
    )
)
async def update_subscription(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    subscription_id: Annotated[
        str | None,
        "The unique identifier of the subscription you want to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateSubscription'."]:
    """Update subscription details with new or cleared values.

    This tool updates subscription details by modifying existing values or clearing fields by setting them to `null`. Use this when you need to make changes to a subscription in the Square system.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATESUBSCRIPTION"],
            "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 subscription_id:
        missing_params.append(("subscription_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\n" + REQUEST_BODY_SCHEMAS["UPDATESUBSCRIPTION"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATESUBSCRIPTION"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/subscriptions/{subscription_id}".format(  # noqa: UP032
            subscription_id=subscription_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATESUBSCRIPTION"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["SUBSCRIPTIONS_WRITE"]))
async def delete_subscription_action(
    context: ToolContext,
    subscription_id: Annotated[
        str,
        "The ID of the subscription for which the action is to be deleted. Provide the correct subscription ID to ensure accurate targeting.",  # noqa: E501
    ],
    targeted_action_id: Annotated[
        str, "The ID of the specific action to be deleted from the subscription."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteSubscriptionAction'."]:
    """Delete a scheduled action for a subscription.

    Use this tool to delete a specific scheduled action related to a subscription when changes or cancellations are needed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/subscriptions/{subscription_id}/actions/{action_id}".format(  # noqa: UP032
            subscription_id=subscription_id, action_id=targeted_action_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup", scopes=["SUBSCRIPTIONS_READ", "ITEMS_READ", "SUBSCRIPTIONS_WRITE"]
    )
)
async def change_billing_anchor_date(
    context: ToolContext,
    subscription_id: Annotated[
        str, "The ID of the subscription for which the billing anchor date will be updated."
    ],
    billing_anchor_day: Annotated[
        int | None, "The day (1-31) of the month to set as the billing anchor for the cycle."
    ] = None,
    scheduled_billing_anchor_change_date: Annotated[
        str | None,
        "The `YYYY-MM-DD`-formatted date when the `BILLING_ANCHOR_CHANGE` action occurs. If unspecified or within current billing cycle, change is immediate.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ChangeBillingAnchorDate'."]:
    """Change the billing anchor date for a subscription.

    Use this tool to modify the billing anchor date for a specific subscription. This can be useful for adjusting billing cycles and ensuring proper timing for subscription charges."""  # noqa: E501
    request_data = remove_none_values({
        "monthly_billing_anchor_date": billing_anchor_day,
        "effective_date": scheduled_billing_anchor_change_date,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/subscriptions/{subscription_id}/billing-anchor".format(  # noqa: UP032
            subscription_id=subscription_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["SUBSCRIPTIONS_WRITE"]))
async def cancel_subscription(
    context: ToolContext,
    subscription_id: Annotated[
        str,
        "The unique ID of the subscription to be canceled. Required for scheduling the cancellation action.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelSubscription'."]:
    """Cancel an active subscription at the end of the billing period.

    This tool schedules a cancellation for an active subscription, setting the cancellation date to the end of the current billing cycle. After this date, the subscription changes from ACTIVE to CANCELED."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/subscriptions/{subscription_id}/cancel".format(  # noqa: UP032
            subscription_id=subscription_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["SUBSCRIPTIONS_READ"]))
async def list_subscription_events(
    context: ToolContext,
    subscription_id: Annotated[
        str, "The unique identifier for the subscription whose events are to be retrieved."
    ],
    event_limit: Annotated[
        int | None, "The maximum number of subscription events to retrieve in the response."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "Specify the cursor from a previous response to fetch the next page of subscription events. Leave unset to get the last page.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListSubscriptionEvents'."]:
    """Retrieve all events for a specific subscription.

    This tool retrieves all events related to a specific subscription, providing insight into its activities and changes."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/subscriptions/{subscription_id}/events".format(  # noqa: UP032
            subscription_id=subscription_id
        ),
        method="GET",
        params=remove_none_values({"cursor": pagination_cursor, "limit": event_limit}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup",
        scopes=[
            "ITEMS_READ",
            "INVOICES_WRITE",
            "CUSTOMERS_READ",
            "SUBSCRIPTIONS_WRITE",
            "ORDERS_WRITE",
            "PAYMENTS_WRITE",
        ],
    )
)
async def pause_subscription(
    context: ToolContext,
    subscription_id: Annotated[str, "The unique ID of the subscription you want to pause."],
    pause_cycle_count: Annotated[
        int | None,
        "Specify the number of billing cycles to pause the subscription. A 'RESUME' action will be scheduled at the end of this period. Do not set 'resume_effective_date' or 'resume_change_timing' if this is used.",  # noqa: E501
    ] = None,
    pause_effective_date: Annotated[
        str | None,
        "The `YYYY-MM-DD`-formatted date when the scheduled `PAUSE` action occurs. If unspecified or within the current billing cycle, the subscription pauses at the start of the next cycle.",  # noqa: E501
    ] = None,
    pause_reason: Annotated[
        str | None,
        "The reason provided by the user for pausing the subscription. It should convey the rationale behind the pause action.",  # noqa: E501
    ] = None,
    reactivation_date: Annotated[
        str | None,
        "The date (YYYY-MM-DD) when the subscription is reactivated by a scheduled `RESUME` action. Must be at least one billing cycle after `pause_effective_date`.",  # noqa: E501
    ] = None,
    resume_change_timing: Annotated[
        str | None,
        "Specifies when the subscription resume action takes place. Options are 'IMMEDIATE' or 'END_OF_BILLING_CYCLE'.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'PauseSubscription'."]:
    """Schedule a pause for an active subscription.

    Use this tool to pause an active subscription by scheduling a `PAUSE` action. Call it when you need to temporarily halt a subscription's services."""  # noqa: E501
    request_data = remove_none_values({
        "pause_effective_date": pause_effective_date,
        "pause_cycle_duration": pause_cycle_count,
        "resume_effective_date": reactivation_date,
        "resume_change_timing": resume_change_timing,
        "pause_reason": pause_reason,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/subscriptions/{subscription_id}/pause".format(  # noqa: UP032
            subscription_id=subscription_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup",
        scopes=[
            "ITEMS_READ",
            "INVOICES_WRITE",
            "CUSTOMERS_READ",
            "SUBSCRIPTIONS_WRITE",
            "ORDERS_WRITE",
            "PAYMENTS_WRITE",
        ],
    )
)
async def resume_subscription(
    context: ToolContext,
    subscription_id: Annotated[
        str, "The ID of the subscription to resume. Provide a valid subscription ID string."
    ],
    resume_change_timing: Annotated[
        str | None,
        "Specify when the pending change to resume the subscription takes effect. Choose between 'IMMEDIATE' or 'END_OF_BILLING_CYCLE'.",  # noqa: E501
    ] = None,
    subscription_resume_effective_date: Annotated[
        str | None, "The `YYYY-MM-DD`-formatted date when the subscription is reactivated."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ResumeSubscription'."]:
    """Resume a paused or deactivated subscription.

    Use this tool to schedule a resume action for subscriptions that are currently paused or deactivated."""  # noqa: E501
    request_data = remove_none_values({
        "resume_effective_date": subscription_resume_effective_date,
        "resume_change_timing": resume_change_timing,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/subscriptions/{subscription_id}/resume".format(  # noqa: UP032
            subscription_id=subscription_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(
    requires_auth=OAuth2(
        id="arcade-squareup",
        scopes=[
            "ITEMS_READ",
            "INVOICES_WRITE",
            "CUSTOMERS_READ",
            "SUBSCRIPTIONS_WRITE",
            "ORDERS_WRITE",
            "PAYMENTS_WRITE",
        ],
    )
)
async def swap_subscription_plan(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    subscription_id: Annotated[
        str | None,
        "The ID of the subscription to swap the plan for. This is required to identify which subscription will have its plan changed.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SwapPlan'."]:
    """Swap a subscription plan variation for an existing subscription.

    This tool schedules a SWAP_PLAN action to change the plan variation for an existing subscription. It should be called when a user wants to update the current plan of their subscription. For detailed guidance, refer to the Swap Subscription Plan Variations documentation.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["SWAPSUBSCRIPTIONPLAN"],
            "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 subscription_id:
        missing_params.append(("subscription_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\n" + REQUEST_BODY_SCHEMAS["SWAPSUBSCRIPTIONPLAN"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["SWAPSUBSCRIPTIONPLAN"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/subscriptions/{subscription_id}/swap-plan".format(  # noqa: UP032
            subscription_id=subscription_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["SWAPSUBSCRIPTIONPLAN"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_WRITE"]))
async def create_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",
    ],
    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 'CreateTeamMember'."]:
    """Create a new team member with given and family names.

    This tool is used to create a single TeamMember object by providing the required given name and family name. It returns the created TeamMember object upon success.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATETEAMMEMBER"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATETEAMMEMBER"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATETEAMMEMBER"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/team-members",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATETEAMMEMBER"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_WRITE"]))
async def bulk_create_team_members(
    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 'BulkCreateTeamMembers'."]:
    """Create multiple team members in bulk.

    Use this tool to create multiple team member records at once. It processes each creation individually and returns details for successful operations, along with error information for any failures.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKCREATETEAMMEMBERS"],
            "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\n" + REQUEST_BODY_SCHEMAS["BULKCREATETEAMMEMBERS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["BULKCREATETEAMMEMBERS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/team-members/bulk-create",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKCREATETEAMMEMBERS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_WRITE"]))
async def bulk_update_team_members(
    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 'BulkUpdateTeamMembers'."]:
    """Update multiple team members in bulk.

    This tool updates multiple team member objects at once. It should be called when you need to perform bulk updates to team members. While processing, if any updates fail, the tool still returns the updated members along with specific error details for the failed attempts.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKUPDATETEAMMEMBERS"],
            "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\n" + REQUEST_BODY_SCHEMAS["BULKUPDATETEAMMEMBERS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["BULKUPDATETEAMMEMBERS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/team-members/bulk-update",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKUPDATETEAMMEMBERS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_READ"]))
async def list_jobs(
    context: ToolContext,
    pagination_cursor: Annotated[
        str | None,
        "The pagination cursor for retrieving the next page of results. Use the cursor from the previous call to continue fetching.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListJobs'."]:
    """Retrieve jobs from a seller's account, sorted by title.

    Use this tool to obtain a list of jobs associated with a seller's account. The jobs are organized in ascending order by their title."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/team-members/jobs",
        method="GET",
        params=remove_none_values({"cursor": pagination_cursor}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_WRITE"]))
async def create_job_in_seller_account(
    context: ToolContext,
    unique_creation_request_id: Annotated[
        str,
        "A unique string to identify the `CreateJob` request, ensuring it is not processed multiple times.",  # noqa: E501
    ],
    is_tip_eligible: Annotated[
        bool | None,
        "Indicates whether team members can earn tips for the job. Accepts a boolean value.",
    ] = None,
    job_creation_timestamp: Annotated[
        str | None, "The timestamp for when the job was created, in RFC 3339 format."
    ] = None,
    job_id: Annotated[
        str | None,
        "Unique Square-assigned job ID. This is read-only and used internally by Square.",
    ] = None,
    job_last_updated_timestamp: Annotated[
        str | None, "The timestamp indicating when the job was last updated, in RFC 3339 format."
    ] = None,
    job_title: Annotated[
        str | None, "The title of the job to be created in the seller's account."
    ] = None,
    job_version_readonly: Annotated[
        int | None,
        "Read-only field for the current version of the job, used for optimistic concurrency in `UpdateJob` requests.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateJob'."]:
    """Create a job for a seller account.

    This tool is used to create a job in a seller's account, specifying the job title and its tip eligibility. It should be called when you need to define a new job role for team members in a seller's business operations."""  # noqa: E501
    request_data = remove_none_values({
        "job": {
            "id": job_id,
            "title": job_title,
            "is_tip_eligible": is_tip_eligible,
            "created_at": job_creation_timestamp,
            "updated_at": job_last_updated_timestamp,
            "version": job_version_readonly,
        },
        "idempotency_key": unique_creation_request_id,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/team-members/jobs",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_READ"]))
async def retrieve_job_details(
    context: ToolContext,
    job_identifier: Annotated[str, "The unique string ID of the job to retrieve details for."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveJob'."]:
    """Retrieve details of a specified job.

    Use this tool to fetch detailed information about a specific job using the job's unique identifier."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/team-members/jobs/{job_id}".format(  # noqa: UP032
            job_id=job_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_WRITE"]))
async def update_job_details(
    context: ToolContext,
    job_id_to_update: Annotated[
        str,
        "The unique ID of the job to update. This specifies which job's title or tip eligibility you want to modify.",  # noqa: E501
    ],
    enable_tips_for_job: Annotated[
        bool | None,
        "Set to true to allow team members to earn tips for the job, false to prevent them.",
    ] = None,
    job_creation_timestamp: Annotated[
        str | None, "The timestamp representing when the job was created, formatted in RFC 3339."
    ] = None,
    job_id: Annotated[
        str | None,
        "The unique Square-assigned ID of the job. Obtainable via ListJobs API or from team member wage settings.",  # noqa: E501
    ] = None,
    job_last_updated_timestamp: Annotated[
        str | None,
        "The timestamp when the job was last updated, in RFC 3339 format. Used for optimistic concurrency control.",  # noqa: E501
    ] = None,
    job_title: Annotated[str | None, "The new title of the job to update."] = None,
    job_version_for_concurrency: Annotated[
        int | None,
        "The current version of the job for optimistic concurrency control. Must match the server version to proceed with updates.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateJob'."]:
    """Update job title or tip eligibility in the system.

    Use this tool to update the title or tip eligibility of a job within the system. The changes will affect all related job assignments, shifts, and team member wages associated with the job ID."""  # noqa: E501
    request_data = remove_none_values({
        "job": {
            "id": job_id,
            "title": job_title,
            "is_tip_eligible": enable_tips_for_job,
            "created_at": job_creation_timestamp,
            "updated_at": job_last_updated_timestamp,
            "version": job_version_for_concurrency,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/team-members/jobs/{job_id}".format(  # noqa: UP032
            job_id=job_id_to_update
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_READ"]))
async def search_team_members(
    context: ToolContext,
    filter_by_location_ids: Annotated[
        list[str] | None,
        "Filter team members by specified location IDs. If empty, includes all locations.",
    ] = None,
    maximum_team_members_per_page: Annotated[
        int | None, "Specify the maximum number of team members to return per page, default is 100."
    ] = None,
    pagination_cursor: Annotated[
        str | None, "The cursor used to retrieve the next page of results in a paginated list."
    ] = None,
    return_account_owner_only: Annotated[
        bool | None, "Set to true to return only the team member who is the Square account owner."
    ] = None,
    team_member_status: Annotated[
        str | None, "Filter team members by their status: 'ACTIVE' or 'INACTIVE'."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchTeamMembers'."]:
    """Retrieve a filtered list of team members for a business.

    This tool retrieves a paginated list of `TeamMember` objects associated with a business. It allows filtering by location IDs, status (`ACTIVE` or `INACTIVE`), or whether the team member is the Square account owner. Use this to manage or view team member details based on specific criteria."""  # noqa: E501
    request_data = remove_none_values({
        "query": {
            "filter": {
                "location_ids": filter_by_location_ids,
                "status": team_member_status,
                "is_owner": return_account_owner_only,
            }
        },
        "limit": maximum_team_members_per_page,
        "cursor": pagination_cursor,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/team-members/search",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_READ"]))
async def get_team_member_details(
    context: ToolContext,
    team_member_id: Annotated[
        str, "The unique identifier for the team member to retrieve their details from Squareup."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveTeamMember'."]:
    """Retrieve details for a specific team member by ID.

    Use this tool to get information about a specific team member using their ID. Useful for managing team details and troubleshooting within the Squareup platform."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/team-members/{team_member_id}".format(  # noqa: UP032
            team_member_id=team_member_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_WRITE"]))
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",
    ],
    team_member_id: Annotated[
        str | None,
        "The unique identifier of the team member to update.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateTeamMember'."]:
    """Updates a single TeamMember object.

    Use this tool to update details of a TeamMember in your organization. It should be called when you need to modify information about an existing team member. The updated TeamMember object is returned upon successful update.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATETEAMMEMBER"],
            "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_member_id:
        missing_params.append(("team_member_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\n" + REQUEST_BODY_SCHEMAS["UPDATETEAMMEMBER"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATETEAMMEMBER"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/team-members/{team_member_id}".format(  # noqa: UP032
            team_member_id=team_member_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATETEAMMEMBER"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_READ"]))
async def retrieve_team_member_wage_setting(
    context: ToolContext,
    team_member_id: Annotated[
        str, "The unique identifier for the team member whose wage setting needs to be retrieved."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveWageSetting'."]:
    """Retrieve wage settings for a specified team member.

    This tool retrieves the wage settings of a team member using their TeamMember ID. It's useful for accessing specific wage details related to a team member. If troubleshooting is needed, refer to the linked Square documentation."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/team-members/{team_member_id}/wage-setting".format(  # noqa: UP032
            team_member_id=team_member_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["EMPLOYEES_WRITE"]))
async def update_wage_setting(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    team_member_id: Annotated[
        str | None,
        "The unique ID of the team member whose WageSetting will be updated or created.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateWageSetting'."]:
    """Create or update a team member's wage setting.

    This tool creates or updates a WageSetting object for a specified team member. Use it to manage wage settings by creating a new object if it doesn't exist, or replacing an existing one. Upon successful update, it returns the updated WageSetting object.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEWAGESETTING"],
            "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_member_id:
        missing_params.append(("team_member_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\n" + REQUEST_BODY_SCHEMAS["UPDATEWAGESETTING"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATEWAGESETTING"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/team-members/{team_member_id}/wage-setting".format(  # noqa: UP032
            team_member_id=team_member_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEWAGESETTING"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE"]))
async def create_terminal_action(
    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 'CreateTerminalAction'."]:
    """Create and send a terminal action request to a device.

    This tool creates a terminal action request and sends it to the specified device using the SquareUp API. It should be called when you need to initiate an action on a terminal device.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATETERMINALACTION"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATETERMINALACTION"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATETERMINALACTION"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/terminals/actions",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATETERMINALACTION"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_READ"]))
async def search_terminal_actions(
    context: ToolContext,
    device_id_filter: Annotated[
        str | None,
        "Filter TerminalActions by a specific device ID. Leave blank to include all devices.",
    ] = None,
    end_time_rfc3339: Annotated[
        str | None, "A datetime in RFC 3339 format indicating the end of the time range."
    ] = None,
    filter_terminal_action_status: Annotated[
        str | None,
        "Filter results by the status of the TerminalAction (e.g., `PENDING`, `IN_PROGRESS`, `CANCEL_REQUESTED`, `CANCELED`, `COMPLETED`).",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A pagination cursor from a previous response to retrieve the next set of results.",
    ] = None,
    result_limit: Annotated[
        int | None, "Limit the number of results returned for a single request."
    ] = None,
    result_sort_order: Annotated[
        str | None, "Defines the order ('DESC' or 'ASC') for sorting the terminal action requests."
    ] = None,
    start_time_rfc3339: Annotated[
        str | None, "The start datetime in RFC 3339 format for filtering terminal actions."
    ] = None,
    terminal_action_type: Annotated[
        str | None,
        "Specify the type of terminal action, such as 'QR_CODE', 'PING', etc. This helps filter actions by their purpose.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchTerminalActions'."]:
    """Retrieve a filtered list of terminal action requests.

    Use this tool to get a list of terminal action requests created by your account. Terminal actions are accessible for 30 days."""  # noqa: E501
    request_data = remove_none_values({
        "query": {
            "filter": {
                "device_id": device_id_filter,
                "created_at": {"start_at": start_time_rfc3339, "end_at": end_time_rfc3339},
                "status": filter_terminal_action_status,
                "type": terminal_action_type,
            },
            "sort": {"sort_order": result_sort_order},
        },
        "cursor": pagination_cursor,
        "limit": result_limit,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/terminals/actions/search",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_READ"]))
async def retrieve_terminal_action(
    context: ToolContext,
    terminal_action_id: Annotated[
        str,
        "Unique ID for the desired Terminal Action. This is required to retrieve the specific action request.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTerminalAction'."]:
    """Retrieve a Terminal action request by action ID.

    Call this tool to get detailed information about a specific Terminal action using its action ID. Terminal action requests are available for retrieval up to 30 days."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/terminals/actions/{action_id}".format(  # noqa: UP032
            action_id=terminal_action_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE"]))
async def cancel_terminal_action(
    context: ToolContext,
    terminal_action_id: Annotated[
        str,
        "Unique ID for the `TerminalAction` you want to cancel. This ID helps target the specific action to abort.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelTerminalAction'."]:
    """Cancel a terminal action request if possible.

    This tool cancels a terminal action request with SquareUp if the current status allows it. Use it when you need to abort a terminal action that is still pending or in progress."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/terminals/actions/{action_id}/cancel".format(  # noqa: UP032
            action_id=terminal_action_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def dismiss_terminal_action(
    context: ToolContext,
    terminal_action_id: Annotated[str, "Unique ID for the TerminalAction to be dismissed."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DismissTerminalAction'."]:
    """Dismiss a Terminal action request if permitted.

    This tool dismisses a Terminal action request when the status and type allow it. Use it for canceling or dismissing actions on a Square Terminal. Refer to the Square documentation for more about linking and dismissing actions."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/terminals/actions/{action_id}/dismiss".format(  # noqa: UP032
            action_id=terminal_action_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE"]))
async def create_terminal_checkout(
    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 'CreateTerminalCheckout'."]:
    """Create a Terminal checkout request for payment.

    Initiates a Terminal checkout request to process a payment on a specified device using Square's Terminal API.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATETERMINALCHECKOUT"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATETERMINALCHECKOUT"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATETERMINALCHECKOUT"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/terminals/checkouts",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATETERMINALCHECKOUT"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_READ"]))
async def search_terminal_checkouts(
    context: ToolContext,
    checkout_status_filter: Annotated[
        str | None,
        "Specify the desired status to filter TerminalCheckout results. Options: PENDING, IN_PROGRESS, CANCEL_REQUESTED, CANCELED, COMPLETED.",  # noqa: E501
    ] = None,
    device_id_filter: Annotated[
        str | None,
        "Filters TerminalCheckout objects associated with a specific device. Omitting this shows checkouts for all devices.",  # noqa: E501
    ] = None,
    end_time_rfc3339: Annotated[
        str | None, "A datetime in RFC 3339 format indicating when the time range ends."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A pagination cursor from a previous call, used to fetch the next set of results for the same query. Useful for traversing paginated results.",  # noqa: E501
    ] = None,
    result_limit: Annotated[
        int | None, "Maximum number of results to return in a single request."
    ] = None,
    result_sort_order: Annotated[
        str | None,
        "Specifies the order (DESC or ASC) for results in a request, such as chronological or alphabetical.",  # noqa: E501
    ] = None,
    start_time_range: Annotated[
        str | None, "The start datetime for the TerminalCheckout search in RFC 3339 format."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchTerminalCheckouts'."]:
    """Retrieve filtered Terminal checkout requests for the merchant.

    This tool returns a filtered list of Terminal checkout requests created specifically by the application making the request. It should be called when there's a need to access Terminal checkout requests for the merchant associated with the OAuth token. Requests are available for 30 days."""  # noqa: E501
    request_data = remove_none_values({
        "query": {
            "filter": {
                "device_id": device_id_filter,
                "created_at": {"start_at": start_time_range, "end_at": end_time_rfc3339},
                "status": checkout_status_filter,
            },
            "sort": {"sort_order": result_sort_order},
        },
        "cursor": pagination_cursor,
        "limit": result_limit,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/terminals/checkouts/search",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_READ"]))
async def retrieve_terminal_checkout(
    context: ToolContext,
    checkout_id: Annotated[
        str,
        "The unique ID for the desired TerminalCheckout request. Use this ID to retrieve specific checkout details.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTerminalCheckout'."]:
    """Retrieve a Terminal checkout request by checkout ID.

    Use this tool to get details of a Terminal checkout request using a specific checkout ID. Ideal for accessing checkout information within a 30-day period."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/terminals/checkouts/{checkout_id}".format(  # noqa: UP032
            checkout_id=checkout_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE"]))
async def cancel_terminal_checkout(
    context: ToolContext,
    terminal_checkout_id: Annotated[
        str, "The unique ID for the desired TerminalCheckout to be canceled."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelTerminalCheckout'."]:
    """Cancel a terminal checkout request if possible.

    Use this tool to cancel an ongoing Terminal checkout request if its status allows for cancellation."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/terminals/checkouts/{checkout_id}/cancel".format(  # noqa: UP032
            checkout_id=terminal_checkout_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def dismiss_terminal_checkout(
    context: ToolContext,
    terminal_checkout_id: Annotated[str, "Unique ID for the `TerminalCheckout` to be dismissed."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DismissTerminalCheckout'."]:
    """Dismiss a Terminal checkout request.

    Use this tool to dismiss a Terminal checkout request when the status and type allow dismissal."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/terminals/checkouts/{checkout_id}/dismiss".format(  # noqa: UP032
            checkout_id=terminal_checkout_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE"]))
async def create_terminal_refund(
    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 'CreateTerminalRefund'."]:
    """Creates a refund request for Interac payments on Square Terminal.

    This tool creates a request to refund an Interac payment completed on a Square Terminal. It is specifically for Interac debit card payments in Canada. For other types of refunds, use the Refunds API.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATETERMINALREFUND"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATETERMINALREFUND"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATETERMINALREFUND"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/terminals/refunds",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATETERMINALREFUND"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_READ"]))
async def search_terminal_refunds(
    context: ToolContext,
    device_id_filter: Annotated[
        str | None,
        "`TerminalRefund` objects associated with a specific device. If not provided, all refunds for the account are displayed.",  # noqa: E501
    ] = None,
    end_time_range: Annotated[
        str | None,
        "A datetime value in RFC 3339 format indicating the end of the time range for filtering terminal refund requests.",  # noqa: E501
    ] = None,
    filter_terminal_refund_status: Annotated[
        str | None,
        "Filter terminal refunds by status. Options: `PENDING`, `IN_PROGRESS`, `CANCEL_REQUESTED`, `CANCELED`, or `COMPLETED`.",  # noqa: E501
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A cursor to paginate results, used to retrieve the next set from a previous query.",
    ] = None,
    result_limit: Annotated[
        int | None,
        "Specifies the maximum number of refund results to retrieve in a single request.",
    ] = None,
    sort_order: Annotated[
        str | None,
        "The order in which terminal refund results are listed. Use 'ASC' for oldest to newest or 'DESC' for newest to oldest (default).",  # noqa: E501
    ] = None,
    start_datetime: Annotated[
        str | None,
        "A datetime in RFC 3339 format. Indicates when the time range starts for filtering terminal refunds.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchTerminalRefunds'."]:
    """Retrieve a filtered list of Interac Terminal refund requests.

    Use this tool to get Interac Terminal refund requests created by the seller, available for up to 30 days. Ideal for managing recent refund activities or auditing transactions."""  # noqa: E501
    request_data = remove_none_values({
        "query": {
            "filter": {
                "device_id": device_id_filter,
                "created_at": {"start_at": start_datetime, "end_at": end_time_range},
                "status": filter_terminal_refund_status,
            },
            "sort": {"sort_order": sort_order},
        },
        "cursor": pagination_cursor,
        "limit": result_limit,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/terminals/refunds/search",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_READ"]))
async def get_terminal_refund(
    context: ToolContext,
    terminal_refund_id: Annotated[
        str,
        "The unique ID for the desired TerminalRefund. Use this ID to retrieve specific refund details available for 30 days.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTerminalRefund'."]:
    """Retrieve details of an Interac Terminal refund by ID.

    Call this tool to get information about a specific Interac Terminal refund using its ID. Refund details are available for 30 days after the refund is processed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/terminals/refunds/{terminal_refund_id}".format(  # noqa: UP032
            terminal_refund_id=terminal_refund_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["PAYMENTS_WRITE"]))
async def cancel_terminal_refund(
    context: ToolContext,
    refund_request_id: Annotated[str, "The unique ID of the terminal refund request to cancel."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelTerminalRefund'."]:
    """Cancel a terminal refund request by its ID.

    Use this tool to cancel a terminal refund request when the status allows for cancellation. Provide the refund request ID to execute the cancellation."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/terminals/refunds/{terminal_refund_id}/cancel".format(  # noqa: UP032
            terminal_refund_id=refund_request_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def dismiss_terminal_refund(
    context: ToolContext,
    terminal_refund_unique_id: Annotated[
        str, "Unique ID for the TerminalRefund associated with the refund to be dismissed."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DismissTerminalRefund'."]:
    """Dismiss a Terminal refund request.

    Use this tool to dismiss a Terminal refund request if it's allowable based on the status and type of the request."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/terminals/refunds/{terminal_refund_id}/dismiss".format(  # noqa: UP032
            terminal_refund_id=terminal_refund_unique_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ", "INVENTORY_WRITE"]))
async def create_transfer_order(
    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 'CreateTransferOrder'."]:
    """Create a draft transfer order between locations.

    This tool creates a new transfer order in DRAFT status to move CatalogItemVariations between different locations within a Square account. It should be called when there's a need to initiate the transfer of items, allowing you to add or remove items, modify quantities, or update shipping information before finalizing.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATETRANSFERORDER"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATETRANSFERORDER"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATETRANSFERORDER"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/transfer-orders",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATETRANSFERORDER"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ"]))
async def search_transfer_orders(
    context: ToolContext,
    destination_location_ids: Annotated[
        list[str] | None, "Array of destination location IDs to filter transfer orders."
    ] = None,
    filter_by_order_statuses: Annotated[
        list[str] | None,
        "Filter transfer orders by their statuses. Accepts an array of status strings. Refer to TransferOrderStatus for valid values.",  # noqa: E501
    ] = None,
    maximum_results: Annotated[
        int | None, "Specify the maximum number of results to return, from 1 to 100."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A string token to continue a search from a previous position, enabling pagination through results.",  # noqa: E501
    ] = None,
    sort_by_field: Annotated[
        str | None, "Specify the field to sort transfer orders. Options: CREATED_AT, UPDATED_AT."
    ] = None,
    sort_order: Annotated[
        str | None,
        "Specify the order ('DESC' or 'ASC') in which results are returned. 'DESC' for descending and 'ASC' for ascending.",  # noqa: E501
    ] = None,
    source_location_ids: Annotated[
        list[str] | None,
        "Array of source location IDs to filter transfer orders by their source location.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchTransferOrders'."]:
    """Search for transfer orders using specific filters.

    This tool searches for transfer orders using various filters, returning a paginated list of matching orders sorted by creation date. It can find orders by source or destination location or by a specific transfer order status."""  # noqa: E501
    request_data = remove_none_values({
        "query": {
            "filter": {
                "source_location_ids": source_location_ids,
                "destination_location_ids": destination_location_ids,
                "statuses": filter_by_order_statuses,
            },
            "sort": {"field": sort_by_field, "order": sort_order},
        },
        "cursor": pagination_cursor,
        "limit": maximum_results,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/transfer-orders/search",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_WRITE"]))
async def delete_draft_transfer_order(
    context: ToolContext,
    transfer_order_id: Annotated[
        str,
        "The ID of the transfer order in DRAFT status to delete. Only draft orders are eligible.",
    ],
    optimistic_concurrency_version: Annotated[
        int | None,
        "Version number used for optimistic concurrency control to ensure data consistency when deleting a draft transfer order.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteTransferOrder'."]:
    """Delete a draft transfer order and trigger webhook event.

    Use this tool to delete a transfer order in DRAFT status. Only applicable to orders that have not yet been started. A webhook event 'transfer_order.deleted' is triggered upon deletion."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/transfer-orders/{transfer_order_id}".format(  # noqa: UP032
            transfer_order_id=transfer_order_id
        ),
        method="DELETE",
        params=remove_none_values({"version": optimistic_concurrency_version}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ"]))
async def retrieve_transfer_order_details(
    context: ToolContext,
    transfer_order_id: Annotated[
        str, "The ID of the transfer order to retrieve, required to get order details."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveTransferOrder'."]:
    """Retrieve detailed information of a specific transfer order.

    Use this tool to get comprehensive details of a transfer order by providing its ID. This includes status, dates, notes, line items, source and destination locations, and tracking info if available."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/transfer-orders/{transfer_order_id}".format(  # noqa: UP032
            transfer_order_id=transfer_order_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ", "INVENTORY_WRITE"]))
async def update_transfer_order(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    transfer_order_identifier: Annotated[
        str | None,
        "The unique identifier of the transfer order to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateTransferOrder'."]:
    """Update specific fields of a transfer order.

    Use this tool to modify particular fields of an existing transfer order without affecting others. This will trigger a transfer_order.updated webhook event.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATETRANSFERORDER"],
            "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 transfer_order_identifier:
        missing_params.append(("transfer_order_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\n" + REQUEST_BODY_SCHEMAS["UPDATETRANSFERORDER"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATETRANSFERORDER"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/transfer-orders/{transfer_order_id}".format(  # noqa: UP032
            transfer_order_id=transfer_order_identifier
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATETRANSFERORDER"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ", "INVENTORY_WRITE"]))
async def cancel_transfer_order(
    context: ToolContext,
    transfer_order_id: Annotated[
        str,
        "The ID of the transfer order to cancel. The order must be in STARTED or PARTIALLY_RECEIVED status.",  # noqa: E501
    ],
    unique_request_key: Annotated[
        str,
        "A unique string to identify this request. Must be unique for each UpdateTransferOrder request.",  # noqa: E501
    ],
    transfer_order_version: Annotated[
        int | None,
        "Provide the version number for optimistic concurrency when canceling the transfer order.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CancelTransferOrder'."]:
    """Cancel a transfer order in progress for inventory locations.

    Use this tool to cancel a transfer order that is in either STARTED or PARTIALLY_RECEIVED status. This is useful in situations where items are no longer needed at the destination, the source location requires the inventory, or if the order was created by mistake. Cancelling the order will update inventory levels and trigger a transfer_order.updated webhook event."""  # noqa: E501
    request_data = remove_none_values({
        "idempotency_key": unique_request_key,
        "version": transfer_order_version,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/transfer-orders/{transfer_order_id}/cancel".format(  # noqa: UP032
            transfer_order_id=transfer_order_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ", "INVENTORY_WRITE"]))
async def record_transfer_order_receipt(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    transfer_order_id: Annotated[
        str | None,
        "The ID of the transfer order for which items are being received. This ID is required to process the receipt of items, including partial and damaged quantities, and update inventory accordingly.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",  # noqa: E501
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ReceiveTransferOrder'."]:
    """Record received items for a transfer order.

    Use this tool to record the receipt of items for a transfer order, including handling partial receipts and damaged items. It updates the inventory based on item condition and progresses the order status.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["RECORDTRANSFERORDERRECEIPT"],
            "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 transfer_order_id:
        missing_params.append(("transfer_order_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\n"
                + REQUEST_BODY_SCHEMAS["RECORDTRANSFERORDERRECEIPT"]
                + "\n```"
            ),
        )

    # 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\n"
                + REQUEST_BODY_SCHEMAS["RECORDTRANSFERORDERRECEIPT"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/transfer-orders/{transfer_order_id}/receive".format(  # noqa: UP032
            transfer_order_id=transfer_order_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["RECORDTRANSFERORDERRECEIPT"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["INVENTORY_READ", "INVENTORY_WRITE"]))
async def start_transfer_order(
    context: ToolContext,
    transfer_order_id: Annotated[
        str, "The ID of the transfer order to start. Must be in DRAFT status."
    ],
    unique_request_identifier: Annotated[
        str,
        "A unique string to identify each UpdateTransferOrder request, ensuring it is not repeated for any request.",  # noqa: E501
    ],
    optimistic_concurrency_version: Annotated[
        int | None,
        "Specify the version number for optimistic concurrency control. Ensure the version is current to avoid conflicts.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'StartTransferOrder'."]:
    """Start a transfer order to mark it as in-transit.

    Use this tool to change a draft transfer order to started status, which decrements inventory at the source location and marks the order as in-transit. Once started, the order is locked from deletion but can be canceled. A webhook event is created upon starting the order."""  # noqa: E501
    request_data = remove_none_values({
        "idempotency_key": unique_request_identifier,
        "version": optimistic_concurrency_version,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/transfer-orders/{transfer_order_id}/start".format(  # noqa: UP032
            transfer_order_id=transfer_order_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["VENDOR_WRITE"]))
async def bulk_create_vendors(
    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 'BulkCreateVendors'."]:
    """Create multiple vendor profiles for suppliers.

    Use this tool to create one or more vendor profiles, representing suppliers for a seller, in bulk. Ideal for onboarding multiple vendors at once.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKCREATEVENDORS"],
            "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\n" + REQUEST_BODY_SCHEMAS["BULKCREATEVENDORS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["BULKCREATEVENDORS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/vendors/bulk-create",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKCREATEVENDORS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["VENDOR_READ"]))
async def retrieve_vendors(
    context: ToolContext,
    vendor_ids: Annotated[
        list[str] | None,
        "List of vendor IDs to retrieve details for. Provide the IDs as an array of strings.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'BulkRetrieveVendors'."]:
    """Retrieve detailed information about specific vendors.

    Use this tool to obtain detailed information for vendors by their IDs. Ideal for managing vendor relationships or verifying vendor details."""  # noqa: E501
    request_data = remove_none_values({"vendor_ids": vendor_ids})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/vendors/bulk-retrieve",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["VENDOR_WRITE"]))
async def bulk_update_vendors(
    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 'BulkUpdateVendors'."]:
    """Update multiple vendor records simultaneously.

    Use this tool to update details of multiple vendors at once. It modifies existing vendor records that are suppliers to a seller, streamlining vendor management tasks.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["BULKUPDATEVENDORS"],
            "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\n" + REQUEST_BODY_SCHEMAS["BULKUPDATEVENDORS"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["BULKUPDATEVENDORS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/vendors/bulk-update",
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["BULKUPDATEVENDORS"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["VENDOR_WRITE"]))
async def create_vendor(
    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 'CreateVendor'."]:
    """Create a vendor for a supplier to a seller.

    This tool creates a new vendor object to represent a supplier in the system. Use it when you need to add or register a vendor for business operations.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEVENDOR"],
            "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\n" + REQUEST_BODY_SCHEMAS["CREATEVENDOR"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["CREATEVENDOR"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/vendors/create",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEVENDOR"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["VENDOR_READ"]))
async def search_vendors(
    context: ToolContext,
    pagination_cursor: Annotated[
        str | None,
        "A string used to retrieve the next set of results for a previous query. Follow the pagination guide for details.",  # noqa: E501
    ] = None,
    sort_field_for_vendors: Annotated[
        str | None,
        "Specify the vendor property field to sort the results by. Options include 'NAME' or 'CREATED_AT'.",  # noqa: E501
    ] = None,
    sort_order: Annotated[
        str | None,
        "Specify the order (e.g., chronological or alphabetical) for sorting the results. Options are 'ASC' or 'DESC'.",  # noqa: E501
    ] = None,
    vendor_names_to_filter: Annotated[
        list[str] | None,
        "Array of vendor names to filter the search results by. Only vendors matching these names will be retrieved.",  # noqa: E501
    ] = None,
    vendor_statuses: Annotated[
        list[str] | None,
        "List of vendor statuses to filter the search results. Refer to possible values in VendorStatus.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SearchVendors'."]:
    """Search for vendors using filters and sorters.

    Use this tool to find vendors by applying filters to vendor properties and sorting the results as needed. It is useful for retrieving vendor information based on specific criteria."""  # noqa: E501
    request_data = remove_none_values({
        "filter": {"name": vendor_names_to_filter, "status": vendor_statuses},
        "sort": {"field": sort_field_for_vendors, "order": sort_order},
        "cursor": pagination_cursor,
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/vendors/search",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["VENDOR_READ"]))
async def retrieve_vendor_details(
    context: ToolContext,
    vendor_id: Annotated[str, "The unique ID of the vendor to retrieve details for."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveVendor'."]:
    """Retrieve detailed information about a vendor by ID.

    This tool retrieves information about a specific vendor using their Vendor ID and should be called when vendor details are needed."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/vendors/{vendor_id}".format(vendor_id=vendor_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup", scopes=["VENDOR_WRITE"]))
async def update_vendor_info(
    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 'UpdateVendor'."]:
    """Update an existing vendor's information.

    Use this tool to modify the details of an existing vendor who supplies to a seller. It updates the vendor's profile in the system.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """  # noqa: E501
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEVENDORINFO"],
            "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\n" + REQUEST_BODY_SCHEMAS["UPDATEVENDORINFO"] + "\n```"
            ),
        )

    # 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\n" + REQUEST_BODY_SCHEMAS["UPDATEVENDORINFO"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://connect.squareup.com/v2/vendors/{vendor_id}",
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEVENDORINFO"],
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def list_webhook_event_types(
    context: ToolContext,
    api_version_for_event_types: Annotated[
        str | None, "Specify the API version to list event types, overriding the default version."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListWebhookEventTypes'."]:
    """Retrieve all webhook event types available for subscription.

    This tool is used to fetch a list of all available webhook event types that can be subscribed to. It helps users identify which events they can set up webhooks for in order to receive notifications."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/webhooks/event-types",
        method="GET",
        params=remove_none_values({"api_version": api_version_for_event_types}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def list_webhook_subscriptions(
    context: ToolContext,
    include_disabled_subscriptions: Annotated[
        bool | None,
        "If set to true, includes disabled subscriptions in the results. By default, only enabled subscriptions are returned.",  # noqa: E501
    ] = False,
    maximum_results_per_page: Annotated[
        int | None, "The maximum number of results returned in one page, up to 100."
    ] = None,
    pagination_cursor: Annotated[
        str | None,
        "A cursor from a previous call for paginating results. Use it to retrieve the next set of results.",  # noqa: E501
    ] = None,
    sort_order: Annotated[
        str | None,
        "Sort returned list by subscription creation date. Options are 'ASC' for ascending or 'DESC' for descending. Defaults to 'ASC'.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'ListWebhookSubscriptions'."]:
    """Lists all webhook subscriptions owned by the application.

    Use this tool to retrieve all webhook subscriptions associated with your Square application. It provides a comprehensive list of active webhook subscriptions."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/webhooks/subscriptions",
        method="GET",
        params=remove_none_values({
            "cursor": pagination_cursor,
            "include_disabled": include_disabled_subscriptions,
            "sort_order": sort_order,
            "limit": maximum_results_per_page,
        }),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def create_webhook_subscription(
    context: ToolContext,
    api_version: Annotated[
        str | None,
        "Specifies the API version for the webhook subscription. Optional; defaults to the application's current API version if not provided.",  # noqa: E501
    ] = None,
    enable_subscription: Annotated[
        bool | None, "Indicates whether the subscription is enabled (`true`) or not (`false`)."
    ] = None,
    event_types: Annotated[
        list[str] | None, "An array of event types for the subscription, each as a string."
    ] = None,
    subscription_created_at: Annotated[
        str | None,
        'The creation timestamp of the subscription in RFC 3339 format, e.g., "2016-09-04T23:59:33.123Z".',  # noqa: E501
    ] = None,
    subscription_last_updated_timestamp: Annotated[
        str | None,
        "The timestamp of when the subscription was last updated, in RFC 3339 format (e.g., '2016-09-04T23:59:33.123Z').",  # noqa: E501
    ] = None,
    subscription_name: Annotated[str | None, "The name for the webhook subscription."] = None,
    subscription_signature_key: Annotated[
        str | None,
        "The Square-generated signature key used to validate the origin of the webhook event.",
    ] = None,
    subscription_unique_id: Annotated[
        str | None, "A Square-generated unique ID for the webhook subscription."
    ] = None,
    unique_request_identifier: Annotated[
        str | None,
        "A unique string to ensure the idempotence of the CreateWebhookSubscription request, preventing duplicate entries.",  # noqa: E501
    ] = None,
    webhook_notification_url: Annotated[
        str | None,
        "The URL where webhook notifications will be sent. Ensure it is accessible and accurately formatted.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateWebhookSubscription'."]:
    """Creates a webhook subscription.

    Use this tool to establish a webhook subscription for receiving event notifications from the service."""  # noqa: E501
    request_data = remove_none_values({
        "idempotency_key": unique_request_identifier,
        "subscription": {
            "id": subscription_unique_id,
            "name": subscription_name,
            "enabled": enable_subscription,
            "event_types": event_types,
            "notification_url": webhook_notification_url,
            "api_version": api_version,
            "signature_key": subscription_signature_key,
            "created_at": subscription_created_at,
            "updated_at": subscription_last_updated_timestamp,
        },
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/webhooks/subscriptions",
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def delete_webhook_subscription(
    context: ToolContext,
    webhook_subscription_id: Annotated[
        str,
        "The ID of the webhook subscription to delete. This ID is required to specify which subscription needs to be removed.",  # noqa: E501
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteWebhookSubscription'."]:
    """Deletes a specified webhook subscription.

    Use this tool to delete an existing webhook subscription by providing the subscription ID. It should be called when there is a need to remove a webhook subscription."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/webhooks/subscriptions/{subscription_id}".format(  # noqa: UP032
            subscription_id=webhook_subscription_id
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def retrieve_webhook_subscription(
    context: ToolContext,
    webhook_subscription_id: Annotated[
        str, "The unique ID of the webhook subscription to retrieve."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RetrieveWebhookSubscription'."]:
    """Retrieve details of a specific webhook subscription.

    This tool retrieves information about a specific webhook subscription using its unique ID. It should be called when you need to access details of a webhook provided by the subscription ID."""  # noqa: E501
    request_data = remove_none_values({})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/webhooks/subscriptions/{subscription_id}".format(  # noqa: UP032
            subscription_id=webhook_subscription_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            )
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def update_webhook_subscription(
    context: ToolContext,
    subscription_id: Annotated[
        str,
        "The unique ID of the subscription to update. This is required to identify the webhook subscription that needs to be modified.",  # noqa: E501
    ],
    api_version_of_subscription: Annotated[
        str | None,
        "Specify the API version for the subscription. Optional for creation and defaults to the application's API version if not provided.",  # noqa: E501
    ] = None,
    event_types: Annotated[
        list[str] | None,
        "An array of event types associated with this subscription that trigger the webhook.",
    ] = None,
    signature_key: Annotated[
        str | None, "The Square-generated signature key for validating the webhook origin."
    ] = None,
    subscription_created_timestamp: Annotated[
        str | None,
        "The timestamp indicating when the subscription was created, formatted in RFC 3339.",
    ] = None,
    subscription_enabled: Annotated[
        bool | None, "Set to `true` to enable the subscription or `false` to disable it."
    ] = None,
    subscription_name: Annotated[
        str | None, "The new name for the webhook subscription to update."
    ] = None,
    subscription_unique_id: Annotated[
        str | None,
        "A Square-generated unique ID for the webhook subscription to be updated. This is required to identify which subscription to modify.",  # noqa: E501
    ] = None,
    updated_at_timestamp: Annotated[
        str | None,
        'The timestamp of the last update for the subscription, in RFC 3339 format (e.g., "2016-09-04T23:59:33.123Z").',  # noqa: E501
    ] = None,
    webhook_notification_url: Annotated[
        str | None,
        "The URL to which webhooks are sent. Must be a valid URL that can receive POST requests.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateWebhookSubscription'."]:
    """Update a webhook subscription to modify its settings.

    This tool is used to update an existing webhook subscription by providing necessary changes to its settings. It should be called when there's a need to modify the webhook's parameters or configuration."""  # noqa: E501
    request_data = remove_none_values({
        "subscription": {
            "id": subscription_unique_id,
            "name": subscription_name,
            "enabled": subscription_enabled,
            "event_types": event_types,
            "notification_url": webhook_notification_url,
            "api_version": api_version_of_subscription,
            "signature_key": signature_key,
            "created_at": subscription_created_timestamp,
            "updated_at": updated_at_timestamp,
        }
    })
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/webhooks/subscriptions/{subscription_id}".format(  # noqa: UP032
            subscription_id=subscription_id
        ),
        method="PUT",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def update_webhook_signature_key(
    context: ToolContext,
    webhook_subscription_id: Annotated[
        str, "The ID of the Webhook Subscription to update. This is a required field."
    ],
    unique_request_identifier: Annotated[
        str | None,
        "A unique string to identify the UpdateWebhookSubscriptionSignatureKey request, ensuring idempotency.",  # noqa: E501
    ] = None,
) -> Annotated[
    dict[str, Any], "Response from the API endpoint 'UpdateWebhookSubscriptionSignatureKey'."
]:
    """Update a webhook subscription's signature key.

    Use this tool to replace the existing signature key for a webhook subscription with a new one. This is specifically for managing webhook security by updating the signature key."""  # noqa: E501
    request_data = remove_none_values({"idempotency_key": unique_request_identifier})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/webhooks/subscriptions/{subscription_id}/signature-key".format(  # noqa: UP032
            subscription_id=webhook_subscription_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}


@tool(requires_auth=OAuth2(id="arcade-squareup"))
async def test_webhook_subscription(
    context: ToolContext,
    webhook_subscription_id: Annotated[
        str,
        "The ID of the Webhook Subscription to test. This is required for sending a test event.",
    ],
    test_event_type: Annotated[
        str | None,
        "Specifies the event type for testing the webhook subscription. It must match an event type in the subscription's event list.",  # noqa: E501
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'TestWebhookSubscription'."]:
    """Send a test event to a webhook subscription URL.

    Use this tool to verify if a webhook subscription is correctly configured by sending a test event to the specified notification URL."""  # noqa: E501
    request_data = remove_none_values({"event_type": test_event_type})
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://connect.squareup.com/v2/webhooks/subscriptions/{subscription_id}/test".format(  # noqa: UP032
            subscription_id=webhook_subscription_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": "application/json",
            "Authorization": "Bearer {authorization}".format(  # noqa: UP032
                authorization=context.get_auth_token_or_empty()
            ),
        }),
        content=content,
    )
    try:
        return {"response_json": response.json()}
    except Exception:
        return {"response_text": response.text}
