Skip to content

Abdeltoto/smartsheet-controller

Repository files navigation

Hey everyone! Welcome to my GitHub!

Smartsheet Controller

Talk to Your Smartsheet.

Stop clicking. Start talking. Your sheets understand you now.

Created by Abdel ATIA
Smartsheet Lover & Power User since 2013
GitHub


Talk to Your Smartsheet — voice-driven AI agent



Smartsheet API FastAPI OpenAI Anthropic +5 LLMs MCP Tests License


73 Smartsheet tools · 80 formulas · 7 LLM providers · 445 green tests · 81 in-app prompts · Full voice control · One conversation.

Chrome extension: the same Controller runs in Chrome’s side panel beside Smartsheet (details & install).

A community-built AI agent that speaks the Smartsheet API fluently — built for developers and power-users. Read, write, analyze, automate, share, and visualize — by just asking. BYOT (bring your own token) · server-side memory · audit log · real-time webhooks · MCP-ready.

Built on top of the official @smartsheet REST API v2.0 — community project, not affiliated with Smartsheet Inc.


Quick Start · What's New · Chrome extension · The Power · Cross-sheet workflow · Prompts library · Bug reports · What You Can Say · 73 Tools · Agent Reliability · MCP Server · Tests · Deploy



The Problem

You know the drill. Open Smartsheet. Navigate. Click. Scroll. Find the right column. Write a formula. Test it. Fix it. Click again. Repeat for every single sheet.

What if you could just... say it?


Before After
Open sheet, scroll to row 47, find the column, manually edit the cell "Update row 47, set Status to Done"
Google the SUMIFS syntax, write it, debug the brackets "What's the total revenue for Q1?"
Navigate to sharing settings, add email, choose permission "Share this sheet with Marie as Editor"
Open 3 sheets, cross-reference manually, copy-paste values "Pull the budget total from the Finance sheet into this one"
Export data, open another tool, build a chart "Generate a visual of my project timeline"

"Don't navigate your sheets. Command them."



What's New — April 2026

Three shipped improvements that move the agent from "impressive demo" to "boring, dependable workflow tool".


Cross-sheet data flowing between two sheets

Cross-sheet, the right way
4-step workflow built into the system prompt · sheet IDs always in context · 25+ FR/EN intent triggers · 38 live formula tests.
In-app prompts library

Prompts library & Help
Hot-editable JSON catalogue · 81 prompts in 13 categories · in-app modal (Ctrl+Shift+L) + dedicated /help page (one click from the header).
In-app bug report box

One-click bug reports
Header button (Ctrl+Shift+B) · auto-attaches sheet, last messages, agent metrics · SQLite + JSONL crash-safe mirror.

Also shipped this month

  • Prompts sidebar in the chat margin (Ctrl+Shift+K) — the full prompts library is now docked to the right of the chat, one click from the input. Hover any prompt to reveal a Copy button; click the title to insert the prompt directly into the chat. The sidebar keeps state across reloads, leaves a vertical re-open rail when collapsed, and slides into a full-width drawer on mobile.
  • Header logout button with a discreet red-hover SVG icon (Ctrl+Shift+Q) — clears the session token and reopens the connect screen in one click.
  • Direct Help button in the header — opens the full-screen /help page in a new tab so you can keep the chat alive while scanning the catalogue.
  • Restyled global scrollbars — slim, gradient (violet → cyan), Firefox-aware. Same look across the chat, the modals, and the dedicated /help page.
  • Chrome extension (extension/) — MV3 side panel beside Smartsheet, sheet ID from the URL, embed mode ssc_ext=1 for a focused UI (Chrome extension).

Want the recipes ? Open the in-app modal (Ctrl+Shift+L) or visit the dedicated /help page once the server is running.


Illustration: Chrome side panel beside a sheet grid with an AI chat

Concept art — Manifest V3 side panel next to your sheet.


Chrome extension

The web app isn’t browser-only fiction: extension/ is a Manifest V3 Chrome extension that loads your running Controller (same FastAPI backend) inside Chrome’s side panel, docked next to Smartsheet. The content script detects the sheet ID from app.smartsheet.com URLs and passes ssc_ext=1 so the embedded iframe can strip extra chrome; the toolbar icon can show when a sheet page is detected.

Install Load unpacked from chrome://extensions (Developer mode) → choose the extension/ folder (step-by-step).
Hotkey Ctrl+Shift+Y — open side panel (see extension README).
Privacy / Store Web Store checklist and packaging live under extension/store/.
Trademark Google Chrome and the Chrome logo are trademarks of Google LLC.

The landing page frontend/index.html also highlights the extension using assets under frontend/assets/branding/.



The Power

What makes this different from everything else.


Your Data Answers Back

