Skip to content

feat: add titles and complete MCP tool annotations - #705

Merged
taylorwilsdon merged 7 commits into
taylorwilsdon:mainfrom
seidnerj:feat/mcp-tool-annotations
Apr 28, 2026
Merged

feat: add titles and complete MCP tool annotations#705
taylorwilsdon merged 7 commits into
taylorwilsdon:mainfrom
seidnerj:feat/mcp-tool-annotations

Conversation

@seidnerj

@seidnerj seidnerj commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds title and all four annotation fields (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) to every tool across all 13 tool modules (115 tools total)
  • Adds from mcp.types import ToolAnnotations import to each file that uses it
  • The MCP spec defaults destructiveHint to true when 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

  • Read-only tools (list/get/search/query/export): readOnlyHint=True, idempotentHint=True, destructiveHint=False
  • Delete operations: destructiveHint=True, readOnlyHint=False, idempotentHint=False
  • Write/create/update tools: readOnlyHint=False, destructiveHint=False, idempotentHint=False
  • All tools: openWorldHint=True (all call Google APIs)

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Testing

  • Verify server imports without errors: uv run python -c "import core.server; print('OK')"
  • Confirm all 13 tool files have ToolAnnotations import
  • Confirm all tools have title= and all four annotation fields in their decorator
  • No tool implementations, signatures, control flow, or error handling were changed

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works (N/A - metadata only change)
  • Allow edits from maintainers

Summary by CodeRabbit

  • Documentation
    • Added human-readable titles and capability hints (ToolAnnotations) for many MCP tools across Workspace integrations for clearer tool metadata and discoverability.
  • Bug Fixes
    • Docs handling: a tab without document content is now treated as missing (returns None instead of raising).
  • Tests
    • Updated tests to reflect the new docs tab semantics.
  • Style
    • Minor formatting refinements in Gmail-related helpers and tests.

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
@coderabbitai

coderabbitai Bot commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds explicit title and ToolAnnotations metadata to many @server.tool(...) registrations across multiple modules; no tool logic changed. Also updates gdocs/docs_tools._find_tab_end_index to return None when a matching tab lacks documentTab instead of raising an exception.

Changes

