Skip to content

feat: add list_recent_activity tool (Drive Activity API)#535

Open
Metzpapa wants to merge 2 commits into
taylorwilsdon:mainfrom
Metzpapa:feat/list-recent-activity
Open

feat: add list_recent_activity tool (Drive Activity API)#535
Metzpapa wants to merge 2 commits into
taylorwilsdon:mainfrom
Metzpapa:feat/list-recent-activity

Conversation

@Metzpapa
Copy link
Copy Markdown

@Metzpapa Metzpapa commented Mar 2, 2026

Summary

  • Adds list_recent_activity tool that shows WHO did WHAT and WHEN across Google Drive
  • Uses the Drive Activity API v2 (driveactivity.googleapis.com) for a collaboration feed
  • Supports filtering by specific file ID or browsing all Drive activity
  • Handles action types: create, edit, move, rename, delete, restore, permission changes, comments, copies, uploads
  • Adds drive.activity.readonly scope and driveactivity service config

Motivation

list_recent_files (PR #534) shows what files changed recently, but not who changed them or what they did. list_recent_activity fills this gap — it's the collaboration log. Useful for seeing teammate edits, shares, comments, renames, and moves in chronological order.

Changes

  • auth/scopes.py: Add DRIVE_ACTIVITY_READONLY_SCOPE constant and include in DRIVE_SCOPES
  • auth/service_decorator.py: Add driveactivity service config and drive_activity_read scope group
  • gdrive/drive_tools.py: Add list_recent_activity tool and _format_activity_action helper

Note

Requires the Drive Activity API to be enabled in the Google Cloud project. The tool gracefully returns the standard API enablement error if it's not enabled.

Test plan

  • Tested list_recent_activity with default params (all Drive activity)
  • Verified edit, create, rename, copy, share/permission events all format correctly
  • Verified pagination via nextPageToken
  • Verified scope request triggers re-auth flow correctly when drive.activity.readonly is missing
  • Confirmed no impact on existing Drive tools

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Enabled Google Drive Activity read-only access to retrieve and monitor Drive activity logs.
    • Added sorting and ordering capability for Drive file listing queries.
    • Introduced new tools to display recently modified files and recent Drive activity, including detailed metadata such as ownership, modification times, and granular activity actions.
    • New activity tools support pagination for browsing large result sets.

Metzpapa and others added 2 commits March 2, 2026 00:53
Adds a new Drive tool that lists recently modified, viewed, shared, or
created files across ALL of Google Drive (not limited to a single folder).

Features:
- Sort by modifiedTime, viewedByMeTime, sharedWithMeTime, or createdTime
- Filter by ownership: all, owned (my files), or shared (others' files)
- Filter by file type (doc, sheet, pdf, etc.)
- Returns lastModifyingUser and owner info for each file
- Pagination support via page_token

Also adds backward-compatible `order_by` parameter to
build_drive_list_params helper.

Uses existing Drive API v3 files.list — no new scopes or auth required.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lastModifyingUser

- list_recent_files: now includes lastModifyingUser and owner info
- list_recent_activity: new tool using Drive Activity API v2 for
  collaboration feed (who did what, when, to which file)
- Added driveactivity service config and drive.activity.readonly scope
- Added orderBy support to build_drive_list_params helper

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 2, 2026

📝 Walkthrough

Walkthrough

Introduces Google Drive Activity API support by adding a new read-only scope constant, extending authentication configurations, enabling Drive service integration, adding optional ordering to list operations, and implementing two new async tools for querying recent file modifications and activity logs with comprehensive filtering and pagination.

Changes

Cohort / File(s) Summary
Auth Scope Management
auth/scopes.py, auth/service_decorator.py
Introduces DRIVE_ACTIVITY_READONLY_SCOPE constant and integrates it into SERVICE_CONFIGS for the "driveactivity" v2 service with corresponding "drive_activity_read" scope group alias.
Drive List Utilities
gdrive/drive_helpers.py
Adds optional order_by parameter to build_drive_list_params() function, enabling customizable result ordering via the orderBy field in API requests.
Drive Tools
gdrive/drive_tools.py
Adds two new async tools: list_recent_files() for querying recently modified files with ownership and file-type filtering, and list_recent_activity() for retrieving activity logs; includes _format_activity_action() helper for converting activity details to human-readable format, with pagination support via page tokens.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Hops through scopes with glee,
Activity logs now we shall see,
Drive service springs to life so bright,
Recent files sorted just right! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is comprehensive, covering Summary, Motivation, Changes, and Test plan sections. However, it lacks the required checkbox-based Type of Change, Testing, and Checklist sections from the repository template. Add the required template sections: Type of Change (mark New feature), Testing (mark applicable checkboxes), and Checklist (mark items completed, especially the 'Allow edits from maintainers' requirement).
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main feature being added—the list_recent_activity tool using the Drive Activity API—and is concise and specific.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
gdrive/drive_tools.py (1)

782-793: Minor: Default label "folder" may be misleading.

When parent folders lack a title, the default "folder" is used regardless of the actual item type. This could produce output like "moved to folder from folder" which isn't very informative.

Consider using a more generic default or extracting additional context:

💡 Suggested improvement
     elif "move" in action_detail:
         move = action_detail["move"]
         added = move.get("addedParents", [])
         removed = move.get("removedParents", [])
         parts = []
         if added:
-            titles = [p.get("driveItem", {}).get("title", "folder") for p in added]
+            titles = [p.get("driveItem", {}).get("title") or "(unknown)" for p in added]
             parts.append(f"moved to {', '.join(titles)}")
         if removed:
-            titles = [p.get("driveItem", {}).get("title", "folder") for p in removed]
+            titles = [p.get("driveItem", {}).get("title") or "(unknown)" for p in removed]
             parts.append(f"from {', '.join(titles)}")
         return " ".join(parts) if parts else "moved"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gdrive/drive_tools.py` around lines 782 - 793, The fallback label "folder" in
the move-handling block (variables: action_detail -> move, added, removed,
addedParents, removedParents, parts, titles) can be misleading; update the two
list comprehensions that build titles to use a more generic default like "item"
or derive a better label from the driveItem (e.g., driveItem.get("mimeType") or
driveItem.get("type")) when title is missing, so replace p.get("driveItem",
{}).get("title", "folder") with something like p.get("driveItem",
{}).get("title") or p.get("driveItem", {}).get("mimeType") or "item" to produce
clearer messages.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@gdrive/drive_tools.py`:
- Around line 714-732: The code is appending People API resource names
(knownUser.personName like "people/123...") as if they were display names;
update the actor-processing logic (the loop using actors, actor_names,
knownUser, person_name) to detect when person_name starts with "people/" and
either (A) resolve it to a human-readable name via the People API (call
people_service.people().get(resourceName=person_name, personFields='names') and
use the first names[0].displayName, with proper error handling and honoring the
contacts_read scope) or (B) if you cannot call the People API, change the
fallback to a clearer placeholder and document the limitation in the function's
docstring/return description; ensure all fallbacks still add entries to
actor_names and that the rest of the checks (deletedUser, administrator, system,
impersonation, unknown) remain intact.

---

Nitpick comments:
In `@gdrive/drive_tools.py`:
- Around line 782-793: The fallback label "folder" in the move-handling block
(variables: action_detail -> move, added, removed, addedParents, removedParents,
parts, titles) can be misleading; update the two list comprehensions that build
titles to use a more generic default like "item" or derive a better label from
the driveItem (e.g., driveItem.get("mimeType") or driveItem.get("type")) when
title is missing, so replace p.get("driveItem", {}).get("title", "folder") with
something like p.get("driveItem", {}).get("title") or p.get("driveItem",
{}).get("mimeType") or "item" to produce clearer messages.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 04b9ae0 and 9dfb85c.

📒 Files selected for processing (4)
  • auth/scopes.py
  • auth/service_decorator.py
  • gdrive/drive_helpers.py
  • gdrive/drive_tools.py

Comment thread gdrive/drive_tools.py
Comment on lines +714 to +732
actors = activity.get("actors", [])
actor_names = []
for actor in actors:
user_info = actor.get("user", {})
known_user = user_info.get("knownUser", {})
person_name = known_user.get("personName", "")
if person_name:
actor_names.append(person_name)
elif user_info.get("deletedUser"):
actor_names.append("(deleted user)")
elif actor.get("administrator"):
actor_names.append("(admin)")
elif actor.get("system"):
actor_names.append("(system)")
elif actor.get("impersonation"):
actor_names.append("(impersonation)")
else:
actor_names.append("(unknown)")
actor_str = ", ".join(actor_names) if actor_names else "(unknown)"
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 | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Google Drive Activity API v2 KnownUser personName field format documentation

💡 Result:

In Google Drive Activity API v2, actors[].user.knownUser.personName is a People API resource name. The documented format is:

  • people/ACCOUNT_ID (a.k.a. the user’s People API “person” resource name) [1]
  • Google’s Drive Activity v2 data model examples also show personName: "people/ACCOUNT_ID" [2]

You can pass this personName to the People API (for example, people.get) to fetch more details—subject to what the caller is permitted to access [1].

Sources: [1], [2]


Actor names will display as resource identifiers instead of human-readable names.

The personName field from knownUser is a People API resource name in the format people/ACCOUNT_ID, not a display name. The current code adds these resource identifiers directly to the output, resulting in actors being displayed as people/12345678901234567890 instead of readable names.

Consider either:

  1. Documenting this limitation in the function's return description
  2. Using the People API to resolve personName to display names (requires contacts_read scope)
  3. Checking if other user information fields are available in the activity data that contain readable names
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gdrive/drive_tools.py` around lines 714 - 732, The code is appending People
API resource names (knownUser.personName like "people/123...") as if they were
display names; update the actor-processing logic (the loop using actors,
actor_names, knownUser, person_name) to detect when person_name starts with
"people/" and either (A) resolve it to a human-readable name via the People API
(call people_service.people().get(resourceName=person_name,
personFields='names') and use the first names[0].displayName, with proper error
handling and honoring the contacts_read scope) or (B) if you cannot call the
People API, change the fallback to a clearer placeholder and document the
limitation in the function's docstring/return description; ensure all fallbacks
still add entries to actor_names and that the rest of the checks (deletedUser,
administrator, system, impersonation, unknown) remain intact.

@taylorwilsdon
Copy link
Copy Markdown
Owner

Same comment as the other PR, would like to build this all into an existing tool. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants