Answer growth questions in plain English. No SQL required.
GrowthOS is an open-source MCP analytics server that connects your AI assistant (Claude, Cursor, or any MCP-compatible client) directly to your marketing and product data. Ask questions about funnels, retention, CAC, churn, anomalies, channel performance, forecasts, Shapley attribution, and narrative summaries — and get structured, benchmark-referenced answers in seconds.
| CAC & LTV by Channel | Funnel Analysis |
|---|---|
![]() |
![]() |
| Churn Segmentation | Weekly Growth Summary |
|---|---|
![]() |
![]() |
GrowthOS sits between your AI assistant and your data. It exposes 49 MCP tools that your AI can call to run real analysis — funnel breakdowns, CAC/LTV calculations, cohort retention matrices, anomaly detection, SQL queries, and more — then formats results with benchmark comparisons so you get context, not just numbers.
Claude / Cursor / any MCP client
│
▼
┌──────────────────────────────┐
│ GrowthOS MCP Server │
│ 49 tools · DuckDB · SQL │
│ Semantic layer · Benchmarks │
└────────────┬─────────────────┘
│
┌──────────────────────┐
│ Your data sources │
│ CSV · PostgreSQL │
│ Stripe · Meta Ads │
│ Google Ads · HubSpot│
│ Mixpanel · Amplitude│
└──────────────────────┘
-
"Why did CAC increase last week and is it sustainable?" Channel-level CAC, LTV, and ROAS with automated invest/cut/watch classification.
-
"Where is our funnel breaking and what should we test first?" Step-by-step conversion rates with the biggest drop-off identified automatically.
-
"How bad is our retention and how does it compare to benchmarks?" Cohort retention matrix vs. B2B SaaS medians with Good / Average / Poor rating.
-
"Which users are at risk of churning and how do we bring them back?" Active / At-Risk / Churned segmentation using configurable inactivity thresholds.
-
"What will our spend and signups look like in the next 30 days?" Linear regression and exponential smoothing forecasts with confidence bands.
- Growth teams who want fast, repeatable answers without waiting for a data analyst
- Founders and PMs reviewing weekly metrics in Claude, Cursor, or any AI
- Marketing ops managing multi-channel attribution across Google, Meta, Stripe, HubSpot, Mixpanel, Amplitude, and CSV exports
- Teams that need dashboards or BI charts (use Metabase, Looker, etc.)
- Real-time streaming data (GrowthOS reads CSV/DuckDB snapshots)
- Enterprise RBAC or multi-tenant data isolation
pip install growth-osAdd to ~/.claude/claude_desktop_config.json:
{
"mcpServers": {
"growth-os": {
"command": "uvx",
"args": ["growth-os"],
"env": {
"GROWTH_DATA_DIR": "/path/to/your/csv/folder"
}
}
}
}{
"mcpServers": {
"growth-os": {
"command": "uvx",
"args": ["growth-os"],
"env": {
"GROWTH_DATA_DIR": "./data"
}
}
}
}git clone https://github.com/Astoriel/GrowthOS.git
cd GrowthOS
pip install -e ".[dev]"
python -m growth_os.serverSkip GROWTH_DATA_DIR entirely. GrowthOS auto-generates realistic sample marketing data on startup — useful for exploring all 49 tools before connecting real data. Every connector (HubSpot, Mixpanel, Amplitude, Stripe, Meta, Google Ads) also runs in demo mode with synthetic mock data when credentials are absent.
Try it immediately:
pip install growth-os
# In Claude: "Give me a weekly growth summary"
# GrowthOS responds with real analysis on auto-generated sample dataPoint GROWTH_DATA_DIR at a folder containing CSV files. GrowthOS loads all CSVs as queryable DuckDB tables.
export GROWTH_DATA_DIR=/path/to/csv/folderRecommended file names (auto-detected schema):
| File | Key Columns |
|---|---|
marketing_spend.csv |
date, channel, spend, impressions, clicks |
user_events.csv |
user_id, event_type, event_date, utm_source |
campaigns.csv |
campaign_id, channel, name, start_date, spend |
Column names are flexible — use suggest_attribution_mappings to auto-map aliases.
export POSTGRES_URL=postgresql://user:pass@host:5432/dbnameGrowthOS attaches PostgreSQL as a read-only DuckDB extension. All queries are read-only; write/DDL operations are blocked at the AST level.
export STRIPE_API_KEY=sk_live_...Syncs invoices, subscriptions, charges, and events. Call sync_stripe_billing from your AI assistant.
export META_ACCESS_TOKEN=...
export META_AD_ACCOUNT_ID=act_...
export META_API_VERSION=v21.0 # optional, default v21.0Syncs campaigns and daily ad insights. Call sync_meta_ads.
export GOOGLE_ADS_DEVELOPER_TOKEN=...
export GOOGLE_ADS_CUSTOMER_ID=...
export GOOGLE_ADS_LOGIN_CUSTOMER_ID=... # manager account ID, if applicable
export GOOGLE_ADS_ACCESS_TOKEN=...
export GOOGLE_ADS_REFRESH_TOKEN=...
export GOOGLE_ADS_CLIENT_ID=...
export GOOGLE_ADS_CLIENT_SECRET=...
export GOOGLE_ADS_API_VERSION=v19 # optional, default v19Syncs campaigns and click performance. Call sync_google_ads.
Uses a Private App access token (not an API key). Create a Private App in HubSpot Settings → Integrations → Private Apps, and grant these scopes:
crm.objects.contacts.readcrm.objects.deals.read
export HUBSPOT_ACCESS_TOKEN=pat-eu1-... # Private App token
export HUBSPOT_BASE_URL=https://api.hubapi.com # optionalCall sync_hubspot to pull contacts and deals. Falls back to demo mode (100–200 mock rows) when HUBSPOT_ACCESS_TOKEN is unset.
Uses the API Secret from Project Settings (not the project token).
export MIXPANEL_API_SECRET=... # Project API Secret
export MIXPANEL_PROJECT_ID=... # Project numeric ID (optional)
export MIXPANEL_EU=true # true = EU data residency (default)Call sync_mixpanel to pull event streams and funnel data. Funnel data falls back to mock if the account is on the free plan (HTTP 402). Falls back to demo mode (up to 500 mock events + 3 funnel templates) when MIXPANEL_API_SECRET is unset.
Requires both the API Key and Secret Key from the Amplitude project settings (under General → API Keys). Note: the JavaScript SDK snippet key is a different key and will not work here.
export AMPLITUDE_API_KEY=... # From Project Settings → API Keys
export AMPLITUDE_SECRET_KEY=... # Secret key (required alongside API key)
export AMPLITUDE_EU=true # true = EU data residency (default)Call sync_amplitude to pull events and user cohorts. Falls back to demo mode (500 mock events + 8 mock cohorts) when either key is missing.
Run GrowthOS as a standalone HTTP API alongside the MCP server:
pip install "growth-os[api]"
growth-os-apiThe API server starts at http://localhost:8000. Swagger docs at http://localhost:8000/docs.
| Method | Path | Description |
|---|---|---|
GET |
/health |
Server health, loaded tables, timestamp |
POST |
/query |
Execute a read-only SQL query |
GET |
/tables |
List all loaded tables |
GET |
/tables/{name} |
Schema and stats for a specific table |
GET |
/analysis/funnel |
Funnel conversion rates |
GET |
/analysis/cac-ltv |
CAC / LTV / ROAS by channel |
GET |
/analysis/churn |
User churn segmentation |
GET |
/analysis/anomalies |
Anomaly detection in any metric |
GET |
/reports/growth-summary |
Weekly growth KPI summary |
All prompts work in demo mode or with real data.
Weekly review:
"Give me a weekly growth summary comparing this week to last week."
Funnel diagnosis:
"Analyze the signup → activation → purchase funnel and tell me where we're losing users."
CAC/LTV:
"What's our CAC and LTV by channel? Which channels should we scale up?"
Retention check:
"How is our month-1 retention trending and how does it compare to SaaS benchmarks?"
Churn investigation:
"How many users churned last month and how many are at risk right now?"
Anomaly investigation:
"Something looks off in our spend last Tuesday. Detect any anomalies in marketing_spend."
Shapley attribution:
"Compute Shapley-value attribution across all channels for the last 90 days."
Forecasting:
"Forecast our daily signups for the next 30 days using linear trend and exponential smoothing."
Narrative review:
"Write a plain-English growth narrative with headline findings and a recommendation."
Data drift alert:
"Alert me via webhook if spend drifts more than 15% compared to last week."
HubSpot contacts:
"Sync our HubSpot contacts and show a breakdown by lifecycle stage."
Profile setup:
"Save my current setup as a profile called 'production' for next session."
All 49 registered MCP tools:
| Tool | Description |
|---|---|
health_check |
Server status, loaded tables, DuckDB version, timestamp |
list_tables |
List all available data tables |
describe_table |
Schema, sample rows, and column statistics for any table |
run_query |
Execute a read-only SQL query (DuckDB dialect); supports offset/limit pagination |
| Tool | Description |
|---|---|
analyze_funnel |
Step-by-step funnel conversion rates |
compute_cac_ltv |
CAC, LTV, and ROAS by channel with benchmark comparison |
cohort_retention |
Cohort retention matrix (weekly or monthly) vs. B2B SaaS medians |
channel_attribution |
Attributed revenue and ROAS per channel |
analyze_churn |
User segmentation: Active / At-Risk / Churned |
detect_anomalies |
Spike/drop detection in any metric time series |
| Tool | Description |
|---|---|
funnel_diagnosis |
Funnel + churn in one response with prioritized next-step recommendations |
channel_efficiency_review |
CAC + LTV + ROAS with automated invest / cut / watch classification |
anomaly_explanation |
Anomalies + likely cause hypotheses + investigation actions |
detect_data_drift |
Compare current vs. previous period; flags shifts >20% |
funnel_ab_comparison |
Side-by-side funnel conversion for two time periods with Δ (pp) column |
drift_alert |
Detect metric drift and optionally fire a webhook notification |
scheduled_report_preview |
Preview weekly growth report and optionally deliver it via webhook |
| Tool | Description |
|---|---|
forecast_metric |
Forecast any metric column using linear regression or exponential smoothing, with confidence bands |
forecast_growth_kpis |
Forecast spend and daily active users for the next N days |
| Tool | Description |
|---|---|
growth_summary |
Weekly growth KPI summary with period-over-period comparison |
weekly_growth_review |
Full weekly growth review workflow |
executive_summary |
Board-level executive summary |
paid_growth_review |
Paid spend vs. Stripe revenue comparison |
campaign_performance_review |
Campaign ranking by spend, efficiency, and risk |
attribution_bridge_review |
Campaign-to-revenue attribution bridge |
narrative_growth_review |
Plain-English prose narrative with headline, findings, and recommendation |
| Tool | Description |
|---|---|
validate_data |
Validate tables against GrowthOS schema contracts |
inspect_freshness |
Check data freshness across all loaded tables |
list_connectors |
List configured connectors and their readiness status |
| Tool | Description |
|---|---|
attribution_mapping_diagnostics |
Coverage gaps, unmapped keys, alias rule usage |
suggest_attribution_mappings |
Auto-suggest high-confidence alias rules from your data |
attribution_mapping_review_pack |
Read-only review: coverage deltas, risk flags, audit trail |
preview_apply_attribution_mappings |
Preview coverage lift before applying any changes |
apply_suggested_attribution_mappings |
Apply approved aliases (high-risk rules blocked unless force=true) |
review_attribution_mappings |
Inspect active rules and full audit history |
rollback_attribution_mappings |
Undo specific alias changes by ID |
| Tool | Description |
|---|---|
sync_stripe_billing |
Sync Stripe invoices, subscriptions, and events to local CSV |
stripe_revenue_summary |
Revenue summary from synced Stripe data |
sync_meta_ads |
Sync Meta Ads campaigns and daily insights |
meta_ads_summary |
Spend, clicks, impressions from synced Meta data |
sync_google_ads |
Sync Google Ads campaigns and click performance |
google_ads_summary |
Spend, clicks, conversions from synced Google Ads data |
sync_hubspot |
Sync HubSpot contacts and deals (demo mode when no token) |
hubspot_contacts_summary |
Contacts breakdown by lifecycle stage |
sync_mixpanel |
Sync Mixpanel events and funnels (demo mode when no secret) |
sync_amplitude |
Sync Amplitude events and user cohorts (demo mode when no keys) |
| Tool | Description |
|---|---|
save_workspace_profile |
Save named configuration (data dir, Postgres URL, custom metrics) |
load_workspace_profile |
Load and apply a saved profile |
list_workspace_profiles |
List all saved profiles |
Save any combination of data directory, Postgres URL, and business mode as a named profile:
> "Save my current setup as a profile called 'production'."
> "Load the 'production' profile."
Profiles support custom metric definitions — define SQL-based KPIs with benchmark thresholds and units that GrowthOS evaluates alongside built-in metrics:
> "Add a custom metric: 'avg_order_value' = revenue / orders, benchmark 45, unit USD."
src/growth_os/
├── app/
│ ├── server.py # FastMCP server factory
│ ├── registry.py # 49 MCP tool registrations
│ └── lifespan.py # Startup sample data provisioning
├── api/
│ └── server.py # FastAPI REST API (optional)
├── config/
│ ├── settings.py # Pydantic-settings env config
│ └── profiles.py # Workspace profiles + custom metrics
├── connectors/
│ ├── duckdb.py # DuckDB engine + query cache
│ ├── csv.py # CSV ingestion
│ ├── postgres.py # PostgreSQL attach (read-only)
│ ├── stripe.py # Stripe Billing API
│ ├── meta_ads.py # Meta Marketing API
│ ├── google_ads.py # Google Ads API
│ ├── hubspot.py # HubSpot CRM v3 (contacts + deals)
│ ├── mixpanel.py # Mixpanel Export + Funnels API
│ ├── amplitude.py # Amplitude Export + Cohorts API
│ └── webhook.py # Outbound webhook dispatcher
├── domain/
│ ├── models.py # ToolEnvelope, shared models
│ ├── exceptions.py # ConnectorError, QueryError
│ ├── enums.py # FreshnessStatus, Severity, ChurnMode
│ └── contracts.py # Schema contracts for validation
├── ingestion/
│ ├── catalog.py # Table catalog
│ ├── freshness.py # Freshness detection
│ ├── validators.py # Data quality validators
│ ├── mapping.py # Attribution mapping engine
│ └── loaders.py # Data loading orchestration
├── observability/
│ ├── logging.py # Structured JSON logging
│ ├── tracing.py # @trace decorator
│ └── audit.py # Append-only audit log
├── presentation/
│ └── markdown.py # ToolEnvelope formatter + narrative builder
├── query/
│ ├── safety.py # AST-based SQL validator (sqlglot)
│ └── builder.py # Query builder
├── semantic/
│ ├── metrics.py # CAC, LTV, churn (3 modes), ROAS
│ ├── benchmarks.py # B2B SaaS benchmark data
│ ├── funnels.py # Funnel step definitions
│ ├── retention.py # Retention cohort logic
│ ├── attribution.py # UTM attribution + Shapley values
│ └── forecasting.py # Linear OLS + exponential smoothing
├── services/
│ ├── analysis_service.py
│ ├── catalog_service.py
│ ├── diagnostics_service.py
│ ├── reporting_service.py
│ ├── integration_service.py
│ ├── notification_service.py
│ └── forecasting_service.py
├── demo/
│ ├── sample_generator.py # Synthetic marketing data
│ └── scenarios.py # 4 curated demo scenarios
└── tools/
├── discovery/ # health_check, list_tables, describe_table, run_query
├── analysis/ # funnel, cac_ltv, retention, churn, anomalies
├── reports/ # growth_summary, executive, narrative, forecasting
└── admin/ # profiles, connectors, attribution mapping
Key design decisions:
- Read-only enforcement — All SQL is validated by an AST parser (sqlglot) before execution. Any write, DDL, multi-statement, or PRAGMA operation is rejected.
- Demo-first — Every connector and every analysis runs on synthetic data with no configuration. Real credentials are optional.
- Benchmark context — Metrics are always compared to B2B SaaS median benchmarks (A16Z, OpenView) so raw numbers come with meaning.
- Structured output — All tools return
ToolEnvelopeobjects rendered as clean markdown tables, not raw JSON blobs.
219 tests across 14 test files, covering unit tests, integration tests, and smoke tests.
pytest tests/ -q --tb=short
# 219 passed in ~8s| File | What it covers |
|---|---|
test_app_smoke.py |
Server creation, tool registration, FastMCP integration |
test_connector.py |
HubSpot, Mixpanel, Amplitude — demo mode, real API mock, error paths |
test_demo_runtime.py |
Full demo scenario execution end-to-end |
test_formatters.py |
ToolEnvelope markdown rendering, narrative formatting, business mode |
test_google_ads_connector.py |
Google Ads mock API, pagination, error handling |
test_guardrails.py |
Attribution guardrails: high-risk rule blocking, force override |
test_ingestion.py |
CSV loading, freshness detection, schema validation, mapping engine |
test_meta_ads_connector.py |
Meta Ads API mock, rate limits, data normalization |
test_metrics.py |
CAC/LTV/ROAS computation, churn modes, Shapley attribution |
test_profiles.py |
Save/load/list/delete profiles, custom metric definitions |
test_schema.py |
Schema contracts, data validation, column type inference |
test_sql_safety.py |
SQL AST validator — 40+ blocked patterns, allowed queries |
test_stripe_connector.py |
Stripe invoice/subscription sync, pagination, normalization |
test_workflow_tools.py |
funnel_diagnosis, channel_efficiency_review, anomaly_explanation |
Connectors were tested against live APIs in development. Results:
| Connector | Auth | Data | Notes |
|---|---|---|---|
HubSpot (pat-eu1-...) |
✅ Token accepted | Requires CRM scopes | Add crm.objects.contacts.read + crm.objects.deals.read to Private App |
| Mixpanel | ✅ Auth confirmed | 0 events (empty project) | Funnels fallback to mock on free plan (HTTP 402) |
| Amplitude | ✅ Auth confirmed | 6 real cohorts fetched | Export 404 = no events tracked yet |
| Stripe | ✅ (mock) | Full invoice/subscription sync | Tested against Stripe test mode |
| Meta Ads | ✅ (mock) | Campaign + insights sync | Tested against Graph API mock |
| Google Ads | ✅ (mock) | Campaign + click sync | Full OAuth flow tested |
All settings are environment variables (case-insensitive):
| Variable | Default | Description |
|---|---|---|
GROWTH_DATA_DIR |
(none) | Path to folder with CSV files |
POSTGRES_URL |
(none) | PostgreSQL connection URL (read-only) |
DB_PATH |
(in-memory) | DuckDB file path (set to persist between runs) |
SERVER_NAME |
growth-os |
MCP server display name |
MAX_QUERY_ROWS |
10000 |
Maximum rows returned per query |
QUERY_TIMEOUT_SECONDS |
30 |
Query execution timeout |
DEFAULT_TRANSPORT |
stdio |
MCP transport: stdio or sse |
BUSINESS_MODE |
false |
Enable business-friendly output mode |
SEMANTIC_PROFILE_PATH |
(none) | Path to semantic profile JSON |
| Variable | Default | Description |
|---|---|---|
STRIPE_API_KEY |
(none) | Stripe secret key (sk_live_... or sk_test_...) |
STRIPE_BASE_URL |
https://api.stripe.com/v1 |
Override for testing |
| Variable | Default | Description |
|---|---|---|
META_ACCESS_TOKEN |
(none) | User access token |
META_AD_ACCOUNT_ID |
(none) | Ad account ID (act_...) |
META_API_VERSION |
v21.0 |
Graph API version |
META_BASE_URL |
https://graph.facebook.com |
Override for testing |
| Variable | Default | Description |
|---|---|---|
GOOGLE_ADS_DEVELOPER_TOKEN |
(none) | Developer token |
GOOGLE_ADS_CUSTOMER_ID |
(none) | Customer (CID) without dashes |
GOOGLE_ADS_LOGIN_CUSTOMER_ID |
(none) | Manager account CID (if applicable) |
GOOGLE_ADS_ACCESS_TOKEN |
(none) | OAuth access token |
GOOGLE_ADS_REFRESH_TOKEN |
(none) | OAuth refresh token |
GOOGLE_ADS_CLIENT_ID |
(none) | OAuth client ID |
GOOGLE_ADS_CLIENT_SECRET |
(none) | OAuth client secret |
GOOGLE_ADS_API_VERSION |
v19 |
Ads API version |
| Variable | Default | Description |
|---|---|---|
HUBSPOT_ACCESS_TOKEN |
(none) | Private App token (pat-eu1-...). Omit = demo mode |
HUBSPOT_BASE_URL |
https://api.hubapi.com |
Override for testing |
| Variable | Default | Description |
|---|---|---|
MIXPANEL_API_SECRET |
(none) | Project API Secret. Omit = demo mode |
MIXPANEL_PROJECT_ID |
(none) | Project numeric ID (optional) |
MIXPANEL_EU |
true |
Use EU data residency endpoints |
| Variable | Default | Description |
|---|---|---|
AMPLITUDE_API_KEY |
(none) | Project API key. Both keys required for live mode |
AMPLITUDE_SECRET_KEY |
(none) | Project Secret key |
AMPLITUDE_EU |
true |
Use EU data residency endpoints |
# Build
docker build -t growth-os .
# Demo mode (no credentials)
docker run -p 8000:8000 growth-os
# With CSV data
docker run -e GROWTH_DATA_DIR=/data -v /local/csv:/data growth-os
# With PostgreSQL
docker run -e POSTGRES_URL=postgresql://user:pass@host:5432/db growth-os
# With Stripe and HubSpot
docker run \
-e STRIPE_API_KEY=sk_live_... \
-e HUBSPOT_ACCESS_TOKEN=pat-eu1-... \
growth-os- Read-only enforcement. GrowthOS enforces an AST-based SQL sandbox (sqlglot) that blocks any write, DDL, or multi-statement operation. You cannot accidentally modify data.
- Attribution is approximate. UTM-based attribution has known gaps: direct traffic, cross-device, and offline conversions are not captured.
- Forecasts are statistical, not causal. Linear and exponential smoothing forecasts extrapolate historical trends — they do not account for seasonality, planned campaigns, or market shocks. Use for directional guidance only.
- Benchmarks are medians. Industry benchmarks (B2B SaaS, A16Z, OpenView) reflect median-performing companies. Valid reasons to be above or below exist for every metric.
- Data freshness matters. Stale CSVs produce stale answers. Always run
inspect_freshnessbefore drawing conclusions from time-sensitive analyses. - Connector demo mode produces synthetic data. Mock rows use fixed random seeds so output is deterministic and will not match your actual business metrics.
- HubSpot scope requirement. The Private App must have
crm.objects.contacts.readandcrm.objects.deals.readscopes. Without these scopes the connector returns HTTP 403 even with a valid token. - Mixpanel free plan. The Funnels API returns HTTP 402 on free-tier projects. GrowthOS automatically falls back to synthetic funnel data in this case.
- Amplitude key types. Only the server-side API Key + Secret Key pair works for the Export API. The JavaScript SDK snippet key will not authenticate.
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest tests/ -q --tb=short
# Lint
python -m ruff check src/ tests/
# Verify server starts cleanly
python -c "from growth_os.app.server import create_mcp_server; create_mcp_server()"pip install -e ".[api]"
growth-os-api
# Swagger: http://localhost:8000/docsMIT — see LICENSE.




