| name | lisa-posthog-access |
| description | Vendor-neutral access layer for PostHog. PostHog skills and observability rules MUST delegate through this skill rather than calling PostHog MCP tools or REST directly. Per the credential-substrate-precedence contract, resolves POSTHOG_PERSONAL_API_KEY bearer auth first when present and identity-matched to the configured project, then falls back to the PostHog MCP. |
| allowed-tools | ["Bash","Read","Skill"] |
PostHog Access: $ARGUMENTS
Single chokepoint for PostHog operations. Caller skills and rules MUST NOT call
mcp__posthog__* tools or PostHog REST directly.
Invocation Contract
operation: query project_id:<ID> payload:{...}
operation: insights project_id:<ID>
operation: persons project_id:<ID> [query:<QUERY>]
operation: events project_id:<ID> [after:<ISO>] [before:<ISO>]
Return parsed JSON in a <result> block.
Substrate Selection
Probe in order — the ordering is the shared credential-substrate-precedence
contract, not a PostHog-local choice. The first tier that is ready and
identity-matches the configured project is used; a substrate authenticated against
a different project is skipped, never used.
- Tier 1 — configured-provider substrate:
POSTHOG_PERSONAL_API_KEY bearer
token against the configured PostHog host, resolved through
lisa-secrets-access.
- Tier 2 — interactive MCP fallback: PostHog MCP, if available and
authenticated. Used when tier 1 is genuinely unavailable: no
POSTHOG_PERSONAL_API_KEY, no REST adapter for the operation, or a PostHog
outage.
PostHog documents personal API keys and bearer authentication, and the same key
works interactively and headlessly — which is why it leads. The REST tier uses:
POSTHOG_HOST=${POSTHOG_HOST:-https://app.posthog.com}
read_posthog_key() {
[ -n "${POSTHOG_PERSONAL_API_KEY:-}" ] && { echo "$POSTHOG_PERSONAL_API_KEY"; return; }
local candidates=()
if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
candidates+=()
[ -n ];
candidates+=()
candidates+=(node_modules/@codyswann/lisa/plugins/lisa/skills/lisa-secrets-access/scripts/resolve-secret.mjs)
resolver
tried=()
resolver ;
tried+=()
[ -f ];
via_lisa
via_lisa=$(node get POSTHOG_PERSONAL_API_KEY 2>/dev/null) \
&& [ -n ] && { ; ; }
>&2
>&2
>&2
1
}
() {
path=
method=
body=
key
key=$(read_posthog_key) || {
>&2
>&2
1
}
args=(-sS -X -H )
[ -n ] && args+=(-H --data-binary )
curl
}
If neither tier works, fail with:
Error: no PostHog access substrate available. Authenticate the PostHog MCP or set POSTHOG_PERSONAL_API_KEY.
Mutation boundary
Every operation in the Invocation Contract is read-only — analytics
retrieval. query is an HTTP POST, but it reads: it submits a query body and
changes no PostHog state. So the credential-substrate-precedence guarded
fallback for mutating operations (write, read back, assert the tenant from the
response, roll back on mismatch) is not engaged here, and a failed tier is
simply skipped. Adding a genuinely mutating operation — creating an insight,
editing a feature flag — pulls that protocol in: a write of unknown outcome MUST
reconcile by read-back before any retry.
Invariants
- Tier order is
credential-substrate-precedence: POSTHOG_PERSONAL_API_KEY
first, the PostHog MCP as a preserved first-class fallback. Identity-match
against the configured project is mandatory on every tier.
- The key is resolved through
lisa-secrets-access, with the bare
POSTHOG_PERSONAL_API_KEY environment variable as the documented fallback.
Never read a second credential store directly.
POSTHOG_HOST defaults to PostHog Cloud but can point at a self-hosted
deployment.
- Consumer skills do not embed PostHog REST paths.