diff --git a/apps/worker/src/run-task/__tests__/run-task.test.ts b/apps/worker/src/run-task/__tests__/run-task.test.ts index 76e42d9bf..38a25333f 100644 --- a/apps/worker/src/run-task/__tests__/run-task.test.ts +++ b/apps/worker/src/run-task/__tests__/run-task.test.ts @@ -23,6 +23,7 @@ const { drainSlackMessagesMock, existsSyncMock, getDecryptedKeyMock, + getCustomStdioMcpServersMock, getMcpServerConfigsMock, harnessManagerInstances, hasActiveInstallationMock, @@ -87,6 +88,7 @@ const { .mockResolvedValue({ resumed: false, reason: 'no_pending_messages' }), existsSyncMock: vi.fn(), getDecryptedKeyMock: vi.fn().mockResolvedValue(undefined), + getCustomStdioMcpServersMock: vi.fn().mockResolvedValue({ servers: {} }), getMcpServerConfigsMock: vi.fn().mockResolvedValue({ servers: {} }), harnessManagerInstances: [] as FakeHarnessManager[], hasActiveInstallationMock: vi.fn().mockResolvedValue(false), @@ -183,6 +185,7 @@ vi.mock('@roomote/sdk/client', () => ({ hasActiveInstallation: hasActiveInstallationMock, }, mcpConnections: { + getCustomStdioMcpServers: getCustomStdioMcpServersMock, getMcpServerConfigs: getMcpServerConfigsMock, isOrgEnabled: isOrgEnabledMock, }, @@ -1691,6 +1694,63 @@ describe('runTask', () => { ); }); + it.each([ + ['user MCP', getMcpServerConfigsMock], + ['custom stdio MCP', getCustomStdioMcpServersMock], + ])( + 'warns the agent when %s configuration cannot be loaded', + async (_, fetchMock) => { + fetchMock.mockRejectedValueOnce( + new Error('integration service unavailable'), + ); + + await runTask({ + taskRun: { + id: 107, + taskId: 'task-107', + payloadKind: TaskPayloadKind.StandardTask, + harness: 'opencode-server', + payload: {}, + result: null, + } as never, + envVars: {}, + workspacePath: '/tmp/workspace', + prompt: '', + harnessInstructions: undefined, + agentInstructions: undefined, + environmentConfig: undefined, + callbacks: {}, + context: {}, + logger: { + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + log: vi.fn(), + } as never, + harnessSessionId: undefined, + workerEnv: { + authToken: 'cloud-token', + roomoteAppUrl: 'https://api.example.test', + trpcUrl: 'https://web.example.test', + buildUserFacingEnv: vi.fn(() => ({ + HOME: '/tmp/home', + PATH: '/usr/bin', + })), + } as never, + }); + + expect(createHarnessMock).toHaveBeenCalledTimes(1); + const developerInstructionsContent = createHarnessMock.mock.calls[0]?.[0] + .developerInstructionsContent as string; + expect(developerInstructionsContent).toContain( + 'Do not conclude that a missing integration or tool is unconfigured.', + ); + expect(developerInstructionsContent).not.toContain( + 'integration service unavailable', + ); + }, + ); + it('passes the proof browser target to the harness when the environment exposes a browser surface', async () => { await runTask({ taskRun: { diff --git a/apps/worker/src/run-task/run-task.ts b/apps/worker/src/run-task/run-task.ts index 36c50b891..c50fa804a 100644 --- a/apps/worker/src/run-task/run-task.ts +++ b/apps/worker/src/run-task/run-task.ts @@ -937,6 +937,7 @@ export const runTask = async ({ // Fetch integration MCP availability. This is best-effort: failures are // logged but never block task execution. const integrations: IntegrationMcpOptions = {}; + let mcpConfigurationLoadFailed = false; try { const { servers } = await sdk.mcpConnections.getMcpServerConfigs(); @@ -948,6 +949,7 @@ export const runTask = async ({ logger.warn( `[runTask] Failed to fetch user MCP server configs: ${error instanceof Error ? error.message : String(error)}`, ); + mcpConfigurationLoadFailed = true; } // Deployment-scoped custom stdio MCP servers. Same best-effort posture: @@ -969,6 +971,7 @@ export const runTask = async ({ logger.warn( `[runTask] Failed to fetch custom stdio MCP server configs: ${error instanceof Error ? error.message : String(error)}`, ); + mcpConfigurationLoadFailed = true; } const slackReplyContext = getSlackReplyContext(taskRun); @@ -1050,6 +1053,9 @@ export const runTask = async ({ ), harnessInstructions, environmentInstructions, + mcpConfigurationLoadFailed + ? `# Integration availability\n\nRoomote could not load the configured MCP integrations for this task. Do not conclude that a missing integration or tool is unconfigured. Tell the user that integration availability could not be verified for this task, and suggest retrying or asking a deployment admin to check Settings > Integrations.` + : undefined, ] .filter((value): value is string => Boolean(value)) .join('\n\n') || undefined;