fix(opencode): provide mcp fallback for provider env

This commit is contained in:
777genius 2026-05-17 00:12:00 +03:00
parent eb1b333879
commit f96d62dc20
3 changed files with 97 additions and 0 deletions

View file

@ -0,0 +1,46 @@
import { resolveAgentTeamsMcpLaunchSpec } from '@main/services/team/TeamMcpConfigBuilder';
import { createLogger } from '@shared/utils/logger';
import type { McpLaunchSpec } from '@main/services/team/TeamMcpConfigBuilder';
const logger = createLogger('Runtime:AgentTeamsMcpLaunchEnv');
const MCP_COMMAND_ENV = 'CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND';
const MCP_ENTRY_ENV = 'CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY';
const MCP_ARGS_JSON_ENV = 'CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON';
export type AgentTeamsMcpLaunchEnv = Record<string, string | undefined>;
export function hasAgentTeamsMcpLocalLaunchEnv(env: AgentTeamsMcpLaunchEnv): boolean {
return Boolean(
env[MCP_COMMAND_ENV]?.trim() && env[MCP_ENTRY_ENV]?.trim() && env[MCP_ARGS_JSON_ENV]?.trim()
);
}
export async function ensureAgentTeamsMcpLocalLaunchEnv(
env: AgentTeamsMcpLaunchEnv,
resolveLaunchSpec: () => Promise<McpLaunchSpec> = resolveAgentTeamsMcpLaunchSpec
): Promise<void> {
if (hasAgentTeamsMcpLocalLaunchEnv(env)) {
return;
}
try {
const launchSpec = await resolveLaunchSpec();
const entry = launchSpec.args[0]?.trim();
const command = launchSpec.command.trim();
if (!command || !entry) {
return;
}
env[MCP_COMMAND_ENV] = command;
env[MCP_ENTRY_ENV] = entry;
env[MCP_ARGS_JSON_ENV] = JSON.stringify(launchSpec.args);
} catch (error) {
logger.warn(
`Unable to resolve Agent Teams MCP local launch env: ${
error instanceof Error ? error.message : String(error)
}`
);
}
}

View file

@ -3,6 +3,7 @@ import { getCachedShellEnv } from '@main/utils/shellEnv';
import { resolveVerifiedAppManagedOpenCodeRuntimeBinaryPath } from '../infrastructure/OpenCodeRuntimeInstallerService';
import { ensureAgentTeamsMcpLocalLaunchEnv } from './agentTeamsMcpLaunchEnv';
import { buildRuntimeBaseEnv } from './buildRuntimeBaseEnv';
import { providerConnectionService } from './ProviderConnectionService';
@ -58,6 +59,9 @@ export async function buildProviderAwareCliEnv(
) {
env.CODEX_CLI_PATH = appManagedCodexBinary;
}
if (!resolvedProviderId || resolvedProviderId === 'opencode') {
await ensureAgentTeamsMcpLocalLaunchEnv(env);
}
if (options.providerId) {
if (!resolvedProviderId) {

View file

@ -12,6 +12,7 @@ const getConfiguredConnectionIssuesMock = vi.fn();
const getConfiguredConnectionLaunchArgsMock = vi.fn();
const resolveVerifiedAppManagedOpenCodeRuntimeBinaryPathMock = vi.fn();
const resolveVerifiedAppManagedCodexRuntimeBinaryPathMock = vi.fn();
const resolveAgentTeamsMcpLaunchSpecMock = vi.fn();
vi.mock('@main/utils/cliEnv', () => ({
buildEnrichedEnv: (...args: Parameters<typeof buildEnrichedEnvMock>) =>
@ -68,6 +69,10 @@ vi.mock('@features/codex-runtime-installer/main', () => ({
resolveVerifiedAppManagedCodexRuntimeBinaryPathMock(),
}));
vi.mock('@main/services/team/TeamMcpConfigBuilder', () => ({
resolveAgentTeamsMcpLaunchSpec: () => resolveAgentTeamsMcpLaunchSpecMock(),
}));
describe('buildProviderAwareCliEnv', () => {
beforeEach(() => {
vi.resetModules();
@ -95,6 +100,10 @@ describe('buildProviderAwareCliEnv', () => {
getConfiguredConnectionIssuesMock.mockResolvedValue({});
resolveVerifiedAppManagedOpenCodeRuntimeBinaryPathMock.mockResolvedValue(null);
resolveVerifiedAppManagedCodexRuntimeBinaryPathMock.mockResolvedValue(null);
resolveAgentTeamsMcpLaunchSpecMock.mockResolvedValue({
command: 'node',
args: ['/app/mcp-server/index.js'],
});
});
it('builds provider-pinned CLI env and returns provider-specific issues', async () => {
@ -168,6 +177,44 @@ describe('buildProviderAwareCliEnv', () => {
expect(result.connectionIssues).toEqual({});
expect(result.providerArgs).toEqual([]);
expect(result.env.OPENCODE_DISABLE_AUTOUPDATE).toBe('1');
expect(result.env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND).toBe('node');
expect(result.env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY).toBe('/app/mcp-server/index.js');
expect(result.env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON).toBe(
'["/app/mcp-server/index.js"]'
);
});
it('adds local Agent Teams MCP launch env for OpenCode provider runtime commands', async () => {
const { buildProviderAwareCliEnv } =
await import('../../../../src/main/services/runtime/providerAwareCliEnv');
const result = await buildProviderAwareCliEnv({
providerId: 'opencode',
});
expect(resolveAgentTeamsMcpLaunchSpecMock).toHaveBeenCalledTimes(1);
expect(result.env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND).toBe('node');
expect(result.env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY).toBe('/app/mcp-server/index.js');
expect(result.env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON).toBe(
'["/app/mcp-server/index.js"]'
);
});
it('preserves explicit local Agent Teams MCP launch env for OpenCode provider commands', async () => {
const { buildProviderAwareCliEnv } =
await import('../../../../src/main/services/runtime/providerAwareCliEnv');
const result = await buildProviderAwareCliEnv({
providerId: 'opencode',
env: {
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND: 'custom-node',
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY: '/custom/mcp.js',
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON: '["/custom/mcp.js"]',
},
});
expect(resolveAgentTeamsMcpLaunchSpecMock).not.toHaveBeenCalled();
expect(result.env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_COMMAND).toBe('custom-node');
expect(result.env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ENTRY).toBe('/custom/mcp.js');
expect(result.env.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_ARGS_JSON).toBe('["/custom/mcp.js"]');
});
it('allows OpenCode auto-update only behind an explicit app override', async () => {