authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-10-10 12:30:29-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-10 12:30:29-07:00
logf82796a16abb707c1e2c41301ad8d6e3153668c5
treef132eae764422ba8eef2b63132eeeee8bab28450
parenta4c8745abee3f73f960c914800974dee530dbc3b
signature Signed by PGP key B5690EEEBB952194

fix(server): send Claude MCP servers over the control channel (#17898)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

2 files changed, 198 insertions(+), 47 deletions(-)

apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts+123-15
...@@ -1,10 +1,15 @@...@@ -1,10 +1,15 @@
1// @effect-diagnostics nodeBuiltinImport:off - the fake Claude CLI hands the SDK Node streams.
2import * as NodeEvents from "node:events";
1import * as NodeOS from "node:os";3import * as NodeOS from "node:os";
4import * as NodeStream from "node:stream";
25
3import type {6import type {
4 Query as ClaudeQuery,7 Query as ClaudeQuery,
5 SDKMessage,8 SDKMessage,
6 SDKResultMessage,9 SDKResultMessage,
7 SDKUserMessage,10 SDKUserMessage,
11 SpawnedProcess,
12 SpawnOptions,
8} from "@anthropic-ai/claude-agent-sdk";13} from "@anthropic-ai/claude-agent-sdk";
9import type { AskUserQuestionInput } from "@anthropic-ai/claude-agent-sdk/sdk-tools";14import type { AskUserQuestionInput } from "@anthropic-ai/claude-agent-sdk/sdk-tools";
10import * as NodeServices from "@effect/platform-node/NodeServices";15import * as NodeServices from "@effect/platform-node/NodeServices";
...@@ -63,7 +68,7 @@ import { WorktreeToolkit } from "../../mcp/toolkits/worktree/tools.ts";...@@ -63,7 +68,7 @@ import { WorktreeToolkit } from "../../mcp/toolkits/worktree/tools.ts";
63import { ThreadToolkit } from "../../mcp/toolkits/thread/tools.ts";68import { ThreadToolkit } from "../../mcp/toolkits/thread/tools.ts";
64import { OrchestratorToolkit } from "../../mcp/toolkits/orchestrator/tools.ts";69import { OrchestratorToolkit } from "../../mcp/toolkits/orchestrator/tools.ts";
65import { ClaudeExecutableFileCheck } from "../../provider/Drivers/ClaudeExecutable.ts";70import { ClaudeExecutableFileCheck } from "../../provider/Drivers/ClaudeExecutable.ts";
66import type * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";71import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
67import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";72import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter";
68import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";73import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests";
69import { makeProviderFailure } from "@t3tools/provider-core/server/failure";74import { makeProviderFailure } from "@t3tools/provider-core/server/failure";
...@@ -499,14 +504,11 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {...@@ -499,14 +504,11 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {
499 type: "http",504 type: "http",
500 url: "http://127.0.0.1:43123/mcp",505 url: "http://127.0.0.1:43123/mcp",
501 headers: {506 headers: {
502 Authorization: "${T3_CODE_MCP_AUTHORIZATION}",507 Authorization: "Bearer secret-claude-token",
503 },508 },
504 timeout: ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_TIMEOUT_MS,509 timeout: ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_TIMEOUT_MS,
505 },510 },
506 } as const;511 } as const;
507 const T3_MCP_ENVIRONMENT = {
508 T3_CODE_MCP_AUTHORIZATION: "Bearer secret-claude-token",
509 } as const;
510512
511 const mcpSessionFor = (513 const mcpSessionFor = (
512 threadId: ThreadId,514 threadId: ThreadId,
...@@ -551,7 +553,6 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {...@@ -551,7 +553,6 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {
551 assert.deepEqual(overrides, {553 assert.deepEqual(overrides, {
552 allowedTools: [ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_WILDCARD],554 allowedTools: [ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_WILDCARD],
553 mcpServers: T3_MCP_SERVERS,555 mcpServers: T3_MCP_SERVERS,
554 mcpEnvironment: T3_MCP_ENVIRONMENT,
555 });556 });
556 });557 });
557558
...@@ -567,7 +568,6 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {...@@ -567,7 +568,6 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {
567 assert.deepEqual(overrides, {568 assert.deepEqual(overrides, {
568 allowedTools: ["Read", "mcp__t3-code__*"],569 allowedTools: ["Read", "mcp__t3-code__*"],
569 mcpServers: T3_MCP_SERVERS,570 mcpServers: T3_MCP_SERVERS,
570 mcpEnvironment: T3_MCP_ENVIRONMENT,
571 });571 });
572 });572 });
573573
...@@ -586,7 +586,6 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {...@@ -586,7 +586,6 @@ describe("ClaudeAdapterV2 MCP query overrides", () => {
586 ...ClaudeAdapterV2.CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS,586 ...ClaudeAdapterV2.CLAUDE_READ_ONLY_T3_MCP_ALLOWED_TOOLS,
587 ],587 ],
588 mcpServers: T3_MCP_SERVERS,588 mcpServers: T3_MCP_SERVERS,
589 mcpEnvironment: T3_MCP_ENVIRONMENT,
590 });589 });
591 assert.isFalse(overrides.allowedTools?.includes(ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_WILDCARD));590 assert.isFalse(overrides.allowedTools?.includes(ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_WILDCARD));
592 });591 });
...@@ -706,12 +705,11 @@ describe("ClaudeAdapterV2 native protocol logging", () => {...@@ -706,12 +705,11 @@ describe("ClaudeAdapterV2 native protocol logging", () => {
706 type: "http",705 type: "http",
707 url: "http://127.0.0.1:43123/mcp",706 url: "http://127.0.0.1:43123/mcp",
708 headers: {707 headers: {
709 Authorization: "${T3_CODE_MCP_AUTHORIZATION}",708 Authorization: "Bearer secret-claude-token",
710 },709 },
711 timeout: ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_TIMEOUT_MS,710 timeout: ClaudeAdapterV2.CLAUDE_T3_MCP_TOOL_TIMEOUT_MS,
712 },711 },
713 },712 },
714 mcpEnvironment: { T3_CODE_MCP_AUTHORIZATION: "Bearer secret-claude-token" },
715 });713 });
716714
717 const options = ClaudeAdapterV2.makeClaudeQueryOptions({715 const options = ClaudeAdapterV2.makeClaudeQueryOptions({
...@@ -724,12 +722,8 @@ describe("ClaudeAdapterV2 native protocol logging", () => {...@@ -724,12 +722,8 @@ describe("ClaudeAdapterV2 native protocol logging", () => {
724 cwd: "/workspace",722 cwd: "/workspace",
725 allowedTools: overrides.allowedTools ?? [],723 allowedTools: overrides.allowedTools ?? [],
726 mcpServers: overrides.mcpServers ?? {},724 mcpServers: overrides.mcpServers ?? {},
727 environment: { ...overrides.mcpEnvironment },725 environment: {},
728 });726 });
729 // mcpServers becomes a CLI argument, readable by every local user; the
730 // credential may only travel in the child's environment.
731 assert.notInclude(JSON.stringify(options.mcpServers), "secret-claude-token");
732 assert.equal(options.env?.T3_CODE_MCP_AUTHORIZATION, "Bearer secret-claude-token");
733 assert.isObject(options.systemPrompt);727 assert.isObject(options.systemPrompt);
734 const systemPrompt = options.systemPrompt as {728 const systemPrompt = options.systemPrompt as {
735 readonly type: string;729 readonly type: string;
...@@ -1850,6 +1844,120 @@ describe("ClaudeAdapterV2 native fork", () => {...@@ -1850,6 +1844,120 @@ describe("ClaudeAdapterV2 native fork", () => {
1850 );1844 );
1851});1845});
18521846
1847// Stands in for the Claude CLI behind the SDK's spawn hook: records how it was
1848// started and answers stdin control requests, except mcp_set_servers if asked.
1849function makeFakeClaudeCli(answerSetServers = true) {
1850 const spawns: Array<SpawnOptions> = [];
1851 const controlRequests: Array<object> = [];
1852 const stdins: Array<NodeStream.PassThrough> = [];
1853 const spawn = (options: SpawnOptions): SpawnedProcess => {
1854 spawns.push(options);
1855 const stdin = new NodeStream.PassThrough();
1856 const stdout = new NodeStream.PassThrough();
1857 stdins.push(stdin);
1858 const child = Object.assign(new NodeEvents.EventEmitter(), {
1859 stdin,
1860 stdout,
1861 killed: false,
1862 exitCode: null as number | null,
1863 kill: () => {
1864 stdout.end();
1865 child.emit("exit", null, "SIGTERM");
1866 return true;
1867 },
1868 });
1869 let pending = "";
1870 stdin.on("data", (chunk: Buffer) => {
1871 pending += chunk.toString("utf8");
1872 for (let end = pending.indexOf("\n"); end >= 0; end = pending.indexOf("\n")) {
1873 const frame = JSON.parse(pending.slice(0, end));
1874 pending = pending.slice(end + 1);
1875 if (frame.type !== "control_request") continue;
1876 controlRequests.push(frame.request);
1877 const setServers = frame.request.subtype === "mcp_set_servers";
1878 if (setServers && !answerSetServers) continue;
1879 const response = {
1880 subtype: "success",
1881 request_id: frame.request_id,
1882 response: setServers ? { added: [], removed: [], errors: {} } : {},
1883 };
1884 stdout.write(`${JSON.stringify({ type: "control_response", response })}\n`);
1885 }
1886 });
1887 stdin.on("end", () => {
1888 stdout.end();
1889 child.emit("exit", 0, null);
1890 });
1891 return child;
1892 };
1893 // The SDK closes the CLI by ending its stdin.
1894 const closed = () => stdins.length > 0 && stdins.every((stdin) => stdin.writableEnded);
1895 return { spawn, spawns, controlRequests, closed };
1896}
1897
1898describe("ClaudeAdapterV2 MCP credential channel", () => {
1899 const t3McpServer = {
1900 type: "http" as const,
1901 url: "http://127.0.0.1:43123/mcp",
1902 headers: { Authorization: "Bearer dummy-mcp-credential" },
1903 };
1904 const openWith = (cli: ReturnType<typeof makeFakeClaudeCli>) =>
1905 Effect.gen(function* () {
1906 const runner = yield* ClaudeAdapterV2.ClaudeAgentSdkQueryRunner;
1907 return yield* runner.open({
1908 threadId: ThreadId.make("thread-claude-mcp-channel"),
1909 providerSessionId: ProviderSessionId.make("provider-session-claude-mcp-channel"),
1910 options: {
1911 ...ClaudeAdapterV2.makeClaudeQueryOptions({
1912 modelSelection: CLAUDE_TEST_MODEL_SELECTION,
1913 nativeThreadId: "native-thread-claude-mcp-channel",
1914 resume: false,
1915 cwd: null,
1916 environment: { PATH: "/usr/bin" },
1917 mcpServers: { "t3-code": t3McpServer },
1918 }),
1919 pathToClaudeCodeExecutable: "/opt/claude/cli.js",
1920 spawnClaudeCodeProcess: cli.spawn,
1921 },
1922 });
1923 }).pipe(
1924 Effect.provide(ClaudeAdapterV2.layerQueryRunner),
1925 Effect.provideService(
1926 ProviderEventLoggers.ProviderEventLoggers,
1927 ProviderEventLoggers.NoOpProviderEventLoggers,
1928 ),
1929 Effect.provide(NodeServices.layer),
1930 );
1931
1932 it.effect("sends MCP servers over stdin, not argv or the environment", () =>
1933 Effect.gen(function* () {
1934 const cli = makeFakeClaudeCli();
1935 const session = yield* openWith(cli);
1936 yield* session.close;
1937
1938 assert.deepInclude(cli.controlRequests, {
1939 subtype: "mcp_set_servers",
1940 servers: { "t3-code": t3McpServer },
1941 });
1942 const spawned = JSON.stringify(cli.spawns);
1943 assert.notInclude(spawned, "--mcp-config");
1944 assert.notInclude(spawned, "dummy-mcp-credential");
1945 }),
1946 );
1947
1948 it.effect("closes the CLI when it never answers the MCP registration", () =>
1949 Effect.gen(function* () {
1950 const cli = makeFakeClaudeCli(false);
1951 const opening = yield* openWith(cli).pipe(Effect.result, Effect.forkChild);
1952 yield* TestClock.adjust("90 seconds");
1953 const opened = yield* Fiber.join(opening);
1954
1955 assert.equal(opened._tag, "Failure");
1956 assert.isTrue(cli.closed());
1957 }),
1958 );
1959});
1960
1853describe("ClaudeAdapterV2 native session identity", () => {1961describe("ClaudeAdapterV2 native session identity", () => {
1854 const openTurnWithOrdinal = (providerTurnOrdinal: number, nativeThreadHasTurns?: boolean) =>1962 const openTurnWithOrdinal = (providerTurnOrdinal: number, nativeThreadHasTurns?: boolean) =>
1855 Effect.scoped(1963 Effect.scoped(
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts+75-32
...@@ -185,6 +185,9 @@ export const CLAUDE_DEFAULT_INSTANCE_ID = defaultInstanceIdForDriver(CLAUDE_PROV...@@ -185,6 +185,9 @@ export const CLAUDE_DEFAULT_INSTANCE_ID = defaultInstanceIdForDriver(CLAUDE_PROV
185const DEFAULT_CLAUDE_SETTINGS = Schema.decodeSync(ClaudeSettings)({});185const DEFAULT_CLAUDE_SETTINGS = Schema.decodeSync(ClaudeSettings)({});
186// Entry extensions the Claude SDK runs through node instead of executing.186// Entry extensions the Claude SDK runs through node instead of executing.
187const CLAUDE_SCRIPT_EXTENSIONS = [".js", ".mjs", ".tsx", ".ts", ".jsx"];187const CLAUDE_SCRIPT_EXTENSIONS = [".js", ".mjs", ".tsx", ".ts", ".jsx"];
188// The SDK puts no deadline on control requests. The CLI answers
189// mcp_set_servers once each server connected or hit its own 30 s timeout.
190const CLAUDE_MCP_REGISTRATION_TIMEOUT = "90 seconds";
188191
189export const ClaudeProviderCapabilitiesV2 = {192export const ClaudeProviderCapabilitiesV2 = {
190 sessions: {193 sessions: {
...@@ -636,11 +639,16 @@ export const layerQueryRunner: Layer.Layer<...@@ -636,11 +639,16 @@ export const layerQueryRunner: Layer.Layer<
636 ),639 ),
637 Stream.toAsyncIterable,640 Stream.toAsyncIterable,
638 );641 );
642 // The SDK would pass `mcpServers` as an inline `--mcp-config` argument,
643 // which every local user can read. They go over the CLI's stdin control
644 // channel instead, so header credentials stay out of its argv and out
645 // of the environment every command the agent runs inherits.
646 const { mcpServers, ...queryOptions } = input.options;
639 // The SDK spawns a native binary as `path ...executableArgs ...sdkArgs`,647 // The SDK spawns a native binary as `path ...executableArgs ...sdkArgs`,
640 // which lets the agent scope wrapper go in front of the CLI while the648 // which lets the agent scope wrapper go in front of the CLI while the
641 // SDK keeps its own stderr capture and exit reporting. The SDK runs a649 // SDK keeps its own stderr capture and exit reporting. The SDK runs a
642 // script entry through node instead, so those launch unwrapped.650 // script entry through node instead, so those launch unwrapped.
643 const binaryPath = input.options.pathToClaudeCodeExecutable;651 const binaryPath = queryOptions.pathToClaudeCodeExecutable;
644 const launch =652 const launch =
645 binaryPath === undefined ||653 binaryPath === undefined ||
646 CLAUDE_SCRIPT_EXTENSIONS.some((ext) => binaryPath.endsWith(ext))654 CLAUDE_SCRIPT_EXTENSIONS.some((ext) => binaryPath.endsWith(ext))
...@@ -650,32 +658,74 @@ export const layerQueryRunner: Layer.Layer<...@@ -650,32 +658,74 @@ export const layerQueryRunner: Layer.Layer<
650 args: [],658 args: [],
651 name: "claude",659 name: "claude",
652 threadId: input.threadId,660 threadId: input.threadId,
653 env: input.options.env,661 env: queryOptions.env,
654 });662 });
655 const options =663 const options =
656 launch === undefined || launch.args.length === 0664 launch === undefined || launch.args.length === 0
657 ? input.options665 ? queryOptions
658 : {666 : {
659 ...input.options,667 ...queryOptions,
660 pathToClaudeCodeExecutable: launch.command,668 pathToClaudeCodeExecutable: launch.command,
661 executableArgs: [...launch.args, ...(input.options.executableArgs ?? [])],669 executableArgs: [...launch.args, ...(queryOptions.executableArgs ?? [])],
662 };670 };
663 const queryRuntime = yield* Effect.try({671 // The CLI is running once query() returns, so it is closed if anything
664 try: () =>672 // before the session is handed out fails or is interrupted.
665 query({673 const queryRuntime = yield* Effect.uninterruptibleMask((restore) =>
666 prompt,674 Effect.try({
667 options,675 try: () => query({ prompt, options }),
668 }),676 catch: (cause) => queryRunnerError(cause, "query"),
669 catch: (cause) => queryRunnerError(cause, "query"),677 }).pipe(
670 });678 Effect.tap((queryRuntime) =>
671 yield* logProtocolEvent({679 restore(
672 direction: "outgoing",680 Effect.gen(function* () {
673 stage: "decoded",681 if (mcpServers !== undefined) {
674 payload: {682 // A rejected request or a server that failed to connect
675 type: "query.open",683 // leaves the turn without those tools, as `--mcp-config`
676 options: loggedClaudeQueryOptions(input.options),684 // did. Error texts and the CLI's mcp_status response can
677 },685 // echo the headers, so only server names are logged.
678 });686 const failed = yield* Effect.tryPromise(() =>
687 queryRuntime.setMcpServers(mcpServers),
688 ).pipe(
689 Effect.map((result) => Object.keys(result.errors)),
690 Effect.orElseSucceed(() => Object.keys(mcpServers)),
691 Effect.timeoutOrElse({
692 duration: CLAUDE_MCP_REGISTRATION_TIMEOUT,
693 orElse: () =>
694 Effect.fail(
695 queryRunnerError(
696 "Claude Code did not answer the MCP server registration.",
697 "setMcpServers",
698 ),
699 ),
700 }),
701 );
702 if (failed.length > 0) {
703 yield* Effect.logWarning("orchestration-v2.claude-mcp-servers-unavailable", {
704 threadId: input.threadId,
705 servers: failed,
706 });
707 }
708 }
709 yield* logProtocolEvent({
710 direction: "outgoing",
711 stage: "decoded",
712 payload: {
713 type: "query.open",
714 options: loggedClaudeQueryOptions(input.options),
715 },
716 });
717 }),
718 ).pipe(
719 Effect.onError(() =>
720 Queue.shutdown(promptQueue).pipe(
721 Effect.andThen(closeClaudeQuery(queryRuntime)),
722 Effect.ignore,
723 ),
724 ),
725 ),
726 ),
727 ),
728 );
679729
680 return {730 return {
681 messages: Stream.fromAsyncIterable(claudeQueryMessages(queryRuntime), (cause) =>731 messages: Stream.fromAsyncIterable(claudeQueryMessages(queryRuntime), (cause) =>
...@@ -1005,11 +1055,8 @@ export const CLAUDE_T3_MCP_TOOL_TIMEOUT_MS = 65 * 60 * 1_000;...@@ -1005,11 +1055,8 @@ export const CLAUDE_T3_MCP_TOOL_TIMEOUT_MS = 65 * 60 * 1_000;
1005// read-only orchestrator tools so a read-only session cannot silently spawn1055// read-only orchestrator tools so a read-only session cannot silently spawn
1006// threads or scheduled tasks.1056// threads or scheduled tasks.
1007//1057//
1008// The SDK passes `mcpServers` to the CLI as an inline `--mcp-config` argument,1058// The header carries the credential itself: the query runner sends
1009// and process arguments are readable by every local user. The credential1059// `mcpServers` over the CLI's stdin, never its argv or environment.
1010// therefore travels in the child's environment, which only its owner can read,
1011// and the CLI expands the `${VAR}` reference when it connects.
1012const CLAUDE_T3_MCP_AUTHORIZATION_ENV = "T3_CODE_MCP_AUTHORIZATION";
10131060
1014export function claudeMcpQueryOverrides(input: {1061export function claudeMcpQueryOverrides(input: {
1015 readonly mcpSession: McpProviderSession.McpProviderSessionConfig | undefined;1062 readonly mcpSession: McpProviderSession.McpProviderSessionConfig | undefined;
...@@ -1018,7 +1065,6 @@ export function claudeMcpQueryOverrides(input: {...@@ -1018,7 +1065,6 @@ export function claudeMcpQueryOverrides(input: {
1018}): {1065}): {
1019 readonly allowedTools?: ReadonlyArray<string>;1066 readonly allowedTools?: ReadonlyArray<string>;
1020 readonly mcpServers?: ClaudeQueryOptions["mcpServers"];1067 readonly mcpServers?: ClaudeQueryOptions["mcpServers"];
1021 readonly mcpEnvironment?: Readonly<Record<string, string>>;
1022} {1068} {
1023 const session = input.mcpSession;1069 const session = input.mcpSession;
1024 if (session === undefined) {1070 if (session === undefined) {
...@@ -1034,12 +1080,11 @@ export function claudeMcpQueryOverrides(input: {...@@ -1034,12 +1080,11 @@ export function claudeMcpQueryOverrides(input: {
1034 type: "http",1080 type: "http",
1035 url: session.endpoint,1081 url: session.endpoint,
1036 headers: {1082 headers: {
1037 Authorization: `\${${CLAUDE_T3_MCP_AUTHORIZATION_ENV}}`,1083 Authorization: session.authorizationHeader,
1038 },1084 },
1039 timeout: CLAUDE_T3_MCP_TOOL_TIMEOUT_MS,1085 timeout: CLAUDE_T3_MCP_TOOL_TIMEOUT_MS,
1040 },1086 },
1041 },1087 },
1042 mcpEnvironment: { [CLAUDE_T3_MCP_AUTHORIZATION_ENV]: session.authorizationHeader },
1043 };1088 };
1044}1089}
10451090
...@@ -1670,7 +1715,6 @@ export function claudeEffectiveQueryPolicyKey(...@@ -1670,7 +1715,6 @@ export function claudeEffectiveQueryPolicyKey(
1670 mcpOverrides: {1715 mcpOverrides: {
1671 readonly allowedTools?: ReadonlyArray<string>;1716 readonly allowedTools?: ReadonlyArray<string>;
1672 readonly mcpServers?: ClaudeQueryOptions["mcpServers"];1717 readonly mcpServers?: ClaudeQueryOptions["mcpServers"];
1673 readonly mcpEnvironment?: Readonly<Record<string, string>>;
1674 },1718 },
1675): string {1719): string {
1676 return JSON.stringify({1720 return JSON.stringify({
...@@ -1681,7 +1725,6 @@ export function claudeEffectiveQueryPolicyKey(...@@ -1681,7 +1725,6 @@ export function claudeEffectiveQueryPolicyKey(
1681 : { allowedTools: mcpOverrides.allowedTools }),1725 : { allowedTools: mcpOverrides.allowedTools }),
1682 }),1726 }),
1683 mcpServers: mcpOverrides.mcpServers,1727 mcpServers: mcpOverrides.mcpServers,
1684 mcpEnvironment: mcpOverrides.mcpEnvironment,
1685 });1728 });
1686}1729}
16871730
...@@ -7488,7 +7531,7 @@ export const makeClaudeAdapterV2 = Effect.fn("makeClaudeAdapterV2")(function* (...@@ -7488,7 +7531,7 @@ export const makeClaudeAdapterV2 = Effect.fn("makeClaudeAdapterV2")(function* (
7488 cwd: turnInput.runtimePolicy.cwd,7531 cwd: turnInput.runtimePolicy.cwd,
7489 attachmentsDir,7532 attachmentsDir,
7490 settings: adapterOptions.settings,7533 settings: adapterOptions.settings,
7491 environment: { ...adapterOptions.environment, ...mcpOverrides.mcpEnvironment },7534 environment: adapterOptions.environment,
7492 tools: queryPolicy.tools ?? CLAUDE_CODE_PRESET_TOOLS,7535 tools: queryPolicy.tools ?? CLAUDE_CODE_PRESET_TOOLS,
7493 ...(mcpOverrides.allowedTools === undefined7536 ...(mcpOverrides.allowedTools === undefined
7494 ? {}7537 ? {}