Skip to content

feat(marketing-analytics): expose conversion goal write tools over MCP#73033

Draft
jabahamondes wants to merge 3 commits into
javier/ma-conversion-goal-endpointfrom
javier/ma-mcp-write-tools
Draft

feat(marketing-analytics): expose conversion goal write tools over MCP#73033
jabahamondes wants to merge 3 commits into
javier/ma-conversion-goal-endpointfrom
javier/ma-mcp-write-tools

Conversation

@jabahamondes

Copy link
Copy Markdown
Contributor

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:

Tool Endpoint Scope
marketing-analytics-create-conversion-goal POST conversion_goals/create marketing_analytics:write
marketing-analytics-update-conversion-goal PATCH conversion_goals/{id}/update marketing_analytics:write
marketing-analytics-delete-conversion-goal DELETE conversion_goals/{id}/delete marketing_analytics:write

All three are gated behind the existing marketing-analytics-mcp feature flag, same as the read tools, and behind marketing_analytics:write (which had no tools using it until now). Delete is annotated destructive: 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 is hogli build:openapi output. 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 at marketing-analytics-conversion-goals first (names must be unique), spell out the goal shape and schema_map, and explain when to set counts_as_customer and counts_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 ConversionGoalFilter shape with per-field descriptions, instead of the z.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 committed
  • Verified in tool-definitions-all.json that each tool carries marketing_analytics:write and the right destructive/idempotent annotations, and that the create tool's input schema is the full goal shape rather than an opaque object
  • Full services/mcp vitest suite - 2218 passing. The one red is tool-schema-snapshots timing 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/mcp suite. A hand-written test here would be testing the codegen, not this change.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

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 an mcp_version YAML key for marking create/update/delete tools as v2-only. The actual Zod config schema in services/mcp/scripts/yaml-config-schema.ts rejects 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-all had 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 the project-settings-update tool-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.

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.
@jabahamondes jabahamondes self-assigned this Jul 22, 2026
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

Bundle size — no change

Uncompressed size of every built .js bundle, compared against the base branch.

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 budget

How 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 import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.23 MiB · 22 files no change ███░░░░░░░ 28.6% of 4.29 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
8.16 MiB · 2,988 files no change █████████░ 88.2% of 9.25 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx

Largest files eagerly shipped from src/index.tsx
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

@trunk-io

trunk-io Bot commented Jul 22, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant