Skip to content

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.

  • 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.

  1. Open app.bridgetown.builders and go to Data Sources (/data).
  2. Click Connect Google Sheet on the Google Sheet card to open the four-step connection wizard.
  3. 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.
  4. 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.
  5. 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).
  6. Step 4 — Done: Bridge Town connects the Sheet and shows the registered tables with a sample query_data call 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).

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.

ErrorCauseFix
OAuth credentials missingWeb app OAuth not completedComplete Step 1
Data source not foundsource_name does not match a connected sheetCall list_data_sources to see connected sheet names
Tab not foundTab name is case-sensitiveVerify exact tab name in Google Sheets
Google API rate limitToo many requestsWait 60 seconds and retry

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 total
FROM revenue_model_actuals_Sales
GROUP BY product_line
ORDER BY total DESC

The 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.

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.

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.

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.

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.

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.

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.

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.

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.

To keep your snapshots current without manual intervention, set a refresh interval when connecting:

"schedule_interval_minutes": 1440

Common 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.

  • drive.file scope 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.
Guide / ToolDescription
Multi-File PipelinesChain files with PIPELINE and /upstream transport — Google Sheets are for external I/O at the edges, not intra-run transport
refresh_google_sheet_snapshotRefresh a connected sheet as a CSV snapshot
list_data_sourcesDiscover connected sheets and their source_name for write-back
publish_run_output_to_google_sheetPreview and publish a model run output with explicit confirmation
create_google_spreadsheetCreate and register an empty linked Sheet
append_google_sheet_rowsAppend explicit rows to a tab, or insert empty rows at a position with start_row
get_google_spreadsheet_metadataGet tab metadata, or the formatting preset catalogue with include_format_presets=true
query_dataQuery snapshots (uploaded files and linked sheets) with SQL
create_fileCreate a file in the model
patch_fileApply targeted model edits from instructions
commit_filesReplace model source with full updated code