Integrating a product with PostHog AI

SkillMedia

Wire a PostHog product surface into the PostHog AI agent from the frontend. Use when attaching scene or entity context, injecting instructions to steer the agent, reacting to the agent calling a tool, applying an agent edit back into an open form, or rendering a product tool card in a thread.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Integrating a product with PostHog AI skill

What this skill tells your AI

The instructions your AI receives, as published by posthog/posthog in .agents/skills/integrating-with-posthog-ai/SKILL.md and read by ahel’s review.

The user-facing doc is products/posthog_ai/README.md. This skill is how to do the work: pick the right seam, avoid the two mistakes that matter, and stop before the deep end.

First: is this a frontend job at all?

There is no backend integration API. An integration has two halves, and the frontend half depends on the backend half:

  1. Capability — the agent reads and writes your entities through MCP tools. If no tool can resolve the thing you want to talk about, start there: /implementing-mcp-tools, products/<name>/mcp/tools.yaml.
  2. Cooperation — the frontend seams below.

Injected context carries references, not data. A reference the agent cannot resolve with a tool is a dead end, so confirm the tool exists before you inject the ref.

Pick the seam

The jobSeamImport fromUse it?Reference
The agent should know what the user is looking at, including unsaved workAttached contextapi/logicsRecommendedinjecting-context.md
Steer how the agent behaves here — which tools to prefer, what "this" means on this page, or hand it a skillTrusted instructionsapi/logicsRecommendedinjecting-instructions.md
The page should update when the agent does somethingTool-event busapi/logicsRecommendedreacting-to-tool-calls.md
Your tool's calls should render as a real card, not the generic fallbackTool registryapi/toolsRecommendedrendering-widgets.md
Build your own agent UI out of the thread, composer, and stream primitivesRun primitivesapi/runSurface, api/primitivesNot recommended — see belowproducts/posthog_ai/frontend/README.md

The first four are the product integration. The fifth exists for the three surfaces that host an agent run — the Max scene, the signals inbox, and the tasks runner — not for products that want to cooperate with one. Reaching for it by default is the main way this goes wrong.

Whole scene at once: useSceneAgentPanel({ sceneKey, contextItems, headlines }) from frontend/src/scenes/max/useSceneAgentPanel.ts bundles context, welcome headlines, and gated auto-open of the side panel. Start there for a scene; drop to the individual hooks for one component. products/workflows/frontend/Workflows/WorkflowScene.tsx is the exemplar.

The import rule

Always a domain-scoped api/<module> entry, never a deep path, and there is deliberately no root barrel:

import { useAttachedContext, useMcpToolApplyBack } from 'products/posthog_ai/frontend/api/logics'

Pick the narrowest module. api/logics and api/types are headless; api/primitives drags in markdown and virtualization; api/tools initializes the registry from built-ins and the central manifest at module load, so importing it is a side effect that is not tree-shaken. A status badge that imports the wrong tier doubles its chunk.

The two mistakes that matter

1. Putting variable data in trusted context. type: 'instructions' items land in <posthog_trusted_context> — guidance the agent follows. Everything else lands in <posthog_untrusted_context>, behind hardening prose. So:

  • Instructions carry only your own build-time strings. Never a user-entered name, an ingested value, or anything interpolated from one. A crafted entity name in trusted context is a prompt injection against whoever reads the thread next.
  • Untrusted context is where user data belongs, and injecting it freely is the point — the user's unsaved editor state is usually the most useful thing you have.
  • If a pointer must vary (which id is open, which step is selected), put it on an untrusted item and have the static instruction refer to it by field name.

2. Sending object shapes instead of identifiers. The context block rides on every message in the conversation. Send { type, key, label } and let the agent fetch details through your MCP tools. The exception is unsaved state the agent cannot fetch — budget it (workflowAgentContext.ts caps at 64k chars and elides rather than truncates) and redact secrets, which sit in cleartext in live form state even though saved secrets never reach the frontend.

Stop at seam 4

The run primitives are public — RunSurface, ReadonlyRunSurface, EmbeddedRunner, the Thread atoms, runStreamLogic — but building a custom agent UI on them is not a product integration. It means owning stream binding, composer and queue state, permission routing, and the eager-vs-lazy tier choice that decides your bundle size. There is no default layout.

If that is genuinely the task, read products/posthog_ai/frontend/README.md and AGENTS.md first, and copy one of the two reference implementations (the signals inbox detail views, or scenes/TaskTracker/components/TaskRunChat.tsx) rather than composing from scratch.

Do not touch

  • The LangGraph runtime is frozen. No new useMaxTool registrations, no new MaxUIContext fields, no new maxContext selectors on scene logics. Use attached context and the tool-event bus instead.
  • The coupling gate. Nothing under products/posthog_ai/frontend may import scenes/max, maxThreadLogic, maxContextLogic, or the conversations API. Max is a consumer of that surface, not a dependency of it. If the surface is missing something you need, lift it to a generic prop or selector there — never special-case a consumer inside it.

Verify

  • pnpm --filter=@posthog/frontend typescript:check for the types.
  • Run the app and open the side panel on your page. Context is invisible by design, so check it landed: an attached item that is not hidden shows as a chip in the composer's context bar, and the agent should be able to answer a question about the thing you attached without being told its id.
  • For a reactivity seam, ask the agent to make the change and confirm the open page updates. Then reload the page — your handler must not fire again, because replay events are suppressed by default.

Signals

GitHub stars
40k
Forks
3k
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
integrating-with-posthog-ai
Source
github.com/posthog/posthog