"""Arcade Starter Tools for Clickup

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")


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-clickup"))
async def get_clickup_access_token(
    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 'GetAccessToken'."]:
    """Obtain an OAuth access token for ClickUp API authentication.

    Use this tool to obtain an OAuth access token for authenticating applications with the ClickUp API. This tool is not for personal API tokens and does not support the 'Try It' feature in browser-based API documentation.

    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["GETCLICKUPACCESSTOKEN"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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["GETCLICKUPACCESSTOKEN"] + "\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["GETCLICKUPACCESSTOKEN"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/oauth/token",
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["GETCLICKUPACCESSTOKEN"],
        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-clickup"))
async def get_clickup_user_details(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAuthorizedUser'."]:
    """Get details of the authenticated ClickUp user's account."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/user",
        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-clickup"))
async def get_authorized_teams(
    context: ToolContext,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAuthorizedTeams'."]:
    """Retrieve the workspaces for the authenticated user.

    Use this tool to get a list of workspaces (teams) available to the user who is currently authenticated. It provides an overview of the user's accessible workspaces in ClickUp."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team",
        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-clickup"))
async def add_checklist_to_task(
    context: ToolContext,
    checklist_name: Annotated[
        str,
        "The name or title of the checklist to be added to the task. It should be a descriptive string identifying the purpose or contents of the checklist.",
    ],
    task_identifier: Annotated[
        str,
        "A unique identifier for the task to which the checklist will be added. It can be a custom or default task ID.",
    ],
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id_for_custom_task: Annotated[
        int | None,
        "Provide the Workspace ID when 'custom_task_ids' is set to true. It's necessary to reference tasks by custom IDs.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateChecklist'."]:
    """Add a new checklist to a task in ClickUp.

    Use this tool to add a checklist to a specific task in ClickUp by providing the task ID."""
    request_data: Any = {"name": checklist_name}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/checklist".format(  # noqa: UP032
            task_id=task_identifier
        ),
        method="POST",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id_for_custom_task,
        }),
        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-clickup"))
