Google Sheets Integration
Bridge Town integrates with Google Sheets through a secure, Picker-based workflow: select a Sheet through Google’s OAuth Picker so Bridge Town gains access to exactly that Sheet, import it as an immutable snapshot using refresh_google_sheet_snapshot, then build models against the snapshot. Results use publish_run_output_to_google_sheet, which previews a bounded payload and requires confirmation before writing. Dedicated low-level tools handle explicit values, tabs, rows, and formatting.
The connection is two-way: Bridge Town reads the tabs you sync, and when asked, it can write run outputs back to the same sheet. Access is limited to the files you explicitly select — Bridge Town uses drive.file scope only and cannot browse or access other spreadsheets in your Drive.
Prerequisites
Section titled “Prerequisites”- A Bridge Town account with at least one model
- Access to the Bridge Town web app at app.bridgetown.builders
- A Google account with access to the sheet(s) you want to import
Step 1 — Connect your Google account and select a Sheet
Section titled “Step 1 — Connect your Google account and select a Sheet”Before Claude can import a Google Sheet, you must select the Sheet through the Bridge Town web app. This is a one-time step per sheet — Bridge Town stores your Google connection so you can add more sheets without re-authenticating.
- Open app.bridgetown.builders and go to
Data Sources (
/data). - Click Connect Google Sheet on the Google Sheet card to open the four-step connection wizard.
- Step 1 — Google account: Connect your Google account or continue with an existing connection. The wizard shows exactly what access is being granted before any redirect. If your account authorization has expired, click Reconnect instead to reauthorize.
- Step 2 — Choose spreadsheet: Click Open Google file picker and select the Sheet you want to connect. If Picker cannot open (popup blocker or load failure), paste the Sheet URL as a fallback.
- Step 3 — Configure: Review real tab names, row counts, and column counts. Select the tabs to sync, confirm the source name (prefilled from the Sheet title) and model, and choose a refresh schedule (Manual, Hourly, or Daily).
- Step 4 — Done: Bridge Town connects the Sheet and shows the registered
tables with a sample
query_datacall you can use immediately.
Once authorised, Bridge Town stores an encrypted OAuth refresh token. You will not need to re-authenticate unless you revoke access in your Google account settings or the token expires (the web app shows a Needs attention pill and Reconnect action when that happens).
Step 2 — Import the sheet as a snapshot
Section titled “Step 2 — Import the sheet as a snapshot”Ask Claude to import the connected sheet, or call refresh_google_sheet_snapshot directly
using the source_name assigned when you connected the sheet in Step 1:
{ "name": "refresh_google_sheet_snapshot", "arguments": { "model_name": "revenue-model", "source_name": "revenue_model_actuals", "tab_names": ["Sales", "Costs"] }}Omit tab_names to import all tabs. Supply schedule_interval_minutes to
enable automatic refresh:
{ "name": "refresh_google_sheet_snapshot", "arguments": { "model_name": "revenue-model", "source_name": "revenue_model_actuals", "schedule_interval_minutes": 60 }}The tool reads each tab, converts it to CSV, and uploads it to Bridge Town’s data store as an immutable Parquet snapshot.
Common errors
Section titled “Common errors”| Error | Cause | Fix |
|---|---|---|
OAuth credentials missing | Web app OAuth not completed | Complete Step 1 |
Data source not found | source_name does not match a connected sheet | Call list_data_sources to see connected sheet names |
Tab not found | Tab name is case-sensitive | Verify exact tab name in Google Sheets |
Google API rate limit | Too many requests | Wait 60 seconds and retry |
Step 3 — Verify the import
Section titled “Step 3 — Verify the import”The refresh_google_sheet_snapshot response includes row counts and the snapshot_taken_at
timestamp:
{ "model_name": "revenue-model", "source_name": "revenue_model_actuals", "source_type": "google_sheets", "data_source_id": "uuid", "tables": ["revenue_model_actuals_Sales", "revenue_model_actuals_Costs"], "row_count": 120, "snapshot_taken_at": "2026-04-09T17:00:00+00:00"}To explore the imported data, use query_data with SQL — Google Sheet snapshots
are queryable alongside uploaded Parquet files. Each tab is exposed as a DuckDB
table named {source_name}_{tab_name} (non-alphanumeric characters in the tab
name are replaced with underscores). For example, source revenue_model_actuals,
tab Q1 Sales → table revenue_model_actuals_Q1_Sales.
SELECT product_line, SUM(revenue) AS totalFROM revenue_model_actuals_SalesGROUP BY product_lineORDER BY total DESCThe sources field in the query_data response includes snapshot_taken_at for
each Google Sheets source so you can verify freshness. Call refresh_google_sheet_snapshot
again with the same source_name to refresh the snapshot before querying when
current data is needed.
Alternatively, generate a model that reads from the snapshot at runtime via
read_csv() for more complex transformations.
Step 4 — Build a model
Section titled “Step 4 — Build a model”Once data is imported, use create_file to create a model file, then
patch_file or commit_files to implement logic that reads from the snapshot at
runtime.
Step 5 — Export results back (optional)
Section titled “Step 5 — Export results back (optional)”After a model run completes, preview and publish its output with
publish_run_output_to_google_sheet. The first call uses confirmed: false,
returns bounded rows, and performs no write. Repeat with confirmed: true only
after reviewing the explicit destination and preview.
Write to a connected sheet
Section titled “Write to a connected sheet”Target the linked sheet by source_name (the name shown in list_data_sources)
— this is the preferred approach because it works naturally with the connected-
sheet workflow and avoids manual ID copy/paste.
Preview and replace a range
Section titled “Preview and replace a range”mode="replace" targets an explicit tab-qualified A1 range. The preview is
bounded and exact; no cells are written until confirmed is true.
{ "name": "publish_run_output_to_google_sheet", "arguments": { "model_name": "revenue-model", "run_id": "uuid-from-run", "output_name": "forecast.json", "source_name": "revenue_model_actuals", "mode": "replace", "cell_range": "Forecast!A1", "preview_rows": 20, "confirmed": false }}After reviewing the response, repeat the call with confirmed: true to perform
the write. Provide exactly one of source_name or spreadsheet_id.
Append run output to a tab
Section titled “Append run output to a tab”Use mode="append" with an explicit sheet_name to add rows without replacing
existing content. The same preview/confirmation rule applies.
{ "name": "publish_run_output_to_google_sheet", "arguments": { "model_name": "revenue-model", "run_id": "uuid-from-run", "output_name": "monthly_actuals.json", "source_name": "revenue_model_actuals", "mode": "append", "sheet_name": "Actuals Log", "confirmed": false }}If the destination tab doesn’t already exist, it is created automatically.
Use append_google_sheet_rows to append raw rows outside a model-run
publish, or set its start_row, sheet_id, and num_rows to insert empty
rows at a position instead.
Create a new Sheet as the write target
Section titled “Create a new Sheet as the write target”To create an empty linked Sheet, call create_google_spreadsheet with a title,
source name, and optional initial tabs. Then publish into it after reviewing the
preview.
{ "name": "create_google_spreadsheet", "arguments": { "model_name": "revenue-model", "source_name": "q2_forecast_board", "title": "Q2 Forecast Board", "initial_tabs": ["Forecast", "Assumptions", "Variance"] }}The response includes the new sheet’s spreadsheet_id, source_name, and URL.
Subsequent calls can reference the same sheet by source_name.
Applying visual formatting
Section titled “Applying visual formatting”Bridge Town does not apply cell formatting (bold, colors, number formats)
programmatically — that capability was retired with no replacement, since
first-party Google Sheets connectors already cover it and no FP&A workflow
depended on Bridge Town doing it specifically. Call
get_google_spreadsheet_metadata with include_format_presets=true to
retrieve the finance-style vocabulary (available presets include
financial_table, kpi_dashboard, variance_report, and audit_log), then
apply the chosen style manually in Google Sheets, or hand it to your own
formatting automation.
Guided onboarding with Claude
Section titled “Guided onboarding with Claude”If you’re in Claude.ai, you can ask Claude to walk you through the entire
connection process using the built-in connect_google_sheets skill:
“Help me connect my Google Sheet to Bridge Town and set up a model.”
Claude will check whether the sheet is connected, discover the source_name via
list_data_sources, run the import using refresh_google_sheet_snapshot, verify the data,
and suggest next steps — all in one conversation.
Scheduled refresh
Section titled “Scheduled refresh”To keep your snapshots current without manual intervention, set a refresh interval when connecting:
"schedule_interval_minutes": 1440Common values: 60 (hourly), 1440 (daily), 10080 (weekly). To change the
schedule on an existing connection, call refresh_google_sheet_snapshot again with the same
source_name and the new interval — this updates the stored schedule without
re-importing.
Security
Section titled “Security”drive.filescope only — Bridge Town can only access Sheets you explicitly selected through Picker or that Bridge Town created for you. It cannot browse or access other spreadsheets in your Drive.- Encryption at rest — OAuth refresh tokens are encrypted with AES-256-GCM before storage. The encryption key is held in AWS Secrets Manager, not in the database.
- No client exposure — refresh tokens are never returned in API responses or MCP tool output. They are decrypted in-memory only, within the MCP server process, for the duration of each API call.
- Revocation — To disconnect a sheet, delete the data source in the web app under Data Sources. This removes the stored credentials immediately.
Related guides and tools
Section titled “Related guides and tools”| Guide / Tool | Description |
|---|---|
| Multi-File Pipelines | Chain files with PIPELINE and /upstream transport — Google Sheets are for external I/O at the edges, not intra-run transport |
refresh_google_sheet_snapshot | Refresh a connected sheet as a CSV snapshot |
list_data_sources | Discover connected sheets and their source_name for write-back |
publish_run_output_to_google_sheet | Preview and publish a model run output with explicit confirmation |
create_google_spreadsheet | Create and register an empty linked Sheet |
append_google_sheet_rows | Append explicit rows to a tab, or insert empty rows at a position with start_row |
get_google_spreadsheet_metadata | Get tab metadata, or the formatting preset catalogue with include_format_presets=true |
query_data | Query snapshots (uploaded files and linked sheets) with SQL |
create_file | Create a file in the model |
patch_file | Apply targeted model edits from instructions |
commit_files | Replace model source with full updated code |