analytics_ops 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +108 -0
- data/CONTRIBUTING.md +1 -0
- data/README.md +141 -13
- data/SECURITY.md +11 -2
- data/docs/ai-connections.md +192 -0
- data/docs/api-support-matrix.md +25 -8
- data/docs/architecture.md +78 -17
- data/docs/authentication.md +53 -3
- data/docs/bigquery.md +128 -0
- data/docs/commands.md +133 -12
- data/docs/configuration-schema-v1.json +608 -50
- data/docs/configuration.md +118 -5
- data/docs/google-client-compatibility.md +21 -1
- data/docs/governance.md +93 -0
- data/docs/health.md +82 -0
- data/docs/live-smoke-test.md +13 -10
- data/docs/rails.md +25 -9
- data/docs/reports.md +165 -3
- data/docs/safety.md +37 -5
- data/docs/troubleshooting.md +81 -7
- data/lib/analytics_ops/bigquery/definition.rb +173 -0
- data/lib/analytics_ops/bigquery.rb +4 -0
- data/lib/analytics_ops/cli/local_commands.rb +80 -0
- data/lib/analytics_ops/cli/options.rb +282 -9
- data/lib/analytics_ops/cli/presenter.rb +263 -28
- data/lib/analytics_ops/cli/presenter_csv.rb +92 -0
- data/lib/analytics_ops/cli/reporting_commands.rb +129 -0
- data/lib/analytics_ops/cli/setup_commands.rb +91 -0
- data/lib/analytics_ops/cli.rb +121 -44
- data/lib/analytics_ops/clients/admin.rb +28 -2
- data/lib/analytics_ops/clients/admin_governance.rb +95 -0
- data/lib/analytics_ops/clients/admin_governance_normalization.rb +90 -0
- data/lib/analytics_ops/clients/admin_resource_normalization.rb +61 -0
- data/lib/analytics_ops/clients/bigquery.rb +313 -0
- data/lib/analytics_ops/clients/data.rb +147 -54
- data/lib/analytics_ops/clients/data_result_normalization.rb +55 -0
- data/lib/analytics_ops/clients/error_translation.rb +13 -1
- data/lib/analytics_ops/configuration/schema.rb +175 -1
- data/lib/analytics_ops/configuration/validator.rb +45 -7
- data/lib/analytics_ops/configuration/validator_experimental.rb +136 -0
- data/lib/analytics_ops/configuration/validator_reporting.rb +93 -0
- data/lib/analytics_ops/configuration/writer.rb +112 -7
- data/lib/analytics_ops/desired_state.rb +28 -3
- data/lib/analytics_ops/errors.rb +33 -1
- data/lib/analytics_ops/funnels.rb +58 -0
- data/lib/analytics_ops/governance.rb +363 -0
- data/lib/analytics_ops/health.rb +310 -0
- data/lib/analytics_ops/mcp_server/custom_report_tool.rb +101 -0
- data/lib/analytics_ops/mcp_server/discovery_tools.rb +79 -0
- data/lib/analytics_ops/mcp_server/experimental_tools.rb +89 -0
- data/lib/analytics_ops/mcp_server/health_tools.rb +80 -0
- data/lib/analytics_ops/mcp_server/overview_tools.rb +49 -0
- data/lib/analytics_ops/mcp_server/standard_report_tool.rb +53 -0
- data/lib/analytics_ops/mcp_server.rb +402 -0
- data/lib/analytics_ops/portfolio.rb +216 -0
- data/lib/analytics_ops/rails/railtie.rb +19 -8
- data/lib/analytics_ops/redaction.rb +10 -6
- data/lib/analytics_ops/reports/csv_export.rb +91 -0
- data/lib/analytics_ops/reports/definition.rb +152 -14
- data/lib/analytics_ops/reports/metadata.rb +158 -0
- data/lib/analytics_ops/reports/period.rb +104 -0
- data/lib/analytics_ops/reports/result.rb +8 -3
- data/lib/analytics_ops/reports.rb +3 -0
- data/lib/analytics_ops/service_account.rb +321 -29
- data/lib/analytics_ops/setup.rb +31 -7
- data/lib/analytics_ops/version.rb +1 -1
- data/lib/analytics_ops/workspace.rb +222 -10
- data/lib/analytics_ops.rb +7 -2
- data/lib/generators/analytics_ops/templates/analytics_ops.yml +1 -1
- data/sig/analytics_ops.rbs +494 -9
- metadata +60 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a081c51447259f1fd4755d6758cadc76878668a5851e96408e286c64885fdcb7
|
|
4
|
+
data.tar.gz: 6e5724c104b4946735b9967d1de1a10e70892828901d7eb7dd0190023af2ee92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8d422c66448843f84a387fdd39526d7fe9e6545f16760fcedaa840250a8356e3f6612be151cd115ee0120b1074b406d39d115546ce460571039c8653b2cdd59d
|
|
7
|
+
data.tar.gz: 0c42714dbfc96aa557344f8930d55e4a1087bce24eb2f3d22db3d904fc83ce8c3803ad122daae2a6a45ffeb8aa5e7d6b304c4e3e3c882192ebe77e6851671339
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,114 @@
|
|
|
3
3
|
Analytics Ops follows Semantic Versioning for its public Ruby, CLI,
|
|
4
4
|
configuration, and plan contracts.
|
|
5
5
|
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.3.0] - 2026-07-23
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Named service-account connections with per-configuration profile mappings,
|
|
13
|
+
`connections`, `profiles`, and `use` commands, plus automatic migration from
|
|
14
|
+
the version-1 single-key pointer.
|
|
15
|
+
- Collision-safe automatic connection names when separate applications both
|
|
16
|
+
use the default `production` profile with different credentials.
|
|
17
|
+
- Safe additive setup for a second property profile without hand-editing YAML.
|
|
18
|
+
- `--last`, `--from`, `--to`, and `--compare` for standard reports and
|
|
19
|
+
overviews, with bounded equal preceding-period comparisons.
|
|
20
|
+
- A read-only `portfolio` summary across every configured property and its
|
|
21
|
+
associated Google connection.
|
|
22
|
+
- A local stdio `mcp` server for ChatGPT desktop, Codex, and Claude Code. Its
|
|
23
|
+
strict tool allowlist covers the read-only capabilities described below and
|
|
24
|
+
contains no mutation operation.
|
|
25
|
+
- Service-account file permission and Git-repository placement warnings in
|
|
26
|
+
setup and doctor.
|
|
27
|
+
- A beginner-friendly AI connection guide with the exact local commands,
|
|
28
|
+
privacy boundary, and optional OpenAI Secure MCP Tunnel explanation.
|
|
29
|
+
- Property-aware report field discovery and compatibility checks, saved YAML
|
|
30
|
+
recipes, bounded one-off custom reports, recursive boolean/list/null/numeric
|
|
31
|
+
filters, and atomic streaming CSV exports up to an explicit hard limit.
|
|
32
|
+
- Deterministic `health` checks for traffic and key-event drops, expected
|
|
33
|
+
events, acquisition quality, Google thresholding/sampling, quota, and
|
|
34
|
+
configuration drift.
|
|
35
|
+
- Generalized portfolio reports and health across all profiles with bounded
|
|
36
|
+
concurrency, profile labels, stable ordering, and isolated per-property
|
|
37
|
+
failures.
|
|
38
|
+
- Experimental read-only governance snapshots and policy checks for Enhanced
|
|
39
|
+
Measurement, stream redaction, reporting identity, attribution, channel
|
|
40
|
+
groups, calculated metrics, event rules, and BigQuery links.
|
|
41
|
+
- Experimental bounded, de-identified change history and portable aggregate
|
|
42
|
+
Data API quota-access reporting.
|
|
43
|
+
- Optional fixed GA4 BigQuery Export recipes and aggregate ordered funnels
|
|
44
|
+
using the official client, a mandatory dry run and byte cap, parameterized
|
|
45
|
+
SQL, and bounded normalized results.
|
|
46
|
+
- Expanded the local MCP allowlist to 21 strictly read-only tools, including
|
|
47
|
+
field discovery, compatible custom reports, health, generalized portfolio,
|
|
48
|
+
governance, and optional cost-capped BigQuery/funnel reads.
|
|
49
|
+
- New Rails tasks and dedicated health, governance, and BigQuery/funnel
|
|
50
|
+
operator guides.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- Verify future RubyGems releases from the published artifact instead of
|
|
55
|
+
waiting for the legacy full index, which can lag behind a successful
|
|
56
|
+
Trusted Publishing upload.
|
|
57
|
+
- Rails generator and CLI setup now share the `production` default. Setup can
|
|
58
|
+
fill the untouched generated placeholder, and Rails tasks use the locally
|
|
59
|
+
selected profile unless `ANALYTICS_OPS_PROFILE` explicitly overrides it.
|
|
60
|
+
- The mode-`0600` user connection file now stores multiple named key paths and
|
|
61
|
+
profile selections while still storing no key material.
|
|
62
|
+
- Google transport errors preserve bounded structured reason, metadata, and
|
|
63
|
+
status values so disabled-API guidance does not depend on English wording.
|
|
64
|
+
- Saved and direct custom reports validate live property metadata and Google's
|
|
65
|
+
compatibility result before running.
|
|
66
|
+
- Portfolio human and CSV output now includes property identity and optional
|
|
67
|
+
local labels.
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
- Declare CSV as an explicit runtime dependency for Ruby 3.4 and newer, where
|
|
72
|
+
it is no longer bundled as a default gem.
|
|
73
|
+
- Show complete redacted before/after values during interactive apply instead
|
|
74
|
+
of truncating the exact operation being approved.
|
|
75
|
+
- Require `--non-interactive --yes` for JSON apply so prompts can never corrupt
|
|
76
|
+
machine-readable standard output.
|
|
77
|
+
- Reject duplicate stream IDs during configuration loading instead of failing
|
|
78
|
+
later during planning.
|
|
79
|
+
- Preserve both the beginning and ending of long human report cells so similar
|
|
80
|
+
URLs remain visibly distinct.
|
|
81
|
+
- Validate saved connection mappings against existing connection names.
|
|
82
|
+
- Use each web stream—not the property root—for event create/edit rule reads,
|
|
83
|
+
and supply the required parent account when requesting change history.
|
|
84
|
+
- Use non-360 aggregate Data Access fields so the default access query works
|
|
85
|
+
on ordinary GA4 properties.
|
|
86
|
+
- Preserve a report definition's starting offset during paginated export.
|
|
87
|
+
- Avoid false health findings when a configured percentage threshold is zero.
|
|
88
|
+
- Reject malformed public BigQuery results and irrelevant null-filter options.
|
|
89
|
+
- Validate configured realtime fields without sending them through the
|
|
90
|
+
standard-only compatibility endpoint.
|
|
91
|
+
- Reject date flags on compatibility checks instead of accepting options the
|
|
92
|
+
Google compatibility request cannot use.
|
|
93
|
+
- Refuse partial portfolio CSV output and reject BigQuery results whose actual
|
|
94
|
+
processed bytes exceed the configured cap.
|
|
95
|
+
- Reject oversized Data API cells and responses that exceed the requested row
|
|
96
|
+
limit so malformed remote output cannot bypass a bounded report or export.
|
|
97
|
+
- Reject compatibility responses that omit, duplicate, or substitute requested
|
|
98
|
+
fields instead of treating an incomplete Google response as compatible.
|
|
99
|
+
|
|
100
|
+
### Security
|
|
101
|
+
|
|
102
|
+
- Every MCP tool is explicitly annotated read-only and non-destructive; the
|
|
103
|
+
server contains no plan, apply, create, update, delete, archive, credential,
|
|
104
|
+
or key-path tool.
|
|
105
|
+
- Starting MCP, listing local profiles, and switching profiles perform no
|
|
106
|
+
Google network access or credential loading.
|
|
107
|
+
- MCP input schemas are strict and bounded, tool errors are redacted, and
|
|
108
|
+
unexpected exceptions do not expose internal details.
|
|
109
|
+
- Change-history output omits actor email and full before/after resources.
|
|
110
|
+
- Optional BigQuery support has no arbitrary-SQL entry point, uses only the
|
|
111
|
+
read-only OAuth scope, never returns funnel user identifiers, and applies
|
|
112
|
+
the configured billing cap to both dry-run and real query jobs.
|
|
113
|
+
|
|
6
114
|
## [0.2.0] - 2026-07-22
|
|
7
115
|
|
|
8
116
|
### Added
|
data/CONTRIBUTING.md
CHANGED
|
@@ -32,6 +32,7 @@ mounted read-only except for source edits.
|
|
|
32
32
|
- Do not introduce telemetry, a database, browser analytics, or boot-time
|
|
33
33
|
network access.
|
|
34
34
|
- Preserve the read-only default and saved-plan confirmation boundary.
|
|
35
|
+
- Keep MCP tools read-only; AI-driven mutation is outside the server boundary.
|
|
35
36
|
- Do not add ordinary delete/archive operations.
|
|
36
37
|
- Use immutable gem-owned public values and typed errors.
|
|
37
38
|
|
data/README.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Analytics Ops
|
|
2
2
|
|
|
3
|
+
[](https://rubygems.org/gems/analytics_ops)
|
|
4
|
+
[](https://github.com/nelsonchaves/analytics_ops/actions/workflows/ci.yml)
|
|
5
|
+
[](https://rubydoc.info/gems/analytics_ops)
|
|
6
|
+
[](LICENSE.txt)
|
|
7
|
+
|
|
3
8
|
**Google Analytics 4 configuration as code and reporting for Ruby and Rails.**
|
|
4
9
|
|
|
5
10
|
Analytics Ops gives you a review-first way to inspect GA4, detect drift, run
|
|
@@ -19,7 +24,7 @@ analytics-ops overview
|
|
|
19
24
|
|
|
20
25
|
`setup` lists the properties the service account can access, lets you choose
|
|
21
26
|
one, and creates the smallest valid `config/analytics_ops.yml` for the default
|
|
22
|
-
`production` profile. It securely remembers the
|
|
27
|
+
`production` profile. It securely remembers the connection, so every later
|
|
23
28
|
command works without another authentication option. You do not need
|
|
24
29
|
`gcloud`, browser authorization, a property ID, or hand-written YAML.
|
|
25
30
|
|
|
@@ -27,6 +32,20 @@ command works without another authentication option. You do not need
|
|
|
27
32
|
traffic acquisition, landing pages, and devices for the previous 28 complete
|
|
28
33
|
days. Both commands are read-only in Google Analytics.
|
|
29
34
|
|
|
35
|
+
After setup, everyday use stays short:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
analytics-ops report traffic --last 7
|
|
39
|
+
analytics-ops overview --compare
|
|
40
|
+
analytics-ops portfolio
|
|
41
|
+
analytics-ops health
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`--last`, `--from`, `--to`, and `--compare` make date changes easy.
|
|
45
|
+
`portfolio` shows users, sessions, and key events across every configured
|
|
46
|
+
property. `health` checks traffic, expected events, attribution quality,
|
|
47
|
+
quota, and configuration drift without changing GA4.
|
|
48
|
+
|
|
30
49
|
## Google Analytics setup (first time only)
|
|
31
50
|
|
|
32
51
|
The setup is easier when you keep these four separate things in mind:
|
|
@@ -148,7 +167,7 @@ For an application that uses Bundler, add:
|
|
|
148
167
|
|
|
149
168
|
```ruby
|
|
150
169
|
# Gemfile
|
|
151
|
-
gem "analytics_ops", "~> 0.
|
|
170
|
+
gem "analytics_ops", "~> 0.3", group: :development
|
|
152
171
|
```
|
|
153
172
|
|
|
154
173
|
Then run:
|
|
@@ -166,7 +185,7 @@ Setup:
|
|
|
166
185
|
- lists the GA4 properties the service account can access
|
|
167
186
|
- lets you choose a property
|
|
168
187
|
- creates `config/analytics_ops.yml`
|
|
169
|
-
- remembers only
|
|
188
|
+
- remembers named connections and profile choices, using only key paths, in
|
|
170
189
|
`~/.config/analytics_ops/connection.json`
|
|
171
190
|
|
|
172
191
|
### H. Verify the connection with read-only commands
|
|
@@ -174,7 +193,9 @@ Setup:
|
|
|
174
193
|
```bash
|
|
175
194
|
bundle exec analytics-ops doctor
|
|
176
195
|
bundle exec analytics-ops properties
|
|
196
|
+
bundle exec analytics-ops profiles
|
|
177
197
|
bundle exec analytics-ops overview
|
|
198
|
+
bundle exec analytics-ops portfolio
|
|
178
199
|
bundle exec analytics-ops report traffic --json
|
|
179
200
|
bundle exec analytics-ops realtime
|
|
180
201
|
bundle exec analytics-ops audit
|
|
@@ -203,7 +224,7 @@ reviewed a saved plan.
|
|
|
203
224
|
- **Permission denied:** Grant Viewer or Editor inside GA4. A Google Cloud IAM
|
|
204
225
|
role does not grant access to GA4 data.
|
|
205
226
|
- **“This app is blocked” opens in a browser:** That is an obsolete OAuth
|
|
206
|
-
flow. Analytics Ops
|
|
227
|
+
flow. Analytics Ops uses a service-account JSON key and does not launch
|
|
207
228
|
browser authorization.
|
|
208
229
|
- **Key unavailable:** Run setup again with the key's new absolute path.
|
|
209
230
|
- **Key creation disabled:** An organization policy may prohibit downloaded
|
|
@@ -227,6 +248,75 @@ analytics-ops properties
|
|
|
227
248
|
analytics-ops discover # includes data streams
|
|
228
249
|
```
|
|
229
250
|
|
|
251
|
+
## More than one property or client
|
|
252
|
+
|
|
253
|
+
Setup can safely add another profile to the same configuration:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
analytics-ops setup \
|
|
257
|
+
--profile client_b \
|
|
258
|
+
--connection client_b \
|
|
259
|
+
--service-account /secure/client-b/service-account.json
|
|
260
|
+
|
|
261
|
+
analytics-ops profiles
|
|
262
|
+
analytics-ops connections
|
|
263
|
+
analytics-ops use client_b
|
|
264
|
+
analytics-ops overview
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The selected profile and its named Google connection are remembered for this
|
|
268
|
+
configuration. Nothing secret enters the YAML. Use `analytics-ops portfolio`
|
|
269
|
+
for one read-only totals table across all configured properties, or:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
analytics-ops portfolio report traffic
|
|
273
|
+
analytics-ops portfolio health
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Those commands isolate per-property failures and use bounded concurrency.
|
|
277
|
+
Optional profile labels make client and brand results easier to identify.
|
|
278
|
+
|
|
279
|
+
## Custom reports without Ruby
|
|
280
|
+
|
|
281
|
+
Discover valid property-specific fields, then run a report:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
analytics-ops reports
|
|
285
|
+
analytics-ops fields event
|
|
286
|
+
analytics-ops report custom \
|
|
287
|
+
--dimension eventName \
|
|
288
|
+
--metric eventCount \
|
|
289
|
+
--where eventName=purchase \
|
|
290
|
+
--last 7
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Saved report recipes can include nested filters and ordering in
|
|
294
|
+
`config/analytics_ops.yml`. Large exports use explicit bounded pagination:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
analytics-ops report traffic \
|
|
298
|
+
--all \
|
|
299
|
+
--max-rows 100000 \
|
|
300
|
+
--output tmp/traffic.csv
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
See [Reports](docs/reports.md) and [Analytics health](docs/health.md).
|
|
304
|
+
|
|
305
|
+
## Ask ChatGPT, Codex, or Claude
|
|
306
|
+
|
|
307
|
+
Analytics Ops includes a local MCP server with read-only tools only:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
analytics-ops mcp --config /absolute/path/to/config/analytics_ops.yml
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
It can list properties and report fields, audit configuration and governance,
|
|
314
|
+
check health, and read overviews, portfolios, saved or custom reports,
|
|
315
|
+
realtime events, and explicitly configured BigQuery/funnel results. It has no
|
|
316
|
+
`plan`, `apply`, create, update, or delete tool. See
|
|
317
|
+
[ChatGPT, Codex, and Claude](docs/ai-connections.md) for the copy-and-paste
|
|
318
|
+
connection commands and the data-sharing boundary.
|
|
319
|
+
|
|
230
320
|
## The safe change workflow
|
|
231
321
|
|
|
232
322
|
```text
|
|
@@ -257,21 +347,37 @@ what failed, and what remains.
|
|
|
257
347
|
| Command | Purpose | Remote writes? |
|
|
258
348
|
| --- | --- | --- |
|
|
259
349
|
| `analytics-ops setup --service-account PATH` | Connect, choose a property, and create minimal configuration | No |
|
|
350
|
+
| `analytics-ops connections` | List saved connection names without exposing key paths | No |
|
|
351
|
+
| `analytics-ops profiles` | List configured properties and their connections | No |
|
|
352
|
+
| `analytics-ops use NAME` | Select a profile and connection locally | No |
|
|
260
353
|
| `analytics-ops properties` | List accessible accounts and properties without configuration | No |
|
|
261
354
|
| `analytics-ops doctor` | Check configuration, credentials, APIs, property access, clients, and clock | No |
|
|
262
355
|
| `analytics-ops discover` | List accessible accounts, properties, and streams without configuration | No |
|
|
263
356
|
| `analytics-ops overview` | Show five useful reports in one bounded batch | No |
|
|
357
|
+
| `analytics-ops portfolio` | Compare totals across every configured property | No |
|
|
358
|
+
| `analytics-ops portfolio report NAME` | Run one report across configured properties | No |
|
|
359
|
+
| `analytics-ops portfolio health` | Check every configured property with isolated failures | No |
|
|
360
|
+
| `analytics-ops health` | Check traffic, events, attribution quality, quota, and drift | No |
|
|
264
361
|
| `analytics-ops snapshot` | Print normalized managed remote state | No |
|
|
265
362
|
| `analytics-ops audit` | Show drift without writing a plan file | No |
|
|
266
363
|
| `analytics-ops plan --output FILE` | Review and save deterministic changes | No |
|
|
267
364
|
| `analytics-ops apply FILE` | Apply one reviewed, non-stale plan | **Yes** |
|
|
268
365
|
| `analytics-ops verify` | Check convergence | No |
|
|
269
366
|
| `analytics-ops report NAME` | Run a built-in standard report | No |
|
|
367
|
+
| `analytics-ops reports` | List built-in and saved report recipes | No |
|
|
368
|
+
| `analytics-ops fields SEARCH` | Find property-aware dimensions and metrics | No |
|
|
369
|
+
| `analytics-ops compatibility NAME` | Check whether a report's fields work together | No |
|
|
270
370
|
| `analytics-ops realtime` | Run the realtime-events report | No |
|
|
371
|
+
| `analytics-ops governance` | Audit broader Alpha GA4 settings | No |
|
|
372
|
+
| `analytics-ops changes` | Read de-identified recent configuration changes | No |
|
|
373
|
+
| `analytics-ops access` | Read aggregate Data API access-quota usage | No |
|
|
374
|
+
| `analytics-ops raw NAME` | Run an optional fixed, cost-capped BigQuery recipe | No |
|
|
375
|
+
| `analytics-ops funnel NAME` | Run an optional aggregate ordered funnel | No |
|
|
376
|
+
| `analytics-ops mcp` | Start the strictly read-only local AI connection | No |
|
|
271
377
|
| `analytics-ops schema --format json` | Print the configuration schema | No |
|
|
272
378
|
|
|
273
|
-
Use `--json` or `--format json` for automation. CSV is available
|
|
274
|
-
|
|
379
|
+
Use `--json` or `--format json` for automation. CSV is available for report
|
|
380
|
+
results, portfolio reports, and optional BigQuery/funnel results:
|
|
275
381
|
|
|
276
382
|
```bash
|
|
277
383
|
bundle exec analytics-ops report landing-pages --csv
|
|
@@ -280,6 +386,14 @@ bundle exec analytics-ops report landing-pages --csv
|
|
|
280
386
|
Friendly `traffic` and `landing-pages` aliases leave the original
|
|
281
387
|
`traffic_acquisition` and `landing_pages` names fully supported.
|
|
282
388
|
|
|
389
|
+
Choose report dates without editing Ruby:
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
analytics-ops report traffic --last 7
|
|
393
|
+
analytics-ops report traffic --from 2026-07-01 --to 2026-07-07
|
|
394
|
+
analytics-ops overview --last 30 --compare
|
|
395
|
+
```
|
|
396
|
+
|
|
283
397
|
See [Commands](docs/commands.md) for every option and exit status.
|
|
284
398
|
|
|
285
399
|
## IDs: the quick distinction
|
|
@@ -309,8 +423,14 @@ plan.write("tmp/ga4-plan.json")
|
|
|
309
423
|
report = workspace.report("calculator_completions")
|
|
310
424
|
report.rows.each { |row| puts row.fetch("eventCount") }
|
|
311
425
|
|
|
426
|
+
ranges = AnalyticsOps::Reports::Period.resolve(last_days: 7, compare: true)
|
|
427
|
+
comparison = workspace.report("traffic", date_ranges: ranges)
|
|
428
|
+
|
|
312
429
|
overview = workspace.overview
|
|
313
430
|
puts overview.report("overview_totals").rows
|
|
431
|
+
|
|
432
|
+
health = workspace.health
|
|
433
|
+
puts health.findings.map(&:message)
|
|
314
434
|
```
|
|
315
435
|
|
|
316
436
|
Loading the gem, loading YAML, and booting Rails do not contact Google.
|
|
@@ -324,12 +444,14 @@ gem "analytics_ops", require: "analytics_ops/rails", group: :development
|
|
|
324
444
|
```
|
|
325
445
|
|
|
326
446
|
```bash
|
|
447
|
+
bin/rails generate analytics_ops:install
|
|
327
448
|
bundle exec analytics-ops setup \
|
|
328
|
-
--profile development \
|
|
329
449
|
--service-account /absolute/path/to/service-account.json
|
|
330
|
-
bin/rails generate analytics_ops:install
|
|
331
450
|
bin/rake analytics:doctor
|
|
332
451
|
bin/rake analytics:overview
|
|
452
|
+
bin/rake analytics:portfolio
|
|
453
|
+
bin/rake analytics:health
|
|
454
|
+
bin/rake analytics:governance
|
|
333
455
|
bin/rake 'analytics:report[traffic_acquisition]'
|
|
334
456
|
```
|
|
335
457
|
|
|
@@ -340,12 +462,14 @@ boot-time network calls. See [Rails integration](docs/rails.md).
|
|
|
340
462
|
## What Analytics Ops does not do
|
|
341
463
|
|
|
342
464
|
- It does not inject browser analytics or manage consent banners.
|
|
343
|
-
- It stores only
|
|
344
|
-
it never copies
|
|
465
|
+
- It stores only named service-account key paths in a mode-`0600` user file;
|
|
466
|
+
it never copies keys, tokens, or report rows.
|
|
345
467
|
- It does not delete accounts, properties, streams, or unmanaged resources.
|
|
346
468
|
- It does not claim to manage settings that Google exposes only in the UI.
|
|
347
|
-
- Experimental
|
|
348
|
-
|
|
469
|
+
- Experimental governance features are read-only and never enter ordinary
|
|
470
|
+
plans or apply.
|
|
471
|
+
- Optional BigQuery support runs only fixed `SELECT` recipes behind a required
|
|
472
|
+
dry run and byte cap; it does not accept arbitrary SQL.
|
|
349
473
|
|
|
350
474
|
## Documentation
|
|
351
475
|
|
|
@@ -355,7 +479,11 @@ boot-time network calls. See [Rails integration](docs/rails.md).
|
|
|
355
479
|
| [Live smoke test](docs/live-smoke-test.md) | Real-app read-only verification and release gate |
|
|
356
480
|
| [Configuration](docs/configuration.md) | Complete strict YAML contract |
|
|
357
481
|
| [Commands](docs/commands.md) | CLI syntax, formats, and exit statuses |
|
|
358
|
-
| [Reports](docs/reports.md) | Built-in
|
|
482
|
+
| [Reports](docs/reports.md) | Built-in, saved, custom, and exported reports |
|
|
483
|
+
| [Analytics health](docs/health.md) | Deterministic traffic and data-quality checks |
|
|
484
|
+
| [Governance](docs/governance.md) | Experimental read-only Admin checks |
|
|
485
|
+
| [BigQuery and funnels](docs/bigquery.md) | Optional cost-capped raw-data recipes |
|
|
486
|
+
| [ChatGPT, Codex, and Claude](docs/ai-connections.md) | Strictly read-only local AI connection |
|
|
359
487
|
| [Rails](docs/rails.md) | Generator and Rake tasks |
|
|
360
488
|
| [Safety](docs/safety.md) | Plans, stale-state protection, rollback, and credentials |
|
|
361
489
|
| [Plan format](docs/plan-format.md) | Version-1 JSON contract |
|
data/SECURITY.md
CHANGED
|
@@ -40,12 +40,21 @@ Analytics Ops:
|
|
|
40
40
|
- rejects stale and cross-property plans
|
|
41
41
|
- excludes delete/archive operations from ordinary plans
|
|
42
42
|
- never logs report results automatically
|
|
43
|
+
- exposes only read-only, non-destructive tools through MCP and has no AI
|
|
44
|
+
mutation tool
|
|
45
|
+
- validates custom report fields against live property metadata and
|
|
46
|
+
compatibility before execution
|
|
47
|
+
- omits actor email and full resource bodies from change-history output
|
|
48
|
+
- allows only fixed optional BigQuery SELECT recipes, with a dry run,
|
|
49
|
+
mandatory byte cap, bounded rows, and no arbitrary SQL input
|
|
43
50
|
- has no telemetry, private-key/token store, database, or browser injection
|
|
44
51
|
- publishes through RubyGems Trusted Publishing without a stored API key
|
|
45
52
|
|
|
46
53
|
Plan files still describe operational configuration and are created with mode
|
|
47
|
-
0600. Report output may contain sensitive
|
|
48
|
-
under the operator's data policy.
|
|
54
|
+
0600. Report, governance, BigQuery, and funnel output may contain sensitive
|
|
55
|
+
aggregate data and should be handled under the operator's data policy. MCP
|
|
56
|
+
tool results are sent to the connected AI provider; credentials and saved key
|
|
57
|
+
paths are not.
|
|
49
58
|
|
|
50
59
|
## Operator responsibilities
|
|
51
60
|
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# ChatGPT, Codex, and Claude
|
|
2
|
+
|
|
3
|
+
Analytics Ops can let an AI read your Google Analytics data through the Model
|
|
4
|
+
Context Protocol (MCP). The connection is deliberately read-only.
|
|
5
|
+
|
|
6
|
+
The simple idea is:
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
ChatGPT, Codex, or Claude
|
|
10
|
+
→ local Analytics Ops MCP process
|
|
11
|
+
→ Google Analytics read-only APIs
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The AI can inspect and explain data. It cannot change GA4 through this
|
|
15
|
+
connection.
|
|
16
|
+
|
|
17
|
+
## Before connecting
|
|
18
|
+
|
|
19
|
+
Finish normal Analytics Ops setup first:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
analytics-ops doctor
|
|
23
|
+
analytics-ops overview
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Both should work. Then identify:
|
|
27
|
+
|
|
28
|
+
- the absolute Analytics Ops config path
|
|
29
|
+
- the command that starts Analytics Ops
|
|
30
|
+
|
|
31
|
+
For a Rails or Bundler application, the generated binstub is easiest:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
/Users/example/sites/my_app/bin/analytics-ops
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
All paths below are fake. Replace them with your own absolute paths.
|
|
38
|
+
|
|
39
|
+
## ChatGPT desktop and Codex
|
|
40
|
+
|
|
41
|
+
The ChatGPT desktop app, Codex CLI, and Codex IDE extension share local MCP
|
|
42
|
+
configuration on the same Codex host.
|
|
43
|
+
|
|
44
|
+
The fastest setup is:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
codex mcp add analytics-ops -- \
|
|
48
|
+
/Users/example/sites/my_app/bin/analytics-ops \
|
|
49
|
+
mcp \
|
|
50
|
+
--config /Users/example/sites/my_app/config/analytics_ops.yml
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Then restart ChatGPT desktop or Codex. In ChatGPT desktop, you can also open
|
|
54
|
+
**Settings → MCP servers → Add server**, choose **STDIO**, and enter the same
|
|
55
|
+
command and arguments.
|
|
56
|
+
|
|
57
|
+
Check the saved server with:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
codex mcp list
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Claude Code
|
|
64
|
+
|
|
65
|
+
Add the same local stdio server:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
claude mcp add --transport stdio --scope user analytics-ops -- \
|
|
69
|
+
/Users/example/sites/my_app/bin/analytics-ops \
|
|
70
|
+
mcp \
|
|
71
|
+
--config /Users/example/sites/my_app/config/analytics_ops.yml
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Restart Claude Code, then use `/mcp` to check the connection.
|
|
75
|
+
|
|
76
|
+
## What you can ask
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
|
|
80
|
+
- “Show my Analytics overview for the last 7 complete days.”
|
|
81
|
+
- “Compare traffic with the preceding 30 days.”
|
|
82
|
+
- “Which landing pages have the most sessions?”
|
|
83
|
+
- “Show totals across all configured properties.”
|
|
84
|
+
- “Check every property for a large traffic drop.”
|
|
85
|
+
- “Find report fields related to revenue.”
|
|
86
|
+
- “Run an event-count report filtered to purchases.”
|
|
87
|
+
- “Check whether my required data-redaction settings are enabled.”
|
|
88
|
+
- “Audit my GA4 configuration and explain any drift.”
|
|
89
|
+
- “Are events arriving in realtime?”
|
|
90
|
+
|
|
91
|
+
The AI can call these tools:
|
|
92
|
+
|
|
93
|
+
- list local profiles and connection names
|
|
94
|
+
- list accessible GA4 properties
|
|
95
|
+
- run doctor
|
|
96
|
+
- read a normalized configuration snapshot
|
|
97
|
+
- audit configuration drift
|
|
98
|
+
- list built-in and configured report recipes
|
|
99
|
+
- search property-aware dimensions and metrics
|
|
100
|
+
- check report-field compatibility
|
|
101
|
+
- run bounded built-in, configured, or one-off custom reports
|
|
102
|
+
- run overview, generalized portfolio reports, and portfolio health
|
|
103
|
+
- run deterministic traffic and data-quality health checks
|
|
104
|
+
- audit experimental governance settings
|
|
105
|
+
- read an aggregate Data API quota-access report without user email or IP
|
|
106
|
+
- read realtime event counts
|
|
107
|
+
- run a fixed cost-capped BigQuery recipe or configured aggregate funnel when
|
|
108
|
+
that optional feature is present
|
|
109
|
+
|
|
110
|
+
There are 21 tools. Every one is explicitly marked read-only and
|
|
111
|
+
non-destructive. BigQuery tools accept only fixed recipe or funnel names; they
|
|
112
|
+
do not accept SQL.
|
|
113
|
+
|
|
114
|
+
## What the AI cannot do
|
|
115
|
+
|
|
116
|
+
The MCP server has no tools for:
|
|
117
|
+
|
|
118
|
+
- `plan`
|
|
119
|
+
- `apply`
|
|
120
|
+
- create or update
|
|
121
|
+
- delete or archive
|
|
122
|
+
- reading a credential file or key path
|
|
123
|
+
- reading user-email change history
|
|
124
|
+
- arbitrary SQL
|
|
125
|
+
|
|
126
|
+
Every tool is marked read-only and non-destructive in MCP metadata. Starting
|
|
127
|
+
the server does not load a credential or contact Google. Google access begins
|
|
128
|
+
only after the AI calls a read tool.
|
|
129
|
+
|
|
130
|
+
If a recommended change looks useful, leave the AI connection and use the
|
|
131
|
+
normal human-reviewed CLI workflow:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
analytics-ops plan --output tmp/ga4-plan.json
|
|
135
|
+
less tmp/ga4-plan.json
|
|
136
|
+
analytics-ops apply tmp/ga4-plan.json
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The AI cannot perform that final command through MCP.
|
|
140
|
+
|
|
141
|
+
## Privacy boundary
|
|
142
|
+
|
|
143
|
+
The service-account JSON key, access token, authorization header, and saved
|
|
144
|
+
key path stay local and are never returned by an MCP tool.
|
|
145
|
+
|
|
146
|
+
Requested tool results do go to the connected AI product. Those results can
|
|
147
|
+
include:
|
|
148
|
+
|
|
149
|
+
- GA4 property and account names
|
|
150
|
+
- property IDs
|
|
151
|
+
- configuration values
|
|
152
|
+
- traffic, event, and landing-page report rows
|
|
153
|
+
- optional BigQuery recipe and aggregate funnel rows
|
|
154
|
+
|
|
155
|
+
Connect only an AI account or workspace whose data policy is appropriate for
|
|
156
|
+
your analytics information. Do not ask the model to repeat or store sensitive
|
|
157
|
+
report data unnecessarily.
|
|
158
|
+
|
|
159
|
+
## ChatGPT on the web
|
|
160
|
+
|
|
161
|
+
Local stdio is the easiest route for ChatGPT desktop, Codex, and Claude Code.
|
|
162
|
+
ChatGPT on the web does not read your local MCP configuration directly.
|
|
163
|
+
|
|
164
|
+
If web access is required, OpenAI's official
|
|
165
|
+
[Secure MCP Tunnel](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels)
|
|
166
|
+
can forward an OpenAI-hosted tunnel to the same local stdio command over an
|
|
167
|
+
outbound-only connection. It requires a Platform tunnel ID, a runtime API key,
|
|
168
|
+
the `tunnel-client`, and the appropriate ChatGPT workspace permissions. This
|
|
169
|
+
is an optional advanced deployment; Analytics Ops does not need a public
|
|
170
|
+
server or inbound firewall port.
|
|
171
|
+
|
|
172
|
+
## Troubleshooting
|
|
173
|
+
|
|
174
|
+
- **Server starts and appears to hang:** Correct. Stdio MCP waits silently for
|
|
175
|
+
protocol messages. The AI client starts and controls it.
|
|
176
|
+
- **Config not found:** Use an absolute `--config` path.
|
|
177
|
+
- **Gem not found:** Use the application's absolute `bin/analytics-ops`
|
|
178
|
+
binstub, or install the gem globally.
|
|
179
|
+
- **No properties:** Add the exact service-account email to the correct GA4
|
|
180
|
+
account or property.
|
|
181
|
+
- **More than one connection:** Run `analytics-ops profiles` and
|
|
182
|
+
`analytics-ops use PROFILE --connection NAME`.
|
|
183
|
+
- **Changed MCP settings:** Restart the AI client.
|
|
184
|
+
- **Need to inspect protocol tools:** `codex mcp list` or Claude Code `/mcp`
|
|
185
|
+
shows the configured local server.
|
|
186
|
+
|
|
187
|
+
Official client references:
|
|
188
|
+
|
|
189
|
+
- [OpenAI MCP documentation](https://learn.chatgpt.com/docs/extend/mcp)
|
|
190
|
+
- [OpenAI Secure MCP Tunnel](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels)
|
|
191
|
+
- [Claude Code MCP documentation](https://code.claude.com/docs/en/mcp)
|
|
192
|
+
- [Model Context Protocol transports](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports)
|