async def edit_checklist(
    context: ToolContext,
    checklist_id: Annotated[
        str, "The unique identifier (UUID) of the checklist to be edited or reordered."
    ],
    checklist_name: Annotated[
        str | None, "The new name for the checklist. Leave empty if you do not wish to rename."
    ] = None,
    checklist_position: Annotated[
        int | None,
        "Specify the order in which the checklist should appear on a task. Use 0 to place it at the top.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'EditChecklist'."]:
    """Rename or reorder a task checklist in ClickUp.

    Use this tool to rename a checklist or change its order within a task in ClickUp. It is suitable when you need to update the checklist name or adjust its position in relation to other checklists in a specific task."""
    request_data: Any = {"name": checklist_name, "position": checklist_position}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/checklist/{checklist_id}".format(  # noqa: UP032
            checklist_id=checklist_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-clickup"))
async def delete_checklist(
    context: ToolContext,
    checklist_id: Annotated[
        str, "The unique identifier for the checklist to be deleted. It should be in UUID format."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteChecklist'."]:
    """Deletes a checklist from a task in ClickUp.

    Use this tool to delete a specific checklist from a task in ClickUp by providing the checklist ID. Ideal for when you need to manage or reorganize task checklists."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/checklist/{checklist_id}".format(  # noqa: UP032
            checklist_id=checklist_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-clickup"))
async def add_checklist_item_clickup(
    context: ToolContext,
    checklist_identifier: Annotated[
        str, "A unique identifier for the checklist. Must be in UUID format."
    ],
    assignee_user_id: Annotated[
        int | None,
        "The unique ID of the user assigned to the checklist item. This should be an integer value representing the user's ID.",
    ] = None,
    checklist_item_name: Annotated[
        str | None,
        "The name of the checklist item to be added. This should clearly describe the task or item to be completed.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateChecklistItem'."]:
    """Add an item to a checklist in ClickUp tasks.

    Use this tool to add a new line item to an existing checklist within a task in ClickUp. This can be useful for task management and ensuring all steps are documented within a checklist."""
    request_data: Any = {"name": checklist_item_name, "assignee": assignee_user_id}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/checklist/{checklist_id}/checklist_item".format(  # noqa: UP032
            checklist_id=checklist_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-clickup"))
async def update_checklist_item(
    context: ToolContext,
    checklist_item_uuid: Annotated[str, "The UUID for the specific checklist item to be updated."],
    checklist_unique_identifier: Annotated[
        str, "The UUID of the checklist to update. Example: b8a8-48d8-a0c6-b4200788a683."
    ],
    assign_item_to_user: Annotated[
        str | None,
        "The user ID to which the checklist item will be assigned. This should be a string representing a valid user identifier in ClickUp.",
    ] = None,
    checklist_item_name: Annotated[
        str | None, "The new name for the checklist item. Provide a string to rename the item."
    ] = None,
    mark_as_resolved: Annotated[
        bool | None, "Boolean to mark the checklist item as resolved (true) or unresolved (false)."
    ] = None,
    parent_checklist_item_id: Annotated[
        str | None, "Include another item's `checklist_item_id` to nest this item under it."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'EditChecklistItem'."]:
    """Modify or update a specific task checklist item.

    This tool updates an individual line item in a task checklist. It can rename the item, set the assignee, mark it as resolved, or nest it under another item."""
    request_data: Any = {
        "name": checklist_item_name,
        "assignee": assign_item_to_user,
        "resolved": mark_as_resolved,
        "parent": parent_checklist_item_id,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/checklist/{checklist_id}/checklist_item/{checklist_item_id}".format(  # noqa: UP032
            checklist_id=checklist_unique_identifier, checklist_item_id=checklist_item_uuid
        ),
        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-clickup"))
async def delete_task_checklist_item(
    context: ToolContext,
    checklist_identifier: Annotated[
        str,
        "The unique identifier (UUID) for the checklist. Used to specify the checklist from which the item will be deleted.",
    ],
    checklist_item_uuid: Annotated[
        str, "The unique identifier (UUID) of the checklist item to be deleted."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteChecklistItem'."]:
    """Delete an item from a task checklist in ClickUp.

    Use this tool to delete a specific line item from a checklist associated with a task. Ideal for managing and updating task checklists by removing unnecessary or completed items."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/checklist/{checklist_id}/checklist_item/{checklist_item_id}".format(  # noqa: UP032
            checklist_id=checklist_identifier, checklist_item_id=checklist_item_uuid
        ),
        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-clickup"))
async def get_task_comments(
    context: ToolContext,
    task_identifier: Annotated[
        str, "Specify the unique identifier of the task whose comments you want to retrieve."
    ],
    comment_date_unix_time_ms: Annotated[
        int | None,
        "Specify the date of a task comment using Unix time in milliseconds for pagination.",
    ] = None,
    comment_start_id: Annotated[
        str | None, "The ID of the earliest comment to start retrieving from, used for pagination."
    ] = None,
    use_custom_task_ids: Annotated[
        bool | None, "Set to `true` if you want to reference a task by its custom task ID."
    ] = None,
    workspace_id_for_custom_task: Annotated[
        int | None,
        "Provide the Workspace ID when using a custom task ID (requires `custom_task_ids` to be true).",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTaskComments'."]:
    """Retrieve comments from a specified task in ClickUp.

    Use this tool to view comments on a specific task within ClickUp. If no 'start' and 'start_id' parameters are provided, it will return the latest 25 comments. Provide 'start' and 'start_id' to paginate through older comments."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/comment".format(task_id=task_identifier),  # noqa: UP032
        method="GET",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id_for_custom_task,
            "start": comment_date_unix_time_ms,
            "start_id": comment_start_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-clickup"))
async def add_task_comment(
    context: ToolContext,
    comment_content: Annotated[
        str,
        "The text of the comment to be added to the task. It should contain any updates, feedback, or relevant information.",
    ],
    send_notifications_to_all: Annotated[
        bool,
        "If true, notifications will be sent to everyone, including the creator of the comment.",
    ],
    specific_task_id: Annotated[
        str, "The ID of the task to add the comment to. Required for identifying the target task."
    ],
    assignee_group: Annotated[
        str | None,
        "Specifies a group of users (as a comma-separated string) to be assigned to the comment. Ensure the group is relevant to the task.",
    ] = None,
    comment_assignee_id: Annotated[
        int | None,
        "An integer representing the user ID of the assignee for the comment on the task.",
    ] = None,
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id_for_custom_task: Annotated[
        int | None,
        "Provide the Workspace ID when referencing a task by its custom task ID (set `custom_task_ids` to true).",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateTaskComment'."]:
    """Add a new comment to a specific task on ClickUp.

    This tool allows users to add a new comment to a specified task on ClickUp. It should be called when there's a need to comment on a task, offering a way to communicate updates, feedback, or any relevant information directly related to the task in question."""
    request_data: Any = {
        "comment_text": comment_content,
        "assignee": comment_assignee_id,
        "group_assignee": assignee_group,
        "notify_all": send_notifications_to_all,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/comment".format(  # noqa: UP032
            task_id=specific_task_id
        ),
        method="POST",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id_for_custom_task,
        }),
        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-clickup"))
async def view_chat_comments(
    context: ToolContext,
    chat_view_id: Annotated[
        str,
        "The unique identifier for the Chat view to retrieve comments from. It should be a string, typically '105'.",
    ],
    comment_start_date_unix_ms: Annotated[
        int | None,
        "The start date of a Chat view comment in Unix time (milliseconds) to fetch older comments.",
    ] = None,
    start_comment_id: Annotated[
        str | None,
        "The comment ID to start retrieving older comments from in the Chat view. Use this to fetch comments beyond the most recent 25.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetChatViewComments'."]:
    """Retrieve the most recent comments from a Chat view.

    This tool fetches comments from a Chat view in ClickUp. It returns the 25 most recent comments if no parameters are provided. To access older comments, use `start` and `start_id` parameters."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/view/{view_id}/comment".format(view_id=chat_view_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({
            "start": comment_start_date_unix_ms,
            "start_id": start_comment_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-clickup"))
async def add_chat_view_comment(
    context: ToolContext,
    comment_text: Annotated[str, "The text content of the comment to be added to the Chat view."],
    send_notifications_to_all: Annotated[
        bool, "Set to true to send notifications to everyone, including the comment creator."
    ],
    view_id: Annotated[
        str, "The ID of the Chat view where the comment will be added. Expected as a string."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateChatViewComment'."]:
    """Add a new comment to a Chat view.

    Use this tool to add a comment to a Chat view in ClickUp. This can be used to facilitate discussion and share information in a specific View context."""
    request_data: Any = {"comment_text": comment_text, "notify_all": send_notifications_to_all}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/view/{view_id}/comment".format(view_id=view_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-clickup"))
async def get_list_comments(
    context: ToolContext,
    list_id: Annotated[
        int,
        "The unique integer identifier of the ClickUp list for which comments are being retrieved.",
    ],
    oldest_comment_id: Annotated[
        str | None, "ID of the oldest comment to start retrieving additional comments from."
    ] = None,
    start_date_unix_millis: Annotated[
        int | None,
        "Enter the date of a list info comment using Unix time in milliseconds to retrieve comments starting from this timestamp.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetListComments'."]:
    """View comments from a specific ClickUp list.

    Use this tool to retrieve the most recent comments from a specified ClickUp list. It returns up to 25 comments by default. To fetch older comments, utilize the optional parameters `start` and `start_id`."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}/comment".format(list_id=list_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({"start": start_date_unix_millis, "start_id": oldest_comment_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-clickup"))
async def add_comment_to_list(
    context: ToolContext,
    assignee_id: Annotated[
        int,
        "The ID of the user to whom the comment is assigned. This should be an integer value representing the user's unique identifier.",
    ],
    comment_text: Annotated[
        str,
        "The text of the comment to be added to the list. This should contain the message or information you wish to convey.",
    ],
    list_identifier: Annotated[int, "The unique ID of the list where the comment will be added."],
    notify_all: Annotated[
        bool, "If true, notifications are sent to everyone, including the comment creator."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateListComment'."]:
    """Add a comment to a specific list in ClickUp.

    This tool allows you to add a comment to a specified list in ClickUp. Use it when you need to provide additional information or feedback on a list."""
    request_data: Any = {
        "comment_text": comment_text,
        "assignee": assignee_id,
        "notify_all": notify_all,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}/comment".format(list_id=list_identifier),  # 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-clickup"))
async def update_task_comment(
    context: ToolContext,
    assignee_user_id: Annotated[
        int,
        "The ID of the user to assign the comment to. This should be a numeric user ID in ClickUp.",
    ],
    comment_identifier: Annotated[
        int, "The unique identifier of the comment to update. It must be an integer."
    ],
    mark_comment_as_resolved: Annotated[
        bool,
        "Set to true to mark the comment as resolved; false to leave it unresolved. Accepts a boolean value.",
    ],
    new_comment_content: Annotated[
        str, "The new content for the task comment. This will replace the existing comment text."
    ],
    assign_to_group: Annotated[
        int | None, "Assign the comment to a group by providing the group's ID."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateComment'."]:
    """Update a task comment in ClickUp.

    Use this tool to replace a task comment's content, assign it, or mark it as resolved in ClickUp."""
    request_data: Any = {
        "comment_text": new_comment_content,
        "assignee": assignee_user_id,
        "group_assignee": assign_to_group,
        "resolved": mark_comment_as_resolved,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/comment/{comment_id}".format(  # noqa: UP032
            comment_id=comment_identifier
        ),
        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-clickup"))
async def delete_task_comment(
    context: ToolContext,
    comment_id: Annotated[int, "The unique integer identifier of the comment to be deleted."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteComment'."]:
    """Delete a comment from a task.

    Use this tool to delete a specific comment from a task. It should be called when you need to remove a comment based on its unique identifier."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/comment/{comment_id}".format(comment_id=comment_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-clickup"))
async def view_threaded_comments(
    context: ToolContext,
    thread_comment_id: Annotated[
        int,
        "The ID of the comment for which threaded replies are to be retrieved. This ID should be an integer and corresponds to the comment in a ClickUp task whose replies you want to view.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetThreadedComments'."]:
    """Retrieve threaded replies to a comment.

    This tool allows for the retrieval of threaded comments in a ClickUp task, excluding the parent comment. Use this tool to view discussions or replies linked to a specific comment by providing the comment ID."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/comment/{comment_id}/reply".format(  # noqa: UP032
            comment_id=thread_comment_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-clickup"))
async def create_threaded_comment(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    parent_comment_id: Annotated[
        int | None,
        "The ID of the parent comment to which the threaded reply will be attached. It should be an integer.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateThreadedComment'."]:
    """Create a threaded comment in a ClickUp task.

    This tool is used to create a threaded comment as a reply to an existing comment in a ClickUp task. It is useful when you want to continue a discussion or provide additional information to a specific comment thread.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATETHREADEDCOMMENT"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 parent_comment_id:
        missing_params.append(("parent_comment_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["CREATETHREADEDCOMMENT"] + "\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["CREATETHREADEDCOMMENT"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/comment/{comment_id}/reply".format(  # noqa: UP032
            comment_id=parent_comment_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATETHREADEDCOMMENT"],
        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-clickup"))
async def view_list_custom_fields(
    context: ToolContext,
    content_type: Annotated[
        str, "The MIME type of the request body, typically set to 'application/json'."
    ],
    list_id: Annotated[
        int, "The unique identifier of the list to retrieve accessible custom fields for."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetAccessibleCustomFields'."]:
    """Retrieve accessible custom fields for a specific list.

    Use this tool to view the custom fields you have access to in a specific list. This is useful for understanding which custom fields are available for use or modification in your lists."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}/field".format(list_id=list_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": content_type,
            "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-clickup"))
async def get_folder_custom_fields(
    context: ToolContext,
    content_type: Annotated[
        str, "The MIME type of the content being sent. Typically, use 'application/json'."
    ],
    folder_id: Annotated[
        int,
        "The unique identifier of the folder to retrieve custom fields from. Must be an integer corresponding to a specific folder in ClickUp.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getFolderAvailableFields'."]:
    """Retrieve accessible custom fields from a folder in ClickUp.

    This tool retrieves the custom fields that are accessible at the folder level in ClickUp. It's useful when you need to know what folder-level custom fields are available for a specific folder. Custom fields created at the list level aren't included."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/folder/{folder_id}/field".format(folder_id=folder_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": content_type,
            "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-clickup"))
async def get_space_custom_fields(
    context: ToolContext,
    content_type_header: Annotated[
        str, "The MIME type for the request header, typically 'application/json'."
    ],
    space_identifier: Annotated[
        int,
        "The unique identifier for the ClickUp space from which to fetch available custom fields. It should be an integer.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getSpaceAvailableFields'."]:
    """Retrieve custom fields accessible in a specific ClickUp space.

    This tool retrieves the custom fields available to you in a specified ClickUp space. It returns only the custom fields created at the space level, excluding those at the folder and list levels."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/space/{space_id}/field".format(  # noqa: UP032
            space_id=space_identifier
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def view_workspace_custom_fields(
    context: ToolContext,
    content_type_header: Annotated[
        str, "The MIME type of the content. Typically set to 'application/json' for JSON data."
    ],
    workspace_id: Annotated[
        int,
        "The ID of the Workspace to retrieve custom fields for. This identifies which Workspace's fields you want to view.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'getTeamAvailableFields'."]:
    """Retrieve Workspace-level Custom Fields in ClickUp.

    Use this tool to view custom fields accessible at the workspace level in ClickUp. It doesn't include fields from Space, Folder, or List levels."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/field".format(team_id=workspace_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def update_task_custom_field(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    task_id: Annotated[
        str | None,
        "The ID of the task to be updated with new custom field data.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    custom_field_uuid: Annotated[
        str | None,
        "The UUID of the custom field to update for a specific task.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    workspace_id: Annotated[
        int | None,
        "Provide the Workspace ID when referencing a task by its Custom Task ID (`custom_task_ids` must be true).  Only used when mode is 'execute'.",
    ] = None,
    use_custom_task_id_reference: Annotated[
        bool | None,
        "Set to `true` to reference a task using its Custom Task ID.  Only used when mode is 'execute'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SetCustomFieldValue'."]:
    """Update a custom field value for a specific task in ClickUp.

    Use this tool to add data to a custom field on a task. Requires the `task_id` of the task and the `field_id` of the custom field. Retrieve `field_id` using the 'Get Accessible Custom Fields' or 'Get Task' endpoints.

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

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

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

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

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

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

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/task/{task_id}/field/{field_id}".format(  # noqa: UP032
            task_id=task_id, field_id=custom_field_uuid
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATETASKCUSTOMFIELD"],
        params=remove_none_values({
            "custom_task_ids": use_custom_task_id_reference,
            "team_id": workspace_id,
        }),
        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-clickup"))
async def remove_custom_field_value(
    context: ToolContext,
    custom_field_id: Annotated[
        str,
        "UUID of the custom field to be removed from the task. Example: b8a8-48d8-a0c6-b4200788a683",
    ],
    task_identifier: Annotated[
        str,
        "The unique identifier of the task from which you want to remove the custom field value.",
    ],
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id: Annotated[
        int | None, "Provide the Workspace ID when referencing a task by custom task id."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveCustomFieldValue'."]:
    """Remove a custom field value from a ClickUp task.

    Use this tool to remove the data from a custom field on a ClickUp task without deleting the field option itself."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/field/{field_id}".format(  # noqa: UP032
            task_id=task_identifier, field_id=custom_field_id
        ),
        method="DELETE",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_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-clickup"))
async def set_task_dependency(
    context: ToolContext,
    task_id_of_dependency: Annotated[
        str, "The ID of the task that is waiting on or blocking another task."
    ],
    dependent_task_id: Annotated[
        str | None,
        "The ID of the task that the specified task depends on or is blocking. This establishes the task dependency relationship.",
    ] = None,
    depends_on_task_id: Annotated[
        str | None,
        "Specify the task ID that this task depends on or is blocked by. It should be a valid task ID in ClickUp.",
    ] = None,
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id: Annotated[
        int | None,
        "Provide the Workspace ID when `custom_task_ids` is true. Necessary for task identification.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddDependency'."]:
    """Set a task as waiting on or blocking another task.

    Use this tool to establish a dependency between tasks in ClickUp, either setting a task as waiting on another or as blocking one."""
    request_data: Any = {"depends_on": depends_on_task_id, "depedency_of": dependent_task_id}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/dependency".format(  # noqa: UP032
            task_id=task_id_of_dependency
        ),
        method="POST",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id,
        }),
        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-clickup"))
async def remove_task_dependency(
    context: ToolContext,
    dependent_task_id: Annotated[
        str, "The task ID that is dependent on another. Provide a valid task ID as a string."
    ],
    depends_on_task_id: Annotated[
        str,
        "The ID of the task that another task depends on. Provide a valid task ID to specify the dependent task.",
    ],
    task_id_to_remove_dependency: Annotated[
        str,
        "Specify the task ID from which the dependency is to be removed. This is required to identify the task involved in the dependency relationship.",
    ],
    use_custom_task_ids: Annotated[
        bool | None, "Set to true if referencing tasks by their custom task IDs is desired."
    ] = None,
    workspace_id: Annotated[
        int | None,
        "Provide the Workspace ID when using custom task IDs by setting `custom_task_ids` to `true`.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteDependency'."]:
    """Remove a dependency relationship between tasks.

    This tool is used to remove the dependency relationship between two or more tasks in ClickUp. Call this when two tasks that are currently dependent on one another need to be independent."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/dependency".format(  # noqa: UP032
            task_id=task_id_to_remove_dependency
        ),
        method="DELETE",
        params=remove_none_values({
            "depends_on": depends_on_task_id,
            "dependency_of": dependent_task_id,
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_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-clickup"))
async def link_tasks_clickup(
    context: ToolContext,
    source_task_id: Annotated[str, "The ID of the task from which the link will be initiated."],
    task_to_link_to: Annotated[str, "The ID of the task to link to the initiating task."],
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id_for_custom_task: Annotated[
        int | None,
        "Provide the Workspace ID if referencing a task by custom task id (when custom_task_ids is true).",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddTaskLink'."]:
    """Link two ClickUp tasks together.

    This tool allows you to link two tasks in ClickUp via the Task Links feature in the right-hand sidebar of a task. It should be called when you need to associate tasks directly, but does not support linking to other types of objects."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/link/{links_to}".format(  # noqa: UP032
            task_id=source_task_id, links_to=task_to_link_to
        ),
        method="POST",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id_for_custom_task,
        }),
        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-clickup"))
async def remove_task_link(
    context: ToolContext,
    linked_task_id: Annotated[
        str,
        "The task ID of the task to which the original task is linked. This specifies the connection to be removed.",
    ],
    primary_task_id: Annotated[
        str,
        "The ID of the primary task from which to remove the link. This is required to identify the task.",
    ],
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id: Annotated[
        int | None, "Provide the Workspace ID when `custom_task_ids` is set to `true`."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteTaskLink'."]:
    """Remove the link between two tasks.

    Use this tool to delete an existing link between two tasks in ClickUp. It is helpful when you need to unlink tasks that are no longer related."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/link/{links_to}".format(  # noqa: UP032
            task_id=primary_task_id, links_to=linked_task_id
        ),
        method="DELETE",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_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-clickup"))
async def get_space_folders(
    context: ToolContext,
    space_id: Annotated[
        int,
        "The unique identifier of the space from which to retrieve folders. This is required to specify which space's folders are being requested.",
    ],
    include_archived_folders: Annotated[
        bool | None, "Set to true to include archived folders in the results."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetFolders'."]:
    """Retrieve a list of folders from a specified space.

    Use this tool to view folders within a specific space in ClickUp. It is useful when you need to access or manage the folders in a particular workspace."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/space/{space_id}/folder".format(space_id=space_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({"archived": include_archived_folders}),
        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-clickup"))
async def create_folder_in_space(
    context: ToolContext,
    folder_name: Annotated[
        str,
        "The name of the new folder to be created in the specified space. It should be a string representing the folder's title.",
    ],
    space_id: Annotated[
        int,
        "The unique identifier for the ClickUp Space where the folder will be created. It should be an integer.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateFolder'."]:
    """Add a new Folder to a Space in ClickUp.

    This tool allows you to create a new folder within a specified space in ClickUp. Use it when you need to organize tasks or projects into a new folder under an existing space."""
    request_data: Any = {"name": folder_name}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/space/{space_id}/folder".format(space_id=space_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-clickup"))
async def view_folder_lists(
    context: ToolContext,
    folder_id: Annotated[
        int,
        "The unique identifier for the folder whose lists you want to retrieve. This must be an integer.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetFolder'."]:
    """Retrieve lists contained in a specified folder.

    Call this tool to access all the lists that are organized under a specific folder in ClickUp by providing the folder ID."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/folder/{folder_id}".format(folder_id=folder_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-clickup"))
async def rename_clickup_folder(
    context: ToolContext,
    folder_id_clickup: Annotated[
        int, "The unique identifier for the ClickUp folder you wish to rename."
    ],
    new_folder_name: Annotated[
        str,
        "Specify the new name for the folder. This is the name that the folder will be renamed to in ClickUp.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateFolder'."]:
    """Rename a folder in ClickUp.

    Use this tool to update the name of a folder in ClickUp by specifying the folder ID and the new name."""
    request_data: Any = {"name": new_folder_name}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/folder/{folder_id}".format(folder_id=folder_id_clickup),  # 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-clickup"))
async def delete_workspace_folder(
    context: ToolContext,
    folder_id: Annotated[
        int,
        "The unique ID of the folder to be deleted from your ClickUp workspace. Ensure this ID is correct to avoid unintended deletions.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteFolder'."]:
    """Delete a folder from your ClickUp workspace.

    Use this tool to permanently delete a specific folder from your ClickUp workspace. Ensure you have the correct folder ID before performing this action."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/folder/{folder_id}".format(folder_id=folder_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-clickup"))
async def view_workspace_goals(
    context: ToolContext,
    workspace_id: Annotated[int, "The unique identifier for the workspace to view its goals."],
    include_completed_goals: Annotated[
        bool | None,
        "Indicate whether to include completed goals in the results. Set to true to include completed goals, or false to exclude them.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetGoals'."]:
    """View the Goals available in a Workspace.

    This tool retrieves the goals available in a specific workspace on ClickUp. It should be used when you need to access or display information about the goals associated with a given workspace."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/goal".format(team_id=workspace_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({"include_completed": include_completed_goals}),
        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-clickup"))
async def create_workspace_goal(
    context: ToolContext,
    allow_multiple_owners: Annotated[
        bool, "Set to true to allow a goal to have multiple owners, or false for a single owner."
    ],
    due_date_timestamp: Annotated[
        int,
        "The due date for the goal as a Unix timestamp in milliseconds. Represents when the goal should be completed.",
    ],
    goal_color: Annotated[
        str,
        "The color code for the goal. Expected to be a string representing a color, such as a hex code like '#FF5733'.",
    ],
    goal_description: Annotated[
        str, "A brief explanation of the goal to be added, providing context and details."
    ],
    goal_name: Annotated[str, "The name or title of the goal to be added to the workspace."],
    goal_owners: Annotated[
        list[int], "Array of user IDs for those assigned to own the goal, allowing multiple owners."
    ],
    workspace_id: Annotated[
        int, "The ID of the workspace where the goal will be added. This is a numeric value."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateGoal'."]:
    """Add a new goal to a specified workspace.

    This tool allows you to create a new goal in a specific workspace by providing the necessary details. Use it to set objectives and track progress within teams."""
    request_data: Any = {
        "name": goal_name,
        "due_date": due_date_timestamp,
        "description": goal_description,
        "multiple_owners": allow_multiple_owners,
        "owners": goal_owners,
        "color": goal_color,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/goal".format(team_id=workspace_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-clickup"))
async def get_goal_details(
    context: ToolContext,
    goal_identifier: Annotated[
        str, "The unique identifier (UUID) for the goal to retrieve details and targets."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetGoal'."]:
    """Retrieve detailed information about a specific goal including its targets."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/goal/{goal_id}".format(goal_id=goal_identifier),  # 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-clickup"))
async def update_goal_details(
    context: ToolContext,
    goal_color: Annotated[
        str,
        "Set the color of the goal. Accepts a string representing the color, such as a hex code.",
    ],
    goal_description: Annotated[
        str,
        "The new description for the goal. This should provide an overview or details of the goal's purpose.",
    ],
    goal_due_date: Annotated[
        int,
        "An integer representing the new due date for the goal, usually in Unix timestamp format.",
    ],
    goal_id: Annotated[str, "The unique identifier for the goal to be updated. This is a UUID."],
    goal_name: Annotated[
        str, "The new name for the goal. This will replace the current goal name."
    ],
    new_owners_to_add: Annotated[list[int], "List of user IDs to add as new owners for the goal."],
    remove_owners_user_ids: Annotated[
        list[int], "Array of user IDs to be removed as owners of the goal."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateGoal'."]:
    """Update goal details such as name, due date, and owners.

    Use this tool to rename a goal, set its due date, update its description, manage owners, or change its color in ClickUp."""
    request_data: Any = {
        "name": goal_name,
        "due_date": goal_due_date,
        "description": goal_description,
        "rem_owners": remove_owners_user_ids,
        "add_owners": new_owners_to_add,
        "color": goal_color,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/goal/{goal_id}".format(goal_id=goal_id),  # 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-clickup"))
async def delete_goal(
    context: ToolContext,
    content_type_header: Annotated[
        str, "Specify the Content-Type header. Typically set to 'application/json'."
    ],
    goal_identifier: Annotated[
        str, "The unique identifier for the goal to be deleted. It must be a valid UUID."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteGoal'."]:
    """Deletes a goal from your workspace in ClickUp.

    Use this tool to remove a specified goal from your ClickUp workspace by providing the goal ID."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/goal/{goal_id}".format(goal_id=goal_identifier),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def add_target_to_goal(
    context: ToolContext,
    goal_identifier: Annotated[
        str, "The unique identifier (UUID) of the goal to which the target will be added."
    ],
    initial_value_steps: Annotated[
        int, "Specify the starting value for the target's progress steps, as an integer."
    ],
    linked_task_ids: Annotated[
        list[str], "An array of task IDs to associate the target with tasks."
    ],
    list_ids: Annotated[
        list[str], "Array of List IDs to associate the target with multiple Lists."
    ],
    target_name: Annotated[
        str,
        "Specify the name for the target being added to the goal. It should be a descriptive label for easy identification.",
    ],
    target_owners_ids: Annotated[
        list[int], "An array of user IDs representing the owners of the key result target."
    ],
    target_steps_end: Annotated[
        int,
        "Specify the final value for the target steps. It indicates the goal completion threshold.",
    ],
    target_type: Annotated[
        str,
        "Specify the type of target (key result) as one of the following: `number`, `currency`, `boolean`, `percentage`, or `automatic`.",
    ],
    target_unit: Annotated[
        str, "Specify the unit for the target if using types like number, currency, or percentage."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateKeyResult'."]:
    """Add a target to a specific goal in ClickUp.

    Use this tool to add a key result target to an existing goal in ClickUp, enhancing goal tracking and achievement."""
    request_data: Any = {
        "name": target_name,
        "owners": target_owners_ids,
        "type": target_type,
        "steps_start": initial_value_steps,
        "steps_end": target_steps_end,
        "unit": target_unit,
        "task_ids": linked_task_ids,
        "list_ids": list_ids,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/goal/{goal_id}/key_result".format(  # noqa: UP032
            goal_id=goal_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-clickup"))
async def update_key_result_target(
    context: ToolContext,
    current_steps_value: Annotated[
        int,
        "The current number of steps completed for the key result target. Provide an integer value.",
    ],
    key_result_identifier: Annotated[
        str, "Unique identifier for the key result to be updated, provided as a UUID."
    ],
    note_update_description: Annotated[
        str,
        "Text for the note associated with the key result. Use to add or update content related to the key result.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'EditKeyResult'."]:
    """Update the target of a specific key result.

    Use this tool to update the target value of a specified key result. It should be called when there's a need to modify the target associated with a key result in ClickUp."""
    request_data: Any = {"steps_current": current_steps_value, "note": note_update_description}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/key_result/{key_result_id}".format(  # noqa: UP032
            key_result_id=key_result_identifier
        ),
        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-clickup"))
async def delete_goal_target(
    context: ToolContext,
    goal_target_id: Annotated[
        str, "The unique identifier (UUID) of the key result to delete from the goal in ClickUp."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteKeyResult'."]:
    """Delete a target from a goal in ClickUp.

    Use this tool to delete a key result (target) from a goal in ClickUp when a specific target needs to be removed."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/key_result/{key_result_id}".format(  # noqa: UP032
            key_result_id=goal_target_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-clickup"))
async def invite_guest_to_workspace(
    context: ToolContext,
    guest_email: Annotated[
        str,
        "The email address of the guest to be invited to the workspace. Ensure it is correctly formatted.",
    ],
    workspace_id: Annotated[
        int, "The integer ID of the Workspace to which the guest will be invited."
    ],
    allow_guest_to_create_views: Annotated[
        bool | None,
        "Indicates if the guest can create views in the workspace. Accepts a boolean value.",
    ] = None,
    allow_tag_editing: Annotated[
        bool | None, "Set to true if the guest should be allowed to edit tags in the workspace."
    ] = None,
    allow_view_time_spent: Annotated[
        bool | None,
        "Allow the guest to view time spent in the workspace. Accepts a boolean value: true to allow, false to deny.",
    ] = None,
    can_view_estimated_times: Annotated[
        bool | None, "Set to true to allow the guest to view estimated times for tasks."
    ] = None,
    can_view_points_estimated: Annotated[
        bool | None, "Set to true to allow the guest to view estimated points for tasks."
    ] = None,
    custom_role_id: Annotated[
        int | None, "The ID of the custom role to assign to the guest. Must be an integer value."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'InviteGuestToWorkspace'."]:
    """Invite a guest to join a ClickUp workspace.

    This tool is used to invite a guest to a ClickUp workspace on an Enterprise Plan. Ensure the guest has access to specific items in the workspace using additional endpoints."""
    request_data: Any = {
        "email": guest_email,
        "can_edit_tags": allow_tag_editing,
        "can_see_time_spent": allow_view_time_spent,
        "can_see_time_estimated": can_view_estimated_times,
        "can_create_views": allow_guest_to_create_views,
        "can_see_points_estimated": can_view_points_estimated,
        "custom_role_id": custom_role_id,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/guest".format(team_id=workspace_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-clickup"))
async def get_guest_information(
    context: ToolContext,
    guest_identifier: Annotated[
        int,
        "An integer representing the unique ID of the guest whose information is to be retrieved.",
    ],
    workspace_id: Annotated[
        int,
        "The ID of the ClickUp workspace. This is required for identifying the specific workspace where the guest information is being retrieved.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetGuest'."]:
    """Retrieve information about a guest in a workspace.

    This tool is used to view details about a guest in a ClickUp workspace. It is available only for Workspaces on the Enterprise Plan. Use it when you need to obtain guest-related information, such as profile or access details."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/guest/{guest_id}".format(  # noqa: UP032
            team_id=workspace_id, guest_id=guest_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-clickup"))
async def configure_workspace_guest(
    context: ToolContext,
    guest_identifier: Annotated[
        int, "The unique identifier for the guest to be configured in the workspace."
    ],
    workspace_id: Annotated[
        int,
        "The unique identifier for the ClickUp workspace where the guest is being configured. This is required for identifying the specific workspace.",
    ],
    allow_guest_to_edit_tags: Annotated[
        bool | None,
        "Set to true to allow the guest to edit tags in the workspace; false to disallow.",
    ] = None,
    allow_view_creation: Annotated[
        bool | None,
        "A boolean to specify if the guest can create views. True allows view creation, false denies it.",
    ] = None,
    allow_viewing_points_estimated: Annotated[
        bool | None,
        "Specify if the guest can view estimated points in the workspace. True allows viewing; false restricts it.",
    ] = None,
    allow_viewing_time_spent: Annotated[
        bool | None,
        "Set to true to allow the guest to view time spent on tasks, false to restrict.",
    ] = None,
    can_see_time_estimates: Annotated[
        bool | None,
        "Determines if the guest can view time estimates. Use true to allow, false to restrict.",
    ] = None,
    guest_custom_role_id: Annotated[
        int | None,
        "An integer representing the custom role ID assigned to the guest in the workspace.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'EditGuestOnWorkspace'."]:
    """Configure options for a guest in a workspace.

    This tool adjusts settings for a guest in a ClickUp workspace, specifically for teams on the Enterprise Plan."""
    request_data: Any = {
        "can_see_points_estimated": allow_viewing_points_estimated,
        "can_edit_tags": allow_guest_to_edit_tags,
        "can_see_time_spent": allow_viewing_time_spent,
        "can_see_time_estimated": can_see_time_estimates,
        "can_create_views": allow_view_creation,
        "custom_role_id": guest_custom_role_id,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/guest/{guest_id}".format(  # noqa: UP032
            team_id=workspace_id, guest_id=guest_identifier
        ),
        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-clickup"))
async def remove_guest_from_workspace(
    context: ToolContext,
    guest_id: Annotated[
        int,
        "The unique identifier for the guest to be removed from the workspace. This should be an integer value.",
    ],
    workspace_id: Annotated[
        int,
        "The unique ID of the ClickUp workspace from which the guest will be removed. This is an integer.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveGuestFromWorkspace'."]:
    """Revoke a guest's access to a ClickUp workspace.

    Remove a guest from a ClickUp workspace. Available only for Workspaces on the Enterprise Plan."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/guest/{guest_id}".format(  # noqa: UP032
            team_id=workspace_id, guest_id=guest_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-clickup"))
async def share_task_with_guest(
    context: ToolContext,
    guest_id: Annotated[int, "The unique identifier for the guest to share the task with."],
    guest_permission_level: Annotated[
        str,
        "Defines the level of access for the guest. Options: 'read', 'comment', 'edit', 'create'.",
    ],
    task_id: Annotated[str, "The unique identifier for the task to share with the guest."],
    include_shared_details: Annotated[
        bool | None,
        "Set to `true` to include details of items shared with the guest. Defaults to `true`.",
    ] = None,
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id_when_custom_task_ids_enabled: Annotated[
        int | None,
        "Provide the Workspace ID when referencing tasks by custom task IDs. Required if `custom_task_ids` is `true`.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddGuestToTask'."]:
    """Share a task with a guest in the ClickUp Workspace.

    This tool shares a specified task with a guest, available only on ClickUp's Enterprise Plan."""
    request_data: Any = {"permission_level": guest_permission_level}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/guest/{guest_id}".format(  # noqa: UP032
            task_id=task_id, guest_id=guest_id
        ),
        method="POST",
        params=remove_none_values({
            "include_shared": include_shared_details,
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id_when_custom_task_ids_enabled,
        }),
        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-clickup"))
async def remove_guest_from_task(
    context: ToolContext,
    guest_id: Annotated[
        int, "The ID of the guest to remove from the task. This is a required integer value."
    ],
    task_id: Annotated[
        str,
        "The unique identifier of the task from which the guest's access should be revoked. This is required to specify the task.",
    ],
    include_shared_details: Annotated[
        bool | None,
        "Set to `true` to include details of items shared with the guest. Default is `true`.",
    ] = None,
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id_for_custom_task: Annotated[
        int | None,
        "Provide the Workspace ID when referencing a task by its custom task ID. This is required if 'custom_task_ids' is set to 'true'.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveGuestFromTask'."]:
    """Revoke a guest's access to a specific task in ClickUp.

    Use this tool to remove a guest from a task in ClickUp. This action is available only for Workspaces on the Enterprise Plan."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/guest/{guest_id}".format(  # noqa: UP032
            task_id=task_id, guest_id=guest_id
        ),
        method="DELETE",
        params=remove_none_values({
            "include_shared": include_shared_details,
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id_for_custom_task,
        }),
        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-clickup"))
async def add_guest_to_list(
    context: ToolContext,
    guest_id: Annotated[
        int, "The unique identifier for the guest to be added. This should be an integer value."
    ],
    guest_permission_level: Annotated[
        str,
        "Permission level for the guest on the list. Options are `read`, `comment`, `edit`, or `create`.",
    ],
    list_id: Annotated[int, "The identifier of the list to which the guest will be added."],
    include_shared_details: Annotated[
        bool | None,
        "Set to false to exclude shared item details from the guest view; defaults to true to include them.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddGuestToList'."]:
    """Add a guest to a specific list in ClickUp.

    This tool adds a guest to a list within ClickUp, available only for Enterprise Plan workspaces."""
    request_data: Any = {"permission_level": guest_permission_level}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}/guest/{guest_id}".format(  # noqa: UP032
            list_id=list_id, guest_id=guest_id
        ),
        method="POST",
        params=remove_none_values({"include_shared": include_shared_details}),
        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-clickup"))
async def remove_guest_from_list(
    context: ToolContext,
    guest_user_id: Annotated[
        int, "The unique identifier of the guest whose access is to be revoked from the list."
    ],
    list_identifier: Annotated[
        int,
        "The unique identifier for the list from which the guest's access will be revoked. This must be an integer.",
    ],
    include_shared_details: Annotated[
        bool | None,
        "Set to `true` to include details of items shared with the guest. Default is `true`.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveGuestFromList'."]:
    """Revoke a guest's access to a specific list in ClickUp.

    This tool removes a guest's access from a specified list within ClickUp. It is available only for Workspaces on the Enterprise Plan."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}/guest/{guest_id}".format(  # noqa: UP032
            list_id=list_identifier, guest_id=guest_user_id
        ),
        method="DELETE",
        params=remove_none_values({"include_shared": include_shared_details}),
        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-clickup"))
async def add_guest_to_folder(
    context: ToolContext,
    folder_id: Annotated[int, "The unique integer ID of the folder to be shared with the guest."],
    guest_identifier: Annotated[
        int,
        "The unique identifier for the guest to whom the folder will be shared. This should be an integer representing the guest's ID.",
    ],
    guest_permission_level: Annotated[
        str,
        "Defines guest's access level: 'read' for view only, 'comment', 'edit', or 'create' for full access.",
    ],
    include_shared_items: Annotated[
        bool | None,
        "Set to true to include details of items shared with the guest. Default is true.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddGuestToFolder'."]:
    """Share a folder with a guest in ClickUp's Enterprise Plan.

    This tool shares a specific folder with a guest, available only for ClickUp Workspaces on the Enterprise Plan. Use it to collaborate by granting access to specified guests."""
    request_data: Any = {"permission_level": guest_permission_level}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/folder/{folder_id}/guest/{guest_id}".format(  # noqa: UP032
            folder_id=folder_id, guest_id=guest_identifier
        ),
        method="POST",
        params=remove_none_values({"include_shared": include_shared_items}),
        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-clickup"))
async def remove_guest_from_folder(
    context: ToolContext,
    folder_identifier: Annotated[
        int,
        "The unique identifier for the folder from which the guest's access should be revoked. This ID is essential to specify the exact folder within the ClickUp workspace.",
    ],
    guest_identifier: Annotated[
        int,
        "The unique numeric ID of the guest to be removed from the folder. This is required to identify which guest's access is being revoked.",
    ],
    include_shared_items: Annotated[
        bool | None,
        "Set to true to include details of items shared with the guest. Defaults to true.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveGuestFromFolder'."]:
    """Revoke a guest's access to a specified folder.

    Use this tool to remove a guest's access to a specific folder in ClickUp workspaces on the Enterprise Plan."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/folder/{folder_id}/guest/{guest_id}".format(  # noqa: UP032
            folder_id=folder_identifier, guest_id=guest_identifier
        ),
        method="DELETE",
        params=remove_none_values({"include_shared": include_shared_items}),
        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-clickup"))
async def view_lists_in_folder(
    context: ToolContext,
    folder_id: Annotated[
        int,
        "The unique identifier for the folder whose lists are to be retrieved. This is required to specify which folder's lists to view.",
    ],
    include_archived_lists: Annotated[
        bool | None,
        "Specify whether to include archived lists. Set to true to include and false to exclude.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetLists'."]:
    """Retrieve lists from a specific folder.

    Use this tool to view all the lists contained within a specified folder by providing the folder ID. It should be called when you need to access or manage lists within a folder within ClickUp."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/folder/{folder_id}/list".format(folder_id=folder_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({"archived": include_archived_lists}),
        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-clickup"))
async def create_clickup_list(
    context: ToolContext,
    folder_id: Annotated[
        int, "The unique integer identifier for the folder where the new list will be added."
    ],
    list_name: Annotated[
        str, "The name of the new list to be created within the specified ClickUp folder."
    ],
    due_date_timestamp: Annotated[
        int | None,
        "The due date for the list in Unix timestamp format. Determines when the list should be completed.",
    ] = None,
    formatted_list_description: Annotated[
        str | None,
        "Provide a markdown-formatted description for the List. Use this instead of plain text content.",
    ] = None,
    include_time_in_due_date: Annotated[
        bool | None, "Set to true to include a specific time with the due date."
    ] = None,
    list_assignee_user_id: Annotated[
        int | None,
        "The user ID to assign this list to a specific user. This identifies who will be responsible for the list.",
    ] = None,
    list_color: Annotated[
        str | None, "Specifies the color of the List, not related to task statuses."
    ] = None,
    list_description: Annotated[
        str | None,
        "A plain text description for the list. Use this to provide details about the list's purpose.",
    ] = None,
    list_priority: Annotated[
        int | None,
        "An integer value indicating the priority of the list, where a higher number typically means higher priority.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateList'."]:
    """Create a new list in a ClickUp folder.

    Use this tool to add a new list to a specified folder in ClickUp. Ideal for organizing tasks or projects within a folder."""
    request_data: Any = {
        "name": list_name,
        "content": list_description,
        "markdown_content": formatted_list_description,
        "due_date": due_date_timestamp,
        "due_date_time": include_time_in_due_date,
        "priority": list_priority,
        "assignee": list_assignee_user_id,
        "status": list_color,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/folder/{folder_id}/list".format(folder_id=folder_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-clickup"))
async def create_folder_from_template(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    clickup_space_id: Annotated[
        str | None,
        "ID of the ClickUp Space where the folder will be created.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    folder_template_id: Annotated[
        str | None,
        "The ID of the folder template to be used for creating a new folder in a ClickUp space. Ensure the template is added to your Workspace.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateFolderFromTemplate'."]:
    """Creates a new folder from a template in a ClickUp space.

    This tool creates a new folder using a predefined template within a specified ClickUp space. It can also incorporate nested assets like lists and tasks from the template into the new folder. Templates must be added to your Workspace before use. The operation can be executed asynchronously or synchronously.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATEFOLDERFROMTEMPLATE"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 clickup_space_id:
        missing_params.append(("clickup_space_id", "path"))
    if not folder_template_id:
        missing_params.append(("folder_template_id", "path"))

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

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

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/space/{space_id}/folder_template/{template_id}".format(  # noqa: UP032
            space_id=clickup_space_id, template_id=folder_template_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATEFOLDERFROMTEMPLATE"],
        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-clickup"))
async def get_folderless_lists(
    context: ToolContext,
    space_identifier: Annotated[
        int,
        "The unique identifier of the space to retrieve the folderless lists from. It should be an integer.",
    ],
    include_archived_lists: Annotated[
        bool | None, "Specify true to include archived lists, or false to exclude them."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetFolderlessLists'."]:
    """View Lists in a Space not located in a Folder.

    Use this tool to retrieve lists within a specified space that are not contained within any folder, providing an organized view of unassigned lists."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/space/{space_id}/list".format(  # noqa: UP032
            space_id=space_identifier
        ),
        method="GET",
        params=remove_none_values({"archived": include_archived_lists}),
        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-clickup"))
async def add_folderless_list_to_space(
    context: ToolContext,
    list_name: Annotated[str, "The name of the new list to be created within the space."],
    space_id: Annotated[int, "The unique identifier of the space where the list will be added."],
    due_date_timestamp: Annotated[
        int | None,
        "An integer representing the UNIX timestamp for the list's due date. This defines the deadline for the list.",
    ] = None,
    include_due_time: Annotated[
        bool | None, "Set to true if the due date should include a specific time."
    ] = None,
    list_color_status: Annotated[
        str | None,
        "Specifies the color representing the List. This is for visual identification and does not affect task statuses.",
    ] = None,
    list_description: Annotated[
        str | None,
        "A text description for the new list. Use plain text. For markdown, use `markdown_content`.",
    ] = None,
    list_markdown_description: Annotated[
        str | None,
        "Markdown formatted description for the list. Use this instead of a plain text description.",
    ] = None,
    list_owner_user_id: Annotated[
        int | None, "The user ID for the list owner to be assigned to the new list."
    ] = None,
    list_priority_level: Annotated[
        int | None,
        "Set the priority level for the list. It should be an integer value indicating the list's urgency or importance.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateFolderlessList'."]:
    """Add a new folderless list to a specified space.

    This tool is used to create a new list within a specified space in ClickUp without associating it with a folder. It facilitates the organization of tasks directly within the space."""
    request_data: Any = {
        "name": list_name,
        "content": list_description,
        "markdown_content": list_markdown_description,
        "due_date": due_date_timestamp,
        "due_date_time": include_due_time,
        "priority": list_priority_level,
        "assignee": list_owner_user_id,
        "status": list_color_status,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/space/{space_id}/list".format(space_id=space_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-clickup"))
async def view_list_details(
    context: ToolContext,
    list_id: Annotated[
        int,
        "The unique ID of the list to view details. Right-click the list in your ClickUp sidebar, select 'Copy link', and paste the URL's last string.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetList'."]:
    """Retrieve details of a specific list in ClickUp.

    Use this tool to get detailed information about a specific list in ClickUp by providing the list ID. It helps in obtaining metadata and current status of the list."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}".format(list_id=list_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-clickup"))
async def update_clickup_list(
    context: ToolContext,
    list_identifier: Annotated[
        str, "The unique identifier of the ClickUp list to update. This is a required string field."
    ],
    list_name: Annotated[str, "The new name for the ClickUp list. This should be a string value."],
    formatted_list_description: Annotated[
        str | None, "Formatted description of the list using Markdown syntax instead of plain text."
    ] = None,
    include_due_date_time: Annotated[
        bool | None, "Set to true to include a specific time with the due date."
    ] = None,
    list_assignee_id: Annotated[
        str | None, "The ID of the user to assign to the list. Provide a valid user ID string."
    ] = None,
    list_color: Annotated[
        str | None,
        "Specify the color of the list. This refers to the List color rather than task statuses.",
    ] = None,
    list_description_content: Annotated[
        str | None,
        "The plain text description to update for the ClickUp list. Use this instead of markdown for simple text updates.",
    ] = None,
    list_due_date: Annotated[
        int | None,
        "Set the list's due date as a Unix timestamp in milliseconds, representing the time the list is due.",
    ] = None,
    list_priority: Annotated[
        int | None,
        "Set the list's priority as an integer. Usually, 1 is high, 2 is medium, and 3 is low priority.",
    ] = None,
    remove_list_color: Annotated[
        bool | None, "Set to `true` to remove the List color; default is `false`."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateList'."]:
    """Update the details of a ClickUp list.

    Use this tool to rename a ClickUp list, update its description, set a due date or time, change the priority, assign someone, or modify the list color."""
    request_data: Any = {
        "name": list_name,
        "content": list_description_content,
        "markdown_content": formatted_list_description,
        "due_date": list_due_date,
        "due_date_time": include_due_date_time,
        "priority": list_priority,
        "assignee": list_assignee_id,
        "status": list_color,
        "unset_status": remove_list_color,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}".format(list_id=list_identifier),  # 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-clickup"))
async def delete_workspace_list(
    context: ToolContext,
    content_type_header: Annotated[
        str, "Specifies the media type of the request. Typically set to 'application/json'."
    ],
    workspace_list_id: Annotated[
        int, "The unique integer ID of the list to be deleted from the ClickUp workspace."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteList'."]:
    """Delete a list from your ClickUp workspace.

    Use this tool to remove a specific list from your ClickUp workspace when it's no longer needed."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}".format(list_id=workspace_list_id),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def add_task_to_clickup_list(
    context: ToolContext,
    target_list_id: Annotated[
        int,
        "The unique identifier for the target list where the task will be added. This is required to associate the task with the correct list in ClickUp.",
    ],
    task_identifier: Annotated[
        str, "Specify the ID of the task to be added to an additional list in ClickUp."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddTaskToList'."]:
    """Add a task to an additional list in ClickUp.

    This tool adds a specified task to an additional list in ClickUp, requiring the 'Tasks in Multiple List' ClickApp to be enabled. Use this tool to manage tasks across multiple lists efficiently."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}/task/{task_id}".format(  # noqa: UP032
            list_id=target_list_id, task_id=task_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-clickup"))
async def remove_task_from_additional_list(
    context: ToolContext,
    additional_list_id: Annotated[
        int,
        "The ID of the additional list from which the task should be removed. This is required for identifying the secondary list, not the task's home list.",
    ],
    task_identifier: Annotated[
        str, "The unique identifier for the task to be removed from the additional list."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveTaskFromList'."]:
    """Remove a task from an additional list in ClickUp.

    Use this tool to remove a task from an additional list, not the task's home list. Requires the 'Tasks in Multiple Lists' feature to be enabled in ClickUp."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}/task/{task_id}".format(  # noqa: UP032
            list_id=additional_list_id, task_id=task_identifier
        ),
        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-clickup"))
async def get_task_members(
    context: ToolContext,
    task_identifier: Annotated[
        str,
        "The unique identifier of the task to retrieve members for. This ID is necessary to specify the task in question.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTaskMembers'."]:
    """Retrieve members with direct access to a task.

    Use this tool to view the people who have direct access to a specific task in ClickUp. It does not include members with inherited permissions through the hierarchy."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/member".format(task_id=task_identifier),  # 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-clickup"))
async def get_list_members(
    context: ToolContext,
    list_id: Annotated[
        int,
        "The unique identifier for the list in ClickUp. It is required to fetch the members with access to this list.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetListMembers'."]:
    """Retrieve members with access to a specific list in ClickUp.

    Use this tool to get the members of a ClickUp workspace who have access to a specified list. It helps in managing and viewing user access to project resources."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}/member".format(list_id=list_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-clickup"))
async def get_workspace_custom_roles(
    context: ToolContext,
    workspace_id: Annotated[
        int, "The unique identifier for the workspace to retrieve custom roles from."
    ],
    include_members: Annotated[
        bool | None,
        "Include member details in the response. Set to true to include, false to exclude.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCustomRoles'."]:
    """Retrieve custom roles from a specific workspace.

    This tool is used to retrieve the custom roles available in a specified workspace on ClickUp. It should be called when you need to view or manage roles in a ClickUp workspace."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/customroles".format(  # noqa: UP032
            team_id=workspace_id
        ),
        method="GET",
        params=remove_none_values({"include_members": include_members}),
        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-clickup"))
async def view_shared_hierarchy(
    context: ToolContext,
    workspace_id: Annotated[int, "The Workspace ID to view shared tasks, lists, and folders."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'SharedHierarchy'."]:
    """View shared tasks, lists, and folders.

    Use this tool to view tasks, lists, and folders shared with the authenticated user in a specified team."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/shared".format(team_id=workspace_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-clickup"))
async def retrieve_available_spaces(
    context: ToolContext,
    workspace_id: Annotated[int, "The ID of the workspace to retrieve available spaces from."],
    include_archived_spaces: Annotated[
        bool | None,
        "Set to true to include archived Spaces in the results. Otherwise, only active Spaces are returned.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSpaces'."]:
    """View available Spaces in a Workspace.

    The tool retrieves the Spaces within a specified Workspace, providing member info for private Spaces if applicable."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/space".format(team_id=workspace_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({"archived": include_archived_spaces}),
        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-clickup"))
async def create_space_in_workspace(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    workspace_id: Annotated[
        int | None,
        "The ID of the workspace where the new space will be added. It should be an integer value.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateSpace'."]:
    """Add a new Space to a Workspace.

    Use this tool to create a new space within a specified workspace on ClickUp. It should be called when a user wants to organize tasks or projects under a new space in their team environment.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATESPACEINWORKSPACE"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 workspace_id:
        missing_params.append(("workspace_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["CREATESPACEINWORKSPACE"] + "\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["CREATESPACEINWORKSPACE"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/team/{team_id}/space".format(team_id=workspace_id),  # noqa: UP032
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATESPACEINWORKSPACE"],
        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-clickup"))
async def get_workspace_spaces(
    context: ToolContext,
    workspace_space_id: Annotated[
        int, "The unique identifier for the specific space in the workspace to retrieve details."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSpace'."]:
    """Retrieve available Spaces in a Workspace.

    Use this tool to get information about the Spaces available in a specific Workspace identified by its space ID."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/space/{space_id}".format(space_id=workspace_space_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-clickup"))
async def update_clickup_space(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    space_identifier: Annotated[
        int | None,
        "The unique identifier for the ClickUp space to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateSpace'."]:
    """Update space attributes in ClickUp.

    Use this tool to rename a ClickUp space, change its color, and enable or disable ClickApps for it. Call this when you need to modify the settings of an existing space.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATECLICKUPSPACE"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 space_identifier:
        missing_params.append(("space_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["UPDATECLICKUPSPACE"] + "\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["UPDATECLICKUPSPACE"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/space/{space_id}".format(space_id=space_identifier),  # noqa: UP032
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATECLICKUPSPACE"],
        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-clickup"))
async def delete_workspace_space(
    context: ToolContext,
    workspace_space_id: Annotated[
        int,
        "The unique identifier for the space to delete in your ClickUp workspace. Provide the specific space ID to permanently remove the space and its data.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteSpace'."]:
    """Delete a space from your ClickUp workspace.

    Use this tool to delete a specific space in your ClickUp workspace by providing the space ID. It permanently removes the space and all associated data."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/space/{space_id}".format(space_id=workspace_space_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-clickup"))
async def get_space_tags(
    context: ToolContext,
    content_type_header: Annotated[
        str, "The MIME type of the request. Generally set to 'application/json'."
    ],
    space_identifier: Annotated[
        int,
        "An integer representing the ID of the space for which to retrieve task tags. This ID is required to specify the space.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSpaceTags'."]:
    """Retrieve task tags for a specified space.

    Use this tool to view the task tags available within a specific space. It helps in identifying and managing tags associated with tasks."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/space/{space_id}/tag".format(space_id=space_identifier),  # noqa: UP032
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def add_space_task_tag(
    context: ToolContext,
    space_identifier: Annotated[
        int,
        "The unique identifier for the space where the tag will be added. It must be an integer.",
    ],
    tag_background_color: Annotated[
        str,
        "Hex code representing the background color for the tag. It should be a string in the format '#RRGGBB'.",
    ],
    tag_foreground_color: Annotated[
        str, "Hex code for the tag's foreground color. It defines the text color of the tag."
    ],
    tag_name: Annotated[
        str,
        "Name of the new tag to be added to the space. It should be a descriptive and concise identifier for categorizing tasks.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateSpaceTag'."]:
    """Add a new task tag to a specified space in ClickUp.

    Use this tool to create a new tag for tasks within a specific space in ClickUp. It's useful for organizing tasks by category or priority."""
    request_data: Any = {
        "tag": {"name": tag_name, "tag_fg": tag_foreground_color, "tag_bg": tag_background_color}
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/space/{space_id}/tag".format(space_id=space_identifier),  # 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-clickup"))
async def update_task_tag(
    context: ToolContext,
    background_color_of_tag: Annotated[
        str,
        "The background color for the task tag. It should be a valid hex color code (e.g., #FFFFFF).",
    ],
    current_tag_name: Annotated[
        str, "The current name of the tag to be updated in the ClickUp space."
    ],
    new_tag_name: Annotated[
        str,
        "The new name for the task tag to be updated in the ClickUp space. It must be a string representing the desired tag name after the update.",
    ],
    space_id: Annotated[
        int,
        "The unique identifier of the ClickUp space where the tag will be updated. This is required to specify which space's tag needs modification.",
    ],
    tag_foreground_color: Annotated[
        str, "The foreground (text) color of the tag in a valid color format (e.g., HEX)."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'EditSpaceTag'."]:
    """Update a task tag in a ClickUp space.

    Use this tool to update the details of a task tag within a specified ClickUp space. It is helpful for managing and organizing tasks by modifying existing tags."""
    request_data: Any = {
        "tag": {
            "name": new_tag_name,
            "fg_color": tag_foreground_color,
            "bg_color": background_color_of_tag,
        }
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/space/{space_id}/tag/{tag_name}".format(  # noqa: UP032
            space_id=space_id, tag_name=current_tag_name
        ),
        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-clickup"))
async def delete_space_tag(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    space_identifier: Annotated[
        int | None,
        "The unique identifier of the space from which the tag will be deleted. This should be an integer value.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    tag_name_to_delete: Annotated[
        str | None,
        "The name of the tag to be deleted from the specified space. Ensure this tag name exists in the target space.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteSpaceTag'."]:
    """Delete a task tag from a space in ClickUp.

    Use this tool to delete a specific task tag from a designated space in ClickUp. Ideal for cleaning up or reorganizing tags within a space.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["DELETESPACETAG"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 space_identifier:
        missing_params.append(("space_identifier", "path"))
    if not tag_name_to_delete:
        missing_params.append(("tag_name_to_delete", "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["DELETESPACETAG"] + "\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["DELETESPACETAG"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/space/{space_id}/tag/{tag_name}".format(  # noqa: UP032
            space_id=space_identifier, tag_name=tag_name_to_delete
        ),
        method="DELETE",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["DELETESPACETAG"],
        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-clickup"))
async def add_tag_to_task(
    context: ToolContext,
    content_type: Annotated[
        str, "Specifies the media type of the request. Typically set to 'application/json'."
    ],
    tag_name: Annotated[
        str,
        "The name of the tag to add to the task. This should be a string representing the desired tag.",
    ],
    task_identifier: Annotated[
        str,
        "The unique identifier of the task to which the tag will be added. Can be a custom task ID if specified.",
    ],
    use_custom_task_ids: Annotated[
        bool | None, "Set to true if you want to reference a task using its custom task ID."
    ] = None,
    workspace_id_if_custom_task_ids: Annotated[
        int | None,
        "Workspace ID required when referencing a task by its custom task ID. Only needed if `custom_task_ids=true`.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'AddTagToTask'."]:
    """Add a tag to a specific task in ClickUp.

    Use this tool to assign a tag to a task in ClickUp by specifying the task ID and tag name. It helps in organizing and categorizing tasks efficiently."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/tag/{tag_name}".format(  # noqa: UP032
            task_id=task_identifier, tag_name=tag_name
        ),
        method="POST",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id_if_custom_task_ids,
        }),
        headers=remove_none_values({
            "Content-Type": content_type,
            "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-clickup"))
async def remove_tag_from_task(
    context: ToolContext,
    content_type_header: Annotated[
        str, "Specifies the media type of the request. Commonly set to 'application/json'."
    ],
    tag_name_to_remove: Annotated[str, "The name of the tag to remove from the specified task."],
    task_id: Annotated[
        str,
        "The unique identifier of the task from which the tag will be removed. Use the task's regular ID unless custom task IDs are enabled.",
    ],
    use_custom_task_ids: Annotated[
        bool | None,
        "Set to true to reference a task by its custom task ID, or false to use the standard ID.",
    ] = None,
    workspace_id_for_custom_task: Annotated[
        int | None,
        "The Workspace ID required when referencing a task by its custom ID (if custom_task_ids is true).",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveTagFromTask'."]:
    """Remove a tag from a specific task in ClickUp.

    Use this tool to remove a specified tag from a task in ClickUp without deleting the tag from the space."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/tag/{tag_name}".format(  # noqa: UP032
            task_id=task_id, tag_name=tag_name_to_remove
        ),
        method="DELETE",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id_for_custom_task,
        }),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def get_list_tasks(
    context: ToolContext,
    list_identifier: Annotated[
        int,
        "The unique identifier for the list. Find it by copying the link and extracting the number following /li in the URL.",
    ],
    custom_task_type_filters: Annotated[
        list[float] | None,
        "An array of numbers to filter tasks by custom types. Use 0 for tasks, 1 for Milestones, and other numbers for custom types defined in your Workspace.",
    ] = None,
    date_created_less_than: Annotated[
        int | None, "Filter tasks created before the specified Unix timestamp in milliseconds."
    ] = None,
    display_tasks_in_reverse_order: Annotated[
        bool | None, "Set to true to display tasks in reverse order."
    ] = None,
    filter_by_assignees: Annotated[
        list[str] | None,
        "Filter tasks by assignee IDs. Provide an array of assignee IDs to filter tasks assigned to specific users.",
    ] = None,
    filter_by_custom_field: Annotated[
        list[str] | None,
        "Include tasks with specific values in one Custom Field. This can be a Custom Relationship. Provide an array of strings representing the field values.",
    ] = None,
    filter_by_custom_fields: Annotated[
        list[str] | None,
        "Include tasks with specific values in one or more Custom Fields. Use a JSON array of objects, where each object includes 'field_id', 'operator', and 'value'.",
    ] = None,
    filter_by_date_done_before: Annotated[
        int | None, "Filter tasks completed before a specified Unix time in milliseconds."
    ] = None,
    filter_by_statuses: Annotated[
        list[str] | None,
        "Filter tasks by their statuses. Use an array of status strings, such as ['to do', 'in progress'].",
    ] = None,
    filter_by_tags: Annotated[
        list[str] | None,
        "Filter tasks by a list of tags. Provide an array of strings representing the tags to filter by.",
    ] = None,
    filter_by_watchers: Annotated[
        list[str] | None,
        "An array of watcher IDs to filter tasks by watchers. Each ID should be a string.",
    ] = None,
    filter_date_created_after: Annotated[
        int | None, "Filter tasks created after this Unix timestamp in milliseconds."
    ] = None,
    filter_date_done_after: Annotated[
        int | None, "Filter tasks completed after a specified date in Unix time (milliseconds)."
    ] = None,
    filter_date_updated_after: Annotated[
        int | None, "Filter tasks updated after the specified Unix timestamp in milliseconds."
    ] = None,
    filter_date_updated_less_than: Annotated[
        int | None, "Filter tasks updated before a specific date, using Unix time in milliseconds."
    ] = None,
    filter_due_date_before: Annotated[
        int | None,
        "Filter tasks with due dates earlier than the specified Unix time in milliseconds.",
    ] = None,
    filter_due_date_greater_than: Annotated[
        int | None,
        "Filter tasks by a due date greater than the provided Unix time in milliseconds.",
    ] = None,
    include_archived_tasks: Annotated[
        bool | None,
        "Set to true to include archived tasks in the results. By default, archived tasks are excluded.",
    ] = None,
    include_closed_tasks: Annotated[
        bool | None,
        "Set to true to include closed tasks in the response. Defaults to false to exclude them.",
    ] = None,
    include_markdown_task_descriptions: Annotated[
        bool | None, "Set to true to return task descriptions in Markdown format."
    ] = None,
    include_subtasks: Annotated[
        bool | None, "Set to true to include subtasks; false to exclude them. Defaults to false."
    ] = None,
    include_tasks_in_multiple_lists: Annotated[
        bool | None,
        "Set to true to include tasks that exist in multiple lists. By default, these tasks are excluded.",
    ] = None,
    order_by_field: Annotated[
        str | None,
        "Specify the field to order tasks by. Options: 'id', 'created', 'updated', 'due_date'. Defaults to 'created'.",
    ] = None,
    page_number_to_fetch: Annotated[
        int | None, "Specify the page number to fetch tasks from, starting at 0."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTasks'."]:
    """Retrieve tasks from a specific list in ClickUp.

    This tool retrieves tasks from a specified list in ClickUp, limited to 100 tasks per page. It includes tasks where the specified list is their home, while also allowing inclusion of tasks from multiple lists by using the `include_timl` parameter. It provides details accessible to the user, including time spent on tasks with time entries."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}/task".format(list_id=list_identifier),  # noqa: UP032
        method="GET",
        params=remove_none_values({
            "archived": include_archived_tasks,
            "include_markdown_description": include_markdown_task_descriptions,
            "page": page_number_to_fetch,
            "order_by": order_by_field,
            "reverse": display_tasks_in_reverse_order,
            "subtasks": include_subtasks,
            "statuses": filter_by_statuses,
            "include_closed": include_closed_tasks,
            "include_timl": include_tasks_in_multiple_lists,
            "assignees": filter_by_assignees,
            "watchers": filter_by_watchers,
            "tags": filter_by_tags,
            "due_date_gt": filter_due_date_greater_than,
            "due_date_lt": filter_due_date_before,
            "date_created_gt": filter_date_created_after,
            "date_created_lt": date_created_less_than,
            "date_updated_gt": filter_date_updated_after,
            "date_updated_lt": filter_date_updated_less_than,
            "date_done_gt": filter_date_done_after,
            "date_done_lt": filter_by_date_done_before,
            "custom_fields": filter_by_custom_fields,
            "custom_field": filter_by_custom_field,
            "custom_items": custom_task_type_filters,
        }),
        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-clickup"))
async def create_new_clickup_task(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    target_list_id: Annotated[
        int | None,
        "The ID of the list where the new task will be created. This should be an integer identifying the list in ClickUp.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateTask'."]:
    """Create a new task in ClickUp.

    This tool is used to create a new task in a specified list on ClickUp. It should be called when you need to add a task to your ClickUp project management 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.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATENEWCLICKUPTASK"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 target_list_id:
        missing_params.append(("target_list_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["CREATENEWCLICKUPTASK"] + "\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["CREATENEWCLICKUPTASK"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/list/{list_id}/task".format(list_id=target_list_id),  # noqa: UP032
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATENEWCLICKUPTASK"],
        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-clickup"))
async def view_task_details(
    context: ToolContext,
    task_identifier: Annotated[
        str, "The unique identifier for the task you want to retrieve details for in ClickUp."
    ],
    filter_custom_fields: Annotated[
        list[str] | None,
        "Include tasks with specific values in one or more custom fields using the specified JSON format. Custom Relationships are supported.",
    ] = None,
    include_markdown_description: Annotated[
        bool | None, "Set to true to return task descriptions in Markdown format."
    ] = None,
    include_subtasks: Annotated[
        bool | None, "Include subtasks in the task details if set to true. Defaults to false."
    ] = None,
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id_for_custom_task: Annotated[
        int | None,
        "Provide the Workspace ID when referencing a task by its custom task ID. Required if `custom_task_ids` is true.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTask'."]:
    """Retrieve detailed information about a specific task.

    Use this tool to get detailed information on a task you have access to in ClickUp, including any attachments associated with the task."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}".format(task_id=task_identifier),  # noqa: UP032
        method="GET",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id_for_custom_task,
            "include_subtasks": include_subtasks,
            "include_markdown_description": include_markdown_description,
            "custom_fields": filter_custom_fields,
        }),
        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-clickup"))
async def update_task_in_clickup(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    task_identifier: Annotated[
        str | None,
        "The ID of the task to be updated. Provide either the standard task ID or a custom task ID if 'custom_task_ids' is true.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    workspace_id_for_custom_task: Annotated[
        int | None,
        "Provide the Workspace ID when referencing a task by its custom task ID (requires `custom_task_ids` set to true).  Only used when mode is 'execute'.",
    ] = None,
    use_custom_task_ids: Annotated[
        bool | None,
        "Set to true to reference a task by its custom task ID.  Only used when mode is 'execute'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateTask'."]:
    """Update task details in ClickUp.

    Use this tool to update specific fields of a task in ClickUp by providing the task ID and desired changes.

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

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

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

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not task_identifier:
        missing_params.append(("task_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["UPDATETASKINCLICKUP"] + "\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["UPDATETASKINCLICKUP"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/task/{task_id}".format(task_id=task_identifier),  # noqa: UP032
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATETASKINCLICKUP"],
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id_for_custom_task,
        }),
        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-clickup"))
async def delete_task(
    context: ToolContext,
    content_type: Annotated[
        str, "Specify the media type of the resource. Typically set as 'application/json'."
    ],
    task_id: Annotated[
        str,
        "The ID of the task to be deleted. This is mandatory and should be a valid task identifier.",
    ],
    use_custom_task_ids: Annotated[
        bool | None, "Set to true if referencing a task by custom task ID is required."
    ] = None,
    workspace_id: Annotated[
        int | None,
        "Provide the Workspace ID when referencing a task by its custom task ID. Required if `custom_task_ids` is true.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteTask'."]:
    """Delete a task from your ClickUp Workspace.

    Use this tool to delete a specific task from your ClickUp Workspace by providing the task ID. It should be called when you want to permanently remove a task."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}".format(task_id=task_id),  # noqa: UP032
        method="DELETE",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id,
        }),
        headers=remove_none_values({
            "Content-Type": content_type,
            "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-clickup"))
async def get_filtered_team_tasks(
    context: ToolContext,
    workspace_id: Annotated[
        int, "The ID of the workspace to retrieve tasks from. Must be an integer."
    ],
    assignee_ids: Annotated[
        list[str] | None, "Filter tasks by assignee using their ClickUp user IDs."
    ] = None,
    custom_task_type_filters: Annotated[
        list[float] | None,
        "Filter tasks by custom task types. Use `0` for tasks, `1` for Milestones, and other numbers for Workspace-defined types.",
    ] = None,
    display_tasks_in_reverse_order: Annotated[
        bool | None, "Set to true to display tasks in reverse order."
    ] = None,
    due_date_before: Annotated[
        int | None,
        "Filter tasks with due dates earlier than the specified Unix timestamp in milliseconds.",
    ] = None,
    due_date_greater_than: Annotated[
        int | None,
        "Filter tasks by a due date greater than the specified Unix time in milliseconds.",
    ] = None,
    filter_by_custom_fields: Annotated[
        list[str] | None,
        "Include tasks with specific values in Custom Fields. Provide an array of objects with field_id, operator, and value keys.",
    ] = None,
    filter_by_date_created_before: Annotated[
        int | None, "Filter tasks created before this date. Specify as Unix time in milliseconds."
    ] = None,
    filter_by_date_done_after: Annotated[
        int | None, "Filter tasks by the completion date after the given Unix time in milliseconds."
    ] = None,
    filter_by_done_date_before: Annotated[
        int | None,
        "Filter tasks completed before a specific date. Provide the date in Unix time (milliseconds).",
    ] = None,
    filter_by_list_ids: Annotated[
        list[str] | None, 'An array of list IDs to filter tasks by. Example: ["1234", "6789"].'
    ] = None,
    filter_by_project_ids: Annotated[
        list[str] | None,
        "An array of folder IDs to filter tasks by specific folders. For example, ['1234', '6789'].",
    ] = None,
    filter_by_space_ids: Annotated[
        list[str] | None,
        "An array of space IDs to filter tasks by. Example values: ['1234', '6789'].",
    ] = None,
    filter_by_tags: Annotated[
        list[str] | None,
        "Filter tasks by tags. Use `%20` for spaces within tags. Example: `urgent%20task`.",
    ] = None,
    filter_by_update_date_before: Annotated[
        int | None,
        "Filter tasks updated before a specific date, provided as Unix time in milliseconds.",
    ] = None,
    filter_by_updated_date_greater_than: Annotated[
        int | None,
        "Filter tasks by their updated date, greater than the specified Unix time in milliseconds.",
    ] = None,
    filter_created_date_after: Annotated[
        int | None,
        "Filter tasks by creation date greater than the specified Unix time in milliseconds.",
    ] = None,
    include_closed_tasks: Annotated[
        bool | None,
        "Set to true to include closed tasks, false to exclude them. By default, closed tasks are excluded.",
    ] = None,
    include_markdown_description: Annotated[
        bool | None, "Set to true to return task descriptions in Markdown format. Default is false."
    ] = None,
    include_subtasks: Annotated[
        bool | None,
        "Set to true to include subtasks, or false to exclude them. Defaults to false (exclude).",
    ] = None,
    order_tasks_by: Annotated[
        str | None,
        "Specify the field by which to order tasks. Options include: 'id', 'created', 'updated', 'due_date'. Defaults to 'created'.",
    ] = None,
    page_number_to_fetch: Annotated[
        int | None, "Page number to fetch, starting at 0, in the paginated list of tasks."
    ] = None,
    parent_task_id: Annotated[
        str | None, "Include the parent task ID to return subtasks in the response."
    ] = None,
    status_filters: Annotated[
        list[str] | None,
        "Filter tasks by their statuses. Use '%20' for spaces. Example: ['to%20do', 'in%20progress'].",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetFilteredTeamTasks'."]:
    """Retrieve tasks from a workspace based on specified filters.

    This tool retrieves tasks from a specified workspace that meet certain criteria. The response is limited to 100 tasks per page, and only tasks that the user has access to are returned. The tool is useful for viewing filtered task information from multiple lists, folders, or spaces."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_Id}/task".format(team_Id=workspace_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({
            "page": page_number_to_fetch,
            "order_by": order_tasks_by,
            "reverse": display_tasks_in_reverse_order,
            "subtasks": include_subtasks,
            "space_ids[]": filter_by_space_ids,
            "project_ids[]": filter_by_project_ids,
            "list_ids[]": filter_by_list_ids,
            "statuses[]": status_filters,
            "include_closed": include_closed_tasks,
            "assignees[]": assignee_ids,
            "tags[]": filter_by_tags,
            "due_date_gt": due_date_greater_than,
            "due_date_lt": due_date_before,
            "date_created_gt": filter_created_date_after,
            "date_created_lt": filter_by_date_created_before,
            "date_updated_gt": filter_by_updated_date_greater_than,
            "date_updated_lt": filter_by_update_date_before,
            "date_done_gt": filter_by_date_done_after,
            "date_done_lt": filter_by_done_date_before,
            "custom_fields": filter_by_custom_fields,
            "parent": parent_task_id,
            "include_markdown_description": include_markdown_description,
            "custom_items[]": custom_task_type_filters,
        }),
        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-clickup"))
async def merge_tasks_in_clickup(
    context: ToolContext,
    source_task_ids_to_merge: Annotated[
        list[str], "Array of task IDs to merge into the target task in ClickUp."
    ],
    target_task_id: Annotated[str, "ID of the target task into which other tasks will be merged."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'mergeTasks'."]:
    """Merge multiple tasks into a target task in ClickUp.

    Use this tool to merge several tasks into a specified target task within ClickUp. The target task is identified by the task_id, with source tasks provided in the request. This is useful when consolidating tasks for better project management."""
    request_data: Any = {"source_task_ids": source_task_ids_to_merge}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/merge".format(task_id=target_task_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-clickup"))
async def task_status_duration(
    context: ToolContext,
    content_type: Annotated[
        str, "Specify the content type for the API request, typically 'application/json'."
    ],
    task_identifier: Annotated[
        str,
        "The unique identifier for the task you want to query. Use this to specify which task's status duration you are interested in.",
    ],
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id: Annotated[
        int | None,
        "The Workspace ID must be provided when referencing a task by its custom task ID and `custom_task_ids` is set to `true`.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTask'sTimeinStatus'."]:
    """Get the duration a task spends in each status.

    Use this tool to find out how long a task has been in each status. Ensure that the 'Total time in Status' ClickApp is enabled by the Workspace owner or admin before using this tool."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/time_in_status".format(  # noqa: UP032
            task_id=task_identifier
        ),
        method="GET",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id,
        }),
        headers=remove_none_values({
            "Content-Type": content_type,
            "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-clickup"))
async def get_task_time_in_status(
    context: ToolContext,
    content_type_header: Annotated[
        str, "The MIME type of the body of the request. Typically set to 'application/json'."
    ],
    task_ids_list: Annotated[
        str,
        "A list of up to 100 task IDs to check duration in status. Include each task ID separated by commas.",
    ],
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference tasks by custom task IDs."
    ] = None,
    workspace_id_for_custom_task_ids: Annotated[
        int | None,
        "Provide the Workspace ID if using custom task IDs. Required when `custom_task_ids` is `true`.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetBulkTasks'TimeinStatus'."]:
    """Retrieve duration of tasks in various statuses.

    Use this tool to find out how long multiple tasks have remained in each status. The Total Time in Status ClickApp must be enabled by the Workspace owner or an admin."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/bulk_time_in_status/task_ids",
        method="GET",
        params=remove_none_values({
            "task_ids": task_ids_list,
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id_for_custom_task_ids,
        }),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def view_task_templates(
    context: ToolContext,
    content_type_header: Annotated[
        str, "Sets the 'Content-Type' for the API request, typically 'application/json'."
    ],
    page_number: Annotated[int, "The page number of results to retrieve. Used for pagination."],
    workspace_id: Annotated[
        int,
        "The ID of the workspace for which to retrieve task templates. This is used to specify the target workspace in ClickUp.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTaskTemplates'."]:
    """View available task templates in a workspace.

    Retrieve a list of task templates available for a specified workspace in ClickUp. Use this tool to explore task templates and plan tasks efficiently."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/taskTemplate".format(  # noqa: UP032
            team_id=workspace_id
        ),
        method="GET",
        params=remove_none_values({"page": page_number}),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def create_task_from_template(
    context: ToolContext,
    target_list_id: Annotated[
        int,
        "The ID of the list where the task will be created. This should be an integer associated with the desired list in your workspace.",
    ],
    task_name: Annotated[str, "The name of the task to be created using the template."],
    task_template_id: Annotated[
        str,
        "A string representing the ID of the task template to be used for task creation. Ensure the template is added to your workspace.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateTaskFromTemplate'."]:
    """Create a task using an existing template.

    This tool creates a new task in ClickUp using a specified task template from your workspace. Templates must be added to your Workspace before use. Ideal for automating the creation of standardized tasks."""
    request_data: Any = {"name": task_name}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}/taskTemplate/{template_id}".format(  # noqa: UP032
            list_id=target_list_id, template_id=task_template_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-clickup"))
async def create_list_from_folder_template(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    folder_identifier: Annotated[
        str | None,
        "The ID of the folder where the new list will be created using the specified template.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    template_id_for_list_creation: Annotated[
        str | None,
        "ID of the template to use for creating a new list in the folder. Ensure the template is added to your Workspace library.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateFolderListFromTemplate'."]:
    """Create a new list in a folder using a template.

    Use this tool to create a new list in a ClickUp folder using an existing list template. Ensure the template is added to your Workspace library before invoking this tool. The tool returns the future List ID immediately, though the list creation may continue asynchronously.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATELISTFROMFOLDERTEMPLATE"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 folder_identifier:
        missing_params.append(("folder_identifier", "path"))
    if not template_id_for_list_creation:
        missing_params.append(("template_id_for_list_creation", "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["CREATELISTFROMFOLDERTEMPLATE"]
                + "\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["CREATELISTFROMFOLDERTEMPLATE"]
                + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/folder/{folder_id}/list_template/{template_id}".format(  # noqa: UP032
            folder_id=folder_identifier, template_id=template_id_for_list_creation
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATELISTFROMFOLDERTEMPLATE"],
        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-clickup"))
async def create_list_from_template(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    space_id_for_list_creation: Annotated[
        str | None,
        "ID of the ClickUp Space where the new List will be created using the template.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    template_id: Annotated[
        str | None,
        "ID of the template to use for creating the list in the specified ClickUp space. It must be accessible in your Workspace.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateSpaceListFromTemplate'."]:
    """Create a new list in a ClickUp space using a template.

    This tool creates a new list within a specified ClickUp space using a provided list template. Publicly shared templates must be added to the Workspace before they can be used. The operation can be performed asynchronously or synchronously based on the 'return_immediately' parameter.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATELISTFROMTEMPLATE"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 space_id_for_list_creation:
        missing_params.append(("space_id_for_list_creation", "path"))
    if not template_id:
        missing_params.append(("template_id", "path"))

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

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

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/space/{space_id}/list_template/{template_id}".format(  # noqa: UP032
            space_id=space_id_for_list_creation, template_id=template_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATELISTFROMTEMPLATE"],
        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-clickup"))
async def get_workspace_seat_details(
    context: ToolContext,
    workspace_id: Annotated[
        str, "The unique ID of the workspace for which seat details are to be retrieved."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetWorkspaceseats'."]:
    """Retrieve seat details for a workspace.

    Use this tool to view the used, total, and available member and guest seats for a specified workspace in ClickUp."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/seats".format(team_id=workspace_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-clickup"))
async def get_workspace_plan(
    context: ToolContext,
    workspace_id: Annotated[
        str, "The unique ID for the workspace whose subscription plan you want to retrieve."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetWorkspaceplan'."]:
    """Retrieve the current subscription plan for a workspace.

    Use this tool to get details about the current subscription plan for a specified workspace in ClickUp. It should be called when you need to view the plan or pricing details associated with a workspace."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/plan".format(team_id=workspace_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-clickup"))
async def create_user_group(
    context: ToolContext,
    group_name: Annotated[
        str,
        "The name of the user group to be created within the workspace. This should be a descriptive name to identify the group easily.",
    ],
    user_group_members: Annotated[
        list[int],
        "List of user IDs to include in the user group. Each ID should be an integer representing a user within the workspace.",
    ],
    workspace_id: Annotated[
        int, "The unique ID of the ClickUp workspace where the user group will be created."
    ],
    group_handle: Annotated[
        str | None,
        "A unique string identifier for the user group to be created. This will be used as the group's handle within the workspace.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateUserGroup'."]:
    """Create a user group within a ClickUp workspace.

    Use this tool to organize and manage users by creating a user group within a specific ClickUp workspace. Note that adding a guest with view-only permissions converts them to a paid guest, potentially incurring additional costs."""
    request_data: Any = {"name": group_name, "handle": group_handle, "members": user_group_members}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/group".format(team_id=workspace_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-clickup"))
async def get_workspace_custom_task_types(
    context: ToolContext,
    workspace_id: Annotated[int, "The ID of the Workspace to retrieve custom task types for."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetCustomItems'."]:
    """Retrieve custom task types for a specific workspace.

    Use this tool to view the custom task types available in a specified workspace. Ideal for gaining insight into workspace-specific configurations and custom workflows."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/custom_item".format(  # noqa: UP032
            team_id=workspace_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-clickup"))
async def update_user_group(
    context: ToolContext,
    user_group_id: Annotated[
        str,
        "The unique identifier of the User Group within the Workspace. This ID is required to specify which group to update.",
    ],
    add_member_ids: Annotated[
        list[int] | None,
        "An array of user IDs to add to the User Group. Each ID should be an integer.",
    ] = None,
    handle_identifier: Annotated[
        str | None,
        "A unique identifier or handle for the User Group. This is used to reference the group within ClickUp.",
    ] = None,
    remove_members_ids: Annotated[
        list[int] | None,
        "An array of integer IDs representing the users to be removed from the User Group.",
    ] = None,
    user_group_name: Annotated[
        str | None,
        "The new name for the User Group within the ClickUp Workspace. This should be a string representing the desired name.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateTeam'."]:
    """Update and manage user groups within a ClickUp Workspace.

    Use this tool to update user groups in your ClickUp Workspace. Useful for managing team membership, permissions, or configurations. Note that adding a guest with view-only permissions will incur additional charges as they convert to paid guests."""
    request_data: Any = {
        "name": user_group_name,
        "handle": handle_identifier,
        "members": {"add": add_member_ids, "rem": remove_members_ids},
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/group/{group_id}".format(group_id=user_group_id),  # 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-clickup"))
async def delete_user_group(
    context: ToolContext,
    user_group_id: Annotated[
        str, "The identifier of the user group to be deleted from the workspace."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteTeam'."]:
    """Delete a user group from your ClickUp workspace.

    This tool removes a user group from your ClickUp workspace based on the provided group ID."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/group/{group_id}".format(group_id=user_group_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-clickup"))
async def get_user_groups_in_workspace(
    context: ToolContext,
    workspace_id: Annotated[int, "The ID of the ClickUp workspace to retrieve user groups from."],
    user_group_ids: Annotated[
        str | None,
        "List one or more User Group IDs to retrieve details about specific user groups in the workspace.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTeams1'."]:
    """Retrieve user groups in a ClickUp workspace.

    Use this tool to view the user groups within a specific ClickUp workspace. It's useful for managing and organizing user permissions and roles within a team environment."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/group",
        method="GET",
        params=remove_none_values({"team_id": workspace_id, "group_ids": user_group_ids}),
        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-clickup"))
async def get_tracked_time_for_task(
    context: ToolContext,
    set_content_type_header: Annotated[
        str, "Set the Content-Type header for the request, typically as 'application/json'."
    ],
    task_id: Annotated[
        str, "The unique identifier for the task whose tracked time you want to retrieve."
    ],
    use_custom_task_ids: Annotated[
        bool | None,
        "Set to true to reference tasks by their custom task IDs instead of default IDs.",
    ] = None,
    workspace_id: Annotated[
        int | None,
        "The ID of the Workspace to be provided when referencing a task by its custom task ID. Required if `custom_task_ids` is `true`.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Gettrackedtime'."]:
    """Fetch tracked time for a specific task.

    Use this tool to get the tracked time recorded for a specific task in ClickUp. It's useful for retrieving time spent details on tasks, though it doesn't handle new time entries."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/time".format(task_id=task_id),  # noqa: UP032
        method="GET",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id,
        }),
        headers=remove_none_values({
            "Content-Type": set_content_type_header,
            "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-clickup"))
async def clickup_legacy_time_tracking(
    context: ToolContext,
    clickup_task_id: Annotated[
        str,
        "The unique identifier of the task for which time is being logged. This can refer to either the standard task ID or a custom task ID if specified.",
    ],
    end_timestamp: Annotated[
        int, "Epoch timestamp indicating when the time tracking ended for the task."
    ],
    start_time_unix_epoch: Annotated[
        int,
        "The start time of the time entry in Unix epoch format. This is required to log time for a task.",
    ],
    time_spent_seconds: Annotated[
        int,
        "Duration of time spent on the task in seconds. This is the time you want to log for the specific task.",
    ],
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id: Annotated[
        int | None, "Provide the Workspace ID when `custom_task_ids` is `true`."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Tracktime'."]:
    """Log time entry for a ClickUp task using legacy endpoint.

    This tool allows logging time entries for a specific ClickUp task using a legacy API endpoint. It should be called when you need to record time spent on tasks, although it's recommended to use the updated Time Tracking API for new implementations."""
    request_data: Any = {
        "start": start_time_unix_epoch,
        "end": end_timestamp,
        "time": time_spent_seconds,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/time".format(task_id=clickup_task_id),  # noqa: UP032
        method="POST",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id,
        }),
        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-clickup"))
async def edit_legacy_time_entry(
    context: ToolContext,
    end_time_epoch: Annotated[
        int,
        "The end time of the tracked interval in Unix epoch format. It marks when the time entry should conclude.",
    ],
    legacy_time_interval_id: Annotated[
        str, "The unique identifier for the time interval to be edited."
    ],
    start_timestamp: Annotated[
        int, "The start time of the time entry as a Unix timestamp in milliseconds."
    ],
    task_identifier: Annotated[
        str,
        "The unique identifier of the task for which the legacy time entry is being edited. This is required to specify the task in ClickUp.",
    ],
    time_duration_in_seconds: Annotated[
        int,
        "The total time duration (in seconds) for the time entry to be updated. This modifies the tracked time for a specific task.",
    ],
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id: Annotated[
        int | None,
        "The Workspace ID required when custom task IDs are used. This must be set if `custom_task_ids` is `true`.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Edittimetracked'."]:
    """Edit a legacy time tracked entry for a task in ClickUp.

    Use this tool to modify an existing time entry on a specific task using the legacy endpoint in ClickUp. It's recommended to use newer APIs for time tracking, but this can be used for older integrations."""
    request_data: Any = {
        "start": start_timestamp,
        "end": end_time_epoch,
        "time": time_duration_in_seconds,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/time/{interval_id}".format(  # noqa: UP032
            task_id=task_identifier, interval_id=legacy_time_interval_id
        ),
        method="PUT",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id,
        }),
        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-clickup"))
async def remove_time_entry(
    context: ToolContext,
    content_type_header: Annotated[
        str, "Specify the media type of the resource (usually 'application/json')."
    ],
    interval_id_for_removal: Annotated[
        str, "The unique identifier of the time entry to delete from a task."
    ],
    task_identifier: Annotated[
        str,
        "The identifier of the task from which to delete the time entry. This must match the task ID used in ClickUp.",
    ],
    use_custom_task_ids: Annotated[
        bool | None, "Set to true to reference a task by its custom task ID."
    ] = None,
    workspace_id: Annotated[
        int | None,
        "Provide the Workspace ID when `use_custom_task_ids` is `true`. Required to identify the workspace in ClickUp.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Deletetimetracked'."]:
    """Delete a specific time entry from a task.

    Use this tool to delete a specific time tracking entry from a task. It's useful for managing or correcting time logs by removing unwanted entries."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/task/{task_id}/time/{interval_id}".format(  # noqa: UP032
            task_id=task_identifier, interval_id=interval_id_for_removal
        ),
        method="DELETE",
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id,
        }),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def get_time_entries_in_date_range(
    context: ToolContext,
    content_type: Annotated[
        str, "Specifies the format of the response content. Usually set to 'application/json'."
    ],
    workspace_id: Annotated[
        int, "Specify the Workspace ID for filtering time entries when using custom task IDs."
    ],
    end_date_unix_milliseconds: Annotated[
        float | None,
        "Specify the end date in Unix time (milliseconds) to filter time entries up to this point.",
    ] = None,
    filter_by_assignee: Annotated[
        float | None,
        "Filter by user IDs. Use commas to separate multiple IDs (e.g., '1234,9876'). Only accessible to Workspace Owners/Admins.",
    ] = None,
    folder_id: Annotated[
        int | None,
        "Include time entries for tasks in a specific folder by providing its ID. Only one location filter (space, folder, list, or task) can be used at a time.",
    ] = None,
    include_approval_details: Annotated[
        bool | None,
        "Include detailed approval information for each time entry, such as Approver ID, Approved Time, List of Approvers, and Approval Status.",
    ] = None,
    include_approval_history: Annotated[
        bool | None,
        "Set to true to include the approval history for each time entry, with status changes, notes, and approvers.",
    ] = None,
    include_location_names: Annotated[
        bool | None,
        "Include the names of the List, Folder, and Space in the response along with their IDs when set to true.",
    ] = None,
    include_only_billable_entries: Annotated[
        bool | None,
        "Set to `true` to include only billable time entries, or `false` for non-billable entries.",
    ] = None,
    include_task_tags: Annotated[
        bool | None, "Set to true to include task tags in the response for associated time entries."
    ] = None,
    space_id: Annotated[
        int | None,
        "Include time entries associated only with tasks in the specified Space using its ID.",
    ] = None,
    specific_list_id: Annotated[
        int | None,
        "Include only time entries associated with tasks in a specified List by providing the List ID.",
    ] = None,
    specific_task_id: Annotated[
        str | None, "Include only time entries associated with the specified task."
    ] = None,
    start_date_in_unix_milliseconds: Annotated[
        float | None, "The start date of the time entries in Unix time (milliseconds)."
    ] = None,
    use_custom_task_ids: Annotated[
        bool | None,
        "Set to true to reference tasks by their custom task IDs. Requires specifying the team_id.",
    ] = None,
    workspace_id_for_custom_task_ids: Annotated[
        int | None,
        "Provide the Workspace ID when referencing a task by its custom task ID, and `custom_task_ids` is set to true.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Gettimeentrieswithinadaterange'."]:
    """Retrieve time entries within a specified date range.

    Use this tool to view time entries filtered by start and end dates. By default, it returns entries from the last 30 days for the authenticated user. To access entries for other users, use the 'assignee' parameter. You may apply location filters with 'space_id', 'folder_id', 'list_id', or 'task_id', but only one at a time. Note: Negative duration entries indicate a running timer."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_Id}/time_entries".format(  # noqa: UP032
            team_Id=workspace_id
        ),
        method="GET",
        params=remove_none_values({
            "start_date": start_date_in_unix_milliseconds,
            "end_date": end_date_unix_milliseconds,
            "assignee": filter_by_assignee,
            "include_task_tags": include_task_tags,
            "include_location_names": include_location_names,
            "include_approval_history": include_approval_history,
            "include_approval_details": include_approval_details,
            "space_id": space_id,
            "folder_id": folder_id,
            "list_id": specific_list_id,
            "task_id": specific_task_id,
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id_for_custom_task_ids,
            "is_billable": include_only_billable_entries,
        }),
        headers=remove_none_values({
            "Content-Type": content_type,
            "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-clickup"))
async def create_time_entry(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    team_id_numeric: Annotated[
        int | None,
        "The numeric ID of the workspace. Required if referencing a task by custom task ID.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    workspace_id: Annotated[
        int | None,
        "Required if `custom_task_ids` is true. Provide the Workspace ID for the team.  Only used when mode is 'execute'.",
    ] = None,
    use_custom_task_ids: Annotated[
        bool | None,
        "Set to true to reference a task by its custom task id.  Only used when mode is 'execute'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Createatimeentry'."]:
    """Create a new time entry for tracking work.

    Use this tool to log a new time entry for a specific team. If the duration is negative, it indicates that a timer is running for the user.

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

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

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

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not team_id_numeric:
        missing_params.append(("team_id_numeric", "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["CREATETIMEENTRY"] + "\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["CREATETIMEENTRY"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/team/{team_Id}/time_entries".format(  # noqa: UP032
            team_Id=team_id_numeric
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATETIMEENTRY"],
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id,
        }),
        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-clickup"))
async def view_time_entry(
    context: ToolContext,
    content_type: Annotated[
        str,
        "Specifies the format of the content being sent or received, such as 'application/json'.",
    ],
    time_entry_id: Annotated[
        str,
        "The ID of a specific time entry, which can be found using the Get Time Entries Within a Date Range endpoint.",
    ],
    workspace_id: Annotated[int, "The ID of the workspace (team) to which the time entry belongs."],
    include_approval_details: Annotated[
        bool | None, "Include the details of the approval for the time entry when true."
    ] = None,
    include_approval_history: Annotated[
        bool | None,
        "Include the approval history of the time entry in the response. Set to true to include.",
    ] = None,
    include_location_names: Annotated[
        bool | None,
        "Include names of the List, Folder, and Space in the response along with their respective IDs.",
    ] = None,
    include_task_tags: Annotated[
        bool | None, "Set to true to include task tags in the response."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Getsingulartimeentry'."]:
    """Retrieve details of a specific time entry.

    Use this tool to view information about a single time entry, including its duration. If the duration is negative, it indicates an active timer for the user."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/time_entries/{timer_id}".format(  # noqa: UP032
            team_id=workspace_id, timer_id=time_entry_id
        ),
        method="GET",
        params=remove_none_values({
            "include_task_tags": include_task_tags,
            "include_location_names": include_location_names,
            "include_approval_history": include_approval_history,
            "include_approval_details": include_approval_details,
        }),
        headers=remove_none_values({
            "Content-Type": content_type,
            "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-clickup"))
async def delete_time_entry(
    context: ToolContext,
    content_type_header: Annotated[
        str, "Specify the content type of the request, usually 'application/json'."
    ],
    timer_ids_to_delete: Annotated[int, "Comma-separated list of timer IDs to delete."],
    workspace_id: Annotated[
        int,
        "The unique identifier for the ClickUp workspace from which you want to delete the time entry.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteatimeEntry'."]:
    """Deletes a time entry from a ClickUp workspace.

    Use this tool to delete a specific time entry from a ClickUp workspace. This should be called when you need to remove a recorded time entry by specifying the team and timer identifiers."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/time_entries/{timer_id}".format(  # noqa: UP032
            team_id=workspace_id, timer_id=timer_ids_to_delete
        ),
        method="DELETE",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def update_time_entry(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    workspace_id: Annotated[
        int | None,
        "The Workspace ID is required when referencing a task by its custom task ID. Provide it if `custom_task_ids` is set to `true`.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    team_identifier: Annotated[
        int | None,
        "The ID of the workspace (team) where the time entry is located. This is an integer value.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    timer_id: Annotated[
        int | None,
        "The unique identifier of the time entry to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    use_custom_task_ids: Annotated[
        bool | None,
        "Set to true to reference tasks by custom task IDs instead of standard IDs.  Only used when mode is 'execute'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateatimeEntry'."]:
    """Update the details of a time entry.

    This tool updates specific details of a time entry in ClickUp. Use it when you need to modify data such as the duration, description, or other attributes of an existing time entry.

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

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

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

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not team_identifier:
        missing_params.append(("team_identifier", "path"))
    if not timer_id:
        missing_params.append(("timer_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["UPDATETIMEENTRY"] + "\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["UPDATETIMEENTRY"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/team/{team_id}/time_entries/{timer_id}".format(  # noqa: UP032
            team_id=team_identifier, timer_id=timer_id
        ),
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATETIMEENTRY"],
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": team_identifier,
        }),
        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-clickup"))
async def view_time_entry_changes(
    context: ToolContext,
    content_type_header: Annotated[
        str,
        "The media type for the request, typically 'application/json'. Required for HTTP headers.",
    ],
    time_entry_id: Annotated[
        str,
        "The ID of a time entry. This ID can be obtained using the Get Time Entries Within a Date Range endpoint.",
    ],
    workspace_id: Annotated[int, "The ID of the workspace (team) where the time entry resides."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Gettimeentryhistory'."]:
    """View a list of changes made to a time entry.

    Use this tool to access the history of modifications for a specific time entry within a team in ClickUp. It provides insights into various changes made on the entry, such as updates or edits."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/time_entries/{timer_id}/history".format(  # noqa: UP032
            team_id=workspace_id, timer_id=time_entry_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def get_current_running_time_entry(
    context: ToolContext,
    content_type_header: Annotated[
        str,
        "The MIME type of the content, e.g., 'application/json'. Required for HTTP content negotiation.",
    ],
    workspace_id: Annotated[
        int,
        "The ID of the workspace to retrieve the running time entry for. It identifies the specific workspace within ClickUp.",
    ],
    assignee_user_id: Annotated[
        float | None,
        "The user ID of the time entry assignee for whom the current running timer is being retrieved. This identifies which user's timer is actively running.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Getrunningtimeentry'."]:
    """Retrieve the current running time entry for the user.

    Use this tool to view the time entry that is currently tracking time for the authenticated user. It indicates which timer is actively running. A negative duration suggests an ongoing timer."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/time_entries/current".format(  # noqa: UP032
            team_id=workspace_id
        ),
        method="GET",
        params=remove_none_values({"assignee": assignee_user_id}),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def remove_tags_from_time_entries(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    workspace_id: Annotated[
        int | None,
        "The unique ID of the Workspace from which to remove labels from time entries. Must be an integer.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Removetagsfromtimeentries'."]:
    """Remove labels from specific time entries.

    Use this tool to remove labels from time entries without affecting the labels in the Workspace.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["REMOVETAGSFROMTIMEENTRIES"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 workspace_id:
        missing_params.append(("workspace_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["REMOVETAGSFROMTIMEENTRIES"] + "\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["REMOVETAGSFROMTIMEENTRIES"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/team/{team_id}/time_entries/tags".format(  # noqa: UP032
            team_id=workspace_id
        ),
        method="DELETE",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["REMOVETAGSFROMTIMEENTRIES"],
        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-clickup"))
async def get_time_entry_tags(
    context: ToolContext,
    content_type_header: Annotated[
        str, "The media type of the resource, usually set to 'application/json'."
    ],
    workspace_id: Annotated[int, "The unique ID of the workspace to retrieve tags from."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Getalltagsfromtimeentries'."]:
    """Retrieve all tags from time entries in a workspace.

    Use this tool to view all labels that have been applied to time entries within a specified workspace."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/time_entries/tags".format(  # noqa: UP032
            team_id=workspace_id
        ),
        method="GET",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": content_type_header,
            "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-clickup"))
async def add_tag_to_time_entry(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    workspace_id: Annotated[
        int | None,
        "The ID of the workspace where the time entry is located. This is required to specify which team the tag should be added to.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Addtagsfromtimeentries'."]:
    """Add a label to a specific time entry in ClickUp.

    This tool is used to attach a label to a time entry for a specific team in ClickUp. It should be called when you want to organize or categorize time entries with tags for better tracking and management.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["ADDTAGTOTIMEENTRY"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 workspace_id:
        missing_params.append(("workspace_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["ADDTAGTOTIMEENTRY"] + "\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["ADDTAGTOTIMEENTRY"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/team/{team_id}/time_entries/tags".format(  # noqa: UP032
            team_id=workspace_id
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ADDTAGTOTIMEENTRY"],
        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-clickup"))
async def rename_time_entry_label(
    context: ToolContext,
    current_label_name: Annotated[
        str, "The current name of the time entry label that needs to be renamed."
    ],
    foreground_color_for_tag: Annotated[
        str, "Specify the hexadecimal foreground color for the tag (e.g., '#FFFFFF')."
    ],
    label_background_color: Annotated[
        str, "Hex code for the new background color of the label (e.g., '#FFFFFF')."
    ],
    new_label_name: Annotated[
        str, "The new name for the time entry label to be applied in ClickUp."
    ],
    workspace_id: Annotated[
        int,
        "The ID of the workspace where the label is located. This is required to specify the team in ClickUp whose label you want to rename.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'Changetagnamesfromtimeentries'."]:
    """Rename a time entry label in ClickUp.

    Use this tool to change the name of a label associated with time entries for a specific team in ClickUp."""
    request_data: Any = {
        "name": current_label_name,
        "new_name": new_label_name,
        "tag_bg": label_background_color,
        "tag_fg": foreground_color_for_tag,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/time_entries/tags".format(  # noqa: UP032
            team_id=workspace_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-clickup"))
async def start_timer_clickup(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    workspace_identifier: Annotated[
        int | None,
        "The Workspace ID required when custom task IDs are used.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    workspace_id: Annotated[
        int | None,
        "Provide the Workspace ID when `custom_task_ids` is `true`. Required for task referencing.  Only used when mode is 'execute'.",
    ] = None,
    use_custom_task_ids: Annotated[
        bool | None,
        "Set to true to reference a task by its custom task ID.  Only used when mode is 'execute'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'StartatimeEntry'."]:
    """Start a timer for the authenticated ClickUp user.

    Use this tool to initiate a timer for the authenticated user in ClickUp. It is ideal for tracking time on specific tasks or projects.

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

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

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

    # Mode is EXECUTE - validate parameters
    # Validate required parameters
    missing_params = []
    if not workspace_identifier:
        missing_params.append(("workspace_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["STARTTIMERCLICKUP"] + "\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["STARTTIMERCLICKUP"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/team/{team_Id}/time_entries/start".format(  # noqa: UP032
            team_Id=workspace_identifier
        ),
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["STARTTIMERCLICKUP"],
        params=remove_none_values({
            "custom_task_ids": use_custom_task_ids,
            "team_id": workspace_id,
        }),
        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-clickup"))
async def stop_timer_entry(
    context: ToolContext,
    content_type: Annotated[
        str, "Specifies the media type of the request. Usually 'application/json'."
    ],
    workspace_id: Annotated[
        int, "The ID of the workspace where the timer is running. Required to stop the timer."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'StopatimeEntry'."]:
    """Stops a running timer for the authenticated user.

    Use this tool to stop a currently running timer for the authenticated user on ClickUp. This action is initiated for a specific team, requiring the team ID."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/time_entries/stop".format(  # noqa: UP032
            team_id=workspace_id
        ),
        method="POST",
        params=remove_none_values({}),
        headers=remove_none_values({
            "Content-Type": content_type,
            "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-clickup"))
async def invite_user_to_workspace(
    context: ToolContext,
    invite_as_admin: Annotated[
        bool,
        "Indicate if the user should be invited as an admin. True for admin, False for regular member.",
    ],
    user_email: Annotated[
        str,
        "The email address of the user to be invited to the Workspace. Must be a valid email format.",
    ],
    workspace_id: Annotated[
        int, "The unique ID of the workspace to which the user is being invited."
    ],
    custom_role_id: Annotated[
        int | None,
        "The ID of the custom role to assign to the user in the Workspace. Must be an integer.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'InviteUserToWorkspace'."]:
    """Invite a user to your ClickUp Workspace as a member.

    Use this tool to invite someone to your ClickUp Workspace as a member. This is suitable for Workspaces on the Enterprise Plan. For inviting guests, use the appropriate tool instead."""
    request_data: Any = {
        "email": user_email,
        "admin": invite_as_admin,
        "custom_role_id": custom_role_id,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/user".format(team_id=workspace_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-clickup"))
async def get_workspace_user_info(
    context: ToolContext,
    user_id: Annotated[
        int,
        "The unique identifier of the user to retrieve information for. This is required to specify which user's information is being accessed in the workspace.",
    ],
    workspace_id: Annotated[
        int,
        "The unique ID of the Workspace. Used to specify which Workspace's user information is to be retrieved.",
    ],
    show_shared_items: Annotated[
        bool | None,
        "Set to `true` to include details of shared items; `false` excludes them by default.",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetUser'."]:
    """Retrieve user information from a specified workspace.

    Call this tool to get details about a user from a specific workspace on the ClickUp platform. This tool is applicable only to Workspaces that are on the Enterprise Plan."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/user/{user_id}".format(  # noqa: UP032
            team_id=workspace_id, user_id=user_id
        ),
        method="GET",
        params=remove_none_values({"include_shared": show_shared_items}),
        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-clickup"))
async def update_user_workspace_details(
    context: ToolContext,
    assign_admin_role: Annotated[
        bool, "Set to true to assign the user as an admin in the workspace, otherwise false."
    ],
    custom_role_id: Annotated[
        int,
        "An integer representing the custom role ID to assign to the user in the workspace. This is required for users with specific roles.",
    ],
    user_identifier: Annotated[
        int,
        "The unique identifier for the user within the workspace. This value is required to specify which user's details need updating.",
    ],
    user_name: Annotated[
        str, "The new full name of the user to be updated in the ClickUp workspace."
    ],
    workspace_id: Annotated[
        int, "The ID of the ClickUp workspace where the user's details will be updated."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'EditUserOnWorkspace'."]:
    """Update a user's name and role in a ClickUp workspace.

    This tool updates a user's name and role within a specific ClickUp workspace. It's only available to Workspaces on the Enterprise Plan. Use it to change user details in a team."""
    request_data: Any = {
        "username": user_name,
        "admin": assign_admin_role,
        "custom_role_id": custom_role_id,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/user/{user_id}".format(  # noqa: UP032
            team_id=workspace_id, user_id=user_identifier
        ),
        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-clickup"))
async def remove_user_from_workspace(
    context: ToolContext,
    user_id: Annotated[int, "The ID of the user to be deactivated from the workspace."],
    workspace_id: Annotated[
        int, "The ID of the Workspace in ClickUp from which the user will be removed."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'RemoveUserFromWorkspace'."]:
    """Remove a user from a ClickUp workspace.

    Use this tool to deactivate a user from a ClickUp workspace, applicable for Workspaces on the Enterprise Plan."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/user/{user_id}".format(  # noqa: UP032
            team_id=workspace_id, user_id=user_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-clickup"))
async def get_team_views(
    context: ToolContext,
    workspace_id: Annotated[
        int, "The ID of the workspace to retrieve task and page views from in ClickUp."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetTeamViews'."]:
    """Retrieve task and page views at the workspace level.

    Use this tool to view the task and page views available at the Everything Level of a specified workspace in ClickUp."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/view".format(team_id=workspace_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-clickup"))
async def create_team_view(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    workspace_id: Annotated[
        int | None,
        "The unique identifier for the workspace where the view will be added. This corresponds to the team or workspace ID.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateTeamView'."]:
    """Add various views to a workspace at the Everything Level.

    This tool allows you to add different types of views such as List, Board, Calendar, Table, Timeline, Workload, Activity, Map, Chat, or Gantt to a specified workspace within a team. Use this when you need to create or organize views for better workspace management.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["CREATETEAMVIEW"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 workspace_id:
        missing_params.append(("workspace_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["CREATETEAMVIEW"] + "\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["CREATETEAMVIEW"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/team/{team_id}/view".format(team_id=workspace_id),  # noqa: UP032
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["CREATETEAMVIEW"],
        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-clickup"))
async def get_space_views(
    context: ToolContext,
    space_identifier: Annotated[
        int, "The unique integer ID of the space for which to retrieve task and page views."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetSpaceViews'."]:
    """Retrieve the task and page views for a specified Space.

    This tool is used to obtain the task and page views available within a specific Space in ClickUp. It should be called when you need to explore or list the views associated with a given space by providing the space ID."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/space/{space_id}/view".format(  # noqa: UP032
            space_id=space_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-clickup"))
async def add_space_view(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    space_id: Annotated[
        int | None,
        "The unique identifier of the ClickUp space where the view will be added. It should be an integer.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateSpaceView'."]:
    """Add a new view to a ClickUp space.

    Use this tool to add a List, Board, Calendar, Table, Timeline, Workload, Activity, Map, Chat, or Gantt view to a ClickUp Space.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["ADDSPACEVIEW"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 space_id:
        missing_params.append(("space_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["ADDSPACEVIEW"] + "\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["ADDSPACEVIEW"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/space/{space_id}/view".format(space_id=space_id),  # noqa: UP032
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ADDSPACEVIEW"],
        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-clickup"))
async def get_folder_views(
    context: ToolContext,
    folder_id: Annotated[
        int, "The ID of the folder to retrieve views for. This should be an integer value."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetFolderViews'."]:
    """Retrieve available task and page views for a Folder in ClickUp."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/folder/{folder_id}/view".format(folder_id=folder_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-clickup"))
async def add_view_to_folder(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    folder_id: Annotated[
        int | None,
        "The unique integer ID of the ClickUp folder where the view will be added. Required for specifying the target folder.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateFolderView'."]:
    """Add various view types to a ClickUp folder.

    Use this tool to add a List, Board, Calendar, Table, Timeline, Workload, Activity, Map, Chat, or Gantt view to a specific folder in ClickUp.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["ADDVIEWTOFOLDER"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 folder_id:
        missing_params.append(("folder_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["ADDVIEWTOFOLDER"] + "\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["ADDVIEWTOFOLDER"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/folder/{folder_id}/view".format(folder_id=folder_id),  # noqa: UP032
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ADDVIEWTOFOLDER"],
        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-clickup"))
async def get_list_views(
    context: ToolContext,
    target_list_id: Annotated[
        int, "The unique identifier for the list to retrieve views from. This should be an integer."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetListViews'."]:
    """Retrieve available views for a specific list.

    Use this tool to view the task and page views available for a specific list in ClickUp. It retrieves both regular and required views."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/list/{list_id}/view".format(list_id=target_list_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-clickup"))
async def add_view_to_clickup_list(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    clickup_list_id: Annotated[
        int | None,
        "An integer representing the ID of the ClickUp list to which the view will be added.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateListView'."]:
    """Add various views to a ClickUp list.

    Use this tool to add different types of views (List, Board, Calendar, etc.) to a specific ClickUp list using the list ID.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["ADDVIEWTOCLICKUPLIST"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 clickup_list_id:
        missing_params.append(("clickup_list_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["ADDVIEWTOCLICKUPLIST"] + "\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["ADDVIEWTOCLICKUPLIST"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/list/{list_id}/view".format(list_id=clickup_list_id),  # noqa: UP032
        method="POST",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["ADDVIEWTOCLICKUPLIST"],
        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-clickup"))
async def view_task_or_page_info(
    context: ToolContext,
    view_identifier: Annotated[
        str,
        "A unique identifier for the specific task or page view in ClickUp to be retrieved. This is required to obtain the relevant information.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetView'."]:
    """Retrieve details of a specific task or page view.

    Use this tool to get detailed information about a specific task or page view in ClickUp using the view ID."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/view/{view_id}".format(view_id=view_identifier),  # 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-clickup"))
async def update_view_settings(
    context: ToolContext,
    mode: Annotated[
        ToolMode,
        "Operation mode: 'get_request_schema' returns the OpenAPI spec "
        "for the request body, 'execute' performs the actual operation",
    ],
    view_identifier: Annotated[
        str | None,
        "The unique identifier for the view to be updated.  Required when mode is 'execute', ignored when mode is 'get_request_schema'.",
    ] = None,
    request_body: Annotated[
        str | None,
        "Stringified JSON representing the request body. Required when "
        "mode is 'execute', ignored when mode is 'get_request_schema'",
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateView'."]:
    """Update the settings and configuration of a view.

    Rename, regroup, sort, filter, and modify columns and settings of a view when needing to adjust its configuration.

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

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

    If you need the schema, call with mode='get_request_schema' ONCE, then execute.
    """
    if mode == ToolMode.GET_REQUEST_SCHEMA:
        return {
            "request_body_schema": REQUEST_BODY_SCHEMAS["UPDATEVIEWSETTINGS"],
            "instructions": (
                "Use the request_body_schema to construct a valid JSON object. "
                "Once you have populated 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 view_identifier:
        missing_params.append(("view_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["UPDATEVIEWSETTINGS"] + "\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["UPDATEVIEWSETTINGS"] + "\n```"
            ),
        ) from e

    response = await make_request_with_schema_validation(
        url="https://api.clickup.com/api/v2/view/{view_id}".format(view_id=view_identifier),  # noqa: UP032
        method="PUT",
        request_data=request_data,
        schema=REQUEST_BODY_SCHEMAS["UPDATEVIEWSETTINGS"],
        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-clickup"))
async def delete_view(
    context: ToolContext,
    view_id: Annotated[
        str, "The ID of the view to be deleted in ClickUp. Must be provided as a string."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteView'."]:
    """Delete a specified view from ClickUp.

    Use this tool to delete a specific view in ClickUp by providing the view ID. Useful for managing and organizing views by removing those that are no longer needed."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/view/{view_id}".format(view_id=view_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-clickup"))
async def get_visible_tasks_in_view(
    context: ToolContext,
    pagination_page_number: Annotated[
        int,
        "The specific page number of tasks to retrieve. Used for pagination in task lists, starting at 1.",
    ],
    view_identifier: Annotated[
        str, "The ID of the ClickUp view from which to retrieve visible tasks. Must be a string."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetViewTasks'."]:
    """Retrieve all visible tasks from a ClickUp view.

    Use this tool to obtain a list of all tasks that are visible in a specific view within ClickUp. It is useful for task management and organization within the platform."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/view/{view_id}/task".format(view_id=view_identifier),  # noqa: UP032
        method="GET",
        params=remove_none_values({"page": pagination_page_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-clickup"))
async def get_workspace_webhooks(
    context: ToolContext,
    workspace_id: Annotated[int, "The ID of the Workspace to retrieve the webhooks for."],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'GetWebhooks'."]:
    """Retrieve webhooks for a workspace.

    Use this tool to view the webhooks created via the API for a specific Workspace. It returns webhooks created by the authenticated user."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/webhook".format(team_id=workspace_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-clickup"))
async def setup_clickup_webhook(
    context: ToolContext,
    event_types: Annotated[
        list[str],
        "An array of event types to subscribe to, or use `*` to subscribe to all events. Refer to ClickUp documentation for available options.",
    ],
    webhook_url: Annotated[
        str,
        "The URL where the webhook will send POST requests. Must be reachable to receive event data.",
    ],
    workspace_id: Annotated[
        int,
        "The ID of the workspace where the webhook will be set up. Use this to specify the team context for monitoring.",
    ],
    folder_id: Annotated[
        int | None,
        "Specify the folder ID in ClickUp for which the webhook is to be created. It should be an integer value representing the folder.",
    ] = None,
    space_identifier: Annotated[
        int | None,
        "The numeric ID of the space within a ClickUp workspace for which the webhook is set up.",
    ] = None,
    specific_task_id: Annotated[
        str | None,
        "Unique identifier for a specific task to monitor. Leave empty if not targeting a specific task.",
    ] = None,
    target_list_id: Annotated[
        int | None, "The ID of the list in ClickUp for which you want to set up a webhook."
    ] = None,
) -> Annotated[dict[str, Any], "Response from the API endpoint 'CreateWebhook'."]:
    """Set up a ClickUp webhook to monitor events.

    Use this tool to create a webhook in ClickUp, enabling monitoring of events for a specific team. Useful for triggering actions or notifications based on certain events within a ClickUp team."""
    request_data: Any = {
        "endpoint": webhook_url,
        "events": event_types,
        "space_id": space_identifier,
        "folder_id": folder_id,
        "list_id": target_list_id,
        "task_id": specific_task_id,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/team/{team_id}/webhook".format(team_id=workspace_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-clickup"))
async def update_clickup_webhook_events(
    context: ToolContext,
    monitored_events: Annotated[
        str,
        "A comma-separated list of events for the webhook to monitor. Use valid event names as per ClickUp webhook documentation.",
    ],
    webhook_endpoint_url: Annotated[
        str,
        "The URL where the webhook should send POST requests for the events. Must be a valid and accessible URL.",
    ],
    webhook_identifier: Annotated[
        str, "The unique identifier for the webhook to be updated, formatted as a UUID."
    ],
    webhook_status: Annotated[
        str,
        "Specify the new status of the webhook. Use 'active' to enable or 'inactive' to disable it.",
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'UpdateWebhook'."]:
    """Update a ClickUp webhook to modify monitored events.

    Use this tool to update the events a ClickUp webhook monitors, allowing for changes to event tracking as needed."""
    request_data: Any = {
        "endpoint": webhook_endpoint_url,
        "events": monitored_events,
        "status": webhook_status,
    }
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/webhook/{webhook_id}".format(  # noqa: UP032
            webhook_id=webhook_identifier
        ),
        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-clickup"))
async def delete_webhook(
    context: ToolContext,
    webhook_id: Annotated[
        str, "The unique identifier for the webhook to be deleted. Must be in UUID format."
    ],
) -> Annotated[dict[str, Any], "Response from the API endpoint 'DeleteWebhook'."]:
    """Delete a webhook to stop event monitoring.

    Use this tool to delete a webhook, which will stop monitoring events and locations associated with it. Useful for managing and updating your event tracking preferences."""
    request_data: Any = {}
    if isinstance(request_data, dict):
        request_data = remove_none_values(request_data)
    content = json.dumps(request_data) if request_data else None
    response = await make_request(
        url="https://api.clickup.com/api/v2/webhook/{webhook_id}".format(webhook_id=webhook_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}