Ask a question in plain English (or French). The AI reads your sheet, understands the structure, and answers with formatted tables, rich Markdown, and inline images. Not raw JSON. Not a wall of text. Clean, structured, actionable answers.

73 Tools. Zero Friction.

Every Smartsheet operation — from reading a cell to creating a webhook — is a tool the agent can call autonomously. You describe what you want. The agent decides which API calls to make, chains them together, and delivers the result. You never touch the API.

Voice → AI → 73 tools → Smartsheet

Voice in. AI in the middle. 73 tools fanning out. Sheet updated.

80 Formulas. Natively Understood.

The agent doesn't just know Smartsheet formulas — it thinks in them. SUMIFS, INDEX(COLLECT(...)), cross-sheet {{references}}, hierarchy functions like CHILDREN() and ANCESTORS(). Ask for a formula and get the exact Smartsheet syntax, not a generic spreadsheet guess.

Your Voice Is the Interface

Click the mic. Speak. The agent transcribes, understands, and executes. Voice-first, keyboard-optional. Your Smartsheet has ears now.

Real-Time Streaming

Responses flow token by token, just like ChatGPT. You see the agent thinking in real time. No waiting for a wall of text — words appear as they're generated. The app breathes.

Smart Suggestions

After every response, the agent suggests 2-4 follow-up actions as clickable chips. You never face a blank input. The AI guides you forward.

Sheet Switcher

Jump between sheets instantly from a dropdown in the header. No disconnecting, no re-configuring. Multi-sheet workflows in one session.

Conversation History

Every conversation is saved locally and accessible from a sidebar. Reload past analyses, formulas, and decisions anytime. Your Smartsheet memory never fades.

See Everything. Trust Everything.

Every tool call the agent makes is visible in real-time. You see the API call, the parameters, the response. Full transparency. Full control. No black box.

Not Just Text. Visuals Too.

Need a diagram? A chart concept? A visual representation of your data architecture? The agent generates DALL-E 3 images and live Chart.js bar/line/pie/doughnut/scatter charts directly in the conversation. Your chat is a canvas.



Cross-sheet, the right way

Cross-sheet data bridge between two grids

Two sheets. One conversation. Real values flowing where you need them.


Cross-sheet formulas in Smartsheet are powerful but unforgiving — you must first declare a named cross-sheet reference, then quote it inside the formula like {{my_ref}}. The agent now ships a mandatory 4-step workflow baked into its system prompt:

  1. Identify the source sheet (the agent now sees the numeric id of every sheet in your account, no extra list_sheets call needed).
  2. Read the source schema to confirm the exact column titles and types.
  3. Create the cross-sheet reference with create_cross_sheet_ref — the tool description spells out the parameters and the failure modes.
  4. Write the formula using the canonical patterns: VLOOKUP, INDEX/MATCH, INDEX(COLLECT(...)), MAX(COLLECT(...)), JOIN(COLLECT(...)), conditional SUMIFS / COUNTIFS.

