Merge pull request #126 from 777genius/fix/opencode-http-mcp-default
fix(opencode): default to HTTP MCP bridge
This commit is contained in:
commit
b616a53255
5 changed files with 332 additions and 56 deletions
|
|
@ -68,6 +68,13 @@ import { ChangeExtractorService } from '@main/services/team/ChangeExtractorServi
|
|||
import { CrossTeamService } from '@main/services/team/CrossTeamService';
|
||||
import { FileContentResolver } from '@main/services/team/FileContentResolver';
|
||||
import { GitDiffFallback } from '@main/services/team/GitDiffFallback';
|
||||
import {
|
||||
clearOpenCodeLocalMcpLaunchEnv,
|
||||
copyOpenCodeLocalMcpLaunchEnv,
|
||||
hasOpenCodeLocalMcpLaunchEnv,
|
||||
isOpenCodeMcpHttpBridgeEnabled,
|
||||
snapshotOpenCodeLocalMcpLaunchEnv,
|
||||
} from '@main/services/team/opencode/bridge/OpenCodeMcpBridgeEnv';
|
||||
import { ReviewApplierService } from '@main/services/team/ReviewApplierService';
|
||||
import { TeamBackupService } from '@main/services/team/TeamBackupService';
|
||||
import { TeamConfigReader } from '@main/services/team/TeamConfigReader';
|
||||
|
|
@ -353,10 +360,13 @@ async function createOpenCodeRuntimeAdapterRegistry(
|
|||
const bridgeEnv = applyOpenCodeAutoUpdatePolicy({ ...process.env });
|
||||
bridgeEnv.CLAUDE_TEAM_APP_INSTANCE_ID = openCodeManagedHostInstanceId;
|
||||
bridgeEnv.AGENT_TEAMS_MCP_CLAUDE_DIR = getClaudeBasePath();
|
||||
const useHttpMcpBridge = bridgeEnv.CLAUDE_TEAM_OPENCODE_MCP_HTTP === '1';
|
||||
const useHttpMcpBridge = isOpenCodeMcpHttpBridgeEnabled(bridgeEnv);
|
||||
const explicitLocalMcpLaunchEnv = snapshotOpenCodeLocalMcpLaunchEnv(bridgeEnv);
|
||||
if (!useHttpMcpBridge) {
|
||||
// The OpenCode bridge direct tools/list proof currently requires a local MCP command.
|
||||
delete bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL;
|
||||
} else {
|
||||
delete bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL;
|
||||
clearOpenCodeLocalMcpLaunchEnv(bridgeEnv);
|
||||
}
|
||||
const applyMcpLaunchSpecEnv = async (
|
||||
targetEnv: NodeJS.ProcessEnv,
|
||||
|
|
@ -385,6 +395,25 @@ async function createOpenCodeRuntimeAdapterRegistry(
|
|||
);
|
||||
}
|
||||
};
|
||||
const ensureOpenCodeLocalMcpLaunchEnv = async (
|
||||
targetEnv: NodeJS.ProcessEnv,
|
||||
options: { emitProgress?: boolean } = {}
|
||||
): Promise<void> => {
|
||||
if (hasOpenCodeLocalMcpLaunchEnv(bridgeEnv)) {
|
||||
copyOpenCodeLocalMcpLaunchEnv(bridgeEnv, targetEnv);
|
||||
return;
|
||||
}
|
||||
if (explicitLocalMcpLaunchEnv) {
|
||||
copyOpenCodeLocalMcpLaunchEnv(explicitLocalMcpLaunchEnv, targetEnv);
|
||||
copyOpenCodeLocalMcpLaunchEnv(explicitLocalMcpLaunchEnv, bridgeEnv);
|
||||
return;
|
||||
}
|
||||
|
||||
await applyMcpLaunchSpecEnv(targetEnv, options);
|
||||
if (hasOpenCodeLocalMcpLaunchEnv(targetEnv)) {
|
||||
copyOpenCodeLocalMcpLaunchEnv(targetEnv, bridgeEnv);
|
||||
}
|
||||
};
|
||||
try {
|
||||
const appManagedOpenCodeBinary = await resolveVerifiedAppManagedOpenCodeRuntimeBinaryPath();
|
||||
if (appManagedOpenCodeBinary && !bridgeEnv.CLAUDE_MULTIMODEL_OPENCODE_BIN_PATH) {
|
||||
|
|
@ -418,6 +447,7 @@ async function createOpenCodeRuntimeAdapterRegistry(
|
|||
reportProgress('runtime-mcp-http', 'Starting Agent Teams MCP server...');
|
||||
const mcpHttpServer = await agentTeamsMcpHttpServer.ensureStarted();
|
||||
bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL = mcpHttpServer.url;
|
||||
clearOpenCodeLocalMcpLaunchEnv(bridgeEnv);
|
||||
reportProgress('runtime-mcp-http-ready', 'Agent Teams MCP server is ready...');
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
|
|
@ -428,44 +458,26 @@ async function createOpenCodeRuntimeAdapterRegistry(
|
|||
}
|
||||
}
|
||||
if (!bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL) {
|
||||
await applyMcpLaunchSpecEnv(bridgeEnv, { emitProgress: true });
|
||||
await ensureOpenCodeLocalMcpLaunchEnv(bridgeEnv, { emitProgress: true });
|
||||
}
|
||||
|
||||
reportProgress('runtime-bridge', 'Preparing OpenCode bridge...');
|
||||
const resolveBridgeCommandEnv = async (): Promise<NodeJS.ProcessEnv> => {
|
||||
const nextEnv = { ...bridgeEnv };
|
||||
if (!useHttpMcpBridge || !bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL) {
|
||||
if (!useHttpMcpBridge) {
|
||||
return nextEnv;
|
||||
}
|
||||
try {
|
||||
const mcpHttpServer = await agentTeamsMcpHttpServer.ensureStarted();
|
||||
bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL = mcpHttpServer.url;
|
||||
nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL = mcpHttpServer.url;
|
||||
delete nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND;
|
||||
delete nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY;
|
||||
delete nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON;
|
||||
clearOpenCodeLocalMcpLaunchEnv(bridgeEnv);
|
||||
clearOpenCodeLocalMcpLaunchEnv(nextEnv);
|
||||
} catch (error) {
|
||||
delete bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL;
|
||||
delete nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL;
|
||||
if (
|
||||
bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND &&
|
||||
bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY &&
|
||||
bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON
|
||||
) {
|
||||
nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND =
|
||||
bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND;
|
||||
nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY =
|
||||
bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY;
|
||||
nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON =
|
||||
bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON;
|
||||
} else {
|
||||
await applyMcpLaunchSpecEnv(nextEnv);
|
||||
bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND =
|
||||
nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND;
|
||||
bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY =
|
||||
nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY;
|
||||
bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON =
|
||||
nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON;
|
||||
}
|
||||
clearOpenCodeLocalMcpLaunchEnv(nextEnv);
|
||||
await ensureOpenCodeLocalMcpLaunchEnv(nextEnv);
|
||||
logger.warn(
|
||||
`[OpenCode] Runtime adapter bridge MCP HTTP server refresh failed: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,25 @@ export interface OpenCodeBridgeCommandClientOptions {
|
|||
|
||||
const DEFAULT_STDOUT_LIMIT_BYTES = 1_000_000;
|
||||
const DEFAULT_STDERR_LIMIT_BYTES = 256_000;
|
||||
const WINDOWS_BATCH_EXTENSIONS = new Set(['.cmd', '.bat']);
|
||||
|
||||
export function resolveOpenCodeBridgeProcessCwd(
|
||||
binaryPath: string,
|
||||
requestedCwd: string,
|
||||
platform: NodeJS.Platform = process.platform
|
||||
): string {
|
||||
if (platform !== 'win32') {
|
||||
return requestedCwd;
|
||||
}
|
||||
|
||||
const extension = path.win32.extname(binaryPath).toLowerCase();
|
||||
if (!WINDOWS_BATCH_EXTENSIONS.has(extension)) {
|
||||
return requestedCwd;
|
||||
}
|
||||
|
||||
const launcherDirectory = path.win32.dirname(binaryPath);
|
||||
return launcherDirectory && launcherDirectory !== '.' ? launcherDirectory : requestedCwd;
|
||||
}
|
||||
|
||||
export class ExecCliOpenCodeBridgeProcessRunner implements OpenCodeBridgeProcessRunner {
|
||||
async run(input: OpenCodeBridgeProcessRunInput): Promise<OpenCodeBridgeProcessRunResult> {
|
||||
|
|
@ -146,7 +165,7 @@ export class OpenCodeBridgeCommandClient {
|
|||
const processResult = await this.processRunner.run({
|
||||
binaryPath: this.binaryPath,
|
||||
args: ['runtime', 'opencode-command', '--json', '--input', inputPath],
|
||||
cwd: options.cwd,
|
||||
cwd: resolveOpenCodeBridgeProcessCwd(this.binaryPath, options.cwd),
|
||||
timeoutMs: options.timeoutMs,
|
||||
stdoutLimitBytes: options.stdoutLimitBytes ?? DEFAULT_STDOUT_LIMIT_BYTES,
|
||||
stderrLimitBytes: options.stderrLimitBytes ?? DEFAULT_STDERR_LIMIT_BYTES,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
const DISABLED_HTTP_MCP_VALUES = new Set(['0', 'false', 'no', 'off']);
|
||||
|
||||
const LOCAL_MCP_LAUNCH_ENV_KEYS = [
|
||||
'CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND',
|
||||
'CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY',
|
||||
'CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON',
|
||||
] as const;
|
||||
|
||||
export type OpenCodeMcpBridgeEnv = Record<string, string | undefined>;
|
||||
|
||||
export function isOpenCodeMcpHttpBridgeEnabled(env: OpenCodeMcpBridgeEnv = process.env): boolean {
|
||||
const rawValue = env.CLAUDE_TEAM_OPENCODE_MCP_HTTP?.trim().toLowerCase();
|
||||
return rawValue ? !DISABLED_HTTP_MCP_VALUES.has(rawValue) : true;
|
||||
}
|
||||
|
||||
export function hasOpenCodeLocalMcpLaunchEnv(env: OpenCodeMcpBridgeEnv): boolean {
|
||||
return LOCAL_MCP_LAUNCH_ENV_KEYS.every((key) => Boolean(env[key]?.trim()));
|
||||
}
|
||||
|
||||
export function copyOpenCodeLocalMcpLaunchEnv(
|
||||
sourceEnv: OpenCodeMcpBridgeEnv,
|
||||
targetEnv: OpenCodeMcpBridgeEnv
|
||||
): void {
|
||||
for (const key of LOCAL_MCP_LAUNCH_ENV_KEYS) {
|
||||
const value = sourceEnv[key]?.trim();
|
||||
if (value) {
|
||||
targetEnv[key] = value;
|
||||
} else {
|
||||
delete targetEnv[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function snapshotOpenCodeLocalMcpLaunchEnv(
|
||||
env: OpenCodeMcpBridgeEnv
|
||||
): OpenCodeMcpBridgeEnv | null {
|
||||
if (!hasOpenCodeLocalMcpLaunchEnv(env)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const snapshot: OpenCodeMcpBridgeEnv = {};
|
||||
copyOpenCodeLocalMcpLaunchEnv(env, snapshot);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
export function clearOpenCodeLocalMcpLaunchEnv(env: OpenCodeMcpBridgeEnv): void {
|
||||
for (const key of LOCAL_MCP_LAUNCH_ENV_KEYS) {
|
||||
delete env[key];
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|||
import {
|
||||
OpenCodeBridgeCommandClient,
|
||||
redactBridgeDiagnosticText,
|
||||
resolveOpenCodeBridgeProcessCwd,
|
||||
type OpenCodeBridgeDiagnosticsSink,
|
||||
type OpenCodeBridgeProcessRunInput,
|
||||
type OpenCodeBridgeProcessRunResult,
|
||||
|
|
@ -41,10 +42,14 @@ describe('OpenCodeBridgeCommandClient', () => {
|
|||
};
|
||||
const client = createClient();
|
||||
|
||||
const result = await client.execute('opencode.launchTeam', { runId: 'run-1' }, {
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
const result = await client.execute(
|
||||
'opencode.launchTeam',
|
||||
{ runId: 'run-1' },
|
||||
{
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
}
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
|
|
@ -83,10 +88,14 @@ describe('OpenCodeBridgeCommandClient', () => {
|
|||
};
|
||||
const client = createClient();
|
||||
|
||||
const result = await client.execute('opencode.launchTeam', { runId: 'run-1' }, {
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
const result = await client.execute(
|
||||
'opencode.launchTeam',
|
||||
{ runId: 'run-1' },
|
||||
{
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
}
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
|
|
@ -116,10 +125,14 @@ describe('OpenCodeBridgeCommandClient', () => {
|
|||
};
|
||||
const client = createClient();
|
||||
|
||||
const result = await client.execute('opencode.launchTeam', { runId: 'run-1' }, {
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
const result = await client.execute(
|
||||
'opencode.launchTeam',
|
||||
{ runId: 'run-1' },
|
||||
{
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
}
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
|
|
@ -148,10 +161,14 @@ describe('OpenCodeBridgeCommandClient', () => {
|
|||
};
|
||||
const client = createClient();
|
||||
|
||||
const result = await client.execute('opencode.launchTeam', { runId: 'run-1' }, {
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
const result = await client.execute(
|
||||
'opencode.launchTeam',
|
||||
{ runId: 'run-1' },
|
||||
{
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
}
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
|
|
@ -175,10 +192,14 @@ describe('OpenCodeBridgeCommandClient', () => {
|
|||
};
|
||||
const client = createClient();
|
||||
|
||||
const result = await client.execute('opencode.launchTeam', { runId: 'run-1' }, {
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
const result = await client.execute(
|
||||
'opencode.launchTeam',
|
||||
{ runId: 'run-1' },
|
||||
{
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
}
|
||||
);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
|
|
@ -208,14 +229,22 @@ describe('OpenCodeBridgeCommandClient', () => {
|
|||
},
|
||||
});
|
||||
|
||||
await client.execute('opencode.launchTeam', { runId: 'run-1' }, {
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
await client.execute('opencode.launchTeam', { runId: 'run-2' }, {
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
await client.execute(
|
||||
'opencode.launchTeam',
|
||||
{ runId: 'run-1' },
|
||||
{
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
}
|
||||
);
|
||||
await client.execute(
|
||||
'opencode.launchTeam',
|
||||
{ runId: 'run-2' },
|
||||
{
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
}
|
||||
);
|
||||
|
||||
expect(runner.calls[0].env).toMatchObject({
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL: 'http://127.0.0.1:5001/mcp',
|
||||
|
|
@ -226,6 +255,36 @@ describe('OpenCodeBridgeCommandClient', () => {
|
|||
OPENCODE_DISABLE_AUTOUPDATE: '1',
|
||||
});
|
||||
});
|
||||
|
||||
it('runs Windows batch launchers from their launcher directory while preserving envelope cwd', async () => {
|
||||
runner.nextResult = {
|
||||
stdout: `${JSON.stringify(bridgeSuccess({ data: { runId: 'run-1' } }))}\n`,
|
||||
stderr: '',
|
||||
exitCode: 0,
|
||||
timedOut: false,
|
||||
};
|
||||
const client = createClient({
|
||||
binaryPath: 'C:\\runtime\\agent_teams_orchestrator\\cli-dev.cmd',
|
||||
});
|
||||
|
||||
await client.execute(
|
||||
'opencode.launchTeam',
|
||||
{ runId: 'run-1' },
|
||||
{
|
||||
cwd: 'C:\\projects\\team workspace',
|
||||
timeoutMs: 10_000,
|
||||
}
|
||||
);
|
||||
|
||||
expect(runner.calls[0].cwd).toBe(
|
||||
process.platform === 'win32'
|
||||
? 'C:\\runtime\\agent_teams_orchestrator'
|
||||
: 'C:\\projects\\team workspace'
|
||||
);
|
||||
expect(JSON.parse(await runner.readInputEnvelope(0))).toMatchObject({
|
||||
cwd: 'C:\\projects\\team workspace',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('redactBridgeDiagnosticText', () => {
|
||||
|
|
@ -242,6 +301,34 @@ describe('redactBridgeDiagnosticText', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('resolveOpenCodeBridgeProcessCwd', () => {
|
||||
it('keeps non-Windows launchers on the requested project cwd', () => {
|
||||
expect(
|
||||
resolveOpenCodeBridgeProcessCwd('/usr/local/bin/claude-multimodel', '/repo', 'linux')
|
||||
).toBe('/repo');
|
||||
});
|
||||
|
||||
it('uses the launcher directory for Windows batch launchers', () => {
|
||||
expect(
|
||||
resolveOpenCodeBridgeProcessCwd(
|
||||
'C:\\runtime\\agent_teams_orchestrator\\cli-dev.cmd',
|
||||
'C:\\projects\\team workspace',
|
||||
'win32'
|
||||
)
|
||||
).toBe('C:\\runtime\\agent_teams_orchestrator');
|
||||
});
|
||||
|
||||
it('keeps Windows exe launchers on the requested project cwd', () => {
|
||||
expect(
|
||||
resolveOpenCodeBridgeProcessCwd(
|
||||
'C:\\runtime-cache\\claude-multimodel.exe',
|
||||
'C:\\projects\\team workspace',
|
||||
'win32'
|
||||
)
|
||||
).toBe('C:\\projects\\team workspace');
|
||||
});
|
||||
});
|
||||
|
||||
function createClient(
|
||||
overrides: Partial<ConstructorParameters<typeof OpenCodeBridgeCommandClient>[0]> = {}
|
||||
): OpenCodeBridgeCommandClient {
|
||||
|
|
|
|||
108
test/main/services/team/OpenCodeMcpBridgeEnv.test.ts
Normal file
108
test/main/services/team/OpenCodeMcpBridgeEnv.test.ts
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
clearOpenCodeLocalMcpLaunchEnv,
|
||||
copyOpenCodeLocalMcpLaunchEnv,
|
||||
hasOpenCodeLocalMcpLaunchEnv,
|
||||
isOpenCodeMcpHttpBridgeEnabled,
|
||||
snapshotOpenCodeLocalMcpLaunchEnv,
|
||||
} from '@main/services/team/opencode/bridge/OpenCodeMcpBridgeEnv';
|
||||
|
||||
describe('OpenCodeMcpBridgeEnv', () => {
|
||||
it('uses the app-owned HTTP MCP bridge by default', () => {
|
||||
expect(isOpenCodeMcpHttpBridgeEnabled({})).toBe(true);
|
||||
expect(isOpenCodeMcpHttpBridgeEnabled({ CLAUDE_TEAM_OPENCODE_MCP_HTTP: '1' })).toBe(true);
|
||||
expect(isOpenCodeMcpHttpBridgeEnabled({ CLAUDE_TEAM_OPENCODE_MCP_HTTP: 'true' })).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps the legacy local MCP command path behind an explicit opt-out', () => {
|
||||
expect(isOpenCodeMcpHttpBridgeEnabled({ CLAUDE_TEAM_OPENCODE_MCP_HTTP: '0' })).toBe(false);
|
||||
expect(isOpenCodeMcpHttpBridgeEnabled({ CLAUDE_TEAM_OPENCODE_MCP_HTTP: ' false ' })).toBe(
|
||||
false
|
||||
);
|
||||
expect(isOpenCodeMcpHttpBridgeEnabled({ CLAUDE_TEAM_OPENCODE_MCP_HTTP: 'off' })).toBe(false);
|
||||
});
|
||||
|
||||
it('accepts process-style env objects', () => {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
PATH: '/usr/bin',
|
||||
CLAUDE_TEAM_OPENCODE_MCP_HTTP: 'no',
|
||||
};
|
||||
|
||||
expect(isOpenCodeMcpHttpBridgeEnabled(env)).toBe(false);
|
||||
});
|
||||
|
||||
it('detects complete local MCP launch env', () => {
|
||||
expect(
|
||||
hasOpenCodeLocalMcpLaunchEnv({
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND: 'node',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY: 'mcp-server/dist/index.js',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON: '["mcp-server/dist/index.js"]',
|
||||
})
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
hasOpenCodeLocalMcpLaunchEnv({
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND: 'node',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY: '',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON: '["mcp-server/dist/index.js"]',
|
||||
})
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('copies local MCP launch env for HTTP fallback without copying the HTTP URL', () => {
|
||||
const target: NodeJS.ProcessEnv = {
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL: 'http://127.0.0.1:41001/mcp',
|
||||
};
|
||||
|
||||
copyOpenCodeLocalMcpLaunchEnv(
|
||||
{
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND: 'node',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY: 'mcp-server/dist/index.js',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON: '["mcp-server/dist/index.js"]',
|
||||
},
|
||||
target
|
||||
);
|
||||
|
||||
expect(target.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND).toBe('node');
|
||||
expect(target.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY).toBe('mcp-server/dist/index.js');
|
||||
expect(target.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON).toBe('["mcp-server/dist/index.js"]');
|
||||
expect(target.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL).toBe('http://127.0.0.1:41001/mcp');
|
||||
});
|
||||
|
||||
it('snapshots explicit local MCP launch env before HTTP mode clears it', () => {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND: ' node ',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY: ' mcp-server/dist/index.js ',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON: ' ["mcp-server/dist/index.js"] ',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL: 'http://127.0.0.1:41001/mcp',
|
||||
};
|
||||
|
||||
const snapshot = snapshotOpenCodeLocalMcpLaunchEnv(env);
|
||||
clearOpenCodeLocalMcpLaunchEnv(env);
|
||||
|
||||
expect(snapshot).toEqual({
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND: 'node',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY: 'mcp-server/dist/index.js',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON: '["mcp-server/dist/index.js"]',
|
||||
});
|
||||
expect(hasOpenCodeLocalMcpLaunchEnv(snapshot ?? {})).toBe(true);
|
||||
expect(env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND).toBeUndefined();
|
||||
});
|
||||
|
||||
it('removes local MCP launch env when HTTP MCP is active', () => {
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND: 'node',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY: 'mcp-server/dist/index.js',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON: '["mcp-server/dist/index.js"]',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL: 'http://127.0.0.1:41001/mcp',
|
||||
};
|
||||
|
||||
clearOpenCodeLocalMcpLaunchEnv(env);
|
||||
|
||||
expect(env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND).toBeUndefined();
|
||||
expect(env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY).toBeUndefined();
|
||||
expect(env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON).toBeUndefined();
|
||||
expect(env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL).toBe('http://127.0.0.1:41001/mcp');
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue