diff --git a/integrations/catalog/posthog.json b/integrations/catalog/posthog.json index 9b572d04..5853d615 100644 --- a/integrations/catalog/posthog.json +++ b/integrations/catalog/posthog.json @@ -8,7 +8,7 @@ ], "appUrl": "https://posthog.com", "docsUrl": "https://posthog.com/docs/model-context-protocol", - "notes": "Relevant for product analytics and experimentation workflows.", + "notes": "Uses PostHog's official hosted MCP server. OAuth is preferred; the bearer-token option remains for clients or deployments that require a personal API key. The OAuth URL defaults to read-only mode; remove readonly=true only when write access has been approved.", "popularityRank": 37, "iconBg": "#1D4AFF", "logoUrl": "https://cdn.simpleicons.org/posthog/FFFFFF", @@ -18,8 +18,44 @@ "product", "experiments" ], - "installHint": "Authenticate with a PostHog personal API key (PostHog → User settings → API keys, MCP Server preset) — sent as a Bearer token.", + "installHint": "Prefer OAuth. The default OAuth connection uses read-only MCP mode; edit the URL or use the bearer-key fallback only when a workflow needs approved write access. For API keys, create a PostHog personal API key with the MCP Server preset.", "connectionOptions": [ + { + "id": "oauth", + "provider": "mcp", + "auth": { + "strategy": "oauth2", + "oauth": { + "authorizationUrl": "https://oauth.posthog.com/oauth/authorize/", + "tokenUrl": "https://oauth.posthog.com/oauth/token/", + "registrationUrl": "https://oauth.posthog.com/oauth/register/", + "scopes": [ + "openid", + "profile", + "email", + "organization:read", + "project:read", + "dashboard:read", + "insight:read", + "query:read", + "event_definition:read" + ], + "pkce": true, + "clientAuthentication": "none", + "additionalAuthorizationParams": { + "resource": "https://mcp.posthog.com/mcp" + }, + "additionalTokenParams": { + "resource": "https://mcp.posthog.com/mcp" + } + } + }, + "transport": { + "kind": "shttp", + "url": "https://mcp.posthog.com/mcp?readonly=true", + "urlEditable": true + } + }, { "id": "api-key", "provider": "mcp", @@ -31,7 +67,9 @@ "strategy": "bearer", "credentialLabel": "PostHog personal API key", "credentialPlaceholder": "Paste your PostHog personal API key", - "credentialHelp": "Create one under PostHog → User settings → API keys (MCP Server preset). Sent as Authorization: Bearer ." + "credentialHelp": "Create one under PostHog > User settings > API keys using the MCP Server preset. Sent as Authorization: Bearer .", + "credentialSecretName": "POSTHOG_PERSONAL_API_KEY", + "saveCredentialAsSecretByDefault": true } } ] diff --git a/tests/test_catalogs.py b/tests/test_catalogs.py index bb0a35cf..c5a297dc 100644 --- a/tests/test_catalogs.py +++ b/tests/test_catalogs.py @@ -121,6 +121,49 @@ def test_credential_fields_have_helper_text_and_link(): ) +def test_posthog_catalog_prefers_oauth_with_safe_mcp_defaults(): + posthog = next( + entry for entry in load_catalog_entries("integrations/catalog") + if entry["id"] == "posthog" + ) + + assert [option["id"] for option in posthog["connectionOptions"]] == [ + "oauth", + "api-key", + ] + + oauth = posthog["connectionOptions"][0] + assert oauth["provider"] == "mcp" + assert oauth["transport"] == { + "kind": "shttp", + "url": "https://mcp.posthog.com/mcp?readonly=true", + "urlEditable": True, + } + oauth_config = oauth["auth"]["oauth"] + assert oauth_config["authorizationUrl"] == ( + "https://oauth.posthog.com/oauth/authorize/" + ) + assert oauth_config["tokenUrl"] == "https://oauth.posthog.com/oauth/token/" + assert oauth_config["registrationUrl"] == ( + "https://oauth.posthog.com/oauth/register/" + ) + assert oauth_config["pkce"] is True + assert oauth_config["clientAuthentication"] == "none" + assert oauth_config["additionalAuthorizationParams"] == { + "resource": "https://mcp.posthog.com/mcp" + } + assert oauth_config["additionalTokenParams"] == { + "resource": "https://mcp.posthog.com/mcp" + } + assert "query:read" in oauth_config["scopes"] + assert all(not scope.endswith(":write") for scope in oauth_config["scopes"]) + + api_key = posthog["connectionOptions"][1] + assert api_key["auth"]["strategy"] == "bearer" + assert api_key["auth"]["credentialSecretName"] == "POSTHOG_PERSONAL_API_KEY" + assert api_key["auth"]["saveCredentialAsSecretByDefault"] is True + + def test_node_package_exports_catalogs(): script = """ import { INTEGRATION_CATALOG, AUTOMATION_CATALOG } from './index.js';