cleanup
This commit is contained in:
63
README.md
63
README.md
@@ -1105,69 +1105,6 @@ Saved files expire after 1 hour and are cleaned up automatically.
|
||||
|
||||
---
|
||||
|
||||
### 🔄 Tool Consolidation & Migration
|
||||
|
||||
**Tool Count Reduction**: This release consolidates 139 tools down to 111 tools (20% reduction) by combining CRUD operations into action-based `manage_*` tools.
|
||||
|
||||
<details>
|
||||
<summary><b>Migration Mapping</b> <sub><sup>← Old tool → New tool mapping</sup></sub></summary>
|
||||
|
||||
| Old Tool | New Tool | Action Parameter |
|
||||
|----------|----------|------------------|
|
||||
| `create_event` | `manage_event` | `action="create"` |
|
||||
| `modify_event` | `manage_event` | `action="update"` |
|
||||
| `delete_event` | `manage_event` | `action="delete"` |
|
||||
| `share_drive_file` | `manage_drive_access` | `action="grant"` |
|
||||
| `batch_share_drive_file` | `manage_drive_access` | `action="grant_batch"` |
|
||||
| `update_drive_permission` | `manage_drive_access` | `action="update"` |
|
||||
| `remove_drive_permission` | `manage_drive_access` | `action="revoke"` |
|
||||
| `transfer_drive_ownership` | `manage_drive_access` | `action="transfer_owner"` |
|
||||
| `create_gmail_filter` | `manage_gmail_filter` | `action="create"` |
|
||||
| `delete_gmail_filter` | `manage_gmail_filter` | `action="delete"` |
|
||||
| `create_task` | `manage_task` | `action="create"` |
|
||||
| `update_task` | `manage_task` | `action="update"` |
|
||||
| `delete_task` | `manage_task` | `action="delete"` |
|
||||
| `move_task` | `manage_task` | `action="move"` |
|
||||
| `create_task_list` | `manage_task_list` | `action="create"` |
|
||||
| `update_task_list` | `manage_task_list` | `action="update"` |
|
||||
| `delete_task_list` | `manage_task_list` | `action="delete"` |
|
||||
| `clear_completed_tasks` | `manage_task_list` | `action="clear_completed"` |
|
||||
| `create_contact` | `manage_contact` | `action="create"` |
|
||||
| `update_contact` | `manage_contact` | `action="update"` |
|
||||
| `delete_contact` | `manage_contact` | `action="delete"` |
|
||||
| `batch_create_contacts` | `manage_contacts_batch` | `action="create"` |
|
||||
| `batch_update_contacts` | `manage_contacts_batch` | `action="update"` |
|
||||
| `batch_delete_contacts` | `manage_contacts_batch` | `action="delete"` |
|
||||
| `create_contact_group` | `manage_contact_group` | `action="create"` |
|
||||
| `update_contact_group` | `manage_contact_group` | `action="update"` |
|
||||
| `delete_contact_group` | `manage_contact_group` | `action="delete"` |
|
||||
| `modify_contact_group_members` | `manage_contact_group` | `action="modify_members"` |
|
||||
| `create_deployment` | `manage_deployment` | `action="create"` |
|
||||
| `update_deployment` | `manage_deployment` | `action="update"` |
|
||||
| `delete_deployment` | `manage_deployment` | `action="delete"` |
|
||||
| `add_conditional_formatting` | `manage_conditional_formatting` | `action="add"` |
|
||||
| `update_conditional_formatting` | `manage_conditional_formatting` | `action="update"` |
|
||||
| `delete_conditional_formatting` | `manage_conditional_formatting` | `action="delete"` |
|
||||
| `read_document_comments` | `list_document_comments` | N/A (renamed) |
|
||||
| `create_document_comment` | `manage_document_comment` | `action="create"` |
|
||||
| `reply_to_document_comment` | `manage_document_comment` | `action="reply"` |
|
||||
| `resolve_document_comment` | `manage_document_comment` | `action="resolve"` |
|
||||
| `read_spreadsheet_comments` | `list_spreadsheet_comments` | N/A (renamed) |
|
||||
| `create_spreadsheet_comment` | `manage_spreadsheet_comment` | `action="create"` |
|
||||
| `reply_to_spreadsheet_comment` | `manage_spreadsheet_comment` | `action="reply"` |
|
||||
| `resolve_spreadsheet_comment` | `manage_spreadsheet_comment` | `action="resolve"` |
|
||||
| `read_presentation_comments` | `list_presentation_comments` | N/A (renamed) |
|
||||
| `create_presentation_comment` | `manage_presentation_comment` | `action="create"` |
|
||||
| `reply_to_presentation_comment` | `manage_presentation_comment` | `action="reply"` |
|
||||
| `resolve_presentation_comment` | `manage_presentation_comment` | `action="resolve"` |
|
||||
| `search_custom_siterestrict` | `search_custom` | Use `sites` parameter |
|
||||
|
||||
**Breaking Change**: Legacy tools have been removed. Use the new consolidated tools with appropriate action parameters.
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### Connect to Claude Desktop
|
||||
|
||||
The server supports two transport modes:
|
||||
|
||||
@@ -500,6 +500,8 @@ async def manage_deployment(
|
||||
elif action == "update":
|
||||
if not deployment_id:
|
||||
raise ValueError("deployment_id is required for update action")
|
||||
if description is None or description == "":
|
||||
raise ValueError("description is required for update action")
|
||||
return await _update_deployment_impl(
|
||||
service, user_google_email, script_id, deployment_id, description
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ from mcp import Resource
|
||||
|
||||
from auth.service_decorator import require_google_service
|
||||
from core.server import server
|
||||
from core.utils import handle_http_errors
|
||||
from core.utils import UserInputError, handle_http_errors
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -449,7 +449,7 @@ async def manage_contact(
|
||||
"""
|
||||
action = action.lower().strip()
|
||||
if action not in ("create", "update", "delete"):
|
||||
raise Exception(
|
||||
raise UserInputError(
|
||||
f"Invalid action '{action}'. Must be 'create', 'update', or 'delete'."
|
||||
)
|
||||
|
||||
@@ -470,7 +470,7 @@ async def manage_contact(
|
||||
)
|
||||
|
||||
if not body:
|
||||
raise Exception(
|
||||
raise UserInputError(
|
||||
"At least one field (name, email, phone, etc.) must be provided."
|
||||
)
|
||||
|
||||
@@ -489,7 +489,7 @@ async def manage_contact(
|
||||
|
||||
# update and delete both require contact_id
|
||||
if not contact_id:
|
||||
raise Exception(f"contact_id is required for '{action}' action.")
|
||||
raise UserInputError(f"contact_id is required for '{action}' action.")
|
||||
|
||||
# Normalize resource name
|
||||
if not contact_id.startswith("people/"):
|
||||
@@ -520,7 +520,7 @@ async def manage_contact(
|
||||
)
|
||||
|
||||
if not body:
|
||||
raise Exception(
|
||||
raise UserInputError(
|
||||
"At least one field (name, email, phone, etc.) must be provided."
|
||||
)
|
||||
|
||||
@@ -566,6 +566,8 @@ async def manage_contact(
|
||||
logger.info(f"Deleted contact {resource_name} for {user_google_email}")
|
||||
return response
|
||||
|
||||
except UserInputError:
|
||||
raise
|
||||
except HttpError as error:
|
||||
if error.resp.status == 404:
|
||||
message = f"Contact not found: {contact_id}"
|
||||
@@ -764,7 +766,7 @@ async def manage_contacts_batch(
|
||||
"""
|
||||
action = action.lower().strip()
|
||||
if action not in ("create", "update", "delete"):
|
||||
raise Exception(
|
||||
raise UserInputError(
|
||||
f"Invalid action '{action}'. Must be 'create', 'update', or 'delete'."
|
||||
)
|
||||
|
||||
@@ -775,12 +777,12 @@ async def manage_contacts_batch(
|
||||
try:
|
||||
if action == "create":
|
||||
if not contacts:
|
||||
raise Exception(
|
||||
raise UserInputError(
|
||||
"contacts parameter is required for 'create' action."
|
||||
)
|
||||
|
||||
if len(contacts) > 200:
|
||||
raise Exception("Maximum 200 contacts can be created in a batch.")
|
||||
raise UserInputError("Maximum 200 contacts can be created in a batch.")
|
||||
|
||||
contact_bodies = []
|
||||
for contact in contacts:
|
||||
@@ -796,7 +798,7 @@ async def manage_contacts_batch(
|
||||
contact_bodies.append({"contactPerson": body})
|
||||
|
||||
if not contact_bodies:
|
||||
raise Exception("No valid contact data provided.")
|
||||
raise UserInputError("No valid contact data provided.")
|
||||
|
||||
batch_body = {
|
||||
"contacts": contact_bodies,
|
||||
@@ -823,19 +825,19 @@ async def manage_contacts_batch(
|
||||
|
||||
if action == "update":
|
||||
if not updates:
|
||||
raise Exception(
|
||||
raise UserInputError(
|
||||
"updates parameter is required for 'update' action."
|
||||
)
|
||||
|
||||
if len(updates) > 200:
|
||||
raise Exception("Maximum 200 contacts can be updated in a batch.")
|
||||
raise UserInputError("Maximum 200 contacts can be updated in a batch.")
|
||||
|
||||
# Fetch all contacts to get their etags
|
||||
resource_names = []
|
||||
for update in updates:
|
||||
cid = update.get("contact_id")
|
||||
if not cid:
|
||||
raise Exception("Each update must include a contact_id.")
|
||||
raise UserInputError("Each update must include a contact_id.")
|
||||
if not cid.startswith("people/"):
|
||||
cid = f"people/{cid}"
|
||||
resource_names.append(cid)
|
||||
@@ -894,7 +896,7 @@ async def manage_contacts_batch(
|
||||
update_fields_set.add("organizations")
|
||||
|
||||
if not update_bodies:
|
||||
raise Exception("No valid update data provided.")
|
||||
raise UserInputError("No valid update data provided.")
|
||||
|
||||
batch_body = {
|
||||
"contacts": update_bodies,
|
||||
@@ -922,12 +924,12 @@ async def manage_contacts_batch(
|
||||
|
||||
# action == "delete"
|
||||
if not contact_ids:
|
||||
raise Exception(
|
||||
raise UserInputError(
|
||||
"contact_ids parameter is required for 'delete' action."
|
||||
)
|
||||
|
||||
if len(contact_ids) > 500:
|
||||
raise Exception("Maximum 500 contacts can be deleted in a batch.")
|
||||
raise UserInputError("Maximum 500 contacts can be deleted in a batch.")
|
||||
|
||||
resource_names = []
|
||||
for cid in contact_ids:
|
||||
@@ -948,6 +950,8 @@ async def manage_contacts_batch(
|
||||
)
|
||||
return response
|
||||
|
||||
except UserInputError:
|
||||
raise
|
||||
except HttpError as error:
|
||||
message = f"API error: {error}. You might need to re-authenticate. LLM: Try 'start_google_auth' with the user's email ({user_google_email}) and service_name='Google Contacts'."
|
||||
logger.error(message, exc_info=True)
|
||||
@@ -992,7 +996,7 @@ async def manage_contact_group(
|
||||
"""
|
||||
action = action.lower().strip()
|
||||
if action not in ("create", "update", "delete", "modify_members"):
|
||||
raise Exception(
|
||||
raise UserInputError(
|
||||
f"Invalid action '{action}'. Must be 'create', 'update', 'delete', or 'modify_members'."
|
||||
)
|
||||
|
||||
@@ -1003,7 +1007,7 @@ async def manage_contact_group(
|
||||
try:
|
||||
if action == "create":
|
||||
if not name:
|
||||
raise Exception("name is required for 'create' action.")
|
||||
raise UserInputError("name is required for 'create' action.")
|
||||
|
||||
body = {"contactGroup": {"name": name}}
|
||||
|
||||
@@ -1025,7 +1029,7 @@ async def manage_contact_group(
|
||||
|
||||
# All other actions require group_id
|
||||
if not group_id:
|
||||
raise Exception(f"group_id is required for '{action}' action.")
|
||||
raise UserInputError(f"group_id is required for '{action}' action.")
|
||||
|
||||
# Normalize resource name
|
||||
if not group_id.startswith("contactGroups/"):
|
||||
@@ -1035,7 +1039,7 @@ async def manage_contact_group(
|
||||
|
||||
if action == "update":
|
||||
if not name:
|
||||
raise Exception("name is required for 'update' action.")
|
||||
raise UserInputError("name is required for 'update' action.")
|
||||
|
||||
body = {"contactGroup": {"name": name}}
|
||||
|
||||
@@ -1076,7 +1080,7 @@ async def manage_contact_group(
|
||||
|
||||
# action == "modify_members"
|
||||
if not add_contact_ids and not remove_contact_ids:
|
||||
raise Exception(
|
||||
raise UserInputError(
|
||||
"At least one of add_contact_ids or remove_contact_ids must be provided."
|
||||
)
|
||||
|
||||
@@ -1128,6 +1132,8 @@ async def manage_contact_group(
|
||||
)
|
||||
return response
|
||||
|
||||
except UserInputError:
|
||||
raise
|
||||
except HttpError as error:
|
||||
if error.resp.status == 404:
|
||||
message = f"Contact group not found: {group_id}"
|
||||
@@ -1141,4 +1147,3 @@ async def manage_contact_group(
|
||||
logger.exception(message)
|
||||
raise Exception(message)
|
||||
|
||||
|
||||
|
||||
@@ -807,8 +807,12 @@ async def manage_conditional_formatting(
|
||||
"rule_index must be a non-negative integer when provided."
|
||||
)
|
||||
|
||||
condition_values_list = _parse_condition_values(condition_values)
|
||||
gradient_points_list = _parse_gradient_points(gradient_points)
|
||||
condition_values_list = (
|
||||
None
|
||||
if gradient_points_list
|
||||
else _parse_condition_values(condition_values)
|
||||
)
|
||||
|
||||
sheets, sheet_titles = await _fetch_sheets_with_rules(
|
||||
service, spreadsheet_id
|
||||
@@ -903,8 +907,12 @@ async def manage_conditional_formatting(
|
||||
if not isinstance(rule_index, int) or rule_index < 0:
|
||||
raise UserInputError("rule_index must be a non-negative integer.")
|
||||
|
||||
condition_values_list = _parse_condition_values(condition_values)
|
||||
gradient_points_list = _parse_gradient_points(gradient_points)
|
||||
condition_values_list = (
|
||||
None
|
||||
if gradient_points_list is not None
|
||||
else _parse_condition_values(condition_values)
|
||||
)
|
||||
|
||||
sheets, sheet_titles = await _fetch_sheets_with_rules(
|
||||
service, spreadsheet_id
|
||||
|
||||
@@ -15,7 +15,7 @@ from mcp import Resource
|
||||
from auth.oauth_config import is_oauth21_enabled, is_external_oauth21_provider
|
||||
from auth.service_decorator import require_google_service
|
||||
from core.server import server
|
||||
from core.utils import handle_http_errors
|
||||
from core.utils import UserInputError, handle_http_errors
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -352,32 +352,32 @@ async def manage_task_list(
|
||||
|
||||
valid_actions = ("create", "update", "delete", "clear_completed")
|
||||
if action not in valid_actions:
|
||||
raise ValueError(
|
||||
raise UserInputError(
|
||||
f"Invalid action '{action}'. Must be one of: {', '.join(valid_actions)}"
|
||||
)
|
||||
|
||||
if action == "create":
|
||||
if not title:
|
||||
raise ValueError("'title' is required for the 'create' action.")
|
||||
raise UserInputError("'title' is required for the 'create' action.")
|
||||
return await _create_task_list_impl(service, user_google_email, title)
|
||||
|
||||
if action == "update":
|
||||
if not task_list_id:
|
||||
raise ValueError("'task_list_id' is required for the 'update' action.")
|
||||
raise UserInputError("'task_list_id' is required for the 'update' action.")
|
||||
if not title:
|
||||
raise ValueError("'title' is required for the 'update' action.")
|
||||
raise UserInputError("'title' is required for the 'update' action.")
|
||||
return await _update_task_list_impl(
|
||||
service, user_google_email, task_list_id, title
|
||||
)
|
||||
|
||||
if action == "delete":
|
||||
if not task_list_id:
|
||||
raise ValueError("'task_list_id' is required for the 'delete' action.")
|
||||
raise UserInputError("'task_list_id' is required for the 'delete' action.")
|
||||
return await _delete_task_list_impl(service, user_google_email, task_list_id)
|
||||
|
||||
# action == "clear_completed"
|
||||
if not task_list_id:
|
||||
raise ValueError(
|
||||
raise UserInputError(
|
||||
"'task_list_id' is required for the 'clear_completed' action."
|
||||
)
|
||||
return await _clear_completed_tasks_impl(service, user_google_email, task_list_id)
|
||||
@@ -963,13 +963,13 @@ async def manage_task(
|
||||
|
||||
valid_actions = ("create", "update", "delete", "move")
|
||||
if action not in valid_actions:
|
||||
raise ValueError(
|
||||
raise UserInputError(
|
||||
f"Invalid action '{action}'. Must be one of: {', '.join(valid_actions)}"
|
||||
)
|
||||
|
||||
if action == "create":
|
||||
if not title:
|
||||
raise ValueError("'title' is required for the 'create' action.")
|
||||
raise UserInputError("'title' is required for the 'create' action.")
|
||||
return await _create_task_impl(
|
||||
service,
|
||||
user_google_email,
|
||||
@@ -983,7 +983,7 @@ async def manage_task(
|
||||
|
||||
if action == "update":
|
||||
if not task_id:
|
||||
raise ValueError("'task_id' is required for the 'update' action.")
|
||||
raise UserInputError("'task_id' is required for the 'update' action.")
|
||||
return await _update_task_impl(
|
||||
service,
|
||||
user_google_email,
|
||||
@@ -997,14 +997,14 @@ async def manage_task(
|
||||
|
||||
if action == "delete":
|
||||
if not task_id:
|
||||
raise ValueError("'task_id' is required for the 'delete' action.")
|
||||
raise UserInputError("'task_id' is required for the 'delete' action.")
|
||||
return await _delete_task_impl(
|
||||
service, user_google_email, task_list_id, task_id
|
||||
)
|
||||
|
||||
# action == "move"
|
||||
if not task_id:
|
||||
raise ValueError("'task_id' is required for the 'move' action.")
|
||||
raise UserInputError("'task_id' is required for the 'move' action.")
|
||||
return await _move_task_impl(
|
||||
service,
|
||||
user_google_email,
|
||||
@@ -1026,4 +1026,3 @@ async def manage_task(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user