Skip to content

Commit 38af55b

Browse files
authored
feat: enable posthog mcp via oauth (#230)
1 parent 40af94a commit 38af55b

File tree

6 files changed

+401
-292
lines changed

6 files changed

+401
-292
lines changed

apps/array/src/constants/oauth.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,28 @@ export const POSTHOG_DEV_CLIENT_ID = "DC5uRLVbGI02YQ82grxgnK6Qn12SXWpCqdPb60oZ";
77
export const OAUTH_PORT = 8237;
88

99
export const OAUTH_SCOPES = [
10+
// Array app needs
1011
"user:read",
1112
"project:read",
1213
"task:write",
1314
"integration:read",
15+
"introspection",
16+
"dashboard:read",
17+
"error_tracking:read",
18+
"event_definition:read",
19+
"experiment:read",
20+
"feature_flag:read",
21+
"insight:read",
22+
"organization:read",
23+
"property_definition:read",
24+
"query:read",
25+
"survey:read",
26+
"warehouse_table:read",
27+
"dashboard:write",
28+
"experiment:write",
29+
"feature_flag:write",
30+
"insight:write",
31+
"survey:write",
1432
];
1533

1634
// Token refresh settings

apps/array/src/main/preload.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ contextBridge.exposeInMainWorld("electronAPI", {
187187
sdkSessionId?: string;
188188
}): Promise<{ sessionId: string; channel: string } | null> =>
189189
ipcRenderer.invoke("agent-reconnect", params),
190+
agentTokenRefresh: async (
191+
taskRunId: string,
192+
newToken: string,
193+
): Promise<void> =>
194+
ipcRenderer.invoke("agent-token-refresh", taskRunId, newToken),
190195
onAgentEvent: (
191196
channel: string,
192197
listener: (payload: unknown) => void,

apps/array/src/main/services/session-manager.ts

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ export interface PostHogCredentials {
116116
projectId: number;
117117
}
118118

119+
interface AcpMcpServer {
120+
name: string;
121+
type: "http";
122+
url: string;
123+
headers: Array<{ name: string; value: string }>;
124+
}
125+
119126
export interface SessionConfig {
120127
taskId: string;
121128
taskRunId: string; // THE session identifier everywhere
@@ -146,6 +153,7 @@ function getClaudeCliPath(): string {
146153

147154
export class SessionManager {
148155
private sessions = new Map<string, ManagedSession>();
156+
private sessionTokens = new Map<string, string>();
149157
private getMainWindow: () => BrowserWindow | null;
150158
private onLog: OnLogCallback;
151159

@@ -154,6 +162,45 @@ export class SessionManager {
154162
this.onLog = onLog;
155163
}
156164

165+
public updateSessionToken(taskRunId: string, newToken: string): void {
166+
this.sessionTokens.set(taskRunId, newToken);
167+
log.info("Session token updated", { taskRunId });
168+
}
169+
170+
private getSessionToken(taskRunId: string, fallback: string): string {
171+
return this.sessionTokens.get(taskRunId) || fallback;
172+
}
173+
174+
private buildMcpServers(
175+
credentials: PostHogCredentials,
176+
taskRunId: string,
177+
): AcpMcpServer[] {
178+
const servers: AcpMcpServer[] = [];
179+
180+
const mcpUrl = this.getPostHogMcpUrl(credentials.apiHost);
181+
const token = this.getSessionToken(taskRunId, credentials.apiKey);
182+
183+
servers.push({
184+
name: "posthog",
185+
type: "http",
186+
url: mcpUrl,
187+
headers: [{ name: "Authorization", value: `Bearer ${token}` }],
188+
});
189+
190+
return servers;
191+
}
192+
193+
private getPostHogMcpUrl(apiHost: string): string {
194+
if (
195+
apiHost.includes("localhost") ||
196+
apiHost.includes("127.0.0.1") ||
197+
!app.isPackaged
198+
) {
199+
return "http://localhost:8787/mcp";
200+
}
201+
return "https://mcp.posthog.com/mcp";
202+
}
203+
157204
async createSession(config: SessionConfig): Promise<ManagedSession> {
158205
const session = await this.getOrCreateSession(config, false);
159206
if (!session) {
@@ -209,13 +256,15 @@ export class SessionManager {
209256
clientCapabilities: {},
210257
});
211258

259+
const mcpServers = this.buildMcpServers(credentials, taskRunId);
260+
212261
if (isReconnect) {
213262
// Use our custom extension method to resume without replaying history.
214263
// Client fetches history from S3 directly.
215264
await connection.extMethod("_posthog/session/resume", {
216265
sessionId: taskRunId,
217266
cwd: repoPath,
218-
mcpServers: [],
267+
mcpServers,
219268
_meta: {
220269
...(logUrl && {
221270
persistence: { taskId, runId: taskRunId, logUrl },
@@ -226,7 +275,7 @@ export class SessionManager {
226275
} else {
227276
await connection.newSession({
228277
cwd: repoPath,
229-
mcpServers: [],
278+
mcpServers,
230279
_meta: { sessionId: taskRunId },
231280
});
232281
}
@@ -588,4 +637,15 @@ export function registerAgentIpc(
588637
return session ? toSessionResponse(session) : null;
589638
},
590639
);
640+
641+
ipcMain.handle(
642+
"agent-token-refresh",
643+
async (
644+
_event: IpcMainInvokeEvent,
645+
taskRunId: string,
646+
newToken: string,
647+
): Promise<void> => {
648+
sessionManager.updateSessionToken(taskRunId, newToken);
649+
},
650+
);
591651
}

0 commit comments

Comments
 (0)