feat: add titles and complete MCP tool annotations - #705
Conversation
Adds title and all four annotation fields (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) to every @server.tool() decorator across all 13 tool modules (115 tools total). The MCP spec defaults destructiveHint to true when omitted, causing AI clients to treat safe read-only tools as potentially destructive. This change ensures all tools are properly annotated so clients can make correct decisions about tool safety and idempotency. - Read-only tools (list/get/search/query/export): readOnlyHint=True, idempotentHint=True - Delete operations: destructiveHint=True - All tools: openWorldHint=True (all call Google APIs) - Adds from mcp.types import ToolAnnotations to each file
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds explicit Changes
Sequence Diagram(s)Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 20
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (2)
gappsscript/apps_script_tools.py-1365-1373 (1)
1365-1373:⚠️ Potential issue | 🟡 MinorUse
openWorldHint=Falsefor pure code generation.
generate_trigger_codedoes not call Google APIs or otherwise interact with the outside world; marking it open-world is unnecessarily conservative and inconsistent with the actual behavior.Proposed annotation fix
`@server.tool`( title='Generate Trigger Code', annotations=ToolAnnotations( readOnlyHint=True, destructiveHint=False, idempotentHint=True, - openWorldHint=True, + openWorldHint=False, ), )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@gappsscript/apps_script_tools.py` around lines 1365 - 1373, The ToolAnnotation for generate_trigger_code incorrectly sets openWorldHint=True even though the function is pure code generation and does not call external APIs; update the decorator on generate_trigger_code to set openWorldHint=False inside the ToolAnnotations so the annotation accurately reflects its behavior (adjust the `@server.tool`(...) block that contains ToolAnnotations for generate_trigger_code).gappsscript/apps_script_tools.py-11-16 (1)
11-16:⚠️ Potential issue | 🟡 MinorRemove duplicate imports.
require_google_serviceandserverare imported twice in this section (lines 1–2 and 4–5). Remove the first occurrence to clean up the import block.Proposed fix
-from auth.service_decorator import require_google_service -from core.server import server from mcp.types import ToolAnnotations from auth.service_decorator import require_google_service from core.server import server🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@gappsscript/apps_script_tools.py` around lines 11 - 16, There are duplicate imports of require_google_service and server; remove the first occurrence so each symbol is imported only once. Edit the import block to keep a single "from auth.service_decorator import require_google_service" and a single "from core.server import server" and delete the redundant duplicate lines; verify ToolAnnotations import remains intact and run linters to confirm no unused-import warnings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@gappsscript/apps_script_tools.py`:
- Around line 349-357: The ToolAnnotations for tools that can mutate state are
incorrectly marked destructiveHint=False; change the annotations for
update_script_content, run_script_function, and manage_deployment (and the other
similar `@server.tool` decorators around the indicated ranges) to set
destructiveHint=True so clients understand these calls have
destructive/arbitrary side effects; locate each `@server.tool` decorator (e.g.,
the one above the update_script_content function) and update its ToolAnnotations
to destructiveHint=True while keeping other hints as appropriate.
In `@gcalendar/calendar_tools.py`:
- Around line 1299-1307: Update the ToolAnnotations for the calendar management
tools to correctly indicate they can perform deletions: change
destructiveHint=False to destructiveHint=True in the server.tool decorators for
manage_event, manage_out_of_office, and manage_focus_time (i.e., update the
ToolAnnotations(...) used in those server.tool declarations), and apply the same
change for the other occurrences of these calendar manage tools (the other
server.tool blocks that currently set destructiveHint=False) so the tool-level
metadata accurately reflects the delete action risk.
In `@gchat/chat_tools.py`:
- Around line 602-610: The tool decorator for download_chat_attachment
incorrectly marks the tool as read-only and idempotent even though it persists
files; update the ToolAnnotations on the decorator wrapping
download_chat_attachment to set readOnlyHint=False and idempotentHint=False
(keep destructiveHint as-is), and make the same change to the other tool
decorator that wraps the attachment-saving/uploading function with the same
annotations (the second attachment-related tool near the other decorator block)
so both accurately reflect that they persist artifacts.
In `@gcontacts/contacts_tools.py`:
- Around line 853-861: The ToolAnnotations for contact-management tools
incorrectly set destructiveHint=False despite exposing delete operations; update
the decorator annotations for manage_contact, manage_contacts_batch, and
manage_contact_group (and the other similar decorator occurrences around the
file) to set destructiveHint=True so the tools correctly report destructive
risk, keeping other hints unchanged.
In `@gdocs/docs_tools.py`:
- Around line 1055-1063: The tool decorator for batch_update_doc currently sets
destructiveHint=False but the function supports destructive operations
(delete_text, delete_table_row, delete_table_column, delete_named_range,
delete_doc_tab); update the ToolAnnotations for the batch_update_doc server.tool
decorator to mark destructiveHint=True (and similarly update the same annotation
block used for the other batch update tool variant later in the file) so clients
will treat these operations as potentially destructive.
- Around line 1908-1916: The tool annotation for export_doc_to_pdf incorrectly
marks it as read-only and idempotent even though the function uploads a PDF to
Drive; update the ToolAnnotations for export_doc_to_pdf (and the other similar
tool block) to set readOnlyHint=False and idempotentHint=False (leave
destructiveHint/openWorldHint as appropriate) so the metadata accurately
reflects that the tool performs Drive writes and is not safe to auto-run as
read-only/idempotent.
In `@gdrive/drive_tools.py`:
- Around line 1465-1473: The tool decorator for update_drive_file currently sets
ToolAnnotations(..., destructiveHint=False) but the function can trash files;
change destructiveHint to True in the ToolAnnotations for the `@server.tool`
decorator(s) that decorate update_drive_file so the tool metadata correctly
marks the destructive path (update the destructiveHint argument from False to
True wherever ToolAnnotations is used for update_drive_file).
- Around line 2173-2181: The tool metadata for the "Set Drive File Permissions"
server tool is incorrect: when link_sharing="off" the implementation deletes
existing "anyone" permissions so the tool should be marked destructive. Update
the ToolAnnotations for the server.tool decorated entry with title 'Set Drive
File Permissions' (and the duplicate occurrence later) to set
destructiveHint=True (replace destructiveHint=False), keeping other hints
unchanged so clients will prompt for confirmation before access-removing
operations.
- Around line 1718-1726: manage_drive_access performs destructive operations
(permission revocation and ownership transfer) but the ToolAnnotations currently
set destructiveHint=False; update the `@server.tool` metadata for the
manage_drive_access declaration(s) to set destructiveHint=True to conservatively
reflect destructive actions, or alternatively split the destructive paths out
into separate tools (e.g., revoke_access and transfer_ownership) each with
destructiveHint=True while keeping non-destructive listing/inspection actions in
a read-only tool; adjust the ToolAnnotations on the identified function(s)
accordingly.
- Around line 367-375: The ToolAnnotations for the server.tool decorated "Get
Drive File Download URL" declaration incorrectly marks the operation as
read-only and idempotent; update the annotation block used with the server.tool
decorator (the ToolAnnotations instance) to set readOnlyHint=False and
idempotentHint=False (or remove those flags) so clients do not assume safe
automatic retries; apply the same change to the other server.tool declaration
with the same metadata pattern elsewhere in this module.
In `@gforms/forms_tools.py`:
- Around line 490-498: The tool-level annotation for the "Batch Update Form"
server.tool decorator incorrectly sets destructiveHint=False even though the
tool supports deleteItem; update the ToolAnnotations for that decorator (and the
similar decorator around lines 517-524) to set destructiveHint=True so the tool
is correctly marked as destructive, leaving other hints unchanged (e.g., keep
readOnlyHint, idempotentHint, openWorldHint as they are).
In `@gmail/gmail_tools.py`:
- Around line 2900-2908: The server.tool decorators for the Gmail label
modification tools (e.g., the one with title "Modify Gmail Message Labels" and
the batch variant) incorrectly set destructiveHint=False despite operations that
can trash emails; update the ToolAnnotations for these decorators to set
destructiveHint=True (change the annotations on the single-message modifier and
the batch modifier where ToolAnnotations(... destructiveHint=False ...) is used)
so the tools are correctly marked as destructive; keep other hints as-is unless
otherwise required.
- Around line 2823-2831: The tool decorator for the "Manage Gmail Filter"
server.tool currently sets destructiveHint=False but its delete branch performs
destructive work; update the ToolAnnotations for the delete path (the decorator
applied to the Manage Gmail Filter tool/function) to set destructiveHint=True
(or make the annotation conditional so the delete variant uses
destructiveHint=True) — apply the same change to the other decorator block
covering the same tool (the second `@server.tool` instance referenced in the diff
range) so clients receive a conservative/destructive hint when action="delete".
- Around line 1594-1602: The tool decorator for get_gmail_attachment_content
incorrectly marks the tool as readOnlyHint=True and idempotentHint=True even
though the implementation writes attachment files (creating/updating local
artifacts); update the ToolAnnotations in the `@server.tool` decorator for
get_gmail_attachment_content (and the second similar tool block around lines
1675-1760) to set readOnlyHint=False and idempotentHint=False (leave
destructiveHint as appropriate) so the annotations reflect that the tool mutates
local storage.
- Around line 2650-2658: The tool decorator for "Manage Gmail Label" uses
ToolAnnotations with destructiveHint=False but contains an action="delete" path;
update the annotations to reflect destructive behavior or separate the delete
path into its own tool: either set destructiveHint=True in the existing
server.tool(...) ToolAnnotations for the Manage Gmail Label declaration, or
extract the delete branch (action="delete") into a new server.tool(...) named
e.g. "Delete Gmail Label" with ToolAnnotations.destructiveHint=True and leave
the original Manage Gmail Label tool non-destructive; ensure references to the
delete handler (the function handling action="delete") are moved/renamed
accordingly so no mixed-action tool retains a false destructiveHint.
In `@gsheets/sheets_tools.py`:
- Around line 2044-2052: The tool decorator for resize_sheet_dimensions
currently advertises destructiveHint=False while the function exposes
delete_rows and delete_columns; update the ToolAnnotations for the server.tool
on resize_sheet_dimensions to set destructiveHint=True (in both places where the
decorator is defined) so the tool metadata correctly indicates it can remove
spreadsheet data.
- Around line 777-785: The tool registered with server.tool titled 'Manage
Conditional Formatting' is marked with ToolAnnotations destructivelyHint=False
but supports action="delete" and issues deleteConditionalFormatRule; update the
ToolAnnotations for this decorator to set destructiveHint=True to accurately
reflect destructive behavior, and apply the same change to the other server.tool
declarations that manage conditional formatting (the other occurrences of this
decorator in the file) so all conditional-format management tools are marked
destructive.
- Around line 313-321: The tool annotation for modify_sheet_values currently
sets destructiveHint=False even though the tool can clear ranges
(clear_values=True); update the `@server.tool` ToolAnnotations for
modify_sheet_values to set destructiveHint=True so the metadata conservatively
reflects destructive behavior, and make the same change for the other tool
decorators mentioned in the review (the decorators at the other indicated
ranges) so all variants that can clear values are marked destructive.
In `@gslides/slides_tools.py`:
- Around line 168-176: The ToolAnnotations for the server.tool decorated
function handling batch_update_presentation incorrectly sets
destructiveHint=False; change the annotation for the batch_update_presentation
tool (and the duplicate decorator around lines 200-206) to destructiveHint=True
so the tool correctly signals it can perform destructive operations; locate the
`@server.tool`(...) block that wraps the batch_update_presentation implementation
and update the ToolAnnotations destructiveness flag accordingly while keeping
other hints unchanged.
In `@gtasks/tasks_tools.py`:
- Around line 325-333: Update the tool annotations for the task management APIs
to mark them as destructive: locate the `@server.tool` decorated definitions for
manage_task_list and manage_task (the blocks with ToolAnnotations and keys
readOnlyHint, destructiveHint, idempotentHint, openWorldHint) and change
destructiveHint from False to True; apply the same change to all occurrences
noted (the other annotation blocks around lines 384-394, 903-911, and 1001-1006)
so that any tool that can delete lists or tasks or clear completed tasks is
annotated as destructive.
---
Minor comments:
In `@gappsscript/apps_script_tools.py`:
- Around line 1365-1373: The ToolAnnotation for generate_trigger_code
incorrectly sets openWorldHint=True even though the function is pure code
generation and does not call external APIs; update the decorator on
generate_trigger_code to set openWorldHint=False inside the ToolAnnotations so
the annotation accurately reflects its behavior (adjust the `@server.tool`(...)
block that contains ToolAnnotations for generate_trigger_code).
- Around line 11-16: There are duplicate imports of require_google_service and
server; remove the first occurrence so each symbol is imported only once. Edit
the import block to keep a single "from auth.service_decorator import
require_google_service" and a single "from core.server import server" and delete
the redundant duplicate lines; verify ToolAnnotations import remains intact and
run linters to confirm no unused-import warnings.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 49f1157f-564b-4b2b-8840-b27977619143
📒 Files selected for processing (13)
core/server.pygappsscript/apps_script_tools.pygcalendar/calendar_tools.pygchat/chat_tools.pygcontacts/contacts_tools.pygdocs/docs_tools.pygdrive/drive_tools.pygforms/forms_tools.pygmail/gmail_tools.pygsearch/search_tools.pygsheets/sheets_tools.pygslides/slides_tools.pygtasks/tasks_tools.py
| @server.tool( | ||
| title='Manage Conditional Formatting', | ||
| annotations=ToolAnnotations( | ||
| readOnlyHint=False, | ||
| destructiveHint=False, | ||
| idempotentHint=False, | ||
| openWorldHint=True, | ||
| ), | ||
| ) |
There was a problem hiding this comment.
Mark conditional-format deletion as destructive.
This tool supports action="delete" and issues deleteConditionalFormatRule, so destructiveHint=False understates the tool’s possible behavior.
Proposed metadata fix
`@server.tool`(
title='Manage Conditional Formatting',
annotations=ToolAnnotations(
readOnlyHint=False,
- destructiveHint=False,
+ destructiveHint=True,
idempotentHint=False,
openWorldHint=True,
),
)Also applies to: 803-805, 1130-1167
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@gsheets/sheets_tools.py` around lines 777 - 785, The tool registered with
server.tool titled 'Manage Conditional Formatting' is marked with
ToolAnnotations destructivelyHint=False but supports action="delete" and issues
deleteConditionalFormatRule; update the ToolAnnotations for this decorator to
set destructiveHint=True to accurately reflect destructive behavior, and apply
the same change to the other server.tool declarations that manage conditional
formatting (the other occurrences of this decorator in the file) so all
conditional-format management tools are marked destructive.
| @server.tool( | ||
| title='Manage Task List', | ||
| annotations=ToolAnnotations( | ||
| readOnlyHint=False, | ||
| destructiveHint=False, | ||
| idempotentHint=False, | ||
| openWorldHint=True, | ||
| ), | ||
| ) |
There was a problem hiding this comment.
Mark task manage tools as destructive.
manage_task_list can delete task lists and clear completed tasks, and manage_task can delete tasks. The current destructiveHint=False is unsafe for clients relying on these annotations.
Proposed annotation fix
`@server.tool`(
title='Manage Task List',
annotations=ToolAnnotations(
readOnlyHint=False,
- destructiveHint=False,
+ destructiveHint=True,
idempotentHint=False,
openWorldHint=True,
),
)
@@
`@server.tool`(
title='Manage Task',
annotations=ToolAnnotations(
readOnlyHint=False,
- destructiveHint=False,
+ destructiveHint=True,
idempotentHint=False,
openWorldHint=True,
),
)Also applies to: 384-394, 903-911, 1001-1006
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@gtasks/tasks_tools.py` around lines 325 - 333, Update the tool annotations
for the task management APIs to mark them as destructive: locate the
`@server.tool` decorated definitions for manage_task_list and manage_task (the
blocks with ToolAnnotations and keys readOnlyHint, destructiveHint,
idempotentHint, openWorldHint) and change destructiveHint from False to True;
apply the same change to all occurrences noted (the other annotation blocks
around lines 384-394, 903-911, and 1001-1006) so that any tool that can delete
lists or tasks or clear completed tasks is annotated as destructive.
- destructiveHint=True on all tools that expose delete/trash/clear/revoke paths: update_script_content, run_script_function, manage_deployment, manage_event, manage_out_of_office, manage_focus_time, manage_contact, manage_contacts_batch, manage_contact_group, batch_update_doc, update_drive_file, set_drive_file_permissions, manage_drive_access, batch_update_form, modify_gmail_message_labels, batch_modify_gmail_message_labels, manage_gmail_filter, manage_gmail_label, resize_sheet_dimensions, manage_conditional_formatting, modify_sheet_values, batch_update_presentation, manage_task_list, manage_task - readOnlyHint=False, idempotentHint=False on tools that write files or Drive artifacts: download_chat_attachment, export_doc_to_pdf, get_gmail_attachment_content, get_drive_file_download_url - openWorldHint=False on generate_trigger_code (pure local code generation, no external API calls) - Remove duplicate require_google_service and server imports in gappsscript/apps_script_tools.py
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@gdocs/docs_tools.py`:
- Around line 436-443: The ToolAnnotations for the document-modifying tools are
marked destructiveHint=False; change the annotations to destructiveHint=True for
the tools named modify_doc_text, find_and_replace_doc, and
update_doc_headers_footers so clients know these operations overwrite/delete
content; locate each `@server.tool` declaration that constructs ToolAnnotations
for these functions and update the destructiveHint flag to True while keeping
the other annotations unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b2b9b9b1-9522-411c-ac2f-b9718fd21248
📒 Files selected for processing (11)
gappsscript/apps_script_tools.pygcalendar/calendar_tools.pygchat/chat_tools.pygcontacts/contacts_tools.pygdocs/docs_tools.pygdrive/drive_tools.pygforms/forms_tools.pygmail/gmail_tools.pygsheets/sheets_tools.pygslides/slides_tools.pygtasks/tasks_tools.py
🚧 Files skipped from review as they are similar to previous changes (4)
- gchat/chat_tools.py
- gsheets/sheets_tools.py
- gtasks/tasks_tools.py
- gslides/slides_tools.py
…into feat/mcp-tool-annotations # Conflicts: # gdocs/docs_tools.py
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
gchat/chat_tools.py (1)
602-609:⚠️ Potential issue | 🟠 Major
download_chat_attachmentis not read-only/idempotent.Line 605 and Line 607 currently classify a persistence-producing tool as read-only/idempotent. This can mislead clients into unsafe auto-runs/retries.
Suggested annotation fix
`@server.tool`( title="Download Chat Attachment", annotations=ToolAnnotations( - readOnlyHint=True, + readOnlyHint=False, destructiveHint=False, - idempotentHint=True, + idempotentHint=False, openWorldHint=True, ), )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@gchat/chat_tools.py` around lines 602 - 609, The tool annotation for download_chat_attachment incorrectly marks a persistence-producing operation as readOnly and idempotent; update the ToolAnnotations for the download_chat_attachment server.tool decorator to reflect that it is not read-only and not idempotent by setting readOnlyHint=False and idempotentHint=False (leave other hints as appropriate), so clients won't auto-run or retry this persistence-producing operation unsafely.gmail/gmail_tools.py (1)
1643-1650:⚠️ Potential issue | 🟠 Major
get_gmail_attachment_contentshould not be annotated read-only/idempotent.Line 1646 and Line 1648 are still too permissive for a tool that writes attachment artifacts (disk/temp-download lifecycle). This can cause unsafe auto-execution behavior in clients.
Suggested annotation fix
`@server.tool`( title="Get Gmail Attachment Content", annotations=ToolAnnotations( - readOnlyHint=True, + readOnlyHint=False, destructiveHint=False, - idempotentHint=True, + idempotentHint=False, openWorldHint=True, ), )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@gmail/gmail_tools.py` around lines 1643 - 1650, The tool decorator for get_gmail_attachment_content incorrectly marks the tool as readOnly and idempotent; update the ToolAnnotations on the server.tool decorator for get_gmail_attachment_content to set readOnlyHint=False and idempotentHint=False (or remove those True flags) so the tool is not advertised as safe for automatic execution when it writes attachment artifacts; keep other hints (e.g., openWorldHint) as appropriate.gdocs/docs_tools.py (1)
439-446:⚠️ Potential issue | 🟠 MajorSet destructive hints to
Truefor content-overwriting tools.Line 443, Line 726, and Line 984 currently mark tools as non-destructive, but these paths can replace/delete existing document content. This makes client-side safety decisions too optimistic.
Suggested annotation fix
`@server.tool`( title="Modify Doc Text", annotations=ToolAnnotations( readOnlyHint=False, - destructiveHint=False, + destructiveHint=True, idempotentHint=False, openWorldHint=True, ), ) @@ `@server.tool`( title="Find and Replace Doc", annotations=ToolAnnotations( readOnlyHint=False, - destructiveHint=False, + destructiveHint=True, idempotentHint=False, openWorldHint=True, ), ) @@ `@server.tool`( title="Update Doc Headers Footers", annotations=ToolAnnotations( readOnlyHint=False, - destructiveHint=False, + destructiveHint=True, idempotentHint=False, openWorldHint=True, ), )Also applies to: 722-729, 980-987
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@gdocs/docs_tools.py` around lines 439 - 446, The tool registration for content-overwriting endpoints (e.g., the "@server.tool" decorated "Modify Doc Text" entry that uses ToolAnnotations) incorrectly sets destructiveHint=False; update the ToolAnnotations for any tools that can replace/delete document content to set destructiveHint=True (leave other hints as-is unless semantically wrong) so client-side safety decisions reflect the destructive nature of these operations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@gdocs/docs_tools.py`:
- Around line 2489-2491: The code currently returns the numeric sentinel 0 when
a tab lacks "documentTab" (in the block that sets document_tab =
tab.get("documentTab", {})), but downstream populate/write flows only check for
None; change the sentinel to return None instead of 0 (i.e., replace "return 0"
with "return None") so callers that check for None will correctly short-circuit
write operations, and run tests or update any callers that explicitly expect 0
to handle None.
---
Duplicate comments:
In `@gchat/chat_tools.py`:
- Around line 602-609: The tool annotation for download_chat_attachment
incorrectly marks a persistence-producing operation as readOnly and idempotent;
update the ToolAnnotations for the download_chat_attachment server.tool
decorator to reflect that it is not read-only and not idempotent by setting
readOnlyHint=False and idempotentHint=False (leave other hints as appropriate),
so clients won't auto-run or retry this persistence-producing operation
unsafely.
In `@gdocs/docs_tools.py`:
- Around line 439-446: The tool registration for content-overwriting endpoints
(e.g., the "@server.tool" decorated "Modify Doc Text" entry that uses
ToolAnnotations) incorrectly sets destructiveHint=False; update the
ToolAnnotations for any tools that can replace/delete document content to set
destructiveHint=True (leave other hints as-is unless semantically wrong) so
client-side safety decisions reflect the destructive nature of these operations.
In `@gmail/gmail_tools.py`:
- Around line 1643-1650: The tool decorator for get_gmail_attachment_content
incorrectly marks the tool as readOnly and idempotent; update the
ToolAnnotations on the server.tool decorator for get_gmail_attachment_content to
set readOnlyHint=False and idempotentHint=False (or remove those True flags) so
the tool is not advertised as safe for automatic execution when it writes
attachment artifacts; keep other hints (e.g., openWorldHint) as appropriate.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1d022de1-bfa6-48e0-b9ae-1c23fc06bbfc
📒 Files selected for processing (13)
core/server.pygappsscript/apps_script_tools.pygcalendar/calendar_tools.pygchat/chat_tools.pygcontacts/contacts_tools.pygdocs/docs_tools.pygdrive/drive_tools.pygforms/forms_tools.pygmail/gmail_tools.pygsearch/search_tools.pygsheets/sheets_tools.pygslides/slides_tools.pygtasks/tasks_tools.py
✅ Files skipped from review due to trivial changes (4)
- gsearch/search_tools.py
- gsheets/sheets_tools.py
- gforms/forms_tools.py
- gdrive/drive_tools.py
🚧 Files skipped from review as they are similar to previous changes (3)
- gtasks/tasks_tools.py
- gslides/slides_tools.py
- gcontacts/contacts_tools.py
…into feat/mcp-tool-annotations # Conflicts: # gmail/gmail_tools.py
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
gsheets/sheets_tools.py (1)
2215-2215:⚠️ Potential issue | 🟡 MinorAnnotate
move_sheet_rowsto match the established pattern in this module.
move_sheet_rowsis the only tool using bare@server.tool()while all other tools in sheets_tools.py use@server.tool(title="...", annotations=...). This creates inconsistent metadata for clients and leaves one mutating operation unannotated.Suggested decorator update
-@server.tool() +@server.tool( + title="Move Sheet Rows", + annotations=ToolAnnotations( + readOnlyHint=False, + destructiveHint=True, + idempotentHint=False, + openWorldHint=True, + ), +)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@gsheets/sheets_tools.py` at line 2215, The `@server.tool`() decorator on move_sheet_rows should be updated to match the module pattern: replace the bare decorator with `@server.tool`(title="Move sheet rows", annotations={...}) using the same annotation keys/types used by other tools in sheets_tools.py (e.g., "mutates" set to True, any "category" or "description" keys used elsewhere) so clients receive consistent metadata; locate the move_sheet_rows function and mirror the title and annotations format from other tool-decorated functions in the file to mark it as a mutating operation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@gsheets/sheets_tools.py`:
- Line 2215: The `@server.tool`() decorator on move_sheet_rows should be updated
to match the module pattern: replace the bare decorator with
`@server.tool`(title="Move sheet rows", annotations={...}) using the same
annotation keys/types used by other tools in sheets_tools.py (e.g., "mutates"
set to True, any "category" or "description" keys used elsewhere) so clients
receive consistent metadata; locate the move_sheet_rows function and mirror the
title and annotations format from other tool-decorated functions in the file to
mark it as a mutating operation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 59c075b7-256c-49fd-9ef4-c42127e074f7
📒 Files selected for processing (5)
gmail/gmail_helpers.pygmail/gmail_tools.pygsheets/sheets_tools.pytests/gmail/test_get_gmail_thread_content_analysis.pytests/gmail/test_thread_ownership_helpers.py
✅ Files skipped from review due to trivial changes (3)
- tests/gmail/test_get_gmail_thread_content_analysis.py
- gmail/gmail_helpers.py
- tests/gmail/test_thread_ownership_helpers.py
Summary
titleand all four annotation fields (readOnlyHint,destructiveHint,idempotentHint,openWorldHint) to every tool across all 13 tool modules (115 tools total)from mcp.types import ToolAnnotationsimport to each file that uses itdestructiveHinttotruewhen omitted, causing AI clients to treat safe read-only tools as potentially destructive. This PR ensures all tools are properly annotated so clients can make correct decisions about tool safety and idempotency.Annotation logic applied
readOnlyHint=True,idempotentHint=True,destructiveHint=FalsedestructiveHint=True,readOnlyHint=False,idempotentHint=FalsereadOnlyHint=False,destructiveHint=False,idempotentHint=FalseopenWorldHint=True(all call Google APIs)Type of Change
Testing
uv run python -c "import core.server; print('OK')"ToolAnnotationsimporttitle=and all four annotation fields in their decoratorChecklist
Summary by CodeRabbit