Skip to content

Commit 7ebf612

Browse files
fix(agent): preserve cloud MCP relays
Generated-By: PostHog Code Task-Id: c6d78416-85c8-4304-bd69-e309562468c4
1 parent 54efcf0 commit 7ebf612

4 files changed

Lines changed: 47 additions & 4 deletions

File tree

docs/cloud-mcp-relay.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ The built-in `posthog-code-computer-use` designation resolves to the packaged
2222
PostHog local-tools MCP server instead of `~/.claude.json`. It is added to new
2323
cloud runs when the desktop **Computer use** setting is enabled and exposes
2424
only the macOS computer tools. The same relay approval policy applies, so tool
25-
calls execute only while the creating desktop remains connected.
25+
calls execute only while the creating desktop remains connected. Runs with
26+
imported or relayed MCP servers skip pre-warmed sandbox reuse because the
27+
server list is not known when the warm run starts.
2628

2729
## Problem
2830

packages/agent/src/adapters/codex-app-server/local-tools-mcp.test.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ import { LOCAL_TOOLS_MCP_NAME } from "../local-tools";
33
import { buildComputerUseStdioServer } from "../local-tools/stdio-server";
44
import { buildLocalToolsServer } from "./local-tools-mcp";
55

6-
// The dist asset isn't on the walk-up path in unit tests, so make existsSync
7-
// succeed; nothing spawns the script — we only inspect the path.
6+
// Isolate bundled-script resolution and the sandbox env file; the test only
7+
// inspects the generated stdio config.
88
vi.mock("node:fs", async (importActual) => {
99
const actual = await importActual<typeof import("node:fs")>();
10-
return { ...actual, existsSync: vi.fn().mockReturnValue(true) };
10+
return {
11+
...actual,
12+
existsSync: vi.fn().mockReturnValue(true),
13+
readFileSync: vi.fn(() => {
14+
throw new Error("sandbox env file unavailable");
15+
}),
16+
};
1117
});
1218

1319
describe("buildLocalToolsServer", () => {

packages/core/src/task-detail/taskCreationSaga.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { SessionService } from "@posthog/core/sessions/sessionService";
2+
import { CLOUD_COMPUTER_USE_MCP_NAME } from "@posthog/shared";
23
import type { Task, TaskRun } from "@posthog/shared/domain-types";
34
import { beforeEach, describe, expect, it, vi } from "vitest";
45
import type {
@@ -45,6 +46,7 @@ const sessionService = {
4546
disconnectFromTask: vi.fn(),
4647
rememberInitialCloudPrompt: vi.fn(),
4748
markTaskCreationInFlight: vi.fn(),
49+
designateRelayedMcpServers: vi.fn().mockResolvedValue(undefined),
4850
} as unknown as SessionService;
4951

5052
const createTask = (overrides: Partial<Task> = {}): Task => ({
@@ -733,6 +735,35 @@ describe("TaskCreationSaga", () => {
733735
},
734736
);
735737

738+
it("uses a cold run when desktop-relayed MCP servers are requested", async () => {
739+
const createdTask = createTask();
740+
const startedTask = createTask({ latest_run: createRun() });
741+
const createTaskMock = vi.fn().mockResolvedValue(createdTask);
742+
const createTaskRunMock = vi.fn().mockResolvedValue(createRun());
743+
const saga = makeSaga({
744+
createTask: createTaskMock,
745+
createTaskRun: createTaskRunMock,
746+
startTaskRun: vi.fn().mockResolvedValue(startedTask),
747+
});
748+
const relayedMcpServers = [{ name: CLOUD_COMPUTER_USE_MCP_NAME }];
749+
750+
const result = await saga.run({
751+
content: "Ship the fix",
752+
repository: "posthog/posthog",
753+
workspaceMode: "cloud",
754+
branch: "main",
755+
relayedMcpServers,
756+
});
757+
758+
expect(result.success).toBe(true);
759+
expect(mockHost.takeWarmTaskLease).not.toHaveBeenCalled();
760+
expect(createTaskMock.mock.calls[0][0].branch).toBeUndefined();
761+
expect(createTaskRunMock).toHaveBeenCalledWith(
762+
"task-123",
763+
expect.objectContaining({ relayedMcpServers }),
764+
);
765+
});
766+
736767
it("reuses a warm run built from the selected custom image", async () => {
737768
mockHost.takeWarmTaskLease.mockReturnValue({
738769
taskId: "warm-task",

packages/core/src/task-detail/taskCreationSaga.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,10 @@ export class TaskCreationSaga extends Saga<
685685
augmented,
686686
};
687687

688+
if (input.importedMcpServers?.length || input.relayedMcpServers?.length) {
689+
return { ...base, suppressWarmReuse: true };
690+
}
691+
688692
const lease = input.repository
689693
? this.deps.host.takeWarmTaskLease({
690694
repository: input.repository,

0 commit comments

Comments
 (0)