Skip to content
Merged
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
3 changes: 3 additions & 0 deletions posthog/event_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,13 +265,16 @@ class EventSource(StrEnum):
POSTHOG_AI = "posthog_ai"
TERRAFORM = "terraform"
MCP = "mcp"
WIZARD = "wizard"


def get_event_source(request) -> EventSource:
"""Determine the source of an API request for analytics."""
user_agent = request.META.get("HTTP_USER_AGENT", "")
if "posthog/terraform-provider" in user_agent:
return EventSource.TERRAFORM
if "posthog/wizard" in user_agent:
return EventSource.WIZARD
if "posthog/mcp-server" in user_agent:
return EventSource.MCP
if isinstance(getattr(request, "successful_authenticator", None), SessionAuthentication):
Expand Down
5 changes: 3 additions & 2 deletions services/mcp/src/api/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from 'zod'

import { USER_AGENT } from '@/lib/constants'
import { getUserAgent } from '@/lib/constants'
import { ErrorCode } from '@/lib/errors'
import { getSearchParamsFromRecord } from '@/lib/utils.js'
import {
Expand Down Expand Up @@ -125,6 +125,7 @@ export type Result<T, E = Error> = { success: true; data: T } | { success: false
export interface ApiConfig {
apiToken: string
baseUrl: string
clientIdentifier?: string | undefined
}

type Endpoint = Record<string, any>
Expand Down Expand Up @@ -154,7 +155,7 @@ export class ApiClient {
// TODO: should we move rate limiting from `fetchWithSchema` to here?
const defaultHeaders: HeadersInit = {
Authorization: `Bearer ${this.config.apiToken}`,
'User-Agent': USER_AGENT,
'User-Agent': getUserAgent(this.config.clientIdentifier),
}
if (options?.body) {
defaultHeaders['Content-Type'] = 'application/json'
Expand Down
4 changes: 2 additions & 2 deletions services/mcp/src/api/fetcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { USER_AGENT } from '@/lib/constants'
import { getUserAgent } from '@/lib/constants'

import type { ApiConfig } from './client'
import type { createApiClient } from './generated'
Expand All @@ -18,7 +18,7 @@ export const buildApiFetcher: (config: ApiConfig) => Parameters<typeof createApi

const headers = new Headers()
headers.set('Authorization', `Bearer ${config.apiToken}`)
headers.set('User-Agent', USER_AGENT)
headers.set('User-Agent', getUserAgent(config.clientIdentifier))

// Handle query parameters
if (input.urlSearchParams) {
Expand Down
7 changes: 7 additions & 0 deletions services/mcp/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,19 @@ const handleRequest = async (
request.headers.get('x-posthog-organization-id') || url.searchParams.get('organization_id') || undefined
const projectId = request.headers.get('x-posthog-project-id') || url.searchParams.get('project_id') || undefined

// Extract posthog/foo identifier from the client's User-Agent (e.g. "posthog/wizard")
// so we can forward it in outgoing API requests for source attribution
const clientUserAgent = request.headers.get('User-Agent') || ''
const clientIdentifierMatch = clientUserAgent.match(/posthog\/([\w-]+)/)
const clientIdentifier = clientIdentifierMatch ? clientIdentifierMatch[0] : undefined

Object.assign(ctx.props, {
apiToken: token,
userHash: hash(token),
sessionId: sessionId || undefined,
organizationId,
projectId,
clientIdentifier,
})

// Search params are used to build up the list of available tools. If no features are provided, all tools are available.
Expand Down
7 changes: 7 additions & 0 deletions services/mcp/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ import packageJson from '../../package.json'

export const USER_AGENT = `posthog/mcp-server; version: ${packageJson.version}`

export function getUserAgent(clientIdentifier?: string): string {
if (clientIdentifier) {
return `${USER_AGENT}; for ${clientIdentifier}`
}
return USER_AGENT
}

// Region-specific PostHog API base URLs
export const POSTHOG_US_BASE_URL = 'https://us.posthog.com'
export const POSTHOG_EU_BASE_URL = 'https://eu.posthog.com'
Expand Down
2 changes: 2 additions & 0 deletions services/mcp/src/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export type RequestProperties = {
version?: number
organizationId?: string
projectId?: string
clientIdentifier?: string
}

export class MCP extends McpAgent<Env> {
Expand Down Expand Up @@ -137,6 +138,7 @@ export class MCP extends McpAgent<Env> {
this._api = new ApiClient({
apiToken: this.requestProperties.apiToken,
baseUrl,
clientIdentifier: this.requestProperties.clientIdentifier,
})
}

Expand Down
Loading