feat(marketing-analytics): expose conversion goal write tools over MCP#73033
feat(marketing-analytics): expose conversion goal write tools over MCP#73033jabahamondes wants to merge 3 commits into
Conversation
Enables create, update and delete conversion goal as MCP tools, each behind marketing_analytics:write and the marketing-analytics-mcp feature flag. Sits on top of the granular write endpoints.
🤖 CI report✅ Bundle size — no changeUncompressed size of every built Total: 64.22 MiB · no change No file changed by more than 1000 B. Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report ✅ Eager graph — within budgetHow much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy
🟢 Largest files eagerly shipped from
|
| Size | File |
|---|---|
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 24.6 KiB | ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js |
| 6.3 KiB | ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js |
| 4.5 KiB | ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js |
| 3.9 KiB | ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js |
| 1.4 KiB | ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js |
| 1.3 KiB | src/RootErrorBoundary.tsx |
| 912 B | ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js |
| 789 B | src/scenes/ChunkLoadErrorBoundary.tsx |
| 762 B | src/index.tsx |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 281.3 KiB | ../node_modules/.pnpm/posthog-js@1.404.0/node_modules/posthog-js/dist/rrweb.js |
| 267.7 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 235.5 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 223.2 KiB | ../node_modules/.pnpm/posthog-js@1.404.0/node_modules/posthog-js/dist/module.js |
| 164.0 KiB | src/queries/validators.js |
| 154.3 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 105.8 KiB | src/lib/api.ts |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
| 93.2 KiB | ../packages/quill/packages/quill/dist/index.js |
Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479
✅ Dist folder size — 🔺 +136 B (+0.0%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1341.59 MiB · 🔺 +136 B (+0.0%)
ℹ️ MCP UI apps size — 31 app(s), 16602.7 KB JS
Built size of each MCP UI app (main.js + styles.css).
| App | JS | CSS |
|---|---|---|
| debug | 599.5 KB | 187.4 KB |
| action | 457.8 KB | 187.4 KB |
| action-list | 564.3 KB | 187.4 KB |
| cohort | 456.8 KB | 187.4 KB |
| cohort-list | 563.3 KB | 187.4 KB |
| email-template | 456.6 KB | 187.4 KB |
| error-details | 472.4 KB | 187.4 KB |
| error-issue | 457.5 KB | 187.4 KB |
| error-issue-list | 564.2 KB | 187.4 KB |
| experiment | 561.5 KB | 187.4 KB |
| experiment-list | 565.1 KB | 187.4 KB |
| experiment-results | 563.2 KB | 187.4 KB |
| feature-flag | 567.1 KB | 187.4 KB |
| feature-flag-list | 570.9 KB | 187.4 KB |
| feature-flag-testing | 461.0 KB | 187.4 KB |
| insight-actors | 562.1 KB | 187.4 KB |
| invite-email-preview | 456.0 KB | 187.4 KB |
| llm-costs | 559.5 KB | 187.4 KB |
| session-recording | 458.6 KB | 187.4 KB |
| session-summary | 463.9 KB | 187.4 KB |
| survey | 458.4 KB | 187.4 KB |
| survey-global-stats | 562.2 KB | 187.4 KB |
| survey-list | 565.0 KB | 187.4 KB |
| survey-stats | 562.2 KB | 187.4 KB |
| trace-span | 457.2 KB | 187.4 KB |
| trace-span-list | 564.2 KB | 187.4 KB |
| workflow | 457.1 KB | 187.4 KB |
| workflow-list | 563.7 KB | 187.4 KB |
| query-results | 744.8 KB | 187.4 KB |
| render-ui | 825.3 KB | 187.4 KB |
| visual-review-snapshots | 461.6 KB | 187.4 KB |
Note
Top of the stack: #73012 → #72996 → #72798 → #72301. This is the payoff PR; the four below it are the foundation.
Problem
Marketing analytics has eight MCP tools and every one is read-only. An agent can diagnose your setup, list your goals, and suggest new ones, but it cannot actually create a goal. The suggest-utm-mappings tool even tells the model "applying mappings is a separate write tool" - a tool that was never written.
So the loop stays open. Max can tell you what to configure and then hand you back to the UI to do it by hand.
Changes
Three write tools on the marketing analytics MCP surface, one per endpoint added in #73012:
marketing-analytics-create-conversion-goalPOST conversion_goals/createmarketing_analytics:writemarketing-analytics-update-conversion-goalPATCH conversion_goals/{id}/updatemarketing_analytics:writemarketing-analytics-delete-conversion-goalDELETE conversion_goals/{id}/deletemarketing_analytics:writeAll three are gated behind the existing
marketing-analytics-mcpfeature flag, same as the read tools, and behindmarketing_analytics:write(which had no tools using it until now). Delete is annotateddestructive: true.This PR is almost entirely generated. The hand-written part is the three YAML entries in
products/marketing_analytics/mcp/tools.yaml; everything else ishogli build:openapioutput. The tool descriptions do the real work here, since they are what Max reads to decide when and how to call these. They point the model atmarketing-analytics-conversion-goalsfirst (names must be unique), spell out the goal shape andschema_map, and explain when to setcounts_as_customerandcounts_as_revenue.The whole point of the four PRs underneath is visible in the generated input schema: because #72996 typed the config and #73012 typed the endpoint bodies against the schema models, the create tool's parameters expand into the real
ConversionGoalFiltershape with per-field descriptions, instead of thez.unknown()an untyped body would have produced.How did you test this code?
All automated, run by me (Claude):
pnpm --filter=@posthog/mcp lint-tool-names- passes (all three names are under the 52-char limit)hogli build:openapi- clean, generated tools and schemas committedtool-definitions-all.jsonthat each tool carriesmarketing_analytics:writeand the right destructive/idempotent annotations, and that the create tool's input schema is the full goal shape rather than an opaque objectservices/mcpvitest suite - 2218 passing. The one red istool-schema-snapshotstiming out at its hardcoded 10s under full-suite parallel load; it passes in about 6s run on its own, and the content is correct. Flaky on timing, not on this change.No new tests. These tools are generated from YAML and the endpoints they call are covered by the 13 endpoint tests in #73012. The MCP generation pipeline itself is covered by the existing
services/mcpsuite. A hand-written test here would be testing the codegen, not this change.Automatic notifications
Docs update
Worth a follow-up once this stack lands: the marketing analytics docs and the MCP tool list should mention that conversion goals are now writable by agents. Holding it until the endpoints are actually merged.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
I directed this, Claude wrote it. Invoked
/implementing-mcp-tools.Two things surfaced during the work, both worth a reviewer's eye:
First, the skill guide (
implementing-mcp-tools.md) documents anmcp_versionYAML key for marking create/update/delete tools as v2-only. The actual Zod config schema inservices/mcp/scripts/yaml-config-schema.tsrejects that key with a strict-mode error, so the build fails if you follow the doc. I dropped the key to get a green build. The doc and the schema are out of sync - flagging it rather than papering over it, since the fix (add the key to the schema, or correct the doc) is a call for whoever owns the MCP framework.Second, this is a stacked PR and the rebase was not free. When CI auto-committed regenerated types onto the lower branches,
scaffold-yaml --sync-allhad already added my three operations to the YAML as disabled auto-named stubs, which collided with my enabled hand-named entries on rebase. I kept my versions and dropped the stubs. I also moved theproject-settings-updatetool-schema snapshot update down to #72996 where it belongs, since that snapshot changed because of the typed config in that PR, not because of anything here.