Cohort / File(s) Summary
Core
core/server.py
Configured start_google_auth tool decorator with title and ToolAnnotations (metadata only).
Apps Script
gappsscript/apps_script_tools.py
Replaced bare @server.tool() with @server.tool(title=..., annotations=ToolAnnotations(...)) for exported Apps Script tools.
Calendar
gcalendar/calendar_tools.py
Added title and ToolAnnotations to calendar tool decorators (read vs. mutating annotated).
Chat
gchat/chat_tools.py
Added title and ToolAnnotations to chat tools; removed is_read_only=True from @handle_http_errors on download_chat_attachment.
Contacts
gcontacts/contacts_tools.py
Updated contact tool decorators to include title and ToolAnnotations (read-only vs destructive distinguished).
Docs
gdocs/docs_tools.py
Added title and ToolAnnotations to tools; changed _find_tab_end_index to return None when a matching tab lacks documentTab (test updated accordingly).
Drive
gdrive/drive_tools.py
Configured Drive tool decorators with title and ToolAnnotations across search/list/get vs. update/manage tools.
Forms
gforms/forms_tools.py
Added title and ToolAnnotations to form-related tool decorators (read vs mutating annotated).
Gmail
gmail/gmail_tools.py, gmail/gmail_helpers.py
Added title and ToolAnnotations to Gmail tools; adjusted decorator ordering for get_gmail_attachment_content; small formatting changes in helper _analyze_thread_ownership_impl.
Search
gsearch/search_tools.py
Configured search_custom and get_search_engine_info with title and ToolAnnotations (read-only).
Sheets
gsheets/sheets_tools.py
Added title and ToolAnnotations for Sheets tools (read vs mutation semantics applied).
Slides
gslides/slides_tools.py
Updated Slides tool decorators to include title and ToolAnnotations (get vs create/update annotated).
Tasks
gtasks/tasks_tools.py
Added title and ToolAnnotations to task list and task management tool decorators.
Tests (Docs/Gmail)
tests/gdocs/test_insert_doc_tab_response.py, tests/gmail/*
Updated docs test to expect None for non-document tab; several Gmail tests/formattings adjusted (formatting-only changes).

Sequence Diagram(s)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰
I hopped through decorators, tidy and light,
Gave every tool a label, tidy and bright,
Read-only radishes, mutating clover,
Tabs that lack docs now nap over,
Hooray — the meadow’s metadata just right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding titles and MCP tool annotations to all tools across the codebase.
Description check ✅ Passed The description is comprehensive and well-structured, covering summary, annotation logic, type of change, testing performed, and checklist items. However, it deviates from the template by not using the exact checkbox format and combining information differently.
Docstring Coverage ✅ Passed Docstring coverage is 91.28% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟡 Minor

Use openWorldHint=False for pure code generation.

generate_trigger_code does 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 | 🟡 Minor

Remove duplicate imports.

require_google_service and server are 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

📥 Commits

Reviewing files that changed from the base of the PR and between 3872ae4 and 76183b5.

📒 Files selected for processing (13)
  • core/server.py
  • gappsscript/apps_script_tools.py
  • gcalendar/calendar_tools.py
  • gchat/chat_tools.py
  • gcontacts/contacts_tools.py
  • gdocs/docs_tools.py
  • gdrive/drive_tools.py
  • gforms/forms_tools.py
  • gmail/gmail_tools.py
  • gsearch/search_tools.py
  • gsheets/sheets_tools.py
  • gslides/slides_tools.py
  • gtasks/tasks_tools.py

Comment thread gappsscript/apps_script_tools.py
Comment thread gcalendar/calendar_tools.py
Comment thread gchat/chat_tools.py
Comment thread gcontacts/contacts_tools.py
Comment thread gdocs/docs_tools.py
Comment thread gsheets/sheets_tools.py
Comment thread gsheets/sheets_tools.py
Comment on lines +777 to +785
@server.tool(
title='Manage Conditional Formatting',
annotations=ToolAnnotations(
readOnlyHint=False,
destructiveHint=False,
idempotentHint=False,
openWorldHint=True,
),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Comment thread gsheets/sheets_tools.py
Comment thread gslides/slides_tools.py
Comment thread gtasks/tasks_tools.py
Comment on lines +325 to +333
@server.tool(
title='Manage Task List',
annotations=ToolAnnotations(
readOnlyHint=False,
destructiveHint=False,
idempotentHint=False,
openWorldHint=True,
),
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 76183b5 and 1e0d4f6.

📒 Files selected for processing (11)
  • gappsscript/apps_script_tools.py
  • gcalendar/calendar_tools.py
  • gchat/chat_tools.py
  • gcontacts/contacts_tools.py
  • gdocs/docs_tools.py
  • gdrive/drive_tools.py
  • gforms/forms_tools.py
  • gmail/gmail_tools.py
  • gsheets/sheets_tools.py
  • gslides/slides_tools.py
  • gtasks/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

Comment thread gdocs/docs_tools.py
@taylorwilsdon taylorwilsdon self-assigned this Apr 26, 2026
@taylorwilsdon taylorwilsdon added the enhancement New feature or request label Apr 26, 2026
…into feat/mcp-tool-annotations

# Conflicts:
#	gdocs/docs_tools.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
gchat/chat_tools.py (1)

602-609: ⚠️ Potential issue | 🟠 Major

download_chat_attachment is 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_content should 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 | 🟠 Major

Set destructive hints to True for 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1e0d4f6 and a232eb1.

📒 Files selected for processing (13)
  • core/server.py
  • gappsscript/apps_script_tools.py
  • gcalendar/calendar_tools.py
  • gchat/chat_tools.py
  • gcontacts/contacts_tools.py
  • gdocs/docs_tools.py
  • gdrive/drive_tools.py
  • gforms/forms_tools.py
  • gmail/gmail_tools.py
  • gsearch/search_tools.py
  • gsheets/sheets_tools.py
  • gslides/slides_tools.py
  • gtasks/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

Comment thread gdocs/docs_tools.py
…into feat/mcp-tool-annotations

# Conflicts:
#	gmail/gmail_tools.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟡 Minor

Annotate move_sheet_rows to match the established pattern in this module.

move_sheet_rows is 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

📥 Commits

Reviewing files that changed from the base of the PR and between d034f36 and a156c41.

📒 Files selected for processing (5)
  • gmail/gmail_helpers.py
  • gmail/gmail_tools.py
  • gsheets/sheets_tools.py
  • tests/gmail/test_get_gmail_thread_content_analysis.py
  • tests/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

@taylorwilsdon
taylorwilsdon merged commit 010b7c3 into taylorwilsdon:main Apr 28, 2026
5 checks passed
@seidnerj
seidnerj deleted the feat/mcp-tool-annotations branch June 23, 2026 17:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants