fix(posthog): validate integration against live API docs, add self-hosted support + CRUD coverage (#5476)

* fix(posthog): validate integration against live API docs, add self-hosted support + CRUD coverage

Fix missing response.ok checks across ~36 tools that silently treated
error bodies as success. Fix delete_feature_flag ignoring failure
responses, evaluate_flags targeting the undocumented /decide endpoint
without the required api_key body field, and batch_events reporting a
hardcoded events_processed count.

Add self-hosted host support (utils.ts) alongside the existing US/EU
region selector, and complete CRUD coverage with 5 new tools:
update_insight, update_cohort, update_experiment, delete_survey,
create_dashboard.

* fix(posthog): remove dead response.ok checks, fix real schema/endpoint bugs

The prior commit added if (!response.ok) branches inside transformResponse
across ~40 tool files. This is dead code — tools/index.ts already throws
on non-2xx (and error-payload) responses before transformResponse is ever
invoked, so transformResponse only ever receives already-successful
responses. Reverted to the idiomatic no-error-branch pattern used
elsewhere in the codebase (e.g. hunter/email_verifier.ts), and added a
posthog-errors errorExtractor so the framework's own error throw surfaces
PostHog's real {type, code, detail, attr} error shape instead of a
generic status message.

Verified against PostHog's live OpenAPI schema, fixed real bugs:
- delete_feature_flag: DELETE always returns 405 (hard delete not
  allowed); switched to PATCH with deleted: true
- delete_person: no single-person DELETE endpoint exists; switched to
  POST .../persons/bulk_delete/ with ids: [personId]
- create_annotation: insight_short_id is read-only on create; the
  writable field is dashboard_id
- removed nonexistent fields (experiment variants, insight
  filters/saved) not present in the current schema
- added missing trailing slashes on feature-flag/experiment URLs

* fix(posthog): reject unsafe self-hosted host values (SSRF)

The self-hosted host field accepted any string and only stripped
trailing slashes before prepending https://, so a workflow could point
it at loopback/private/link-local addresses (e.g. cloud instance-
metadata endpoints) and the executor would make a real server-side
request to it. Reuse the shared validateExternalUrl SSRF guard, same
pattern already used for Convex's custom deployment URL
(tools/convex/utils.ts); the tool executor separately re-validates with
DNS resolution and pins the resolved IP for the actual request.

Also drop an unused params arg in evaluate_flags' headers function.

* fix(posthog): fail loudly instead of silently dropping data on bad JSON params

Several tools caught JSON.parse failures on user-supplied filter/query/
parameters strings and silently substituted {} or null, which would
wipe the corresponding field on the PATCH/create request instead of
surfacing an error (Cursor Bugbot flagged this for update_cohort,
update_experiment, update_insight; the same pattern existed in their
create_* / update_feature_flag / evaluate_flags counterparts, fixed for
consistency). Now throws a descriptive error, matching the existing
convention in tools/notion/query_database.ts.

Also fixes batch_events: the request body silently sent an empty batch
on invalid JSON, and transformResponse always reported "captured
successfully" even when PostHog's response indicated failure
(data.status !== 1).

* fix(posthog): mark region required for update_experiment

posthog_update_experiment was the only experiment operation missing
from the region field's required-condition list; the other three
(create/get/list) already require it. Region is unconditionally
visible on this block (no condition key gates it), so this was a
required-ness inconsistency rather than a functional bug, but users
updating an EU experiment should still be prompted to select the
region explicitly rather than relying on the default.

* fix(posthog): fix cross-field value leakage and false-success reports

Merges update_insight's query subblock into the same insightQuery id
already used by create_insight (Cursor flagged: it previously reused
the 'query' subblock id shared by posthog_query's HogQL field and the
cohort query fields, so switching operations could carry a stale
HogQL/cohort-JSON value into an insight PATCH). Matches the existing
merged-condition pattern used elsewhere in this block instead of
duplicating the subblock id.

Also fixes two more false-success reports in the same class as the
batch_events/delete_feature_flag fixes: capture_event now checks the
ingest response's status field (same {"status": 1} contract as
/batch/) instead of unconditionally returning success, and
delete_person now reports failure when persons_deleted is 0 instead of
always success: true.
This commit is contained in:
Waleed
2026-07-07 10:31:53 -07:00
committed by GitHub
parent 954fdd8595
commit a48ecd2538
54 changed files with 1792 additions and 384 deletions
+192 -23
View File
@@ -32,12 +32,15 @@ export const PostHogBlock: BlockConfig<PostHogResponse> = {
{ label: 'List Insights', id: 'posthog_list_insights' },
{ label: 'Get Insight', id: 'posthog_get_insight' },
{ label: 'Create Insight', id: 'posthog_create_insight' },
{ label: 'Update Insight', id: 'posthog_update_insight' },
{ label: 'List Dashboards', id: 'posthog_list_dashboards' },
{ label: 'Get Dashboard', id: 'posthog_get_dashboard' },
{ label: 'Create Dashboard', id: 'posthog_create_dashboard' },
{ label: 'List Actions', id: 'posthog_list_actions' },
{ label: 'List Cohorts', id: 'posthog_list_cohorts' },
{ label: 'Get Cohort', id: 'posthog_get_cohort' },
{ label: 'Create Cohort', id: 'posthog_create_cohort' },
{ label: 'Update Cohort', id: 'posthog_update_cohort' },
{ label: 'List Annotations', id: 'posthog_list_annotations' },
{ label: 'Create Annotation', id: 'posthog_create_annotation' },
// Feature Management
@@ -50,11 +53,13 @@ export const PostHogBlock: BlockConfig<PostHogResponse> = {
{ label: 'List Experiments', id: 'posthog_list_experiments' },
{ label: 'Get Experiment', id: 'posthog_get_experiment' },
{ label: 'Create Experiment', id: 'posthog_create_experiment' },
{ label: 'Update Experiment', id: 'posthog_update_experiment' },
// User Engagement
{ label: 'List Surveys', id: 'posthog_list_surveys' },
{ label: 'Get Survey', id: 'posthog_get_survey' },
{ label: 'Create Survey', id: 'posthog_create_survey' },
{ label: 'Update Survey', id: 'posthog_update_survey' },
{ label: 'Delete Survey', id: 'posthog_delete_survey' },
{ label: 'List Session Recordings', id: 'posthog_list_session_recordings' },
{ label: 'Get Session Recording', id: 'posthog_get_session_recording' },
{ label: 'List Recording Playlists', id: 'posthog_list_recording_playlists' },
@@ -95,6 +100,7 @@ export const PostHogBlock: BlockConfig<PostHogResponse> = {
'posthog_list_feature_flags',
// Experiments
'posthog_create_experiment',
'posthog_update_experiment',
'posthog_get_experiment',
'posthog_list_experiments',
// Data Management
@@ -113,12 +119,15 @@ export const PostHogBlock: BlockConfig<PostHogResponse> = {
'posthog_list_insights',
'posthog_get_insight',
'posthog_create_insight',
'posthog_update_insight',
'posthog_list_dashboards',
'posthog_get_dashboard',
'posthog_create_dashboard',
'posthog_list_actions',
'posthog_list_cohorts',
'posthog_get_cohort',
'posthog_create_cohort',
'posthog_update_cohort',
'posthog_list_annotations',
'posthog_create_annotation',
// Surveys & Recordings
@@ -126,6 +135,73 @@ export const PostHogBlock: BlockConfig<PostHogResponse> = {
'posthog_get_survey',
'posthog_create_survey',
'posthog_update_survey',
'posthog_delete_survey',
'posthog_list_session_recordings',
'posthog_get_session_recording',
'posthog_list_recording_playlists',
// Configuration
'posthog_list_projects',
'posthog_get_project',
'posthog_list_organizations',
'posthog_get_organization',
],
},
},
{
id: 'host',
title: 'Self-Hosted Host',
type: 'short-input',
placeholder: 'posthog.mycompany.com',
condition: {
field: 'operation',
value: [
// Feature Flags
'posthog_create_feature_flag',
'posthog_update_feature_flag',
'posthog_delete_feature_flag',
'posthog_get_feature_flag',
'posthog_list_feature_flags',
// Experiments
'posthog_create_experiment',
'posthog_update_experiment',
'posthog_get_experiment',
'posthog_list_experiments',
// Data Management
'posthog_list_property_definitions',
'posthog_get_property_definition',
'posthog_update_property_definition',
'posthog_list_event_definitions',
'posthog_get_event_definition',
'posthog_update_event_definition',
// Core Operations
'posthog_capture_event',
'posthog_batch_events',
'posthog_list_persons',
'posthog_get_person',
'posthog_delete_person',
'posthog_query',
'posthog_evaluate_flags',
// Analytics
'posthog_list_insights',
'posthog_get_insight',
'posthog_create_insight',
'posthog_update_insight',
'posthog_list_dashboards',
'posthog_get_dashboard',
'posthog_create_dashboard',
'posthog_list_actions',
'posthog_list_cohorts',
'posthog_get_cohort',
'posthog_create_cohort',
'posthog_update_cohort',
'posthog_list_annotations',
'posthog_create_annotation',
// Surveys & Recordings
'posthog_list_surveys',
'posthog_get_survey',
'posthog_create_survey',
'posthog_update_survey',
'posthog_delete_survey',
'posthog_list_session_recordings',
'posthog_get_session_recording',
'posthog_list_recording_playlists',
@@ -392,7 +468,7 @@ Return ONLY the JSON array.`,
placeholder: 'HogQL query or JSON object',
condition: {
field: 'operation',
value: 'posthog_create_cohort',
value: ['posthog_create_cohort', 'posthog_update_cohort'],
},
},
{
@@ -431,7 +507,10 @@ Return ONLY the JSON array.`,
title: 'Insight ID',
type: 'short-input',
placeholder: 'Insight ID',
condition: { field: 'operation', value: 'posthog_get_insight' },
condition: {
field: 'operation',
value: ['posthog_get_insight', 'posthog_update_insight'],
},
required: true,
},
{
@@ -442,12 +521,28 @@ Return ONLY the JSON array.`,
condition: { field: 'operation', value: 'posthog_get_dashboard' },
required: true,
},
{
id: 'pinned',
title: 'Pinned',
type: 'switch',
condition: { field: 'operation', value: 'posthog_create_dashboard' },
},
{
id: 'useTemplate',
title: 'Template',
type: 'short-input',
placeholder: 'Product analytics',
condition: { field: 'operation', value: 'posthog_create_dashboard' },
},
{
id: 'cohortId',
title: 'Cohort ID',
type: 'short-input',
placeholder: 'Cohort ID',
condition: { field: 'operation', value: 'posthog_get_cohort' },
condition: {
field: 'operation',
value: ['posthog_get_cohort', 'posthog_update_cohort'],
},
required: true,
},
{
@@ -470,7 +565,10 @@ Return ONLY the JSON array.`,
title: 'Experiment ID',
type: 'short-input',
placeholder: 'Experiment ID',
condition: { field: 'operation', value: 'posthog_get_experiment' },
condition: {
field: 'operation',
value: ['posthog_get_experiment', 'posthog_update_experiment'],
},
required: true,
},
{
@@ -480,7 +578,7 @@ Return ONLY the JSON array.`,
placeholder: 'Survey ID',
condition: {
field: 'operation',
value: ['posthog_get_survey', 'posthog_update_survey'],
value: ['posthog_get_survey', 'posthog_update_survey', 'posthog_delete_survey'],
},
required: true,
},
@@ -525,13 +623,17 @@ Return ONLY the JSON array.`,
field: 'operation',
value: [
'posthog_create_insight',
'posthog_update_insight',
'posthog_create_cohort',
'posthog_update_cohort',
'posthog_create_annotation',
'posthog_create_feature_flag',
'posthog_update_feature_flag',
'posthog_create_experiment',
'posthog_update_experiment',
'posthog_create_survey',
'posthog_update_survey',
'posthog_create_dashboard',
],
},
},
@@ -544,14 +646,18 @@ Return ONLY the JSON array.`,
field: 'operation',
value: [
'posthog_create_insight',
'posthog_update_insight',
'posthog_create_cohort',
'posthog_update_cohort',
'posthog_create_feature_flag',
'posthog_update_feature_flag',
'posthog_create_experiment',
'posthog_update_experiment',
'posthog_create_survey',
'posthog_update_survey',
'posthog_update_event_definition',
'posthog_update_property_definition',
'posthog_create_dashboard',
],
},
wandConfig: {
@@ -602,11 +708,12 @@ Return ONLY the description text.`,
condition: {
field: 'operation',
value: [
'posthog_create_insight',
'posthog_create_feature_flag',
'posthog_update_feature_flag',
'posthog_create_cohort',
'posthog_update_cohort',
'posthog_create_experiment',
'posthog_update_experiment',
],
},
wandConfig: {
@@ -648,21 +755,36 @@ Return ONLY the JSON object.`,
title: 'Query (JSON)',
type: 'long-input',
placeholder: '{"kind": "HogQLQuery", "query": "SELECT ..."}',
condition: { field: 'operation', value: 'posthog_create_insight' },
condition: {
field: 'operation',
value: ['posthog_create_insight', 'posthog_update_insight'],
},
},
{
id: 'dashboards',
title: 'Dashboard IDs (comma-separated)',
type: 'short-input',
placeholder: '123, 456, 789',
condition: { field: 'operation', value: 'posthog_create_insight' },
condition: {
field: 'operation',
value: ['posthog_create_insight', 'posthog_update_insight'],
},
},
{
id: 'insightTags',
title: 'Tags (comma-separated)',
type: 'short-input',
placeholder: 'analytics, revenue, important',
condition: { field: 'operation', value: 'posthog_create_insight' },
condition: {
field: 'operation',
value: ['posthog_create_insight', 'posthog_update_insight'],
},
},
{
id: 'favorited',
title: 'Favorited',
type: 'switch',
condition: { field: 'operation', value: 'posthog_update_insight' },
},
// Feature Flag fields
@@ -703,14 +825,20 @@ Return ONLY the JSON object.`,
title: 'Groups (JSON Array)',
type: 'long-input',
placeholder: '[{"properties": [...]}]',
condition: { field: 'operation', value: 'posthog_create_cohort' },
condition: { field: 'operation', value: ['posthog_create_cohort', 'posthog_update_cohort'] },
},
{
id: 'isStatic',
title: 'Static Cohort',
type: 'switch',
value: () => 'false',
condition: { field: 'operation', value: 'posthog_create_cohort' },
condition: { field: 'operation', value: ['posthog_create_cohort', 'posthog_update_cohort'] },
},
{
id: 'deleted',
title: 'Archive Cohort',
type: 'switch',
condition: { field: 'operation', value: 'posthog_update_cohort' },
},
// Annotation fields
@@ -789,21 +917,29 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
title: 'Parameters (JSON)',
type: 'long-input',
placeholder: '{"minimum_detectable_effect": 5}',
condition: { field: 'operation', value: 'posthog_create_experiment' },
condition: {
field: 'operation',
value: ['posthog_create_experiment', 'posthog_update_experiment'],
},
},
{
id: 'variants',
title: 'Variants (JSON)',
type: 'long-input',
placeholder: '{"control": 50, "test": 50}',
condition: { field: 'operation', value: 'posthog_create_experiment' },
id: 'archived',
title: 'Archived',
type: 'switch',
condition: {
field: 'operation',
value: ['posthog_update_experiment', 'posthog_update_survey'],
},
},
{
id: 'experimentStartDate',
title: 'Start Date (ISO 8601)',
type: 'short-input',
placeholder: '2024-01-01T00:00:00Z',
condition: { field: 'operation', value: 'posthog_create_experiment' },
condition: {
field: 'operation',
value: ['posthog_create_experiment', 'posthog_update_experiment'],
},
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
@@ -823,7 +959,10 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
title: 'End Date (ISO 8601)',
type: 'short-input',
placeholder: '2024-12-31T23:59:59Z',
condition: { field: 'operation', value: 'posthog_create_experiment' },
condition: {
field: 'operation',
value: ['posthog_create_experiment', 'posthog_update_experiment'],
},
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp based on the user's description.
@@ -1096,7 +1235,11 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'tag1, tag2, tag3',
condition: {
field: 'operation',
value: ['posthog_update_event_definition', 'posthog_update_property_definition'],
value: [
'posthog_update_event_definition',
'posthog_update_property_definition',
'posthog_create_dashboard',
],
},
},
@@ -1146,12 +1289,15 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
'posthog_list_insights',
'posthog_get_insight',
'posthog_create_insight',
'posthog_update_insight',
'posthog_list_dashboards',
'posthog_get_dashboard',
'posthog_create_dashboard',
'posthog_list_actions',
'posthog_list_cohorts',
'posthog_get_cohort',
'posthog_create_cohort',
'posthog_update_cohort',
'posthog_list_annotations',
'posthog_create_annotation',
// Feature Management
@@ -1164,11 +1310,13 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
'posthog_list_experiments',
'posthog_get_experiment',
'posthog_create_experiment',
'posthog_update_experiment',
// Engagement
'posthog_list_surveys',
'posthog_get_survey',
'posthog_create_survey',
'posthog_update_survey',
'posthog_delete_survey',
'posthog_list_session_recordings',
'posthog_get_session_recording',
'posthog_list_recording_playlists',
@@ -1222,11 +1370,19 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
params.property_type = params.propertyType
}
if (params.operation === 'posthog_create_insight' && params.insightQuery) {
if (
(params.operation === 'posthog_create_insight' ||
params.operation === 'posthog_update_insight') &&
params.insightQuery
) {
params.query = params.insightQuery
}
if (params.operation === 'posthog_create_insight' && params.insightTags) {
if (
(params.operation === 'posthog_create_insight' ||
params.operation === 'posthog_update_insight') &&
params.insightTags
) {
params.tags = params.insightTags
}
@@ -1234,7 +1390,10 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
params.distinctId = params.distinctIdFilter
}
if (params.operation === 'posthog_create_experiment') {
if (
params.operation === 'posthog_create_experiment' ||
params.operation === 'posthog_update_experiment'
) {
if (params.experimentStartDate) {
params.startDate = params.experimentStartDate
}
@@ -1272,6 +1431,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
region: { type: 'string', description: 'PostHog region (us or eu)' },
host: { type: 'string', description: 'Self-hosted PostHog instance host' },
projectApiKey: { type: 'string', description: 'Project API key for public endpoints' },
apiKey: { type: 'string', description: 'Personal API key for private endpoints' },
projectId: { type: 'string', description: 'PostHog project ID' },
@@ -1304,13 +1464,22 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
active: { type: 'boolean', description: 'Whether flag is active' },
rolloutPercentage: { type: 'number', description: 'Rollout percentage (0-100)' },
groups: { type: 'string', description: 'Cohort groups as JSON' },
isStatic: { type: 'boolean', description: 'Whether the cohort is static' },
deleted: { type: 'boolean', description: 'Whether to archive (soft-delete) the cohort' },
content: { type: 'string', description: 'Annotation content' },
dateMarker: { type: 'string', description: 'Annotation date' },
scope: { type: 'string', description: 'Annotation scope' },
featureFlagKey: { type: 'string', description: 'Feature flag key for experiment' },
parameters: { type: 'string', description: 'Experiment parameters as JSON' },
archived: { type: 'boolean', description: 'Whether to archive the experiment or survey' },
questions: { type: 'string', description: 'Survey questions as JSON array' },
surveyType: { type: 'string', description: 'Survey type (popover or api)' },
insightQuery: { type: 'string', description: 'Insight query as JSON' },
dashboards: { type: 'string', description: 'Comma-separated dashboard IDs' },
insightTags: { type: 'string', description: 'Comma-separated insight tags' },
favorited: { type: 'boolean', description: 'Whether the insight is favorited' },
pinned: { type: 'boolean', description: 'Whether the dashboard is pinned' },
useTemplate: { type: 'string', description: 'Dashboard template name to seed from' },
// List parameters
limit: { type: 'number', description: 'Number of results to return' },
offset: { type: 'number', description: 'Number of results to skip' },
+12
View File
@@ -243,6 +243,17 @@ const ERROR_EXTRACTORS: ErrorExtractorConfig[] = [
return undefined
},
},
{
id: 'posthog-errors',
description: 'PostHog API error format with type/code/detail/attr fields',
examples: ['PostHog API'],
extract: (errorInfo) => {
const detail = errorInfo?.data?.detail
if (typeof detail !== 'string' || !detail.trim()) return undefined
const attr = errorInfo?.data?.attr
return typeof attr === 'string' && attr ? `${detail} (${attr})` : detail
},
},
{
id: 'plain-text-data',
description: 'Plain text error response',
@@ -320,6 +331,7 @@ export const ErrorExtractorId = {
SOAP_FAULT: 'soap-fault',
OAUTH_ERROR_DESCRIPTION: 'oauth-error-description',
NESTED_ERROR_OBJECT: 'nested-error-object',
POSTHOG_ERRORS: 'posthog-errors',
PLAIN_TEXT_DATA: 'plain-text-data',
HTTP_STATUS_TEXT: 'http-status-text',
} as const
+27 -22
View File
@@ -1,8 +1,11 @@
import { getErrorMessage } from '@sim/utils/errors'
import { getPostHogIngestBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
export interface PostHogBatchEventsParams {
projectApiKey: string
region?: 'us' | 'eu'
host?: string
batch: string
}
@@ -35,6 +38,13 @@ export const batchEventsTool: ToolConfig<PostHogBatchEventsParams, PostHogBatchE
description: 'PostHog region: us (default) or eu',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
batch: {
type: 'string',
required: true,
@@ -46,8 +56,7 @@ export const batchEventsTool: ToolConfig<PostHogBatchEventsParams, PostHogBatchE
request: {
url: (params) => {
const baseUrl =
params.region === 'eu' ? 'https://eu.i.posthog.com' : 'https://us.i.posthog.com'
const baseUrl = getPostHogIngestBaseUrl(params.region, params.host)
return `${baseUrl}/batch/`
},
method: 'POST',
@@ -55,40 +64,36 @@ export const batchEventsTool: ToolConfig<PostHogBatchEventsParams, PostHogBatchE
'Content-Type': 'application/json',
}),
body: (params) => {
let batch: any[]
let batch: unknown
try {
batch = JSON.parse(params.batch)
} catch (e) {
batch = []
} catch (error) {
throw new Error(`Invalid batch JSON: ${getErrorMessage(error)}`)
}
if (!Array.isArray(batch)) {
throw new Error('batch must be a JSON array of events')
}
return {
api_key: params.projectApiKey,
batch: batch,
batch,
}
},
},
transformResponse: async (response: Response) => {
if (response.ok) {
const data = await response.json()
return {
success: true,
output: {
status: 'Batch events captured successfully',
events_processed: data.status === 1 ? JSON.parse(data.batch || '[]').length : 0,
},
}
}
transformResponse: async (response: Response, params) => {
const data = await response.json()
const eventsProcessed = params ? (JSON.parse(params.batch) as unknown[]).length : 0
const success = data.status === 1
const error = await response.text()
return {
success: false,
success,
output: {
status: 'Failed to capture batch events',
events_processed: 0,
status: success
? 'Batch events captured successfully'
: `Batch events capture failed (status: ${data.status})`,
events_processed: success ? eventsProcessed : 0,
},
error: error || 'Unknown error occurred',
}
},
+19 -16
View File
@@ -1,8 +1,11 @@
import { getErrorMessage } from '@sim/utils/errors'
import { getPostHogIngestBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
export interface PostHogCaptureEventParams {
projectApiKey: string
region?: 'us' | 'eu'
host?: string
event: string
distinctId: string
properties?: string
@@ -38,6 +41,13 @@ export const captureEventTool: ToolConfig<PostHogCaptureEventParams, PostHogCapt
description: 'PostHog region: us (default) or eu',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
event: {
type: 'string',
required: true,
@@ -69,8 +79,7 @@ export const captureEventTool: ToolConfig<PostHogCaptureEventParams, PostHogCapt
request: {
url: (params) => {
const baseUrl =
params.region === 'eu' ? 'https://eu.i.posthog.com' : 'https://us.i.posthog.com'
const baseUrl = getPostHogIngestBaseUrl(params.region, params.host)
return `${baseUrl}/capture/`
},
method: 'POST',
@@ -87,8 +96,8 @@ export const captureEventTool: ToolConfig<PostHogCaptureEventParams, PostHogCapt
if (params.properties) {
try {
body.properties = JSON.parse(params.properties)
} catch (e) {
body.properties = {}
} catch (error) {
throw new Error(`Invalid properties JSON: ${getErrorMessage(error)}`)
}
}
@@ -101,22 +110,16 @@ export const captureEventTool: ToolConfig<PostHogCaptureEventParams, PostHogCapt
},
transformResponse: async (response: Response) => {
if (response.ok) {
return {
success: true,
output: {
status: 'Event captured successfully',
},
}
}
const data = await response.json()
const success = data.status === 1
const error = await response.text()
return {
success: false,
success,
output: {
status: 'Failed to capture event',
status: success
? 'Event captured successfully'
: `Event capture failed (status: ${data.status})`,
},
error: error || 'Unknown error occurred',
}
},
+29 -9
View File
@@ -1,14 +1,16 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogCreateAnnotationParams {
apiKey: string
projectId: string
region: string
host?: string
content: string
date_marker: string
scope?: string
dashboard_item?: string
insight_short_id?: string
dashboard_id?: string
}
interface PostHogCreateAnnotationResponse {
@@ -21,6 +23,7 @@ interface PostHogCreateAnnotationResponse {
updated_at: string
created_by: Record<string, any> | null
dashboard_item: number | null
dashboard_id: number | null
insight_short_id: string | null
insight_name: string | null
scope: string
@@ -37,6 +40,7 @@ export const createAnnotationTool: ToolConfig<
description:
'Create a new annotation in PostHog. Mark important events on your graphs with date and description.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -58,6 +62,13 @@ export const createAnnotationTool: ToolConfig<
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
content: {
type: 'string',
required: true,
@@ -75,25 +86,28 @@ export const createAnnotationTool: ToolConfig<
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Scope of the annotation: "project" or "dashboard_item" (default: "project")',
description:
'Scope of the annotation: "project", "organization", "dashboard", or "dashboard_item" (default: "project")',
},
dashboard_item: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'ID of dashboard item to attach this annotation to',
description:
'ID of the dashboard tile (insight) to attach this annotation to (used when scope is "dashboard_item")',
},
insight_short_id: {
dashboard_id: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Short ID of the insight to attach this annotation to',
description:
'ID of the dashboard to attach this annotation to (used when scope is "dashboard")',
},
},
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region as 'us' | 'eu' | undefined, params.host)
return `${baseUrl}/api/projects/${params.projectId}/annotations/`
},
method: 'POST',
@@ -115,8 +129,8 @@ export const createAnnotationTool: ToolConfig<
body.dashboard_item = Number(params.dashboard_item)
}
if (params.insight_short_id) {
body.insight_short_id = params.insight_short_id
if (params.dashboard_id) {
body.dashboard_id = Number(params.dashboard_id)
}
return body
@@ -136,6 +150,7 @@ export const createAnnotationTool: ToolConfig<
updated_at: data.updated_at,
created_by: data.created_by || null,
dashboard_item: data.dashboard_item || null,
dashboard_id: data.dashboard_id || null,
insight_short_id: data.insight_short_id || null,
insight_name: data.insight_name || null,
scope: data.scope || '',
@@ -175,6 +190,11 @@ export const createAnnotationTool: ToolConfig<
description: 'ID of dashboard item this annotation is attached to',
optional: true,
},
dashboard_id: {
type: 'number',
description: 'ID of the dashboard this annotation is attached to',
optional: true,
},
insight_short_id: {
type: 'string',
description: 'Short ID of the insight this annotation is attached to',
@@ -187,7 +207,7 @@ export const createAnnotationTool: ToolConfig<
},
scope: {
type: 'string',
description: 'Scope of the annotation (project or dashboard_item)',
description: 'Scope of the annotation (project, organization, dashboard, or dashboard_item)',
},
deleted: {
type: 'boolean',
+11 -1
View File
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogCreateCohortParams {
apiKey: string
projectId: string
region: string
host?: string
name: string
description?: string
filters?: string
@@ -38,6 +40,7 @@ export const createCohortTool: ToolConfig<PostHogCreateCohortParams, PostHogCrea
description:
'Create a new cohort in PostHog. Requires cohort name and filter or query configuration.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -59,6 +62,13 @@ export const createCohortTool: ToolConfig<PostHogCreateCohortParams, PostHogCrea
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
name: {
type: 'string',
required: false,
@@ -101,7 +111,7 @@ export const createCohortTool: ToolConfig<PostHogCreateCohortParams, PostHogCrea
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region as 'us' | 'eu' | undefined, params.host)
return `${baseUrl}/api/projects/${params.projectId}/cohorts/`
},
method: 'POST',
+183
View File
@@ -0,0 +1,183 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogCreateDashboardParams {
apiKey: string
projectId: string
region?: 'us' | 'eu'
host?: string
name: string
description?: string
pinned?: boolean
tags?: string
useTemplate?: string
}
interface PostHogCreateDashboardResponse {
success: boolean
output: {
id: number
name: string
description: string
pinned: boolean
created_at: string
tiles: Array<Record<string, any>>
filters: Record<string, any>
tags: string[]
}
}
export const createDashboardTool: ToolConfig<
PostHogCreateDashboardParams,
PostHogCreateDashboardResponse
> = {
id: 'posthog_create_dashboard',
name: 'PostHog Create Dashboard',
description:
'Create a new dashboard in PostHog. Optionally seed it from a built-in template, then attach insights to it afterward.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'PostHog Personal API Key',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The PostHog project ID (e.g., "12345" or project UUID)',
},
region: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name for the new dashboard',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Description of the dashboard',
},
pinned: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to pin the dashboard to the sidebar',
},
tags: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of tags for the dashboard',
},
useTemplate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Name of a built-in PostHog dashboard template to seed this dashboard from (e.g., "Product analytics")',
},
},
request: {
url: (params) => {
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/dashboards/`
},
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const body: Record<string, any> = {
name: params.name,
}
if (params.description) body.description = params.description
if (params.pinned !== undefined) body.pinned = params.pinned
if (params.useTemplate) body.use_template = params.useTemplate
if (params.tags) {
body.tags = params.tags
.split(',')
.map((tag: string) => tag.trim())
.filter((tag: string) => tag.length > 0)
}
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name || '',
description: data.description || '',
pinned: data.pinned || false,
created_at: data.created_at,
tiles: data.tiles || [],
filters: data.filters || {},
tags: data.tags || [],
},
}
},
outputs: {
id: {
type: 'number',
description: 'Unique identifier for the created dashboard',
},
name: {
type: 'string',
description: 'Name of the dashboard',
},
description: {
type: 'string',
description: 'Description of the dashboard',
},
pinned: {
type: 'boolean',
description: 'Whether the dashboard is pinned',
},
created_at: {
type: 'string',
description: 'ISO timestamp when dashboard was created',
},
tiles: {
type: 'array',
description: 'Tiles/widgets on the dashboard',
},
filters: {
type: 'object',
description: 'Global filters applied to the dashboard',
},
tags: {
type: 'array',
description: 'Tags associated with the dashboard',
},
},
}
+18 -24
View File
@@ -1,15 +1,17 @@
import { getErrorMessage } from '@sim/utils/errors'
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface CreateExperimentParams {
projectId: string
region: 'us' | 'eu'
host?: string
apiKey: string
name: string
description?: string
featureFlagKey: string
parameters?: string
filters?: string
variants?: string
startDate?: string
endDate?: string
}
@@ -22,7 +24,6 @@ interface Experiment {
feature_flag: Record<string, any>
parameters: Record<string, any>
filters: Record<string, any>
variants: Record<string, any>
start_date: string | null
end_date: string | null
created_at: string
@@ -39,6 +40,7 @@ export const createExperimentTool: ToolConfig<CreateExperimentParams, CreateExpe
name: 'PostHog Create Experiment',
description: 'Create a new experiment in PostHog',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -53,6 +55,13 @@ export const createExperimentTool: ToolConfig<CreateExperimentParams, CreateExpe
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -61,9 +70,9 @@ export const createExperimentTool: ToolConfig<CreateExperimentParams, CreateExpe
},
name: {
type: 'string',
required: false,
required: true,
visibility: 'user-or-llm',
description: 'Experiment name (optional)',
description: 'Experiment name',
},
description: {
type: 'string',
@@ -89,12 +98,6 @@ export const createExperimentTool: ToolConfig<CreateExperimentParams, CreateExpe
visibility: 'user-or-llm',
description: 'Experiment filters as JSON string',
},
variants: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Experiment variants as JSON string',
},
startDate: {
type: 'string',
required: false,
@@ -111,7 +114,7 @@ export const createExperimentTool: ToolConfig<CreateExperimentParams, CreateExpe
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/experiments/`
},
method: 'POST',
@@ -132,24 +135,16 @@ export const createExperimentTool: ToolConfig<CreateExperimentParams, CreateExpe
if (params.parameters) {
try {
body.parameters = JSON.parse(params.parameters)
} catch {
body.parameters = {}
} catch (error) {
throw new Error(`Invalid parameters JSON: ${getErrorMessage(error)}`)
}
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
body.filters = {}
}
}
if (params.variants) {
try {
body.variants = JSON.parse(params.variants)
} catch {
body.variants = {}
} catch (error) {
throw new Error(`Invalid filters JSON: ${getErrorMessage(error)}`)
}
}
@@ -185,7 +180,6 @@ export const createExperimentTool: ToolConfig<CreateExperimentParams, CreateExpe
feature_flag: { type: 'object', description: 'Feature flag details' },
parameters: { type: 'object', description: 'Experiment parameters' },
filters: { type: 'object', description: 'Experiment filters' },
variants: { type: 'object', description: 'Experiment variants' },
start_date: { type: 'string', description: 'Start date' },
end_date: { type: 'string', description: 'End date' },
created_at: { type: 'string', description: 'Creation timestamp' },
+14 -3
View File
@@ -1,8 +1,11 @@
import { getErrorMessage } from '@sim/utils/errors'
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface CreateFeatureFlagParams {
projectId: string
region: 'us' | 'eu'
host?: string
apiKey: string
name: string
key: string
@@ -36,6 +39,7 @@ export const createFeatureFlagTool: ToolConfig<CreateFeatureFlagParams, CreateFe
name: 'PostHog Create Feature Flag',
description: 'Create a new feature flag in PostHog',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -50,6 +54,13 @@ export const createFeatureFlagTool: ToolConfig<CreateFeatureFlagParams, CreateFe
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -98,7 +109,7 @@ export const createFeatureFlagTool: ToolConfig<CreateFeatureFlagParams, CreateFe
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/feature_flags/`
},
method: 'POST',
@@ -115,8 +126,8 @@ export const createFeatureFlagTool: ToolConfig<CreateFeatureFlagParams, CreateFe
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
body.filters = {}
} catch (error) {
throw new Error(`Invalid filters JSON: ${getErrorMessage(error)}`)
}
}
+12 -30
View File
@@ -1,12 +1,13 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogCreateInsightParams {
apiKey: string
projectId: string
region: string
host?: string
name: string
description?: string
filters?: string
query?: string
dashboards?: string
tags?: string
@@ -18,12 +19,10 @@ interface PostHogCreateInsightResponse {
id: number
name: string
description: string
filters: Record<string, any>
query: Record<string, any> | null
created_at: string
created_by: Record<string, any> | null
last_modified_at: string
saved: boolean
dashboards: number[]
tags: string[]
}
@@ -35,9 +34,9 @@ export const createInsightTool: ToolConfig<
> = {
id: 'posthog_create_insight',
name: 'PostHog Create Insight',
description:
'Create a new insight in PostHog. Requires insight name and configuration filters or query.',
description: 'Create a new insight in PostHog. Requires insight name and a query configuration.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -59,6 +58,13 @@ export const createInsightTool: ToolConfig<
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
name: {
type: 'string',
required: false,
@@ -72,12 +78,6 @@ export const createInsightTool: ToolConfig<
visibility: 'user-or-llm',
description: 'Description of the insight',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'JSON string of filter configuration for the insight',
},
query: {
type: 'string',
required: false,
@@ -100,7 +100,7 @@ export const createInsightTool: ToolConfig<
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region as 'us' | 'eu' | undefined, params.host)
return `${baseUrl}/api/projects/${params.projectId}/insights/`
},
method: 'POST',
@@ -117,14 +117,6 @@ export const createInsightTool: ToolConfig<
body.description = params.description
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch (e) {
body.filters = {}
}
}
if (params.query) {
try {
body.query = JSON.parse(params.query)
@@ -160,12 +152,10 @@ export const createInsightTool: ToolConfig<
id: data.id,
name: data.name || '',
description: data.description || '',
filters: data.filters || {},
query: data.query || null,
created_at: data.created_at,
created_by: data.created_by || null,
last_modified_at: data.last_modified_at,
saved: data.saved || false,
dashboards: data.dashboards || [],
tags: data.tags || [],
},
@@ -185,10 +175,6 @@ export const createInsightTool: ToolConfig<
type: 'string',
description: 'Description of the insight',
},
filters: {
type: 'object',
description: 'Filter configuration for the insight',
},
query: {
type: 'object',
description: 'Query configuration for the insight',
@@ -207,10 +193,6 @@ export const createInsightTool: ToolConfig<
type: 'string',
description: 'ISO timestamp when insight was last modified',
},
saved: {
type: 'boolean',
description: 'Whether the insight is saved',
},
dashboards: {
type: 'array',
description: 'IDs of dashboards this insight appears on',
+13 -3
View File
@@ -1,3 +1,4 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogSurveyQuestion {
@@ -15,6 +16,7 @@ interface PostHogCreateSurveyParams {
apiKey: string
projectId: string
region?: 'us' | 'eu'
host?: string
name: string
description?: string
type?: 'popover' | 'api'
@@ -60,6 +62,7 @@ export const createSurveyTool: ToolConfig<PostHogCreateSurveyParams, PostHogCrea
description:
'Create a new survey in PostHog. Supports question types: Basic (open), Link, Rating, and Multiple Choice.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -81,11 +84,18 @@ export const createSurveyTool: ToolConfig<PostHogCreateSurveyParams, PostHogCrea
description: 'PostHog cloud region: us or eu (default: us)',
default: 'us',
},
name: {
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Survey name (optional)',
description: 'Survey name',
},
description: {
type: 'string',
@@ -154,7 +164,7 @@ export const createSurveyTool: ToolConfig<PostHogCreateSurveyParams, PostHogCrea
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/surveys/`
},
method: 'POST',
+17 -4
View File
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface DeleteFeatureFlagParams {
projectId: string
flagId: string
region: 'us' | 'eu'
host?: string
apiKey: string
}
@@ -18,6 +20,7 @@ export const deleteFeatureFlagTool: ToolConfig<DeleteFeatureFlagParams, DeleteFe
name: 'PostHog Delete Feature Flag',
description: 'Delete a feature flag from PostHog',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -38,6 +41,13 @@ export const deleteFeatureFlagTool: ToolConfig<DeleteFeatureFlagParams, DeleteFe
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -48,17 +58,20 @@ export const deleteFeatureFlagTool: ToolConfig<DeleteFeatureFlagParams, DeleteFe
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
return `${baseUrl}/api/projects/${params.projectId}/feature_flags/${params.flagId}`
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/feature_flags/${params.flagId}/`
},
method: 'DELETE',
// PostHog does not allow a hard DELETE on feature flags (always returns 405).
// Deletion is a soft-delete via PATCH with deleted: true.
method: 'PATCH',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: () => ({ deleted: true }),
},
transformResponse: async (response: Response) => {
transformResponse: async () => {
return {
success: true,
message: 'Feature flag deleted successfully',
+20 -16
View File
@@ -1,8 +1,10 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
export interface PostHogDeletePersonParams {
apiKey: string
region?: 'us' | 'eu'
host?: string
projectId: string
personId: string
}
@@ -21,6 +23,7 @@ export const deletePersonTool: ToolConfig<PostHogDeletePersonParams, PostHogDele
description:
'Delete a person from PostHog. This will remove all associated events and data. Use with caution.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -36,6 +39,13 @@ export const deletePersonTool: ToolConfig<PostHogDeletePersonParams, PostHogDele
description: 'PostHog region: us (default) or eu',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
projectId: {
type: 'string',
required: true,
@@ -51,34 +61,28 @@ export const deletePersonTool: ToolConfig<PostHogDeletePersonParams, PostHogDele
},
request: {
// PostHog has no single-person DELETE endpoint; deletion is only available
// via the bulk_delete endpoint, called here with a single ID.
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
return `${baseUrl}/api/projects/${params.projectId}/persons/${params.personId}/`
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/persons/bulk_delete/`
},
method: 'DELETE',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => ({ ids: [params.personId] }),
},
transformResponse: async (response: Response) => {
if (response.ok || response.status === 204) {
return {
success: true,
output: {
status: 'Person deleted successfully',
},
}
}
const error = await response.text()
const data = await response.json()
const success = data.persons_deleted > 0
return {
success: false,
success,
output: {
status: 'Failed to delete person',
status: success ? 'Person deleted successfully' : 'No matching person found to delete',
},
error: error || 'Unknown error occurred',
}
},
+89
View File
@@ -0,0 +1,89 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogDeleteSurveyParams {
apiKey: string
projectId: string
surveyId: string
region?: 'us' | 'eu'
host?: string
}
interface PostHogDeleteSurveyResponse {
success: boolean
output: {
status: string
}
}
export const deleteSurveyTool: ToolConfig<PostHogDeleteSurveyParams, PostHogDeleteSurveyResponse> =
{
id: 'posthog_delete_survey',
name: 'PostHog Delete Survey',
description: 'Delete a survey from PostHog. Use this to remove expired or unused surveys.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'PostHog Personal API Key',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'PostHog Project ID (e.g., "12345" or project UUID)',
},
surveyId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Survey ID to delete (e.g., "01234567-89ab-cdef-0123-456789abcdef")',
},
region: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'PostHog cloud region: us or eu (default: us)',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
},
request: {
url: (params) => {
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/surveys/${params.surveyId}/`
},
method: 'DELETE',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async () => {
return {
success: true,
output: {
status: 'Survey deleted successfully',
},
}
},
outputs: {
status: {
type: 'string',
description: 'Status message indicating whether the survey was deleted successfully',
},
},
}
+40 -13
View File
@@ -1,7 +1,10 @@
import { getErrorMessage } from '@sim/utils/errors'
import { getPostHogIngestBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface EvaluateFlagsParams {
region: 'us' | 'eu'
host?: string
projectApiKey: string
distinctId: string
groups?: string
@@ -33,6 +36,13 @@ export const evaluateFlagsTool: ToolConfig<EvaluateFlagsParams, EvaluateFlagsRes
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
projectApiKey: {
type: 'string',
required: true,
@@ -67,41 +77,40 @@ export const evaluateFlagsTool: ToolConfig<EvaluateFlagsParams, EvaluateFlagsRes
request: {
url: (params) => {
const baseUrl =
params.region === 'eu' ? 'https://eu.i.posthog.com' : 'https://us.i.posthog.com'
return `${baseUrl}/decide?v=3`
const baseUrl = getPostHogIngestBaseUrl(params.region, params.host)
return `${baseUrl}/flags/?v=2`
},
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.projectApiKey}`,
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, any> = {
api_key: params.projectApiKey,
distinct_id: params.distinctId,
}
if (params.groups) {
try {
body.groups = JSON.parse(params.groups)
} catch {
body.groups = {}
} catch (error) {
throw new Error(`Invalid groups JSON: ${getErrorMessage(error)}`)
}
}
if (params.personProperties) {
try {
body.person_properties = JSON.parse(params.personProperties)
} catch {
body.person_properties = {}
} catch (error) {
throw new Error(`Invalid personProperties JSON: ${getErrorMessage(error)}`)
}
}
if (params.groupProperties) {
try {
body.group_properties = JSON.parse(params.groupProperties)
} catch {
body.group_properties = {}
} catch (error) {
throw new Error(`Invalid groupProperties JSON: ${getErrorMessage(error)}`)
}
}
@@ -111,10 +120,28 @@ export const evaluateFlagsTool: ToolConfig<EvaluateFlagsParams, EvaluateFlagsRes
transformResponse: async (response: Response) => {
const data = await response.json()
const flags: Record<
string,
{ enabled?: boolean; variant?: string; metadata?: { payload?: string } }
> = data.flags || {}
const feature_flags: FlagEvaluation = {}
const feature_flag_payloads: Record<string, any> = {}
for (const [key, flag] of Object.entries(flags)) {
feature_flags[key] = flag.variant ?? flag.enabled ?? false
if (flag.metadata?.payload !== undefined) {
try {
feature_flag_payloads[key] = JSON.parse(flag.metadata.payload)
} catch {
feature_flag_payloads[key] = flag.metadata.payload
}
}
}
return {
feature_flags: data.featureFlags || {},
feature_flag_payloads: data.featureFlagPayloads || {},
feature_flags,
feature_flag_payloads,
errors_while_computing_flags: data.errorsWhileComputingFlags || false,
}
},
+11 -1
View File
@@ -1,3 +1,4 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogGetCohortParams {
@@ -5,6 +6,7 @@ interface PostHogGetCohortParams {
projectId: string
cohortId: string
region: string
host?: string
}
interface PostHogGetCohortResponse {
@@ -34,6 +36,7 @@ export const getCohortTool: ToolConfig<PostHogGetCohortParams, PostHogGetCohortR
description:
'Get a specific cohort by ID from PostHog. Returns detailed cohort definition, filters, and user count.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -61,11 +64,18 @@ export const getCohortTool: ToolConfig<PostHogGetCohortParams, PostHogGetCohortR
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
},
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region as 'us' | 'eu' | undefined, params.host)
return `${baseUrl}/api/projects/${params.projectId}/cohorts/${params.cohortId}/`
},
method: 'GET',
+11 -1
View File
@@ -1,3 +1,4 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogGetDashboardParams {
@@ -5,6 +6,7 @@ interface PostHogGetDashboardParams {
projectId: string
dashboardId: string
region: string
host?: string
}
interface PostHogGetDashboardResponse {
@@ -32,6 +34,7 @@ export const getDashboardTool: ToolConfig<PostHogGetDashboardParams, PostHogGetD
description:
'Get a specific dashboard by ID from PostHog. Returns detailed dashboard configuration, tiles, and metadata.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -59,11 +62,18 @@ export const getDashboardTool: ToolConfig<PostHogGetDashboardParams, PostHogGetD
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
},
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region as 'us' | 'eu' | undefined, params.host)
return `${baseUrl}/api/projects/${params.projectId}/dashboards/${params.dashboardId}/`
},
method: 'GET',
+11 -15
View File
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogGetEventDefinitionParams {
projectId: string
eventDefinitionId: string
region: 'us' | 'eu'
host?: string
apiKey: string
}
@@ -12,8 +14,6 @@ interface EventDefinition {
name: string
description: string
tags: string[]
volume_30_day: number | null
query_usage_30_day: number | null
created_at: string
last_seen_at: string | null
updated_at: string
@@ -36,6 +36,7 @@ export const getEventDefinitionTool: ToolConfig<PostHogGetEventDefinitionParams,
description:
'Get details of a specific event definition in PostHog. Returns comprehensive information about the event including metadata, usage statistics, and verification status.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -56,6 +57,13 @@ export const getEventDefinitionTool: ToolConfig<PostHogGetEventDefinitionParams,
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -66,7 +74,7 @@ export const getEventDefinitionTool: ToolConfig<PostHogGetEventDefinitionParams,
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/event_definitions/${params.eventDefinitionId}`
},
method: 'GET',
@@ -84,8 +92,6 @@ export const getEventDefinitionTool: ToolConfig<PostHogGetEventDefinitionParams,
name: data.name,
description: data.description || '',
tags: data.tags || [],
volume_30_day: data.volume_30_day ?? null,
query_usage_30_day: data.query_usage_30_day ?? null,
created_at: data.created_at,
last_seen_at: data.last_seen_at ?? null,
updated_at: data.updated_at,
@@ -113,16 +119,6 @@ export const getEventDefinitionTool: ToolConfig<PostHogGetEventDefinitionParams,
type: 'array',
description: 'Tags associated with the event',
},
volume_30_day: {
type: 'number',
description: 'Number of events received in the last 30 days',
optional: true,
},
query_usage_30_day: {
type: 'number',
description: 'Number of times this event was queried in the last 30 days',
optional: true,
},
created_at: {
type: 'string',
description: 'ISO timestamp when the event was created',
+12 -4
View File
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface GetExperimentParams {
projectId: string
experimentId: string
region: 'us' | 'eu'
host?: string
apiKey: string
}
@@ -15,7 +17,6 @@ interface Experiment {
feature_flag: Record<string, any>
parameters: Record<string, any>
filters: Record<string, any>
variants: Record<string, any>
start_date: string | null
end_date: string | null
created_at: string
@@ -34,6 +35,7 @@ export const getExperimentTool: ToolConfig<GetExperimentParams, GetExperimentRes
name: 'PostHog Get Experiment',
description: 'Get details of a specific experiment',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -54,6 +56,13 @@ export const getExperimentTool: ToolConfig<GetExperimentParams, GetExperimentRes
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -64,8 +73,8 @@ export const getExperimentTool: ToolConfig<GetExperimentParams, GetExperimentRes
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
return `${baseUrl}/api/projects/${params.projectId}/experiments/${params.experimentId}`
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/experiments/${params.experimentId}/`
},
method: 'GET',
headers: (params) => ({
@@ -94,7 +103,6 @@ export const getExperimentTool: ToolConfig<GetExperimentParams, GetExperimentRes
feature_flag: { type: 'object', description: 'Feature flag details' },
parameters: { type: 'object', description: 'Experiment parameters' },
filters: { type: 'object', description: 'Experiment filters' },
variants: { type: 'object', description: 'Experiment variants' },
start_date: { type: 'string', description: 'Start date' },
end_date: { type: 'string', description: 'End date' },
created_at: { type: 'string', description: 'Creation timestamp' },
+12 -2
View File
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface GetFeatureFlagParams {
projectId: string
flagId: string
region: 'us' | 'eu'
host?: string
apiKey: string
}
@@ -32,6 +34,7 @@ export const getFeatureFlagTool: ToolConfig<GetFeatureFlagParams, GetFeatureFlag
name: 'PostHog Get Feature Flag',
description: 'Get details of a specific feature flag',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -52,6 +55,13 @@ export const getFeatureFlagTool: ToolConfig<GetFeatureFlagParams, GetFeatureFlag
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -62,8 +72,8 @@ export const getFeatureFlagTool: ToolConfig<GetFeatureFlagParams, GetFeatureFlag
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
return `${baseUrl}/api/projects/${params.projectId}/feature_flags/${params.flagId}`
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/feature_flags/${params.flagId}/`
},
method: 'GET',
headers: (params) => ({
+12 -14
View File
@@ -1,3 +1,4 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogGetInsightParams {
@@ -5,6 +6,7 @@ interface PostHogGetInsightParams {
projectId: string
insightId: string
region: string
host?: string
}
interface PostHogGetInsightResponse {
@@ -13,13 +15,11 @@ interface PostHogGetInsightResponse {
id: number
name: string
description: string
filters: Record<string, any>
query: Record<string, any> | null
created_at: string
created_by: Record<string, any> | null
last_modified_at: string
last_modified_by: Record<string, any> | null
saved: boolean
dashboards: number[]
tags: string[]
favorited: boolean
@@ -30,8 +30,9 @@ export const getInsightTool: ToolConfig<PostHogGetInsightParams, PostHogGetInsig
id: 'posthog_get_insight',
name: 'PostHog Get Insight',
description:
'Get a specific insight by ID from PostHog. Returns detailed insight configuration, filters, and metadata.',
'Get a specific insight by ID from PostHog. Returns detailed insight configuration and metadata.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -59,11 +60,18 @@ export const getInsightTool: ToolConfig<PostHogGetInsightParams, PostHogGetInsig
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
},
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region as 'us' | 'eu' | undefined, params.host)
return `${baseUrl}/api/projects/${params.projectId}/insights/${params.insightId}/`
},
method: 'GET',
@@ -82,13 +90,11 @@ export const getInsightTool: ToolConfig<PostHogGetInsightParams, PostHogGetInsig
id: data.id,
name: data.name || '',
description: data.description || '',
filters: data.filters || {},
query: data.query || null,
created_at: data.created_at,
created_by: data.created_by || null,
last_modified_at: data.last_modified_at,
last_modified_by: data.last_modified_by || null,
saved: data.saved || false,
dashboards: data.dashboards || [],
tags: data.tags || [],
favorited: data.favorited || false,
@@ -109,10 +115,6 @@ export const getInsightTool: ToolConfig<PostHogGetInsightParams, PostHogGetInsig
type: 'string',
description: 'Description of the insight',
},
filters: {
type: 'object',
description: 'Filter configuration for the insight',
},
query: {
type: 'object',
description: 'Query configuration for the insight',
@@ -136,10 +138,6 @@ export const getInsightTool: ToolConfig<PostHogGetInsightParams, PostHogGetInsig
description: 'User who last modified the insight',
optional: true,
},
saved: {
type: 'boolean',
description: 'Whether the insight is saved',
},
dashboards: {
type: 'array',
description: 'IDs of dashboards this insight appears on',
+11 -1
View File
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
export interface PostHogGetOrganizationParams {
organizationId: string
apiKey: string
region?: 'us' | 'eu'
host?: string
}
interface PostHogOrganizationDetail {
@@ -48,6 +50,7 @@ export const getOrganizationTool: ToolConfig<
description:
'Get detailed information about a specific organization by ID. Returns comprehensive organization settings, features, usage, and team information.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
organizationId: {
@@ -68,11 +71,18 @@ export const getOrganizationTool: ToolConfig<
visibility: 'user-only',
description: 'Cloud region: us or eu (default: us)',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
},
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/organizations/${params.organizationId}/`
},
method: 'GET',
+11 -19
View File
@@ -1,8 +1,10 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
export interface PostHogGetPersonParams {
apiKey: string
region?: 'us' | 'eu'
host?: string
projectId: string
personId: string
}
@@ -26,6 +28,7 @@ export const getPersonTool: ToolConfig<PostHogGetPersonParams, PostHogGetPersonR
name: 'PostHog Get Person',
description: 'Get detailed information about a specific person in PostHog by their ID or UUID.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -41,6 +44,13 @@ export const getPersonTool: ToolConfig<PostHogGetPersonParams, PostHogGetPersonR
description: 'PostHog region: us (default) or eu',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
projectId: {
type: 'string',
required: true,
@@ -57,7 +67,7 @@ export const getPersonTool: ToolConfig<PostHogGetPersonParams, PostHogGetPersonR
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/persons/${params.personId}/`
},
method: 'GET',
@@ -68,24 +78,6 @@ export const getPersonTool: ToolConfig<PostHogGetPersonParams, PostHogGetPersonR
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.text()
return {
success: false,
output: {
person: {
id: '',
name: '',
distinct_ids: [],
properties: {},
created_at: '',
uuid: '',
},
},
error: error || 'Failed to get person',
}
}
const data = await response.json()
return {
+11 -1
View File
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
export interface PostHogGetProjectParams {
projectId: string
apiKey: string
region?: 'us' | 'eu'
host?: string
}
interface PostHogProjectDetail {
@@ -50,6 +52,7 @@ export const getProjectTool: ToolConfig<PostHogGetProjectParams, PostHogGetProje
description:
'Get detailed information about a specific project by ID. Returns comprehensive project configuration, settings, and feature flags.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -70,11 +73,18 @@ export const getProjectTool: ToolConfig<PostHogGetProjectParams, PostHogGetProje
visibility: 'user-only',
description: 'Cloud region: us or eu (default: us)',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
},
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/`
},
method: 'GET',
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogGetPropertyDefinitionParams {
projectId: string
propertyDefinitionId: string
region: 'us' | 'eu'
host?: string
apiKey: string
}
@@ -15,9 +17,7 @@ interface PropertyDefinition {
is_numerical: boolean
is_seen_on_filtered_events: boolean | null
property_type: string
type: 'event' | 'person' | 'group'
volume_30_day: number | null
query_usage_30_day: number | null
type: 'event' | 'person' | 'group' | 'session'
created_at: string
updated_at: string
updated_by: {
@@ -30,7 +30,6 @@ interface PropertyDefinition {
verified: boolean
verified_at: string | null
verified_by: string | null
example: string | null
}
export const getPropertyDefinitionTool: ToolConfig<
@@ -42,6 +41,7 @@ export const getPropertyDefinitionTool: ToolConfig<
description:
'Get details of a specific property definition in PostHog. Returns comprehensive information about the property including metadata, type, usage statistics, and verification status.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -62,6 +62,13 @@ export const getPropertyDefinitionTool: ToolConfig<
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -72,7 +79,7 @@ export const getPropertyDefinitionTool: ToolConfig<
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/property_definitions/${params.propertyDefinitionId}`
},
method: 'GET',
@@ -94,15 +101,12 @@ export const getPropertyDefinitionTool: ToolConfig<
is_seen_on_filtered_events: data.is_seen_on_filtered_events ?? null,
property_type: data.property_type,
type: data.type,
volume_30_day: data.volume_30_day ?? null,
query_usage_30_day: data.query_usage_30_day ?? null,
created_at: data.created_at,
updated_at: data.updated_at,
updated_by: data.updated_by ?? null,
verified: data.verified || false,
verified_at: data.verified_at ?? null,
verified_by: data.verified_by ?? null,
example: data.example ?? null,
}
},
@@ -138,17 +142,7 @@ export const getPropertyDefinitionTool: ToolConfig<
},
type: {
type: 'string',
description: 'Property type: event, person, or group',
},
volume_30_day: {
type: 'number',
description: 'Number of times property was seen in the last 30 days',
optional: true,
},
query_usage_30_day: {
type: 'number',
description: 'Number of times this property was queried in the last 30 days',
optional: true,
description: 'Property type: event, person, group, or session',
},
created_at: {
type: 'string',
@@ -177,10 +171,5 @@ export const getPropertyDefinitionTool: ToolConfig<
description: 'User who verified the property',
optional: true,
},
example: {
type: 'string',
description: 'Example value for the property',
optional: true,
},
},
}
@@ -1,3 +1,4 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogGetSessionRecordingParams {
@@ -5,6 +6,7 @@ interface PostHogGetSessionRecordingParams {
projectId: string
recordingId: string
region?: 'us' | 'eu'
host?: string
}
interface PostHogSessionRecording {
@@ -27,13 +29,6 @@ interface PostHogSessionRecording {
name?: string
properties?: Record<string, any>
}
matching_events?: Array<{
id: string
event: string
timestamp: string
properties: Record<string, any>
}>
snapshot_data_by_window_id?: Record<string, any>
}
interface PostHogGetSessionRecordingResponse {
@@ -51,6 +46,7 @@ export const getSessionRecordingTool: ToolConfig<
name: 'PostHog Get Session Recording',
description: 'Get details of a specific session recording in PostHog by ID.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -79,11 +75,18 @@ export const getSessionRecordingTool: ToolConfig<
description: 'PostHog cloud region: us or eu (default: us)',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
},
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/session_recordings/${params.recordingId}/`
},
method: 'GET',
@@ -124,7 +127,6 @@ export const getSessionRecordingTool: ToolConfig<
console_error_count: { type: 'number', description: 'Number of console errors' },
start_url: { type: 'string', description: 'Starting URL of the recording' },
person: { type: 'object', description: 'Person information' },
matching_events: { type: 'array', description: 'Events that occurred during recording' },
},
},
},
+11 -1
View File
@@ -1,3 +1,4 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogGetSurveyParams {
@@ -5,6 +6,7 @@ interface PostHogGetSurveyParams {
projectId: string
surveyId: string
region?: 'us' | 'eu'
host?: string
}
interface PostHogSurveyQuestion {
@@ -48,6 +50,7 @@ export const getSurveyTool: ToolConfig<PostHogGetSurveyParams, PostHogGetSurveyR
name: 'PostHog Get Survey',
description: 'Get details of a specific survey in PostHog by ID.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -75,11 +78,18 @@ export const getSurveyTool: ToolConfig<PostHogGetSurveyParams, PostHogGetSurveyR
description: 'PostHog cloud region: us or eu (default: us)',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
},
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/surveys/${params.surveyId}/`
},
method: 'GET',
+10
View File
@@ -4,12 +4,14 @@ import { batchEventsTool } from '@/tools/posthog/batch_events'
import { captureEventTool } from '@/tools/posthog/capture_event'
import { createAnnotationTool } from '@/tools/posthog/create_annotation'
import { createCohortTool } from '@/tools/posthog/create_cohort'
import { createDashboardTool } from '@/tools/posthog/create_dashboard'
import { createExperimentTool } from '@/tools/posthog/create_experiment'
import { createFeatureFlagTool } from '@/tools/posthog/create_feature_flag'
import { createInsightTool } from '@/tools/posthog/create_insight'
import { createSurveyTool } from '@/tools/posthog/create_survey'
import { deleteFeatureFlagTool } from '@/tools/posthog/delete_feature_flag'
import { deletePersonTool } from '@/tools/posthog/delete_person'
import { deleteSurveyTool } from '@/tools/posthog/delete_survey'
import { evaluateFlagsTool } from '@/tools/posthog/evaluate_flags'
import { getCohortTool } from '@/tools/posthog/get_cohort'
import { getDashboardTool } from '@/tools/posthog/get_dashboard'
@@ -44,8 +46,11 @@ import { listSessionRecordingsTool } from '@/tools/posthog/list_session_recordin
// Engagement
import { listSurveysTool } from '@/tools/posthog/list_surveys'
import { queryTool } from '@/tools/posthog/query'
import { updateCohortTool } from '@/tools/posthog/update_cohort'
import { updateEventDefinitionTool } from '@/tools/posthog/update_event_definition'
import { updateExperimentTool } from '@/tools/posthog/update_experiment'
import { updateFeatureFlagTool } from '@/tools/posthog/update_feature_flag'
import { updateInsightTool } from '@/tools/posthog/update_insight'
import { updatePropertyDefinitionTool } from '@/tools/posthog/update_property_definition'
import { updateSurveyTool } from '@/tools/posthog/update_survey'
@@ -60,12 +65,15 @@ export const posthogQueryTool = queryTool
export const posthogListInsightsTool = listInsightsTool
export const posthogGetInsightTool = getInsightTool
export const posthogCreateInsightTool = createInsightTool
export const posthogUpdateInsightTool = updateInsightTool
export const posthogListDashboardsTool = listDashboardsTool
export const posthogGetDashboardTool = getDashboardTool
export const posthogCreateDashboardTool = createDashboardTool
export const posthogListActionsTool = listActionsTool
export const posthogListCohortsTool = listCohortsTool
export const posthogGetCohortTool = getCohortTool
export const posthogCreateCohortTool = createCohortTool
export const posthogUpdateCohortTool = updateCohortTool
export const posthogListAnnotationsTool = listAnnotationsTool
export const posthogCreateAnnotationTool = createAnnotationTool
@@ -78,11 +86,13 @@ export const posthogEvaluateFlagsTool = evaluateFlagsTool
export const posthogListExperimentsTool = listExperimentsTool
export const posthogGetExperimentTool = getExperimentTool
export const posthogCreateExperimentTool = createExperimentTool
export const posthogUpdateExperimentTool = updateExperimentTool
export const posthogListSurveysTool = listSurveysTool
export const posthogGetSurveyTool = getSurveyTool
export const posthogCreateSurveyTool = createSurveyTool
export const posthogUpdateSurveyTool = updateSurveyTool
export const posthogDeleteSurveyTool = deleteSurveyTool
export const posthogListSessionRecordingsTool = listSessionRecordingsTool
export const posthogGetSessionRecordingTool = getSessionRecordingTool
export const posthogListRecordingPlaylistsTool = listRecordingPlaylistsTool
+19 -1
View File
@@ -1,11 +1,14 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogListActionsParams {
apiKey: string
projectId: string
region: string
host?: string
limit?: number
offset?: number
search?: string
}
interface PostHogListActionsResponse {
@@ -37,6 +40,7 @@ export const listActionsTool: ToolConfig<PostHogListActionsParams, PostHogListAc
description:
'List all actions in a PostHog project. Returns action definitions, steps, and metadata.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -58,6 +62,13 @@ export const listActionsTool: ToolConfig<PostHogListActionsParams, PostHogListAc
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
limit: {
type: 'number',
required: false,
@@ -70,16 +81,23 @@ export const listActionsTool: ToolConfig<PostHogListActionsParams, PostHogListAc
visibility: 'user-or-llm',
description: 'Number of results to skip for pagination (e.g., 0, 100, 200)',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter actions by name',
},
},
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region as 'us' | 'eu' | undefined, params.host)
let url = `${baseUrl}/api/projects/${params.projectId}/actions/`
const queryParams = []
if (params.limit) queryParams.push(`limit=${params.limit}`)
if (params.offset) queryParams.push(`offset=${params.offset}`)
if (params.search) queryParams.push(`search=${encodeURIComponent(params.search)}`)
if (queryParams.length > 0) {
url += `?${queryParams.join('&')}`
+17 -1
View File
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogListAnnotationsParams {
apiKey: string
projectId: string
region: string
host?: string
limit?: number
offset?: number
}
@@ -22,6 +24,7 @@ interface PostHogListAnnotationsResponse {
updated_at: string
created_by: Record<string, any> | null
dashboard_item: number | null
dashboard_id: number | null
insight_short_id: string | null
insight_name: string | null
scope: string
@@ -39,6 +42,7 @@ export const listAnnotationsTool: ToolConfig<
description:
'List all annotations in a PostHog project. Returns annotation content, timestamps, and associated insights.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -60,6 +64,13 @@ export const listAnnotationsTool: ToolConfig<
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
limit: {
type: 'number',
required: false,
@@ -76,7 +87,7 @@ export const listAnnotationsTool: ToolConfig<
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region as 'us' | 'eu' | undefined, params.host)
let url = `${baseUrl}/api/projects/${params.projectId}/annotations/`
const queryParams = []
@@ -113,6 +124,7 @@ export const listAnnotationsTool: ToolConfig<
updated_at: annotation.updated_at,
created_by: annotation.created_by || null,
dashboard_item: annotation.dashboard_item || null,
dashboard_id: annotation.dashboard_id || null,
insight_short_id: annotation.insight_short_id || null,
insight_name: annotation.insight_name || null,
scope: annotation.scope || '',
@@ -162,6 +174,10 @@ export const listAnnotationsTool: ToolConfig<
type: 'number',
description: 'ID of dashboard item this annotation is attached to',
},
dashboard_id: {
type: 'number',
description: 'ID of the dashboard this annotation is attached to',
},
insight_short_id: {
type: 'string',
description: 'Short ID of the insight this annotation is attached to',
+11 -1
View File
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogListCohortsParams {
apiKey: string
projectId: string
region: string
host?: string
limit?: number
offset?: number
}
@@ -39,6 +41,7 @@ export const listCohortsTool: ToolConfig<PostHogListCohortsParams, PostHogListCo
description:
'List all cohorts in a PostHog project. Returns cohort definitions, filters, and user counts.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -60,6 +63,13 @@ export const listCohortsTool: ToolConfig<PostHogListCohortsParams, PostHogListCo
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
limit: {
type: 'number',
required: false,
@@ -76,7 +86,7 @@ export const listCohortsTool: ToolConfig<PostHogListCohortsParams, PostHogListCo
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region as 'us' | 'eu' | undefined, params.host)
let url = `${baseUrl}/api/projects/${params.projectId}/cohorts/`
const queryParams = []
+11 -1
View File
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogListDashboardsParams {
apiKey: string
projectId: string
region: string
host?: string
limit?: number
offset?: number
}
@@ -39,6 +41,7 @@ export const listDashboardsTool: ToolConfig<
description:
'List all dashboards in a PostHog project. Returns dashboard configurations, tiles, and metadata.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -60,6 +63,13 @@ export const listDashboardsTool: ToolConfig<
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
limit: {
type: 'number',
required: false,
@@ -76,7 +86,7 @@ export const listDashboardsTool: ToolConfig<
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region as 'us' | 'eu' | undefined, params.host)
let url = `${baseUrl}/api/projects/${params.projectId}/dashboards/`
const queryParams = []
@@ -1,8 +1,10 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogListEventDefinitionsParams {
projectId: string
region: 'us' | 'eu'
host?: string
apiKey: string
limit?: number
offset?: number
@@ -14,8 +16,6 @@ interface EventDefinition {
name: string
description: string
tags: string[]
volume_30_day: number | null
query_usage_30_day: number | null
created_at: string
last_seen_at: string | null
updated_at: string
@@ -44,6 +44,7 @@ export const listEventDefinitionsTool: ToolConfig<
description:
'List all event definitions in a PostHog project. Event definitions represent tracked events with metadata like descriptions, tags, and usage statistics.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -58,6 +59,13 @@ export const listEventDefinitionsTool: ToolConfig<
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -86,7 +94,7 @@ export const listEventDefinitionsTool: ToolConfig<
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
const queryParams = new URLSearchParams()
if (params.limit) queryParams.append('limit', params.limit.toString())
@@ -115,8 +123,6 @@ export const listEventDefinitionsTool: ToolConfig<
name: event.name,
description: event.description || '',
tags: event.tags || [],
volume_30_day: event.volume_30_day ?? null,
query_usage_30_day: event.query_usage_30_day ?? null,
created_at: event.created_at,
last_seen_at: event.last_seen_at ?? null,
updated_at: event.updated_at,
@@ -150,16 +156,6 @@ export const listEventDefinitionsTool: ToolConfig<
name: { type: 'string', description: 'Event name' },
description: { type: 'string', description: 'Event description' },
tags: { type: 'array', description: 'Tags associated with the event' },
volume_30_day: {
type: 'number',
description: 'Number of events received in the last 30 days',
optional: true,
},
query_usage_30_day: {
type: 'number',
description: 'Number of times this event was queried in the last 30 days',
optional: true,
},
created_at: { type: 'string', description: 'ISO timestamp when the event was created' },
last_seen_at: {
type: 'string',
+11 -3
View File
@@ -1,8 +1,10 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface ListExperimentsParams {
projectId: string
region: 'us' | 'eu'
host?: string
apiKey: string
limit?: number
offset?: number
@@ -16,7 +18,6 @@ interface Experiment {
feature_flag: Record<string, any>
parameters: Record<string, any>
filters: Record<string, any>
variants: Record<string, any>
start_date: string | null
end_date: string | null
created_at: string
@@ -36,6 +37,7 @@ export const listExperimentsTool: ToolConfig<ListExperimentsParams, ListExperime
name: 'PostHog List Experiments',
description: 'List all experiments in a PostHog project',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -50,6 +52,13 @@ export const listExperimentsTool: ToolConfig<ListExperimentsParams, ListExperime
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -72,7 +81,7 @@ export const listExperimentsTool: ToolConfig<ListExperimentsParams, ListExperime
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
const url = new URL(`${baseUrl}/api/projects/${params.projectId}/experiments/`)
if (params.limit) url.searchParams.append('limit', String(params.limit))
@@ -112,7 +121,6 @@ export const listExperimentsTool: ToolConfig<ListExperimentsParams, ListExperime
feature_flag: { type: 'object', description: 'Feature flag details' },
parameters: { type: 'object', description: 'Experiment parameters' },
filters: { type: 'object', description: 'Experiment filters' },
variants: { type: 'object', description: 'Experiment variants' },
start_date: { type: 'string', description: 'Start date', optional: true },
end_date: { type: 'string', description: 'End date', optional: true },
created_at: { type: 'string', description: 'Creation timestamp' },
+11 -1
View File
@@ -1,8 +1,10 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface ListFeatureFlagsParams {
projectId: string
region: 'us' | 'eu'
host?: string
apiKey: string
limit?: number
offset?: number
@@ -34,6 +36,7 @@ export const listFeatureFlagsTool: ToolConfig<ListFeatureFlagsParams, ListFeatur
name: 'PostHog List Feature Flags',
description: 'List all feature flags in a PostHog project',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -48,6 +51,13 @@ export const listFeatureFlagsTool: ToolConfig<ListFeatureFlagsParams, ListFeatur
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -70,7 +80,7 @@ export const listFeatureFlagsTool: ToolConfig<ListFeatureFlagsParams, ListFeatur
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
const url = new URL(`${baseUrl}/api/projects/${params.projectId}/feature_flags/`)
if (params.limit) url.searchParams.append('limit', String(params.limit))
+12 -8
View File
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogListInsightsParams {
apiKey: string
projectId: string
region: string
host?: string
limit?: number
offset?: number
}
@@ -18,13 +20,11 @@ interface PostHogListInsightsResponse {
id: number
name: string
description: string
filters: Record<string, any>
query: Record<string, any> | null
created_at: string
created_by: Record<string, any> | null
last_modified_at: string
last_modified_by: Record<string, any> | null
saved: boolean
dashboards: number[]
}>
}
@@ -35,8 +35,9 @@ export const listInsightsTool: ToolConfig<PostHogListInsightsParams, PostHogList
id: 'posthog_list_insights',
name: 'PostHog List Insights',
description:
'List all insights in a PostHog project. Returns insight configurations, filters, and metadata.',
'List all insights in a PostHog project. Returns insight configurations and metadata.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -58,6 +59,13 @@ export const listInsightsTool: ToolConfig<PostHogListInsightsParams, PostHogList
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
limit: {
type: 'number',
required: false,
@@ -74,7 +82,7 @@ export const listInsightsTool: ToolConfig<PostHogListInsightsParams, PostHogList
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region as 'us' | 'eu' | undefined, params.host)
let url = `${baseUrl}/api/projects/${params.projectId}/insights/`
const queryParams = []
@@ -107,13 +115,11 @@ export const listInsightsTool: ToolConfig<PostHogListInsightsParams, PostHogList
id: insight.id,
name: insight.name || '',
description: insight.description || '',
filters: insight.filters || {},
query: insight.query || null,
created_at: insight.created_at,
created_by: insight.created_by || null,
last_modified_at: insight.last_modified_at,
last_modified_by: insight.last_modified_by || null,
saved: insight.saved || false,
dashboards: insight.dashboards || [],
})),
},
@@ -144,7 +150,6 @@ export const listInsightsTool: ToolConfig<PostHogListInsightsParams, PostHogList
id: { type: 'number', description: 'Unique identifier for the insight' },
name: { type: 'string', description: 'Name of the insight' },
description: { type: 'string', description: 'Description of the insight' },
filters: { type: 'object', description: 'Filter configuration for the insight' },
query: { type: 'object', description: 'Query configuration for the insight' },
created_at: { type: 'string', description: 'ISO timestamp when insight was created' },
created_by: { type: 'object', description: 'User who created the insight' },
@@ -153,7 +158,6 @@ export const listInsightsTool: ToolConfig<PostHogListInsightsParams, PostHogList
description: 'ISO timestamp when insight was last modified',
},
last_modified_by: { type: 'object', description: 'User who last modified the insight' },
saved: { type: 'boolean', description: 'Whether the insight is saved' },
dashboards: {
type: 'array',
description: 'IDs of dashboards this insight appears on',
+12 -2
View File
@@ -1,8 +1,10 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
export interface PostHogListOrganizationsParams {
apiKey: string
region?: 'us' | 'eu'
host?: string
}
interface PostHogOrganization {
@@ -41,6 +43,7 @@ export const listOrganizationsTool: ToolConfig<
description:
'List all organizations the user has access to. Returns organization details including name, slug, membership level, and available product features.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -55,11 +58,18 @@ export const listOrganizationsTool: ToolConfig<
visibility: 'user-only',
description: 'Cloud region: us or eu (default: us)',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
},
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/organizations/`
},
method: 'GET',
@@ -75,7 +85,7 @@ export const listOrganizationsTool: ToolConfig<
return {
success: true,
output: {
organizations: data.results.map((org: any) => ({
organizations: (data.results || []).map((org: any) => ({
id: org.id,
name: org.name,
slug: org.slug,
+11 -12
View File
@@ -1,8 +1,10 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
export interface PostHogListPersonsParams {
apiKey: string
region?: 'us' | 'eu'
host?: string
projectId: string
limit?: number
offset?: number
@@ -33,6 +35,7 @@ export const listPersonsTool: ToolConfig<PostHogListPersonsParams, PostHogListPe
description:
'List persons (users) in PostHog. Returns user profiles with their properties and distinct IDs.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -48,6 +51,13 @@ export const listPersonsTool: ToolConfig<PostHogListPersonsParams, PostHogListPe
description: 'PostHog region: us (default) or eu',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
projectId: {
type: 'string',
required: true,
@@ -83,7 +93,7 @@ export const listPersonsTool: ToolConfig<PostHogListPersonsParams, PostHogListPe
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
const url = new URL(`${baseUrl}/api/projects/${params.projectId}/persons/`)
if (params.limit) url.searchParams.append('limit', params.limit.toString())
@@ -101,17 +111,6 @@ export const listPersonsTool: ToolConfig<PostHogListPersonsParams, PostHogListPe
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.text()
return {
success: false,
output: {
persons: [],
},
error: error || 'Failed to list persons',
}
}
const data = await response.json()
return {
+12 -2
View File
@@ -1,8 +1,10 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
export interface PostHogListProjectsParams {
apiKey: string
region?: 'us' | 'eu'
host?: string
}
interface PostHogProject {
@@ -39,6 +41,7 @@ export const listProjectsTool: ToolConfig<PostHogListProjectsParams, PostHogList
description:
'List all projects in the organization. Returns project details including IDs, names, API tokens, and settings. Useful for getting project IDs needed by other endpoints.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -53,11 +56,18 @@ export const listProjectsTool: ToolConfig<PostHogListProjectsParams, PostHogList
visibility: 'user-only',
description: 'Cloud region: us or eu (default: us)',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
},
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/`
},
method: 'GET',
@@ -73,7 +83,7 @@ export const listProjectsTool: ToolConfig<PostHogListProjectsParams, PostHogList
return {
success: true,
output: {
projects: data.results.map((project: any) => ({
projects: (data.results || []).map((project: any) => ({
id: project.id,
uuid: project.uuid,
organization: project.organization,
@@ -1,13 +1,15 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogListPropertyDefinitionsParams {
projectId: string
region: 'us' | 'eu'
host?: string
apiKey: string
limit?: number
offset?: number
search?: string
type?: 'event' | 'person' | 'group'
type?: 'event' | 'person' | 'group' | 'session'
}
interface PropertyDefinition {
@@ -18,9 +20,7 @@ interface PropertyDefinition {
is_numerical: boolean
is_seen_on_filtered_events: boolean | null
property_type: string
type: 'event' | 'person' | 'group'
volume_30_day: number | null
query_usage_30_day: number | null
type: 'event' | 'person' | 'group' | 'session'
created_at: string
updated_at: string
updated_by: {
@@ -48,6 +48,7 @@ export const listPropertyDefinitionsTool: ToolConfig<
description:
'List all property definitions in a PostHog project. Property definitions represent tracked properties with metadata like descriptions, tags, types, and usage statistics.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -62,6 +63,13 @@ export const listPropertyDefinitionsTool: ToolConfig<
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -96,7 +104,7 @@ export const listPropertyDefinitionsTool: ToolConfig<
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
const queryParams = new URLSearchParams()
if (params.limit) queryParams.append('limit', params.limit.toString())
@@ -130,8 +138,6 @@ export const listPropertyDefinitionsTool: ToolConfig<
is_seen_on_filtered_events: property.is_seen_on_filtered_events ?? null,
property_type: property.property_type,
type: property.type,
volume_30_day: property.volume_30_day ?? null,
query_usage_30_day: property.query_usage_30_day ?? null,
created_at: property.created_at,
updated_at: property.updated_at,
updated_by: property.updated_by ?? null,
@@ -171,17 +177,7 @@ export const listPropertyDefinitionsTool: ToolConfig<
optional: true,
},
property_type: { type: 'string', description: 'The data type of the property' },
type: { type: 'string', description: 'Property type: event, person, or group' },
volume_30_day: {
type: 'number',
description: 'Number of times property was seen in the last 30 days',
optional: true,
},
query_usage_30_day: {
type: 'number',
description: 'Number of times this property was queried in the last 30 days',
optional: true,
},
type: { type: 'string', description: 'Property type: event, person, group, or session' },
created_at: {
type: 'string',
description: 'ISO timestamp when the property was created',
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogListRecordingPlaylistsParams {
apiKey: string
projectId: string
region?: 'us' | 'eu'
host?: string
limit?: number
offset?: number
}
@@ -47,6 +49,7 @@ export const listRecordingPlaylistsTool: ToolConfig<
description:
'List session recording playlists in a PostHog project. Playlists allow you to organize and curate session recordings.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -68,6 +71,13 @@ export const listRecordingPlaylistsTool: ToolConfig<
description: 'PostHog cloud region: us or eu (default: us)',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
limit: {
type: 'number',
required: false,
@@ -84,7 +94,7 @@ export const listRecordingPlaylistsTool: ToolConfig<
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
const url = new URL(
`${baseUrl}/api/projects/${params.projectId}/session_recording_playlists/`
)
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogListSessionRecordingsParams {
apiKey: string
projectId: string
region?: 'us' | 'eu'
host?: string
limit?: number
offset?: number
}
@@ -48,6 +50,7 @@ export const listSessionRecordingsTool: ToolConfig<
description:
'List session recordings in a PostHog project. Session recordings capture user interactions with your application.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -69,6 +72,13 @@ export const listSessionRecordingsTool: ToolConfig<
description: 'PostHog cloud region: us or eu (default: us)',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
limit: {
type: 'number',
required: false,
@@ -85,7 +95,7 @@ export const listSessionRecordingsTool: ToolConfig<
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
const url = new URL(`${baseUrl}/api/projects/${params.projectId}/session_recordings/`)
if (params.limit) {
+11 -1
View File
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogListSurveysParams {
apiKey: string
projectId: string
region?: 'us' | 'eu'
host?: string
limit?: number
offset?: number
}
@@ -45,6 +47,7 @@ export const listSurveysTool: ToolConfig<PostHogListSurveysParams, PostHogListSu
description:
'List all surveys in a PostHog project. Surveys allow you to collect feedback from users.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -66,6 +69,13 @@ export const listSurveysTool: ToolConfig<PostHogListSurveysParams, PostHogListSu
description: 'PostHog cloud region: us or eu (default: us)',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
limit: {
type: 'number',
required: false,
@@ -82,7 +92,7 @@ export const listSurveysTool: ToolConfig<PostHogListSurveysParams, PostHogListSu
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
const url = new URL(`${baseUrl}/api/projects/${params.projectId}/surveys/`)
if (params.limit) {
+11 -12
View File
@@ -1,8 +1,10 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
export interface PostHogQueryParams {
apiKey: string
region?: 'us' | 'eu'
host?: string
projectId: string
query: string
values?: string
@@ -25,6 +27,7 @@ export const queryTool: ToolConfig<PostHogQueryParams, PostHogQueryResponse> = {
description:
"Execute a HogQL query in PostHog. HogQL is PostHog's SQL-like query language for analytics. Use this for advanced data retrieval and analysis.",
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -40,6 +43,13 @@ export const queryTool: ToolConfig<PostHogQueryParams, PostHogQueryResponse> = {
description: 'PostHog region: us (default) or eu',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
projectId: {
type: 'string',
required: true,
@@ -64,7 +74,7 @@ export const queryTool: ToolConfig<PostHogQueryParams, PostHogQueryResponse> = {
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/query/`
},
method: 'POST',
@@ -101,17 +111,6 @@ export const queryTool: ToolConfig<PostHogQueryParams, PostHogQueryResponse> = {
},
transformResponse: async (response: Response) => {
if (!response.ok) {
const error = await response.text()
return {
success: false,
output: {
results: [],
},
error: error || 'Failed to execute query',
}
}
const data = await response.json()
return {
-4
View File
@@ -158,8 +158,6 @@ interface PostHogEventDefinition {
name: string
description: string
tags: string[]
volume_30_day?: number
query_usage_30_day?: number
created_at: string
last_seen_at?: string
verified: boolean
@@ -172,8 +170,6 @@ interface PostHogPropertyDefinition {
tags: string[]
is_numerical: boolean
property_type: 'DateTime' | 'String' | 'Numeric' | 'Boolean'
volume_30_day?: number
query_usage_30_day?: number
verified: boolean
}
+245
View File
@@ -0,0 +1,245 @@
import { getErrorMessage } from '@sim/utils/errors'
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogUpdateCohortParams {
apiKey: string
projectId: string
cohortId: string
region?: 'us' | 'eu'
host?: string
name?: string
description?: string
filters?: string
query?: string
isStatic?: boolean
groups?: string
deleted?: boolean
}
interface PostHogUpdateCohortResponse {
success: boolean
output: {
id: number
name: string
description: string
groups: Array<Record<string, any>>
deleted: boolean
filters: Record<string, any>
query: Record<string, any> | null
created_at: string
is_calculating: boolean
count: number
is_static: boolean
version: number
}
}
export const updateCohortTool: ToolConfig<PostHogUpdateCohortParams, PostHogUpdateCohortResponse> =
{
id: 'posthog_update_cohort',
name: 'PostHog Update Cohort',
description:
'Update an existing cohort in PostHog. Can modify name, description, filters, query, static membership, and deleted status.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'PostHog Personal API Key',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The PostHog project ID (e.g., "12345" or project UUID)',
},
cohortId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The cohort ID to update (e.g., "42")',
},
region: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated name for the cohort',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated description of the cohort',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'JSON string of updated filter configuration for the cohort',
},
query: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'JSON string of updated query configuration for the cohort',
},
isStatic: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether the cohort is static',
},
groups: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'JSON string of updated groups that define the cohort',
},
deleted: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Set to true to archive (soft-delete) the cohort',
},
},
request: {
url: (params) => {
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/cohorts/${params.cohortId}/`
},
method: 'PATCH',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const body: Record<string, any> = {}
if (params.name !== undefined) body.name = params.name
if (params.description !== undefined) body.description = params.description
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch (error) {
throw new Error(`Invalid filters JSON: ${getErrorMessage(error)}`)
}
}
if (params.query) {
try {
body.query = JSON.parse(params.query)
} catch (error) {
throw new Error(`Invalid query JSON: ${getErrorMessage(error)}`)
}
}
if (params.isStatic !== undefined) body.is_static = params.isStatic
if (params.groups) {
try {
body.groups = JSON.parse(params.groups)
} catch (error) {
throw new Error(`Invalid groups JSON: ${getErrorMessage(error)}`)
}
}
if (params.deleted !== undefined) body.deleted = params.deleted
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name || '',
description: data.description || '',
groups: data.groups || [],
deleted: data.deleted || false,
filters: data.filters || {},
query: data.query || null,
created_at: data.created_at,
is_calculating: data.is_calculating || false,
count: data.count || 0,
is_static: data.is_static || false,
version: data.version || 0,
},
}
},
outputs: {
id: {
type: 'number',
description: 'Unique identifier for the cohort',
},
name: {
type: 'string',
description: 'Name of the cohort',
},
description: {
type: 'string',
description: 'Description of the cohort',
},
groups: {
type: 'array',
description: 'Groups that define the cohort',
},
deleted: {
type: 'boolean',
description: 'Whether the cohort is deleted',
},
filters: {
type: 'object',
description: 'Filter configuration for the cohort',
},
query: {
type: 'object',
description: 'Query configuration for the cohort',
optional: true,
},
created_at: {
type: 'string',
description: 'ISO timestamp when cohort was created',
},
is_calculating: {
type: 'boolean',
description: 'Whether the cohort is being calculated',
},
count: {
type: 'number',
description: 'Number of users in the cohort',
},
is_static: {
type: 'boolean',
description: 'Whether the cohort is static',
},
version: {
type: 'number',
description: 'Version number of the cohort',
},
},
}
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogUpdateEventDefinitionParams {
projectId: string
eventDefinitionId: string
region: 'us' | 'eu'
host?: string
apiKey: string
description?: string
tags?: string
@@ -15,8 +17,6 @@ interface EventDefinition {
name: string
description: string
tags: string[]
volume_30_day: number | null
query_usage_30_day: number | null
created_at: string
last_seen_at: string | null
updated_at: string
@@ -41,6 +41,7 @@ export const updateEventDefinitionTool: ToolConfig<
description:
'Update an event definition in PostHog. Can modify description, tags, and verification status to maintain clean event schemas.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -61,6 +62,13 @@ export const updateEventDefinitionTool: ToolConfig<
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -89,7 +97,7 @@ export const updateEventDefinitionTool: ToolConfig<
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/event_definitions/${params.eventDefinitionId}`
},
method: 'PATCH',
@@ -127,8 +135,6 @@ export const updateEventDefinitionTool: ToolConfig<
name: data.name,
description: data.description || '',
tags: data.tags || [],
volume_30_day: data.volume_30_day ?? null,
query_usage_30_day: data.query_usage_30_day ?? null,
created_at: data.created_at,
last_seen_at: data.last_seen_at ?? null,
updated_at: data.updated_at,
@@ -156,16 +162,6 @@ export const updateEventDefinitionTool: ToolConfig<
type: 'array',
description: 'Updated tags associated with the event',
},
volume_30_day: {
type: 'number',
description: 'Number of events received in the last 30 days',
optional: true,
},
query_usage_30_day: {
type: 'number',
description: 'Number of times this event was queried in the last 30 days',
optional: true,
},
created_at: {
type: 'string',
description: 'ISO timestamp when the event was created',
+190
View File
@@ -0,0 +1,190 @@
import { getErrorMessage } from '@sim/utils/errors'
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface UpdateExperimentParams {
projectId: string
experimentId: string
region?: 'us' | 'eu'
host?: string
apiKey: string
name?: string
description?: string
parameters?: string
filters?: string
startDate?: string
endDate?: string
archived?: boolean
}
interface Experiment {
id: number
name: string
description: string
feature_flag_key: string
feature_flag: Record<string, any>
parameters: Record<string, any>
filters: Record<string, any>
start_date: string | null
end_date: string | null
created_at: string
archived: boolean
}
interface UpdateExperimentResponse {
experiment: Experiment
}
export const updateExperimentTool: ToolConfig<UpdateExperimentParams, UpdateExperimentResponse> = {
id: 'posthog_update_experiment',
name: 'PostHog Update Experiment',
description:
'Update an existing experiment in PostHog. Use this to change dates, archive an experiment, or adjust its parameters and filters.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The PostHog project ID (e.g., "12345" or project UUID)',
},
experimentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The experiment ID to update (e.g., "42")',
},
region: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'PostHog cloud region: us or eu (default: us)',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'PostHog Personal API Key',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated experiment name',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated experiment description',
},
parameters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated experiment parameters as JSON string',
},
filters: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated experiment filters as JSON string',
},
startDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated start date (ISO 8601). Set this to launch a draft experiment.',
},
endDate: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated end date (ISO 8601). Set this to conclude a running experiment.',
},
archived: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to archive the experiment',
},
},
request: {
url: (params) => {
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/experiments/${params.experimentId}/`
},
method: 'PATCH',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, any> = {}
if (params.name !== undefined) body.name = params.name
if (params.description !== undefined) body.description = params.description
if (params.parameters) {
try {
body.parameters = JSON.parse(params.parameters)
} catch (error) {
throw new Error(`Invalid parameters JSON: ${getErrorMessage(error)}`)
}
}
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch (error) {
throw new Error(`Invalid filters JSON: ${getErrorMessage(error)}`)
}
}
if (params.startDate !== undefined) body.start_date = params.startDate
if (params.endDate !== undefined) body.end_date = params.endDate
if (params.archived !== undefined) body.archived = params.archived
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
experiment: data,
}
},
outputs: {
experiment: {
type: 'object',
description: 'Updated experiment',
properties: {
id: { type: 'number', description: 'Experiment ID' },
name: { type: 'string', description: 'Experiment name' },
description: { type: 'string', description: 'Experiment description' },
feature_flag_key: { type: 'string', description: 'Associated feature flag key' },
feature_flag: { type: 'object', description: 'Feature flag details' },
parameters: { type: 'object', description: 'Experiment parameters' },
filters: { type: 'object', description: 'Experiment filters' },
start_date: { type: 'string', description: 'Start date', optional: true },
end_date: { type: 'string', description: 'End date', optional: true },
created_at: { type: 'string', description: 'Creation timestamp' },
archived: { type: 'boolean', description: 'Whether the experiment is archived' },
},
},
},
}
+15 -4
View File
@@ -1,9 +1,12 @@
import { getErrorMessage } from '@sim/utils/errors'
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface UpdateFeatureFlagParams {
projectId: string
flagId: string
region: 'us' | 'eu'
host?: string
apiKey: string
name?: string
key?: string
@@ -37,6 +40,7 @@ export const updateFeatureFlagTool: ToolConfig<UpdateFeatureFlagParams, UpdateFe
name: 'PostHog Update Feature Flag',
description: 'Update an existing feature flag in PostHog',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -57,6 +61,13 @@ export const updateFeatureFlagTool: ToolConfig<UpdateFeatureFlagParams, UpdateFe
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -103,8 +114,8 @@ export const updateFeatureFlagTool: ToolConfig<UpdateFeatureFlagParams, UpdateFe
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
return `${baseUrl}/api/projects/${params.projectId}/feature_flags/${params.flagId}`
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/feature_flags/${params.flagId}/`
},
method: 'PATCH',
headers: (params) => ({
@@ -125,8 +136,8 @@ export const updateFeatureFlagTool: ToolConfig<UpdateFeatureFlagParams, UpdateFe
if (params.filters) {
try {
body.filters = JSON.parse(params.filters)
} catch {
body.filters = {}
} catch (error) {
throw new Error(`Invalid filters JSON: ${getErrorMessage(error)}`)
}
}
+218
View File
@@ -0,0 +1,218 @@
import { getErrorMessage } from '@sim/utils/errors'
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogUpdateInsightParams {
apiKey: string
projectId: string
insightId: string
region?: 'us' | 'eu'
host?: string
name?: string
description?: string
query?: string
dashboards?: string
tags?: string
favorited?: boolean
}
interface PostHogUpdateInsightResponse {
success: boolean
output: {
id: number
name: string
description: string
query: Record<string, any> | null
created_at: string
last_modified_at: string
dashboards: number[]
tags: string[]
favorited: boolean
}
}
export const updateInsightTool: ToolConfig<
PostHogUpdateInsightParams,
PostHogUpdateInsightResponse
> = {
id: 'posthog_update_insight',
name: 'PostHog Update Insight',
description:
'Update an existing insight in PostHog. Can modify name, description, query, dashboards, tags, and favorited status.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'PostHog Personal API Key',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The PostHog project ID (e.g., "12345" or project UUID)',
},
insightId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The insight ID to update (e.g., "42" or short ID like "abc123")',
},
region: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'PostHog cloud region: "us" or "eu" (default: "us")',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated name for the insight',
},
description: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Updated description for the insight',
},
query: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'JSON string of updated query configuration for the insight',
},
dashboards: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of dashboard IDs to attach this insight to',
},
tags: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated list of tags for the insight',
},
favorited: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to mark the insight as favorited',
},
},
request: {
url: (params) => {
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/insights/${params.insightId}/`
},
method: 'PATCH',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const body: Record<string, any> = {}
if (params.name !== undefined) body.name = params.name
if (params.description !== undefined) body.description = params.description
if (params.query) {
try {
body.query = JSON.parse(params.query)
} catch (error) {
throw new Error(`Invalid query JSON: ${getErrorMessage(error)}`)
}
}
if (params.dashboards) {
body.dashboards = params.dashboards
.split(',')
.map((id: string) => Number(id.trim()))
.filter((id: number) => !Number.isNaN(id))
}
if (params.tags) {
body.tags = params.tags
.split(',')
.map((tag: string) => tag.trim())
.filter((tag: string) => tag.length > 0)
}
if (params.favorited !== undefined) body.favorited = params.favorited
return body
},
},
transformResponse: async (response: Response) => {
const data = await response.json()
return {
success: true,
output: {
id: data.id,
name: data.name || '',
description: data.description || '',
query: data.query || null,
created_at: data.created_at,
last_modified_at: data.last_modified_at,
dashboards: data.dashboards || [],
tags: data.tags || [],
favorited: data.favorited || false,
},
}
},
outputs: {
id: {
type: 'number',
description: 'Unique identifier for the insight',
},
name: {
type: 'string',
description: 'Name of the insight',
},
description: {
type: 'string',
description: 'Description of the insight',
},
query: {
type: 'object',
description: 'Query configuration for the insight',
optional: true,
},
created_at: {
type: 'string',
description: 'ISO timestamp when insight was created',
},
last_modified_at: {
type: 'string',
description: 'ISO timestamp when insight was last modified',
},
dashboards: {
type: 'array',
description: 'IDs of dashboards this insight appears on',
},
tags: {
type: 'array',
description: 'Tags associated with the insight',
},
favorited: {
type: 'boolean',
description: 'Whether the insight is favorited',
},
},
}
@@ -1,9 +1,11 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogUpdatePropertyDefinitionParams {
projectId: string
propertyDefinitionId: string
region: 'us' | 'eu'
host?: string
apiKey: string
description?: string
tags?: string
@@ -19,9 +21,7 @@ interface PropertyDefinition {
is_numerical: boolean
is_seen_on_filtered_events: boolean | null
property_type: string
type: 'event' | 'person' | 'group'
volume_30_day: number | null
query_usage_30_day: number | null
type: 'event' | 'person' | 'group' | 'session'
created_at: string
updated_at: string
updated_by: {
@@ -34,7 +34,6 @@ interface PropertyDefinition {
verified: boolean
verified_at: string | null
verified_by: string | null
example: string | null
}
export const updatePropertyDefinitionTool: ToolConfig<
@@ -46,6 +45,7 @@ export const updatePropertyDefinitionTool: ToolConfig<
description:
'Update a property definition in PostHog. Can modify description, tags, property type, and verification status to maintain clean property schemas.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
projectId: {
@@ -66,6 +66,13 @@ export const updatePropertyDefinitionTool: ToolConfig<
visibility: 'user-only',
description: 'PostHog cloud region: us or eu',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
apiKey: {
type: 'string',
required: true,
@@ -100,7 +107,7 @@ export const updatePropertyDefinitionTool: ToolConfig<
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/property_definitions/${params.propertyDefinitionId}`
},
method: 'PATCH',
@@ -146,15 +153,12 @@ export const updatePropertyDefinitionTool: ToolConfig<
is_seen_on_filtered_events: data.is_seen_on_filtered_events ?? null,
property_type: data.property_type,
type: data.type,
volume_30_day: data.volume_30_day ?? null,
query_usage_30_day: data.query_usage_30_day ?? null,
created_at: data.created_at,
updated_at: data.updated_at,
updated_by: data.updated_by ?? null,
verified: data.verified || false,
verified_at: data.verified_at ?? null,
verified_by: data.verified_by ?? null,
example: data.example ?? null,
}
},
@@ -190,17 +194,7 @@ export const updatePropertyDefinitionTool: ToolConfig<
},
type: {
type: 'string',
description: 'Property type: event, person, or group',
},
volume_30_day: {
type: 'number',
description: 'Number of times property was seen in the last 30 days',
optional: true,
},
query_usage_30_day: {
type: 'number',
description: 'Number of times this property was queried in the last 30 days',
optional: true,
description: 'Property type: event, person, group, or session',
},
created_at: {
type: 'string',
@@ -229,10 +223,5 @@ export const updatePropertyDefinitionTool: ToolConfig<
description: 'User who verified the property',
optional: true,
},
example: {
type: 'string',
description: 'Example value for the property',
optional: true,
},
},
}
+11 -1
View File
@@ -1,3 +1,4 @@
import { getPostHogAppBaseUrl } from '@/tools/posthog/utils'
import type { ToolConfig } from '@/tools/types'
interface PostHogSurveyQuestion {
@@ -16,6 +17,7 @@ interface PostHogUpdateSurveyParams {
projectId: string
surveyId: string
region?: 'us' | 'eu'
host?: string
name?: string
description?: string
type?: 'popover' | 'api'
@@ -62,6 +64,7 @@ export const updateSurveyTool: ToolConfig<PostHogUpdateSurveyParams, PostHogUpda
description:
'Update an existing survey in PostHog. Can modify questions, appearance, conditions, and other settings.',
version: '1.0.0',
errorExtractor: 'posthog-errors',
params: {
apiKey: {
@@ -89,6 +92,13 @@ export const updateSurveyTool: ToolConfig<PostHogUpdateSurveyParams, PostHogUpda
description: 'PostHog cloud region: us or eu (default: us)',
default: 'us',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description:
'Self-hosted PostHog instance host (e.g., "posthog.mycompany.com"). Overrides the region setting when provided.',
},
name: {
type: 'string',
required: false,
@@ -166,7 +176,7 @@ export const updateSurveyTool: ToolConfig<PostHogUpdateSurveyParams, PostHogUpda
request: {
url: (params) => {
const baseUrl = params.region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
const baseUrl = getPostHogAppBaseUrl(params.region, params.host)
return `${baseUrl}/api/projects/${params.projectId}/surveys/${params.surveyId}/`
},
method: 'PATCH',
+40
View File
@@ -0,0 +1,40 @@
import { validateExternalUrl } from '@/lib/core/security/input-validation'
/**
* Shared PostHog base URL resolution.
*
* PostHog exposes two host families:
* - The "app" REST API (`/api/...`) at `us.posthog.com` / `eu.posthog.com`, authenticated
* with a personal API key.
* - The "ingest" API (`/i/v0/e`, `/batch/`, `/flags/`) at `us.i.posthog.com` / `eu.i.posthog.com`,
* authenticated with a project API key.
*
* Self-hosted PostHog instances serve both families from a single custom host. That host is
* validated with the shared SSRF guard so it can't be pointed at loopback/private/link-local
* addresses (e.g. cloud instance-metadata endpoints); the tool executor additionally
* re-validates with DNS resolution and pins the resolved IP for the actual request.
*/
export function getPostHogAppBaseUrl(region?: 'us' | 'eu', host?: string): string {
if (host?.trim()) {
return normalizeHost(host)
}
return region === 'eu' ? 'https://eu.posthog.com' : 'https://us.posthog.com'
}
export function getPostHogIngestBaseUrl(region?: 'us' | 'eu', host?: string): string {
if (host?.trim()) {
return normalizeHost(host)
}
return region === 'eu' ? 'https://eu.i.posthog.com' : 'https://us.i.posthog.com'
}
function normalizeHost(host: string): string {
const trimmed = host.trim().replace(/\/+$/, '')
const withProtocol = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`
const validation = validateExternalUrl(withProtocol, 'Self-hosted host')
if (!validation.isValid) {
throw new Error(`${validation.error} (e.g., posthog.mycompany.com)`)
}
return withProtocol
}
+10
View File
@@ -2651,12 +2651,14 @@ import {
posthogCaptureEventTool,
posthogCreateAnnotationTool,
posthogCreateCohortTool,
posthogCreateDashboardTool,
posthogCreateExperimentTool,
posthogCreateFeatureFlagTool,
posthogCreateInsightTool,
posthogCreateSurveyTool,
posthogDeleteFeatureFlagTool,
posthogDeletePersonTool,
posthogDeleteSurveyTool,
posthogEvaluateFlagsTool,
posthogGetCohortTool,
posthogGetDashboardTool,
@@ -2686,8 +2688,11 @@ import {
posthogListSessionRecordingsTool,
posthogListSurveysTool,
posthogQueryTool,
posthogUpdateCohortTool,
posthogUpdateEventDefinitionTool,
posthogUpdateExperimentTool,
posthogUpdateFeatureFlagTool,
posthogUpdateInsightTool,
posthogUpdatePropertyDefinitionTool,
posthogUpdateSurveyTool,
} from '@/tools/posthog'
@@ -6564,12 +6569,15 @@ export const tools: Record<string, ToolConfig> = {
posthog_list_insights: posthogListInsightsTool,
posthog_get_insight: posthogGetInsightTool,
posthog_create_insight: posthogCreateInsightTool,
posthog_update_insight: posthogUpdateInsightTool,
posthog_list_dashboards: posthogListDashboardsTool,
posthog_get_dashboard: posthogGetDashboardTool,
posthog_create_dashboard: posthogCreateDashboardTool,
posthog_list_actions: posthogListActionsTool,
posthog_list_cohorts: posthogListCohortsTool,
posthog_get_cohort: posthogGetCohortTool,
posthog_create_cohort: posthogCreateCohortTool,
posthog_update_cohort: posthogUpdateCohortTool,
posthog_list_annotations: posthogListAnnotationsTool,
posthog_create_annotation: posthogCreateAnnotationTool,
posthog_list_feature_flags: posthogListFeatureFlagsTool,
@@ -6581,10 +6589,12 @@ export const tools: Record<string, ToolConfig> = {
posthog_list_experiments: posthogListExperimentsTool,
posthog_get_experiment: posthogGetExperimentTool,
posthog_create_experiment: posthogCreateExperimentTool,
posthog_update_experiment: posthogUpdateExperimentTool,
posthog_list_surveys: posthogListSurveysTool,
posthog_get_survey: posthogGetSurveyTool,
posthog_create_survey: posthogCreateSurveyTool,
posthog_update_survey: posthogUpdateSurveyTool,
posthog_delete_survey: posthogDeleteSurveyTool,
posthog_list_session_recordings: posthogListSessionRecordingsTool,
posthog_get_session_recording: posthogGetSessionRecordingTool,
posthog_list_recording_playlists: posthogListRecordingPlaylistsTool,