feat(web-analytics): Add weekly web analytics summarization to MCP#55403
feat(web-analytics): Add weekly web analytics summarization to MCP#55403jordanm-posthog merged 11 commits intomasterfrom
Conversation
|
| days: zod | ||
| .number() | ||
| .default(webAnalyticsWeeklyDigestQueryDaysDefault) | ||
| .describe('Lookback window in days (1–90). Defaults to 7.'), |
There was a problem hiding this comment.
Missing min/max constraints on
days
The backend enforces min_value=1, max_value=90 on days (_DigestQuerySerializer), but the Zod schema only adds .default(7) with no .min(1).max(90). An MCP client passing e.g. days=0 or days=200 will get a 400 from the API rather than a client-side validation error with a helpful message.
| days: zod | |
| .number() | |
| .default(webAnalyticsWeeklyDigestQueryDaysDefault) | |
| .describe('Lookback window in days (1–90). Defaults to 7.'), | |
| days: zod | |
| .number() | |
| .min(1) | |
| .max(90) | |
| .default(webAnalyticsWeeklyDigestQueryDaysDefault) | |
| .describe('Lookback window in days (1–90). Defaults to 7.'), |
Prompt To Fix With AI
This is a comment left during a code review.
Path: services/mcp/src/generated/web_analytics/api.ts
Line: 33-36
Comment:
**Missing min/max constraints on `days`**
The backend enforces `min_value=1, max_value=90` on `days` (`_DigestQuerySerializer`), but the Zod schema only adds `.default(7)` with no `.min(1).max(90)`. An MCP client passing e.g. `days=0` or `days=200` will get a `400` from the API rather than a client-side validation error with a helpful message.
```suggestion
days: zod
.number()
.min(1)
.max(90)
.default(webAnalyticsWeeklyDigestQueryDaysDefault)
.describe('Lookback window in days (1–90). Defaults to 7.'),
```
How can I resolve this? If you propose a fix, please make it concise.| def test_returns_digest_shape(self): | ||
| with freeze_time(QUERY_TIMESTAMP): | ||
| _create_person(team_id=self.team.pk, distinct_ids=["user_1"]) | ||
| _create_pageview(self.team, distinct_id="user_1", url="https://example.com/", timestamp="2025-01-25") | ||
| flush_persons_and_events() | ||
|
|
||
| response = self.client.get(f"/api/environments/{self.team.id}/web_analytics/weekly_digest/") | ||
|
|
||
| assert response.status_code == status.HTTP_200_OK | ||
| data = response.json() | ||
| assert set(data.keys()) == { | ||
| "visitors", | ||
| "pageviews", | ||
| "sessions", | ||
| "bounce_rate", | ||
| "avg_session_duration", | ||
| "top_pages", | ||
| "top_sources", | ||
| "goals", | ||
| "dashboard_url", | ||
| } | ||
| assert set(data["visitors"].keys()) == {"current", "previous", "change"} | ||
| assert isinstance(data["top_pages"], list) | ||
| assert isinstance(data["top_sources"], list) | ||
| assert isinstance(data["goals"], list) | ||
| assert "/web" in data["dashboard_url"] | ||
|
|
||
| def test_empty_team_returns_zero_metrics(self): | ||
| with freeze_time(QUERY_TIMESTAMP): | ||
| response = self.client.get(f"/api/environments/{self.team.id}/web_analytics/weekly_digest/") | ||
|
|
||
| assert response.status_code == status.HTTP_200_OK | ||
| data = response.json() | ||
| assert data["visitors"]["current"] == 0 | ||
| assert data["pageviews"]["current"] == 0 | ||
| assert data["top_pages"] == [] | ||
| assert data["top_sources"] == [] | ||
| assert data["goals"] == [] | ||
|
|
||
| def test_cannot_read_other_teams_digest(self): | ||
| other_org = Organization.objects.create(name="Other Org") | ||
| other_team = Team.objects.create(organization=other_org, name="Other Team") | ||
|
|
||
| response = self.client.get(f"/api/environments/{other_team.id}/web_analytics/weekly_digest/") | ||
|
|
||
| assert response.status_code in (status.HTTP_403_FORBIDDEN, status.HTTP_404_NOT_FOUND) |
There was a problem hiding this comment.
No parametrized tests for
days / compare parameters
The new days and compare query parameters are exercised nowhere in the test suite — only the default-parameter path is covered. Given the team's preference for parametrized tests, consider adding a test like:
@pytest.mark.parametrize("days,compare", [(1, True), (30, False), (90, True)])
def test_custom_days_and_compare(self, days, compare):
with freeze_time(QUERY_TIMESTAMP):
response = self.client.get(
f"/api/environments/{self.team.id}/web_analytics/weekly_digest/",
{"days": days, "compare": compare},
)
assert response.status_code == status.HTTP_200_OKAlso worth adding a test for boundary-violation inputs (days=0, days=91) to confirm the 400 response.
Prompt To Fix With AI
This is a comment left during a code review.
Path: products/web_analytics/backend/test/test_api.py
Line: 32-77
Comment:
**No parametrized tests for `days` / `compare` parameters**
The new `days` and `compare` query parameters are exercised nowhere in the test suite — only the default-parameter path is covered. Given the team's preference for parametrized tests, consider adding a test like:
```python
@pytest.mark.parametrize("days,compare", [(1, True), (30, False), (90, True)])
def test_custom_days_and_compare(self, days, compare):
with freeze_time(QUERY_TIMESTAMP):
response = self.client.get(
f"/api/environments/{self.team.id}/web_analytics/weekly_digest/",
{"days": days, "compare": compare},
)
assert response.status_code == status.HTTP_200_OK
```
Also worth adding a test for boundary-violation inputs (`days=0`, `days=91`) to confirm the 400 response.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
lricoy
left a comment
There was a problem hiding this comment.
My only nit is that is not quite a weekly digest and more of a configurable web analytics summary
Great reaction!
VojtechBartos
left a comment
There was a problem hiding this comment.
Looks great 👍 could you please also make sure it's covered with the integrations tests? Thanks!
MCP UI Apps size report
|
Query snapshots: Backend query snapshots updatedChanges: 1 snapshots (0 modified, 1 added, 0 deleted) What this means:
Next steps:
|
|
Size Change: 0 B Total Size: 129 MB ℹ️ View Unchanged
|
…osthog into jordanm-posthog/addDigestToMCP
Problem
There is currently no good way to get Web Analytics data via MCP
Changes
Adds a new API that can be called via MCP to get data similar to the Weekly Web Analytics Digest

How did you test this code?
Locally
👉 Stay up-to-date with PostHog coding conventions for a smoother review.
Publish to changelog?
Docs update