Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "posthog",
"description": "Access PostHog analytics, feature flags, experiments, error tracking, and insights directly from Claude Code. Optionally capture Claude Code sessions to PostHog LLM Analytics.",
"version": "1.1.20",
"version": "1.1.21",
"author": {
"name": "PostHog",
"email": "hey@posthog.com",
Expand Down
21 changes: 11 additions & 10 deletions hooks/gate-exec-write.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#!/usr/bin/env bash
# PreToolUse gate for the PostHog MCP `exec` tool.
# PreToolUse gate for the PostHog MCP `exec` / `cli` tools.
#
# The PostHog MCP exposes a single `exec` tool that dispatches subcommands like
# The PostHog MCP exposes a single dispatcher tool (named `exec` or `cli`
# depending on the server build) that runs subcommands like
# `tools | search | info | schema | call <tool_name> [json]`. Once the user
# allow-lists `mcp__posthog__exec`, every subsequent `call` (including writes
# like `experiment-update`, `notebooks-destroy`, `cdp-functions-delete`) runs
# without a prompt. This hook re-introduces a prompt for write `call`s by
# returning `permissionDecision: "ask"`.
# allow-lists `mcp__posthog__exec` (or `mcp__posthog__cli`), every subsequent
# `call` (including writes like `experiment-update`, `notebooks-destroy`,
# `cdp-functions-delete`) runs without a prompt. This hook re-introduces a
# prompt for write `call`s by returning `permissionDecision: "ask"`.
#
# Read-only PostHog tools and non-`call` exec verbs are left alone — the hook
# exits 0 so normal permission flow applies.
Expand All @@ -31,10 +32,10 @@ if [[ "$input" =~ \"tool_name\"[[:space:]]*:[[:space:]]*\"([^\"]+)\" ]]; then
tool_name="${BASH_REMATCH[1]}"
fi

# Match any MCP tool whose name ends in `__exec` regardless of plugin/server
# namespacing (bare `mcp__posthog__exec` or plugin-prefixed variants like
# `mcp__posthog_posthog__exec`).
[[ "$tool_name" =~ __exec$ ]] || exit 0
# Match any MCP tool whose name ends in `__exec` or `__cli` regardless of
# plugin/server namespacing (bare `mcp__posthog__exec` / `mcp__posthog__cli`
# or plugin-prefixed variants like `mcp__posthog_posthog__exec`).
[[ "$tool_name" =~ __(exec|cli)$ ]] || exit 0

# Extract the PostHog tool name from `"command":"call [--json] <tool>..."`.
# Tool names are kebab-case [a-zA-Z0-9_-]+ so the regex stops cleanly at the
Expand Down
4 changes: 2 additions & 2 deletions hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"description": "PostHog LLM Analytics + permission gating for write commands via mcp__posthog__exec",
"description": "PostHog LLM Analytics + permission gating for write commands via mcp__posthog__exec / mcp__posthog__cli",
"hooks": {
"SessionEnd": [
{
Expand All @@ -14,7 +14,7 @@
],
"PreToolUse": [
{
"matcher": "__exec$",
"matcher": "__(exec|cli)$",
"hooks": [
{
"type": "command",
Expand Down
12 changes: 12 additions & 0 deletions tests/test_gate_exec_write.sh
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,18 @@ run_case "write call via plugin-prefixed exec name prompts" \
"$(exec_call llma-skill-update mcp__posthog_posthog__exec)" \
prompt llma-skill-update

run_case "write call via cli tool name prompts" \
"$(exec_call experiment-update mcp__posthog__cli)" \
prompt experiment-update

run_case "write call via plugin-prefixed cli name prompts" \
"$(exec_call notebooks-destroy mcp__posthog_posthog__cli)" \
prompt notebooks-destroy

run_case "read-only call via cli tool name is silent" \
"$(exec_call experiment-get mcp__posthog__cli)" \
silent

run_case "write call with --json flag still extracts tool" \
'{"tool_name":"mcp__posthog__exec","tool_input":{"command":"call --json experiment-update {\"id\":1}"}}' \
prompt experiment-update
Expand Down
Loading