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 ingest_data_source, then build models against the snapshot and write results back with write_gsheet. Claude can also create new Sheets on your behalf as part of a write_gsheet call — no separate tool required.
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 project
- 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 project, 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 ingest_data_source directly
using the source_name assigned when you connected the sheet in Step 1:
{ "name": "ingest_data_source", "arguments": { "project_name": "revenue-model", "spec": { "kind": "google_sheet_snapshot", "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": "ingest_data_source", "arguments": { "project_name": "revenue-model", "spec": { "kind": "google_sheet_snapshot", "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 ingest_data_source response includes row counts and the snapshot_taken_at
timestamp:
{ "project_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 ingest_data_source
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 update_file 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, write its outputs back to Google Sheets with
write_gsheet. Use spec.mode to choose the operation.
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.
Replace a range
Section titled “Replace a range”spec.mode="replace" writes to a targeted A1 notation range. Only cells within
the specified range are overwritten — content outside is preserved.
{ "name": "write_gsheet", "arguments": { "project_name": "revenue-model", "spec": { "mode": "replace", "run_id": "uuid-from-run", "output_name": "forecast.json", "source_name": "revenue_model_actuals", "cell_range": "Forecast!A1" } }}Append to a tab
Section titled “Append to a tab”spec.mode="append" adds rows after existing content without overwriting.
{ "name": "write_gsheet", "arguments": { "project_name": "revenue-model", "spec": { "mode": "append", "run_id": "uuid-from-run", "output_name": "monthly_actuals.json", "source_name": "revenue_model_actuals", "sheet_name": "Actuals Log" } }}write_gsheet also supports spec.mode="clear" and spec.mode="batch" (write
explicit values to multiple ranges in one call).
Create a new Sheet as the write target
Section titled “Create a new Sheet as the write target”If you want to publish model output to a brand-new Google Sheet, pass
target.kind="new_spreadsheet" in the spec instead of source_name. Bridge
Town creates the Sheet in your Drive, registers it as a project data source, and
writes the output — all in one call. No additional Google prompt is required if
you already have an active Google connection.
{ "name": "write_gsheet", "arguments": { "project_name": "revenue-model", "spec": { "mode": "replace", "run_id": "uuid-from-run", "output_name": "forecast.json", "cell_range": "Forecast!A1", "target": { "kind": "new_spreadsheet", "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.
Format the output (recommended)
Section titled “Format the output (recommended)”After writing data, apply an opinionated formatting preset with format_gsheet.
This auto-detects the data bounds and applies header styling, borders, and (for
variance reports) conditional colouring in a single call.
{ "name": "format_gsheet", "arguments": { "project_name": "revenue-model", "preset": "financial_table", "source_name": "revenue_model_actuals", "tab_name": "Forecast" }}Available presets: financial_table (general tables), kpi_dashboard
(two-column key/value), variance_report (actuals vs budget with red/green
variance), audit_log (append/journal tabs with timestamps and alternating row
bands). Call format_gsheet(spec={"kind":"list_presets"}) to see all presets
and their parameters.
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 ingest_data_source, 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 ingest_data_source 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-Model Pipelines | Chain models with PIPELINE and /upstream transport — Google Sheets are for external I/O at the edges, not intra-run transport |
ingest_data_source | Import a connected sheet as a CSV snapshot (or upload a CSV/Excel file) |
list_data_sources | Discover connected sheets and their source_name for write-back |
write_gsheet | Write model output to a connected sheet or create a new Sheet as the write target |
modify_gsheet_structure | Unified structure tool (ensure tab, insert/delete rows) |
format_gsheet | Unified formatting tool (presets, borders, validation, etc.) |
format_gsheet(spec={"kind":"list_presets"}) | Discover available formatting presets |
query_data | Query snapshots (uploaded files and linked sheets) with SQL |
create_file | Create a model file in the project |
patch_file | Apply targeted model edits from instructions |
update_file | Replace model source with full updated code |