Add native Google Sheets tools (list, read, write, create, info)#194
Add native Google Sheets tools (list, read, write, create, info)#194
Conversation
|
@claude code review |
|
Claude finished @RafaelPo's task in 1m 52s —— View job Code Review: Add native Google Sheets tools
Overall this is a well-structured PR. The separation into Issues1. No error handling in sheets tools — API errors surface as raw exceptions 2. 3. Accessing private # Current (fragile)
mcp._tool_manager._tools.pop(name, None)
# Suggested (uses the public API like line 82)
mcp._tool_manager.remove_tool(name)4. Single-tenant token key Nits (non-blocking)
SummaryThe architecture is sound — clean layering with |
3752652 to
67f6ca3
Compare
67f6ca3 to
eab93b2
Compare
|
@claude code review |
|
Claude finished @RafaelPo's task in 1m 46s —— View job Code Review: Add native Google Sheets tools
This is a well-structured PR since the last review. The issues from the previous review have been largely addressed: error handling is now in all tool functions, Issues1. Bug (confirmed): This affects two code paths:
# Current (line 304)
rows.append([str(record.get(h, "")) for h in headers])
# Fix
rows.append([str(v) if v is not None else "" for v in (record.get(h, "") for h in headers)])
# Or more readable:
rows.append(["" if record.get(h) is None else str(record.get(h, "")) for h in headers])2. 3. Nits (non-blocking)
Previously flagged items — status
SummaryThe main actionable item is the |
| from everyrow_mcp.sheets_client import store_google_token # noqa: PLC0415 | ||
|
|
||
| try: | ||
| await store_google_token( | ||
| jwt_claims.get("sub", "unknown"), | ||
| google_access_token, | ||
| google_refresh_token or None, | ||
| expires_in=expires_in, | ||
| ) | ||
| except Exception: | ||
| logger.warning("Could not store Google token during token issue") |
There was a problem hiding this comment.
Bug: In _issue_token_response, a failure to store the Google token is silently ignored. This allows authentication to succeed but causes subsequent calls to Sheets tools to fail.
Severity: MEDIUM
Suggested Fix
The authentication flow should fail if the Google token cannot be stored. Modify _issue_token_response to re-raise the exception or return an error instead of just logging a warning. This ensures the user is immediately aware of the problem and prevents them from entering a state where they are authenticated but the associated functionality is broken. Alternatively, provide a more accurate error message at the tool level that distinguishes between a missing token and a storage failure.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: everyrow-mcp/src/everyrow_mcp/auth.py#L472-L484
Potential issue: During the OAuth flow in `_issue_token_response`, if
`store_google_token` fails (e.g., due to a temporary Redis issue), the exception is
caught and only a warning is logged. The authentication process continues and returns a
valid token to the user. However, when the user later attempts to use a Google Sheets
tool, the application will fail to retrieve the token, raising a `RuntimeError`. This
results in a confusing user experience where the user appears authenticated but cannot
use the related features, and receives a misleading error message suggesting they need
to log in again.
2e7ec65 to
21dd1bc
Compare
5 new MCP tools for Google Sheets integration: - sheets_list: search/list user's spreadsheets via Drive API - sheets_read: read data as JSON records (compatible with input_json) - sheets_write: write/append JSON records to a sheet - sheets_create: create new spreadsheet with optional initial data - sheets_info: get sheet metadata (title, tabs, dimensions) Token management supports HTTP mode (OAuth via Supabase with Google provider tokens stored in Redis) and stdio mode (service account JWT). Also adds: - Google token passthrough in OAuth flow (auth.py) - output_spreadsheet_title option in everyrow_results - Authenticated Google URL fetches in fetch_csv_from_url - google_sheets_credentials_json setting for stdio mode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sheets tools require Google OAuth (HTTP mode only), so remove them from the tool registry in stdio mode. Also strip the service account JWT path from sheets_client since it's no longer needed. Add sheets tools to manifest.json and exclude output_spreadsheet_title from stdio schema. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…docs - Wrap all 5 sheets tool functions in try/except for httpx.HTTPStatusError with user-friendly messages for 403/404/429 and generic Google API errors - Rename `range` parameter to `cell_range` in GoogleSheetsClient methods to avoid shadowing the Python builtin (model field stays `range` for API) - Use public `remove_tool()` API instead of `_tools.pop()` for stdio mode sheets tool removal in server.py - Document single-tenant assumption on Google token key in sheets_client.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tsInput output_spreadsheet_title requires Google OAuth tokens which are only available in HTTP mode. Exposing it in stdio mode caused the function to attempt a Google API call, fail, and return early without writing the CSV — silently breaking the tool's primary functionality. Also bump duplicate-sheet guard from max_results=5 to 50 and document the inherent TOCTOU race condition. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sheets tools were always registered in HTTP mode and removed in stdio. Now they're opt-in via `enable_sheets_tools` (default False), so HTTP deployments without Google OAuth configured don't expose them either. Also strips `output_spreadsheet_title` from the HTTP results schema when sheets are disabled. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e limits, input validation, error sanitization - Encrypt Google tokens at rest in Redis (C1) and key by user_id not "current" (C2) - Add per-user rate limiting on all 5 sheets tools (H3) - Sanitize error messages to avoid leaking response bodies or internal state (H1, L4) - Validate A1 range notation to block path traversal and injection chars (M1) - Sanitize Drive API query to strip non-alphanumeric chars (M6) - Narrow OAuth scope from drive.readonly to drive.metadata.readonly (M3) - Re-raise on token storage failure instead of silently swallowing (M4) - Log only exception type on refresh failure, not full stack trace (M2) - Use server-provided expires_in for token TTL instead of hardcoded constant (L1) - Move sheets import into main() so transport is set before registration (L3) - Mark sheets_write as destructiveHint=True (M5) with audit logging (H4) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
records_to_values used str(record.get(h, "")) which only defaulted to "" for missing keys. When the key existed with value None (from pandas NaN), str(None) produced the string "None" in sheet cells instead of empty. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sheets_write: reads target range before overwriting. If data exists, returns a warning asking the user to confirm_overwrite=True or use append=True. Empty ranges proceed without confirmation. sheets_create: checks Drive for an existing spreadsheet with the same title before creating. Returns a warning if a duplicate is found. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
21dd1bc to
87de872
Compare
|
@claude code review |
|
Claude finished @RafaelPo's task in 1m 49s —— View job Code Review: Add native Google Sheets tools
Good progress since the last two reviews. The Issues1. Silent failure of Google token storage allows broken auth state This is the same issue the Sentry review flagged. Since # auth.py:489 — current
except Exception:
logger.warning("Could not store Google token during token issue")
# Option A: fail the token exchange (recommended if Sheets is critical)
# Just remove the try/except — store_google_token already raises
# Option B: at minimum, make the failure auditable
except Exception:
logger.error(
"Google token storage failed for user=%s — Sheets tools will be unavailable",
jwt_claims.get("sub", "unknown"),
exc_info=True,
)2. 3. A simple fix: call Previously flagged items — status
SummaryThe architecture is clean and the major bugs from previous reviews are resolved. Three items remain open: the silent token storage failure (#1, medium — UX issue), the unused config field (#2, low — cleanup), and the rate limit bypass (#3, low — defence-in-depth). Test coverage is thorough across all layers. |
…ypass - Upgrade silent Google token storage warning to logger.error with exc_info so failures are auditable - Remove unused google_sheets_credentials_json config field (sheets tools are HTTP-only) - Add rate limit check to _write_results_to_sheet so the everyrow_results → output_spreadsheet_title path is rate-limited Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…se.local.yaml Template env vars so they can be overridden at runtime instead of being hardcoded. Required for local tunnel testing with sheets tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
sheets_list,sheets_read,sheets_write,sheets_create,sheets_infooutput_spreadsheet_titleoption oneveryrow_results(both stdio and HTTP) to write results directly to a new Google Sheetfetch_csv_from_urlfor private sheetsTest plan
values_to_recordsandrecords_to_valuesoutput_spreadsheet_titlein both ResultsInput models🤖 Generated with Claude Code