Skip to content

Commit df7c298

Browse files
msukkariclaude
andcommitted
fix(web): fix inflated MCP DAU by threading user identity through tool telemetry
captureEvent() relies on tryGetPostHogDistinctId() which reads cookies, session, or API key headers. In the MCP server and ask agent contexts, none of these are available, so every tool_used event got a random distinct_id — inflating DAU counts (e.g., 8 tool calls = 8 "users"). Fix: add optional distinctId to ToolContext and captureEvent(). Thread user.id from the auth context through: - MCP route → createMcpServer(userId) → ToolContext.distinctId - chat route / askCodebase → createMessageStream(distinctId) → createAgentStream → createTools Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 39aaac0 commit df7c298

File tree

8 files changed

+22
-11
lines changed

8 files changed

+22
-11
lines changed

packages/web/src/app/api/(server)/chat/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export const POST = apiHandler(async (req: NextRequest) => {
112112
modelName: languageModelConfig.displayName ?? languageModelConfig.model,
113113
modelProviderOptions: providerOptions,
114114
modelTemperature: temperature,
115+
distinctId: user?.id,
115116
onFinish: async ({ messages }) => {
116117
await updateChatMessages({ chatId: id, messages, prisma });
117118
},

packages/web/src/app/api/(server)/mcp/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export const POST = apiHandler(async (request: NextRequest) => {
7979
},
8080
});
8181

82-
const mcpServer = await createMcpServer();
82+
const mcpServer = await createMcpServer({ userId: user?.id });
8383
await mcpServer.connect(transport);
8484

8585
return transport.handleRequest(request);

packages/web/src/features/chat/agent.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ interface CreateMessageStreamResponseProps {
4343
modelProviderOptions?: Record<string, Record<string, JSONValue>>;
4444
modelTemperature?: number;
4545
metadata?: Partial<SBChatMessageMetadata>;
46+
/** PostHog distinct ID for telemetry attribution on tool_used events. */
47+
distinctId?: string;
4648
}
4749

4850
export const createMessageStream = async ({
@@ -56,6 +58,7 @@ export const createMessageStream = async ({
5658
modelTemperature,
5759
onFinish,
5860
onError,
61+
distinctId,
5962
}: CreateMessageStreamResponseProps) => {
6063
const latestMessage = messages[messages.length - 1];
6164
const sources = latestMessage.parts
@@ -101,6 +104,7 @@ export const createMessageStream = async ({
101104
inputMessages: messageHistory,
102105
inputSources: sources,
103106
selectedRepos,
107+
distinctId,
104108
onWriteSource: (source) => {
105109
writer.write({
106110
type: 'data-source',
@@ -154,6 +158,7 @@ interface AgentOptions {
154158
onWriteSource: (source: Source) => void;
155159
traceId: string;
156160
chatId: string;
161+
distinctId?: string;
157162
}
158163

159164
const createAgentStream = async ({
@@ -166,6 +171,7 @@ const createAgentStream = async ({
166171
onWriteSource,
167172
traceId,
168173
chatId,
174+
distinctId,
169175
}: AgentOptions) => {
170176
// For every file source, resolve the source code so that we can include it in the system prompt.
171177
const fileSources = inputSources.filter((source) => source.type === 'file');
@@ -202,7 +208,7 @@ const createAgentStream = async ({
202208
providerOptions,
203209
messages: inputMessages,
204210
system: systemPrompt,
205-
tools: createTools({ source: 'sourcebot-ask-agent', selectedRepos }),
211+
tools: createTools({ source: 'sourcebot-ask-agent', selectedRepos, distinctId }),
206212
temperature: temperature ?? env.SOURCEBOT_CHAT_MODEL_TEMPERATURE,
207213
stopWhen: [
208214
stepCountIsGTE(env.SOURCEBOT_CHAT_MAX_STEP_COUNT),

packages/web/src/features/mcp/askCodebase.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ export const askCodebase = (params: AskCodebaseParams): Promise<AskCodebaseResul
159159
modelName,
160160
modelProviderOptions: providerOptions,
161161
modelTemperature: temperature,
162+
distinctId: user?.id,
162163
onFinish: async ({ messages }) => {
163164
finalMessages = messages;
164165
},

packages/web/src/features/mcp/server.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
const dedent = _dedent.withOptions({ alignValues: true });
2727
const logger = createLogger('mcp-server');
2828

29-
export async function createMcpServer(): Promise<McpServer> {
29+
export async function createMcpServer(options?: { userId?: string }): Promise<McpServer> {
3030
const server = new McpServer({
3131
name: 'sourcebot-mcp-server',
3232
version: SOURCEBOT_VERSION,
@@ -37,6 +37,7 @@ export async function createMcpServer(): Promise<McpServer> {
3737

3838
const toolContext: ToolContext = {
3939
source: 'sourcebot-mcp-server',
40+
distinctId: options?.userId,
4041
}
4142

4243
registerMcpTool(server, grepDefinition, toolContext);
@@ -63,7 +64,7 @@ export async function createMcpServer(): Promise<McpServer> {
6364
toolName: 'list_language_models',
6465
source: 'sourcebot-mcp-server',
6566
success: true,
66-
}).catch((error) => {
67+
}, { distinctId: options?.userId }).catch((error) => {
6768
logger.warn('Failed to capture tool_used event:', error);
6869
});
6970
return { content: [{ type: "text", text: JSON.stringify(models) }] };
@@ -114,7 +115,7 @@ export async function createMcpServer(): Promise<McpServer> {
114115
toolName: 'ask_codebase',
115116
source: 'sourcebot-mcp-server',
116117
success: false,
117-
}).catch((error) => {
118+
}, { distinctId: options?.userId }).catch((error) => {
118119
logger.warn('Failed to capture tool_used event:', error);
119120
});
120121
return {
@@ -126,7 +127,7 @@ export async function createMcpServer(): Promise<McpServer> {
126127
toolName: 'ask_codebase',
127128
source: 'sourcebot-mcp-server',
128129
success: true,
129-
}).catch((error) => {
130+
}, { distinctId: options?.userId }).catch((error) => {
130131
logger.warn('Failed to capture tool_used event:', error);
131132
});
132133

packages/web/src/features/tools/adapters.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function toVercelAITool<TName extends string, TShape extends z.ZodRawShap
2525
toolName: def.name,
2626
source: context.source ?? 'unknown',
2727
success,
28-
}).catch((error) => {
28+
}, { distinctId: context.distinctId }).catch((error) => {
2929
logger.warn(`Failed to capture tool_used event for ${def.name}:`, error);
3030
});
3131
}
@@ -63,7 +63,7 @@ export function registerMcpTool<TName extends string, TShape extends z.ZodRawSha
6363
toolName: def.name,
6464
source: context.source ?? 'unknown',
6565
success: true,
66-
}).catch((error) => {
66+
}, { distinctId: context.distinctId }).catch((error) => {
6767
logger.warn(`Failed to capture tool_used event for ${def.name}:`, error);
6868
});
6969
return { content: [{ type: "text" as const, text: result.output }] };
@@ -72,7 +72,7 @@ export function registerMcpTool<TName extends string, TShape extends z.ZodRawSha
7272
toolName: def.name,
7373
source: context.source ?? 'unknown',
7474
success: false,
75-
}).catch((error) => {
75+
}, { distinctId: context.distinctId }).catch((error) => {
7676
logger.warn(`Failed to capture tool_used event for ${def.name}:`, error);
7777
});
7878
const message = error instanceof Error ? error.message : String(error);

packages/web/src/features/tools/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export type Source = z.infer<typeof sourceSchema>;
1717
export interface ToolContext {
1818
source?: string;
1919
selectedRepos?: string[];
20+
/** PostHog distinct ID for telemetry attribution. When set, tool_used events will be attributed to this user. */
21+
distinctId?: string;
2022
}
2123

2224
export interface ToolDefinition<

packages/web/src/lib/posthog.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,12 @@ export const createPostHogClient = async () => {
8787
return posthog;
8888
}
8989

90-
export async function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E]) {
90+
export async function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E], options?: { distinctId?: string }) {
9191
if (env.SOURCEBOT_TELEMETRY_DISABLED === 'true') {
9292
return;
9393
}
9494

95-
const distinctId = await tryGetPostHogDistinctId();
95+
const distinctId = options?.distinctId ?? await tryGetPostHogDistinctId();
9696
const posthog = await createPostHogClient();
9797

9898
const headersList = await headers();

0 commit comments

Comments
 (0)