diff --git a/packages/agent/src/adapters/claude/claude-agent.refresh.test.ts b/packages/agent/src/adapters/claude/claude-agent.refresh.test.ts index 580ec929c..009052d9e 100644 --- a/packages/agent/src/adapters/claude/claude-agent.refresh.test.ts +++ b/packages/agent/src/adapters/claude/claude-agent.refresh.test.ts @@ -83,7 +83,14 @@ function installFakeSession( sessionId, cwd: "/tmp/repo", model: "claude-sonnet-4-6", - mcpServers: { posthog: { type: "http", url: "https://old" } }, + mcpServers: { + posthog: { type: "http", url: "https://old" }, + "posthog-code-tools": { + type: "sdk", + name: "posthog-code-tools", + instance: {}, + }, + }, abortController, }, input, @@ -222,7 +229,8 @@ describe("ClaudeAcpAgent.extMethod refresh_session", () => { expect(oldQuery.interrupt).toHaveBeenCalledTimes(1); expect(endSpy).toHaveBeenCalledTimes(1); - // New query was built with resume identity (not sessionId) and new servers + // New query: resume identity (not sessionId), http server refreshed, and + // the in-process local-tools server preserved. expect(lastQueryCall.options).toMatchObject({ resume: "s-2", forkSession: false, @@ -232,6 +240,11 @@ describe("ClaudeAcpAgent.extMethod refresh_session", () => { url: "https://fresh", headers: { "x-foo": "bar" }, }, + "posthog-code-tools": { + type: "sdk", + name: "posthog-code-tools", + instance: {}, + }, }, }); expect(lastQueryCall.options?.sessionId).toBeUndefined(); @@ -313,4 +326,25 @@ describe("ClaudeAcpAgent.extMethod refresh_session", () => { expect(fetchMcpToolMetadataMock).toHaveBeenCalledTimes(1); expect(fetchMcpToolMetadataMock.mock.calls[0][0]).toBe(createdQueries[0]); }); + + it("preserves the in-process local-tools server across refresh", async () => { + const agent = makeAgent(); + installFakeSession(agent, "s-inprocess"); + + // freshMcpServers carries only external (http) servers, so the sdk server + // must be carried over from the previous session options. + await agent.extMethod(POSTHOG_METHODS.REFRESH_SESSION, { + mcpServers: freshMcpServers, + }); + + const servers = lastQueryCall.options?.mcpServers as Record< + string, + { type?: string } + >; + expect(servers["posthog-code-tools"]).toEqual({ + type: "sdk", + name: "posthog-code-tools", + instance: {}, + }); + }); }); diff --git a/packages/agent/src/adapters/claude/claude-agent.ts b/packages/agent/src/adapters/claude/claude-agent.ts index 53c94340b..e0243ee76 100644 --- a/packages/agent/src/adapters/claude/claude-agent.ts +++ b/packages/agent/src/adapters/claude/claude-agent.ts @@ -1111,9 +1111,18 @@ export class ClaudeAcpAgent extends BaseAcpAgent { // fresh AbortController. const newAbortController = new AbortController(); const { sessionId: _drop, ...rest } = prev.queryOptions; + + // parseMcpServers yields only http/sse/stdio — carry over any in-process + // ("sdk") server so the local-tools server (signed commits) survives. + const preservedInProcess = Object.fromEntries( + Object.entries(prev.queryOptions.mcpServers ?? {}).filter( + ([, cfg]) => (cfg as { type?: string }).type === "sdk", + ), + ); + const newOptions: Options = { ...rest, - mcpServers, + mcpServers: { ...mcpServers, ...preservedInProcess }, resume: this.sessionId, forkSession: false, abortController: newAbortController,