Skip to content

Commit bb091c0

Browse files
msukkariclaude
andcommitted
refactor(web): fix tryGetPostHogDistinctId to use getAuthenticatedUser
tryGetPostHogDistinctId() was missing OAuth Bearer token resolution, causing all MCP tool calls to get random distinct_ids (inflated DAU). Fix: replace the manual cookie/session/API key checks with a call to getAuthenticatedUser(), which already handles all auth methods (session, OAuth, API keys). This removes the need to thread userId through ToolContext, agent options, and all callers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 64bc4b6 commit bb091c0

File tree

8 files changed

+18
-39
lines changed

8 files changed

+18
-39
lines changed

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

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

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({ userId: user?.id });
82+
const mcpServer = await createMcpServer();
8383
await mcpServer.connect(transport);
8484

8585
return transport.handleRequest(request);

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ interface CreateMessageStreamResponseProps {
4343
modelProviderOptions?: Record<string, Record<string, JSONValue>>;
4444
modelTemperature?: number;
4545
metadata?: Partial<SBChatMessageMetadata>;
46-
/** User ID for telemetry attribution on tool_used events. */
47-
userId?: string;
4846
}
4947

5048
export const createMessageStream = async ({
@@ -58,7 +56,6 @@ export const createMessageStream = async ({
5856
modelTemperature,
5957
onFinish,
6058
onError,
61-
userId,
6259
}: CreateMessageStreamResponseProps) => {
6360
const latestMessage = messages[messages.length - 1];
6461
const sources = latestMessage.parts
@@ -104,7 +101,6 @@ export const createMessageStream = async ({
104101
inputMessages: messageHistory,
105102
inputSources: sources,
106103
selectedRepos,
107-
userId,
108104
onWriteSource: (source) => {
109105
writer.write({
110106
type: 'data-source',
@@ -158,7 +154,6 @@ interface AgentOptions {
158154
onWriteSource: (source: Source) => void;
159155
traceId: string;
160156
chatId: string;
161-
userId?: string;
162157
}
163158

164159
const createAgentStream = async ({
@@ -171,7 +166,6 @@ const createAgentStream = async ({
171166
onWriteSource,
172167
traceId,
173168
chatId,
174-
userId,
175169
}: AgentOptions) => {
176170
// For every file source, resolve the source code so that we can include it in the system prompt.
177171
const fileSources = inputSources.filter((source) => source.type === 'file');
@@ -208,7 +202,7 @@ const createAgentStream = async ({
208202
providerOptions,
209203
messages: inputMessages,
210204
system: systemPrompt,
211-
tools: createTools({ source: 'sourcebot-ask-agent', selectedRepos, userId }),
205+
tools: createTools({ source: 'sourcebot-ask-agent', selectedRepos }),
212206
temperature: temperature ?? env.SOURCEBOT_CHAT_MODEL_TEMPERATURE,
213207
stopWhen: [
214208
stepCountIsGTE(env.SOURCEBOT_CHAT_MAX_STEP_COUNT),

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

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

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525

2626
const dedent = _dedent.withOptions({ alignValues: true });
2727

28-
export async function createMcpServer(options?: { userId?: string }): Promise<McpServer> {
28+
export async function createMcpServer(): Promise<McpServer> {
2929
const server = new McpServer({
3030
name: 'sourcebot-mcp-server',
3131
version: SOURCEBOT_VERSION,
@@ -36,7 +36,6 @@ export async function createMcpServer(options?: { userId?: string }): Promise<Mc
3636

3737
const toolContext: ToolContext = {
3838
source: 'sourcebot-mcp-server',
39-
userId: options?.userId,
4039
}
4140

4241
registerMcpTool(server, grepDefinition, toolContext);
@@ -63,7 +62,7 @@ export async function createMcpServer(options?: { userId?: string }): Promise<Mc
6362
toolName: 'list_language_models',
6463
source: 'sourcebot-mcp-server',
6564
success: true,
66-
}, { distinctId: options?.userId });
65+
});
6766
return { content: [{ type: "text", text: JSON.stringify(models) }] };
6867
}
6968
);
@@ -112,7 +111,7 @@ export async function createMcpServer(options?: { userId?: string }): Promise<Mc
112111
toolName: 'ask_codebase',
113112
source: 'sourcebot-mcp-server',
114113
success: false,
115-
}, { distinctId: options?.userId });
114+
});
116115
return {
117116
content: [{ type: "text", text: `Failed to ask codebase: ${result.message}` }],
118117
};
@@ -122,7 +121,7 @@ export async function createMcpServer(options?: { userId?: string }): Promise<Mc
122121
toolName: 'ask_codebase',
123122
source: 'sourcebot-mcp-server',
124123
success: true,
125-
}, { distinctId: options?.userId });
124+
});
126125

127126
const formattedResponse = dedent`
128127
${result.answer}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export function toVercelAITool<TName extends string, TShape extends z.ZodRawShap
2424
toolName: def.name,
2525
source: context.source ?? 'unknown',
2626
success,
27-
}, { distinctId: context.userId });
27+
});
2828
}
2929
},
3030
toModelOutput: ({ output }) => ({
@@ -67,7 +67,7 @@ export function registerMcpTool<TName extends string, TShape extends z.ZodRawSha
6767
toolName: def.name,
6868
source: context.source ?? 'unknown',
6969
success,
70-
}, { distinctId: context.userId });
70+
});
7171
}
7272
},
7373
);

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

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

2422
export interface ToolDefinition<

packages/web/src/lib/posthog.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
44
import * as Sentry from "@sentry/nextjs";
55
import { PosthogEvent, PosthogEventMap } from './posthogEvents';
66
import { cookies, headers } from 'next/headers';
7-
import { auth } from '@/auth';
8-
import { getVerifiedApiObject } from '@/middleware/withAuth';
7+
import { getAuthenticatedUser } from '@/middleware/withAuth';
98

109
/**
1110
* @note: This is a subset of the properties stored in the
@@ -53,28 +52,19 @@ const getPostHogCookie = (cookieStore: Pick<RequestCookies, 'get'>): PostHogCook
5352
* Attempts to retrieve the distinct id of the current user.
5453
*/
5554
export const tryGetPostHogDistinctId = async () => {
56-
// First, attempt to retrieve the distinct id from the cookie.
55+
// First, attempt to retrieve the distinct id from the PostHog cookie
56+
// (set by the client-side PostHog SDK). This preserves identity
57+
// continuity between client-side and server-side events.
5758
const cookieStore = await cookies();
5859
const cookie = getPostHogCookie(cookieStore);
5960
if (cookie) {
6061
return cookie.distinct_id;
6162
}
6263

63-
// Next, from the session.
64-
const session = await auth();
65-
if (session) {
66-
return session.user.id;
67-
}
68-
69-
// Finally, from the api key.
70-
const headersList = await headers();
71-
const apiKeyString = headersList.get("X-Sourcebot-Api-Key") ?? undefined;
72-
if (!apiKeyString) {
73-
return undefined;
74-
}
75-
76-
const apiKey = await getVerifiedApiObject(apiKeyString);
77-
return apiKey?.createdById;
64+
// Fall back to the authenticated user's ID. This covers all auth
65+
// methods: session cookies, OAuth Bearer tokens, and API keys.
66+
const authResult = await getAuthenticatedUser();
67+
return authResult?.user.id;
7868
}
7969

8070
export const createPostHogClient = async () => {
@@ -87,13 +77,13 @@ export const createPostHogClient = async () => {
8777
return posthog;
8878
}
8979

90-
export async function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E], options?: { distinctId?: string }) {
80+
export async function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E]) {
9181
try {
9282
if (env.SOURCEBOT_TELEMETRY_DISABLED === 'true') {
9383
return;
9484
}
9585

96-
const distinctId = options?.distinctId ?? await tryGetPostHogDistinctId();
86+
const distinctId = await tryGetPostHogDistinctId();
9787
const posthog = await createPostHogClient();
9888

9989
const headersList = await headers();

0 commit comments

Comments
 (0)