Backed by:

  • 25+ French & English intent triggers (ramène, récupère, lookup, vlookup, pull from, fetch, another sheet, crosssheet, named range…) so the right tools are always exposed to the LLM when you ask for a cross-sheet pull.
  • 38 live functional tests in tests/functional/run_cross_sheet.py that build two real sheets, create 8 cross-sheet refs (with retry-on-404 to survive Smartsheet's eventual-consistency window), and validate every formula round-trip.
  • Documented Smartsheet quirksAVG instead of AVERAGE, no AVERAGEIFS/MAXIFS/MINIFS (use SUMIFS + COUNTIFS or MAX(COLLECT(...))), JOIN must be wrapped in COLLECT(..., <>"") to drop empty cells.

The original "agent can't bring back values from another sheet" failure mode is now covered by 8 dedicated unit tests × 16 phrasings that lock in the intent-subsetting behaviour.



Prompts library & in-app Help

In-app prompts library with categorised cards

A built-in library of copy-paste recipes. Searchable. Categorised. Hot-editable.


Onboarding new collaborators (or yourself, three months later) is the slowest part of any agent product. The Help section solves it:

  • In-chat sidebar — a dense, always-on-screen panel docked to the right of the chat (toggle with Ctrl+Shift+K or the panel button in the header). One click on a prompt inserts it into the input ready to review and send; hover reveals a one-tap Copy button. Search, accordion categories, and the open/closed state are persisted in localStorage. Collapsing the sidebar leaves a vertical "Prompts" rail on the right edge so it's never more than one click away.
  • In-app modalCtrl+Shift+L or the book button in the header. Live search, collapsible categories, per-prompt difficulty (easy / medium / advanced) and risk (safe / caution / destructive) badges, Copy and Insert into chat actions. Use the modal when you want full prompt bodies and badges visible at a glance.
  • Dedicated full-screen page/help. Reachable in one click from the header, sticky sidebar with category navigation and scroll-spy, ideal for screen-sharing or projecting during onboarding sessions.
  • 81 curated prompts across 13 categories — covering virtually every Smartsheet surface the agent can touch:
    • Exploration · Rows · Columns · Cross-sheet workflows · Formulas · Sharing & permissions · Automations · Maintenance & cleanup
    • Hierarchy & subtasks · Discussions & comments · Attachments & proofs · Reports & dashboards · Workspaces & navigation
  • Hot-editable JSON — the catalogue lives in frontend/data/prompts.json and is re-read on every API call, so you can edit it in production without restarting uvicorn. Override the path with SMARTSHEET_PROMPTS_PATH for deployments where you want to ship the catalogue outside the repo.
  • Safety guardrail — every prompt flagged risk: destructive is automatically validated to contain a confirmation cue (confirm, preview, wait). The unit tests fail otherwise. The agent never silently executes a destructive action.
// frontend/data/prompts.json (excerpt)
{
  "categories": [
    {
      "id": "crosssheet",
      "title": "Cross-sheet workflows",
      "icon": "link",
      "prompts": [
        {
          "id": "xs-pull-by-key",
          "title": "Pull a value from another sheet by key",
          "difficulty": "medium",
          "risk": "safe",
          "tags": ["vlookup", "lookup"],
          "prompt": "From the sheet \"<NAME>\", pull the value of column \"<COL>\" where <KEY> matches my row's <KEY>, and write it in the column \"<TARGET>\"."
        }
      ]
    }
  ]
}


Bring Your Own Model

OpenAI · Anthropic · OpenRouter · Mistral · Groq

Five LLM providers, one chat. Switch model mid-conversation — the context follows you. The agent keeps the same tool repertoire and the same Smartsheet expert prompt no matter which brain you plug in.

Provider What it's great at Models exposed
OpenAI The reference. Best tool-calling reliability + DALL-E 3 image gen. gpt-4o, gpt-4o-mini, gpt-4-turbo
Anthropic Long-form analysis, careful reasoning, dense tables. claude-3-5-sonnet, claude-3-5-haiku, claude-3-opus
OpenRouter One API key, hundreds of models. Try Llama, Gemini, DeepSeek without managing extra keys. Any OpenRouter slug
Mistral EU-hosted, fast, cost-efficient. mistral-large-latest, mistral-small-latest
Groq Sub-second token speed thanks to LPU inference. llama-3.3-70b-versatile, mixtral-8x7b

The header dropdown lists only the providers whose key is detected in .env — set just the ones you actually have.

A live token-usage counter is displayed under the chat for the active provider, and a per-session rate-limiter (60 LLM calls / min, 120 WebSocket frames / min) protects you from runaway loops.



Server Brain — Persistent, Auditable, Live

The server isn't a stateless proxy. It owns a small SQLite store so your account remembers things across sessions and tabs:

  • Conversations — auto-saved on every message, reloadable from the sidebar (with optional auto-generated titles).
  • Favorites — pin sheets to the header for one-click switching.
  • Audit log — every tool call (especially destructive ones) is timestamped and stored. GET /api/audit and GET /api/export give you the full history (RGPD-friendly).
  • Webhook events — incoming Smartsheet webhooks land in POST /api/smartsheet-webhook, are persisted, and fan out in real time to every active WebSocket session that pinned the affected sheet.
  • Watch mode — the UI can subscribe to a sheet and surface new rows / changes the moment they happen.

Combined with the per-session token bucket and Smartsheet 429 retry-with-backoff, the backend stays steady under fast-fire conversations.



Quick Start

1. Install

pip install -r requirements.txt

2. Configure

cp .env.example .env
SMARTSHEET_TOKEN=your_smartsheet_api_token
SHEET_ID=your_default_sheet_id
OPENAI_API_KEY=sk-...

Smartsheet API tokenapp.smartsheet.com > Personal Settings > API Access

Sheet ID — Open any sheet, check the URL or go to File > Properties

3. Launch

uvicorn backend.app:app --reload --port 8100

Open http://localhost:8100 — hit Quick Connect — start talking.

That's it. Three commands. You're in.


What You Can Say

Every one of these is a real command the agent executes.


Data & Analysis

"Show me the full structure of my sheet as a table"

"Read rows 1 to 50 and give me a summary"

"What are the 10 largest amounts in the Budget column?"

"Are there any errors or inconsistencies in my data?"

Formulas & Calculations

"Write a SUMIFS formula to total sales by region"

"Create a cross-sheet formula that pulls the status from the Projects sheet"

"What's the correct syntax for INDEX(COLLECT(...)) with two criteria?"

Structure & Automation

"Add a PICKLIST column 'Priority' with options High, Medium, Low"

"Move completed rows to the Archive sheet"

"Who has access to this sheet? Show me the permissions"

Visuals

"Generate an image that illustrates my sheet architecture"

"Create a visual for the project tracking dashboard"


Every Tool at Your Voice

73 tools organized in 16 categories. The agent picks the right ones automatically.

Account & Navigation — 10 tools
Tool What it does
get_current_user Your Smartsheet profile
list_sheets Every sheet you have access to
search Search across your entire account
search_sheet Search within a specific sheet
list_workspaces All your workspaces
get_workspace Workspace contents and structure
list_folders Home-level folders
get_folder Folder contents
create_folder Create a new folder
get_recent_items Recent items and favorites
Sheet Reading & Analysis — 7 tools
Tool What it does
get_sheet_summary Sheet structure: columns, types, row count
read_rows Read rows with optional range
get_row Get a single row by ID
get_cell_history Full audit trail for any cell
get_summary_fields Sheet-level KPI fields
analyze_sheet Deep analysis: structure, patterns, formulas
detect_issues Find errors, empty columns, inconsistencies
Data Manipulation — 6 tools
Tool What it does
add_rows Add new rows with values
update_rows Update cells (values or formulas)
delete_rows Delete rows by ID
move_rows Move rows to another sheet
copy_rows Copy rows to another sheet
sort_sheet Sort by one or more columns
Column Management — 3 tools
Tool What it does
add_column Add column (TEXT_NUMBER, DATE, PICKLIST, CHECKBOX, CONTACT_LIST, DURATION, PREDECESSOR)
update_column Update column title or description
delete_column Delete a column
Sheet Management — 5 tools
Tool What it does
create_sheet Create a new sheet with columns
delete_sheet Delete a sheet
rename_sheet Rename a sheet
copy_sheet Copy a sheet
move_sheet Move sheet to folder/workspace
Cross-sheet References — 2 tools
Tool What it does
list_cross_sheet_refs List all cross-sheet references
create_cross_sheet_ref Create a ref for INDEX(COLLECT(...)) formulas
Sharing & Collaboration — 4 tools
Tool What it does
list_shares List sharing permissions
share_sheet Share with a user (VIEWER / EDITOR / ADMIN)
update_share Update permission level
delete_share Remove access
Discussions & Comments — 4 tools
Tool What it does
list_discussions Sheet-level discussions
list_row_discussions Discussions on a specific row
add_comment Reply to a discussion
create_row_discussion Start a new discussion on a row
Attachments — 6 tools
Tool What it does
list_attachments List all attachments on a sheet
list_row_attachments List attachments on a single row
get_attachment Get details and download URL
attach_url_to_sheet Attach a URL/link (Drive, Dropbox, OneDrive, web) to a sheet
attach_url_to_row Attach a URL/link to a specific row
delete_attachment Delete an attachment by ID
Forms — 1 tool
Tool What it does
list_sheet_forms List forms on a sheet (falls back to permalink — Smartsheet API exposure is limited)
Update Requests — 3 tools
Tool What it does
list_update_requests List pending update requests
create_update_request Send an update request to one or more emails for specific row(s)
delete_update_request Cancel an outstanding update request
Proofs (Premium) — 2 tools
Tool What it does
list_row_proofs List proofs on a row (Premium feature)
create_row_proof_from_url Create a proof from a URL on a row
Reports & Dashboards — 4 tools
Tool What it does
list_reports List all reports
get_report Get report data
list_dashboards List all dashboards
get_dashboard Dashboard details and widgets
Templates & Webhooks — 5 tools
Tool What it does
list_templates Public templates
list_webhooks Configured webhooks
create_webhook Create a webhook
update_webhook Enable/disable or reconfigure a webhook in place
delete_webhook Delete a webhook
Automations — 4 tools
Tool What it does
list_automations List automation rules
get_automation Get details of one rule
update_automation Enable/disable or rename a rule (CREATE not supported by Smartsheet API)
delete_automation Delete an automation rule
Workspace Sharing — 4 tools
Tool What it does
list_workspace_shares List sharing on a workspace
share_workspace Share workspace with a user (cascades to all sheets inside)
update_workspace_share Change a user's access level on a workspace
delete_workspace_share Remove a user from a workspace
Cell Linking & AI — 3 tools
Tool What it does
create_cell_link One-way live link from a source cell to a target cell (distinct from cross-sheet refs)
generate_image Generate visuals with DALL-E 3 (inline in chat)
generate_chart Render a Chart.js bar/line/pie/etc. inline in chat

Formula Mastery

The agent knows every Smartsheet function. All 80. By heart.


Category Count Functions
Numeric 25 ABS AVG AVGW CEILING CHAR COUNT FLOOR INT LARGE LEN MAX MEDIAN MIN MOD MROUND RANKAVG RANKEQ ROUND ROUNDDOWN ROUNDUP SMALL SUM UNICHAR DECTOHEX HEXTODEC
Logic 16 AND CONTAINS HAS IF IFERROR ISBLANK ISBOOLEAN ISCRITICAL ISDATE ISERROR ISEVEN ISNUMBER ISODD ISTEXT NOT OR
Text 10 FIND JOIN LEFT LOWER MID REPLACE RIGHT SUBSTITUTE UPPER VALUE
Date 14 DATE DATEONLY DAY MONTH NETDAYS NETWORKDAY NETWORKDAYS TIME TODAY WEEKDAY WEEKNUMBER WORKDAY YEAR YEARDAY
Advanced / Lookup 18 AVERAGEIF COLLECT COUNTIF COUNTIFS COUNTM DISTINCT INDEX MATCH NPV PERCENTILE PRORATE STDEVA STDEVP STDEVPA STDEVS SUMIF SUMIFS VLOOKUP
Hierarchy 6 ANCESTORS CHILDREN DESCENDANTS PARENT SUCCESSORS TOTALFLOAT

Beyond functions — the agent masters:

  • [Column]@row references, [Column]:[Column] ranges, absolute row refs
  • Cross-sheet {{reference}} patterns: INDEX(COLLECT({{Values}}, {{Keys}}, [Key]@row), 1)
  • Column formulas, Summary fields, Conditional formatting rules
  • Reports (multi-sheet aggregation), Dashboards (live KPI widgets)
  • Gantt, Calendar, Card/Kanban views
  • Automations, Forms, Proofing, Cell history, Row locking

MCP Server

A complete Model Context Protocol server — plug Smartsheet into Claude Desktop, Cursor, or any MCP client. 52 of the 73 tools are exposed through MCP (the chat-only ones — generate_image, generate_chart, etc. — stay in the web app).

Start

# stdio (Claude Desktop / Cursor)
SMARTSHEET_TOKEN=your_token python -m backend

# HTTP
SMARTSHEET_TOKEN=your_token python -m backend --transport http

Cursor IDE — .cursor/mcp.json

{
  "mcpServers": {
    "smartsheet": {
      "command": "python",
      "args": ["-m", "backend"],
      "cwd": "/path/to/smartsheet-controller",
      "env": { "SMARTSHEET_TOKEN": "your_token_here" }
    }
  }
}

Claude Desktop — claude_desktop_config.json

{
  "mcpServers": {
    "smartsheet": {
      "command": "python",
      "args": ["-m", "backend"],
      "cwd": "/path/to/smartsheet-controller",
      "env": { "SMARTSHEET_TOKEN": "your_token_here" }
    }
  }
}

Resources & Prompts

MCP Resources
smartsheet://sheets All sheets in the account
smartsheet://user Authenticated user profile
smartsheet://sheets/{id}/summary Sheet structure
MCP Prompts
Analyze Sheet Deep structure analysis with suggestions
Sheet Audit Security, sharing & quality audit
Explore Account Full account exploration

Architecture

smartsheet-controller/
├── backend/
│   ├── app.py                 → FastAPI + WebSocket + session mgmt + 20+ HTTP routes
│   ├── agent.py               → AI agent loop, expert prompt, tool dispatch, suggestions
│   ├── tools.py               → 73 tool definitions + dispatcher + DALL-E + Chart.js
│   ├── llm_router.py          → 5-provider streaming router (OpenAI / Anthropic / OpenRouter / Mistral / Groq)
│   ├── smartsheet_client.py   → Smartsheet REST wrapper (httpx, retry, in-memory cache)
│   ├── db.py                  → SQLite persistence (users, sessions, conversations, favorites, audit, webhooks)
│   ├── rate_limit.py          → Token-bucket rate limiter (per-session LLM + WS)
│   ├── logging_config.py      → Structured JSON logs with token redaction
│   ├── mcp_server.py          → MCP server (52 tools, 3 resources, 3 prompts)
│   └── __main__.py            → MCP CLI entry point (stdio / http)
├── frontend/
│   ├── index.html             → Glassmorphism SPA (voice, streaming, history, suggestions, watch)
│   └── assets/                → Brand & marketing assets (PNG, SVG)
├── tests/
│   ├── unit/                  → 196 fast tests (no I/O, mocked transports)
│   │     ├── test_rate_limit.py        rate limiter
│   │     ├── test_llm_router.py        provider parsing + usage tracking
│   │     ├── test_tools.py             intent routing + chart generation
│   │     ├── test_tools_dispatch.py    contract: every tool dispatchable
│   │     ├── test_smartsheet_client.py mock-transport CRUD + retry + cache
│   │     ├── test_db.py                SQLite layer (every CRUD path)
│   │     ├── test_agent.py             agent helpers
│   │     ├── test_agent_loop.py        run() loop: tools, confirm, MAX_ROUNDS
│   │     ├── test_app_helpers.py       _friendly_error, provider detection
│   │     └── test_mcp_smoke.py         MCP server boot + tool registry
│   ├── integration/           → 41 tests against the real Smartsheet API
│   │     ├── test_smartsheet_live.py   read + create→modify→delete lifecycle
│   │     └── test_app_endpoints*.py    every FastAPI HTTP route
│   ├── e2e/                   → 9 full-stack WebSocket flow tests
│   │     ├── test_websocket_flow.py    handshake + agent + suggestions
│   │     └── test_websocket_advanced.py cancel, confirm/reject, rate-limit, multi-turn
│   └── README.md              → How to run, what each layer covers
├── data/                      → SQLite DB (created at first launch, git-ignored)
├── Dockerfile                 → Slim Python 3.11 image, non-root, /health probe
├── docker-compose.yml         → One-command stack with bind-mount for data/
├── pytest.ini                 → markers: unit / integration / e2e / live_llm
├── ROADMAP.md                 → 7+ sprints, journal, deliverables
├── requirements.txt           → Runtime deps
├── requirements-dev.txt       → + pytest, pytest-asyncio, pytest-cov
├── .env                       → API keys (LLM providers + optional SMARTSHEET_TOKEN)
└── README.md
   You (voice or text)
        │
        ▼
    WebSocket ──→ FastAPI ──→ Agent ──→ LLM Router
                                          │      ┌──────────┐
                                          ├─────▶│ OpenAI   │
                                          ├─────▶│ Anthropic│
                                          ├─────▶│OpenRouter│
                                          ├─────▶│ Mistral  │
                                          └─────▶│  Groq    │
                                          │      └──────────┘
                                    73 tool calls
                                          │
                                          ▼
                                  Smartsheet API v2.0  ◀──── webhooks ──┐
                                          │                              │
                                          ▼                              │
                              Streaming · Tables · Images · Charts       │
                                          │                              │
                                          ▼                              │
                                      Chat UI ◀────── live fan-out ──────┘
                                          │
                                          ▼
                          SQLite (conversations · favorites · audit)

Agent Reliability

LLMs hallucinate. They confuse rows with columns, retry the same failing call 12× in a row, invent column names that don't exist, and write formulas with functions Smartsheet doesn't support. The agent loop is wrapped in a safety harness that intercepts these failure modes before they hit the API, gives the model a structured error with a recovery path, and surfaces the rescue to the user as a discrete banner — so you can see exactly when the harness saved the day.

# Safety net What it catches Where it lives
1 Loop killer Same (tool, args) called > 3× in one turn — typically because the model can't read its own error backend/agent.py · LOOP_REPEAT_THRESHOLD
2 Schema-guard add_rows / update_rows payload references columns that don't exist on the sheet (the original "row created with no values" bug) backend/tools.py · _validate_columns_for_write
3 Verb subsetting The intent classifier missed the user's verb ("ajoute", "rajoute", "create", …) so it didn't expose the right write tool backend/tools.py · _WRITE_VERB_TOKENS
4 JSON parse recovery Model emitted malformed tool arguments — we feed back a structured INVALID_JSON error instead of crashing backend/agent.py · __parse_error__ branch
5 Verified formula catalog Empirically tested list of supported Smartsheet functions + workarounds for the unsupported ones (POWER → ^, IFS → nested IF, …) backend/agent.py · SYSTEM_PROMPT
6 Action discipline Seven hard rules in the system prompt that ban known anti-patterns (read spam, fabricated IDs, repeating after error, …) backend/agent.py · ## ACTION DISCIPLINE
7 Contextual recovery hints Translates raw HTTP errors (401, 403, 404, 409, 429, 500) into actionable Smartsheet-specific guidance backend/tools.py · _friendly_http_error
8 Few-shot examples + decision table Six canonical interaction patterns + a 12-row intent→tool cheatsheet so the model has worked examples backend/agent.py · ## EXAMPLES and ## INTENT → TOOL CHEATSHEET

Every activation increments a counter exposed at GET /api/usage (Settings panel):

{
  "agent_metrics": {
    "turns": 42,                  // chat turns processed
    "tool_calls": 117,            // tool calls actually executed
    "tool_errors": 4,             // tool calls that returned an error payload
    "loop_blocked": 1,            // times the loop killer fired
    "schema_guard_triggered": 2,  // times add_rows/update_rows was blocked
    "parse_errors": 0,            // malformed tool-args from the model
    "user_rejections": 1,         // destructive actions you rejected
    "rounds_exhausted": 0         // turns that hit MAX_TOOL_ROUNDS
  }
}

When a safety net activates, the agent also pushes an agent_hint event to the WebSocket so the UI can render a colored banner (info / warn / error) telling the user why the agent is correcting itself rather than burying the explanation inside a tool result.

The harness is covered by 40+ dedicated tests:

# All P3 reliability tests in one shot
pytest tests/unit/test_agent_loop_killer.py \
       tests/unit/test_tools_schema_guard.py \
       tests/unit/test_tools_intent_subsetting.py \
       tests/unit/test_agent_metrics.py \
       tests/unit/test_agent_hints.py \
       tests/e2e/test_agent_safety_net.py

Each scenario reproduces a real failure mode (the original "agent calls add_rows when asked to add a column" bug, dead-end retry loops, etc.) and asserts that the harness intercepts the mistake and the follow-up correction succeeds.


In-app Bug Reports

Once the app is in your team's hands, the fastest way to keep improving it is to make reporting bugs friction-free. Every page now has a 🐛 Report a bug button in the header (also available via Ctrl+Shift+B). Clicking it opens a modal that:

  • Lets the reporter pick a severity (Low / Normal / High / Blocker).
  • Asks for a free-text description of what happened (required) and optional reproduction steps.
  • Auto-attaches a JSON context bundle: current session_id, sheet_id, conversation id, last 6 messages, agent metrics snapshot, browser user-agent, viewport size, WebSocket state, LLM provider/model — so you don't have to play 20 questions to reproduce.
  • Posts to POST /api/bug-reports (no auth required: a bug can occur at the login screen).

Reports are stored two ways for safety:

  1. SQLite — table bug_reports (status: open / triaged / fixed / wontfix).
  2. JSONL append-only mirrordata/bug_reports.jsonl, configurable via BUG_REPORTS_JSONL_PATH. Survives DB resets.

To read or triage reports, set an admin token in your .env:

BUG_REPORTS_ADMIN_TOKEN=pick-a-long-random-secret

Without this env var the admin endpoints are disabled (always 403). No risk of leaking reports if you forget.

# List the 50 most recent open bugs
curl -H "X-Admin-Token: $BUG_REPORTS_ADMIN_TOKEN" \
     "http://localhost:8100/api/bug-reports?status=open&limit=50"

# Mark report #12 as fixed
curl -X POST -H "X-Admin-Token: $BUG_REPORTS_ADMIN_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"status":"fixed"}' \
     http://localhost:8100/api/bug-reports/12/status

The feature is covered by 25 dedicated tests in tests/unit/test_bug_reports.py (DB CRUD, validation, server-side context enrichment, admin gate, JSONL mirror, status transitions).


Running the Test Suite

445 green tests across unit, integration and e2e

445 tests · 3 layers · 0 leaks · ~4 min on a laptop.

pip install -r requirements-dev.txt

pytest                       # everything (445 tests, ~4 min)
pytest tests/unit            # 389 fast tests, <2s, no network
pytest tests/integration     # 41 real Smartsheet API tests
pytest tests/e2e             # 15 WebSocket end-to-end (stubbed LLM, no cost)

pytest --cov=backend --cov-report=html   # coverage report → htmlcov/
Layer Count Speed Network Notable coverage
unit 401 < 3 s none Rate limiter · LLM router · _friendly_error (no secrets leaked) · dispatch contract for all 73 tools · agent.run() loop (confirm, parse-error recovery, MAX_TOOL_ROUNDS, image/chart events) · SQLite CRUD · MCP smoke (52 tools registered) · Loop killer + Schema-guard + Intent subsetting (107 P3 reliability tests) · Cross-sheet intent subsetting (16 phrasings) · Bug reports CRUD + JSONL mirror (25 tests) · Prompts library JSON contract + endpoint + sidebar wiring (25 tests, incl. dynamic per-category validation + destructive-prompt confirmation guardrail across all 81 prompts + 12 sidebar contract tests pinning down the in-chat panel) · _build_welcome resilience (12 tests covering None row/column counts and threshold boundaries)
integration 41 medium Smartsheet Real read calls + create→modify→delete lifecycle on a throwaway sheet · all FastAPI routes including /api/csv-to-sheet, /api/smartsheet-webhook (challenge + payload fan-out), favorites, conversations CRUD, audit, RGPD export, model switching
e2e 15 medium Smartsheet Full FastAPI lifespan · WebSocket handshake · agent loop with stubbed LLM · suggestions extraction · mid-stream cancel · destructive-tool confirm/reject · WS rate-limit response · multi-turn in one connection · scripted-LLM safety-net scenarios (6 tests verifying the harness intercepts column/row confusion, infinite loops, schema violations)
functional (live) 38 slow Smartsheet Cross-sheet formulas: aggregation (SUM/AVG/COUNT), conditional aggregation (SUMIFS, COUNTIFS, MAX/MIN(COLLECT)), lookups (VLOOKUP, INDEX/MATCH, INDEX/COLLECT), date ops, JOIN — with 404-retry logic to survive Smartsheet's eventual-consistency window

Integration & e2e tests skip cleanly when SMARTSHEET_TOKEN / SHEET_ID aren't in .env. Tests never touch the production SQLite DB — each test gets its own temp file. LLM calls are stubbed by default; set ENABLE_LIVE_LLM=1 to opt into real provider calls.


Deployment

Option 1 — Docker (recommended)

cp .env.example .env      # fill in at least OPENAI_API_KEY (SMARTSHEET_TOKEN is optional - users now BYOT)
docker compose up -d --build
# → http://localhost:8100   (health: http://localhost:8100/health)

The container runs uvicorn backend.app:app as a non-root user on port 8100, exposes a /health endpoint for orchestrator probes, and reads secrets from .env.

Option 2 — Bare metal (systemd)

python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
uvicorn backend.app:app --host 0.0.0.0 --port 8100 --proxy-headers --forwarded-allow-ips '*'

Example /etc/systemd/system/smartsheet-controller.service:

[Unit]
Description=Smartsheet Controller
After=network.target

[Service]
Type=simple
User=smartsheet
WorkingDirectory=/opt/smartsheet-controller
EnvironmentFile=/opt/smartsheet-controller/.env
ExecStart=/opt/smartsheet-controller/.venv/bin/uvicorn backend.app:app \
  --host 127.0.0.1 --port 8100 --proxy-headers --forwarded-allow-ips '*'
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now smartsheet-controller
sudo journalctl -u smartsheet-controller -f

Reverse proxy (nginx + TLS + WebSockets)

server {
    listen 443 ssl http2;
    server_name chat.example.com;

    ssl_certificate     /etc/letsencrypt/live/chat.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem;

    client_max_body_size 2m;

    location / {
        proxy_pass http://127.0.0.1:8100;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        # WebSocket upgrade (used by /ws/{session_id})
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        "upgrade";
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }

    location = /health { proxy_pass http://127.0.0.1:8100/health; access_log off; }
}

server {
    listen 80;
    server_name chat.example.com;
    return 301 https://$host$request_uri;
}

Production notes

  • BYOT flow — end users paste their own Smartsheet token in the UI. The server stores it only in-memory for the session; sessions auto-expire after SESSION_IDLE_TIMEOUT seconds (default 1800).
  • Secrets — only LLM provider keys need to live in .env on the server (OPENAI_API_KEY, ANTHROPIC_API_KEY, ...). The SMARTSHEET_TOKEN in .env is only used for the "Quick Connect (dev)" button.
  • Rate limiting — per-session token buckets (LLM 60/min, WS 120/min) are applied automatically; Smartsheet 429s are retried with exponential backoff.
  • Logging — stdout is redacted of API tokens; set LOG_LEVEL=DEBUG to see more.
  • Health probeGET /health returns {"status": "ok", "sessions": N, "uptime": ...}.

Links

Smartsheet — official GitHub org @smartsheet (verified, SDKs Python · JS · Java · C#)
Smartsheet Platform — DevRel GitHub @smartsheet-platform (incl. their own MCP server)
Smartsheet API Docs smartsheet.redoc.ly
Smartsheet Developer Portal developers.smartsheet.com
Smartsheet Functions help.smartsheet.com/functions
Smartsheet Learning Center help.smartsheet.com
Smartsheet Community community.smartsheet.com
Model Context Protocol modelcontextprotocol.io
OpenAI Platform platform.openai.com
Anthropic Docs docs.anthropic.com



Talk to Your Smartsheet.

73 tools · 80 formulas · 7 LLMs · 445 green tests · 81 in-app prompts · Full voice · Streaming · History · Audit · MCP · Bug reports · One-click logout.

Your data listens. Your sheets respond. Your workflow accelerates.


Built with FastAPI · Smartsheet API · OpenAI · Anthropic · OpenRouter · Mistral · Groq · MCP

Smartsheet® and the Smartsheet logo are trademarks of Smartsheet Inc. (@smartsheet). This project is an independent community tool and is not endorsed by or affiliated with Smartsheet Inc.


About

Talk to Your Smartsheet - AI copilot with 53 tools, 80 formulas, voice control, streaming, and MCP server

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors