From d2a2f8eea4538e7d9694a7ce394fbf7a5251764a Mon Sep 17 00:00:00 2001 From: Cody Vandermyn Date: Wed, 10 Dec 2025 12:31:42 -0800 Subject: [PATCH 1/5] feat: Add suppressWarnings option to reduce context window usage Adds a new optional 'suppressWarnings' parameter to build and test tools that filters warning messages from output, helping AI assistants conserve context window space when warnings are not relevant to the task. Affected tools: - build_device, test_device - build_macos, build_run_macos, test_macos - build_sim, build_run_sim, test_sim - clean Includes unit tests for the warning suppression logic. --- src/mcp/tools/device/build_device.ts | 4 ++ src/mcp/tools/device/test_device.ts | 4 ++ src/mcp/tools/macos/build_macos.ts | 4 ++ src/mcp/tools/macos/build_run_macos.ts | 4 ++ src/mcp/tools/macos/test_macos.ts | 4 ++ src/mcp/tools/simulator/build_run_sim.ts | 4 ++ src/mcp/tools/simulator/build_sim.ts | 4 ++ src/mcp/tools/simulator/test_sim.ts | 5 ++ src/mcp/tools/utilities/clean.ts | 4 ++ src/types/common.ts | 1 + .../build-utils-suppress-warnings.test.ts | 70 +++++++++++++++++++ src/utils/build-utils.ts | 3 + src/utils/test-common.ts | 1 + 13 files changed, 112 insertions(+) create mode 100644 src/utils/__tests__/build-utils-suppress-warnings.test.ts diff --git a/src/mcp/tools/device/build_device.ts b/src/mcp/tools/device/build_device.ts index cbb799db..7b351a68 100644 --- a/src/mcp/tools/device/build_device.ts +++ b/src/mcp/tools/device/build_device.ts @@ -25,6 +25,10 @@ const baseSchemaObject = z.object({ derivedDataPath: z.string().optional().describe('Path to derived data directory'), extraArgs: z.array(z.string()).optional().describe('Additional arguments to pass to xcodebuild'), preferXcodebuild: z.boolean().optional().describe('Prefer xcodebuild over faster alternatives'), + suppressWarnings: z + .boolean() + .optional() + .describe('If true, suppresses warning messages from build output to reduce context usage'), }); const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); diff --git a/src/mcp/tools/device/test_device.ts b/src/mcp/tools/device/test_device.ts index 15dbb8df..f6efe974 100644 --- a/src/mcp/tools/device/test_device.ts +++ b/src/mcp/tools/device/test_device.ts @@ -47,6 +47,10 @@ const baseSchemaObject = z.object({ .describe( 'Environment variables to pass to the test runner (TEST_RUNNER_ prefix added automatically)', ), + suppressWarnings: z + .boolean() + .optional() + .describe('If true, suppresses warning messages from build output to reduce context usage'), }); const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); diff --git a/src/mcp/tools/macos/build_macos.ts b/src/mcp/tools/macos/build_macos.ts index 80d41bd7..684f36f5 100644 --- a/src/mcp/tools/macos/build_macos.ts +++ b/src/mcp/tools/macos/build_macos.ts @@ -46,6 +46,10 @@ const baseSchemaObject = z.object({ .boolean() .optional() .describe('If true, prefers xcodebuild over the experimental incremental build system'), + suppressWarnings: z + .boolean() + .optional() + .describe('If true, suppresses warning messages from build output to reduce context usage'), }); const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); diff --git a/src/mcp/tools/macos/build_run_macos.ts b/src/mcp/tools/macos/build_run_macos.ts index 78249bae..458b002e 100644 --- a/src/mcp/tools/macos/build_run_macos.ts +++ b/src/mcp/tools/macos/build_run_macos.ts @@ -37,6 +37,10 @@ const baseSchemaObject = z.object({ .boolean() .optional() .describe('If true, prefers xcodebuild over the experimental incremental build system'), + suppressWarnings: z + .boolean() + .optional() + .describe('If true, suppresses warning messages from build output to reduce context usage'), }); const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); diff --git a/src/mcp/tools/macos/test_macos.ts b/src/mcp/tools/macos/test_macos.ts index 393aa3ce..f20516ff 100644 --- a/src/mcp/tools/macos/test_macos.ts +++ b/src/mcp/tools/macos/test_macos.ts @@ -48,6 +48,10 @@ const baseSchemaObject = z.object({ .describe( 'Environment variables to pass to the test runner (TEST_RUNNER_ prefix added automatically)', ), + suppressWarnings: z + .boolean() + .optional() + .describe('If true, suppresses warning messages from build output to reduce context usage'), }); const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); diff --git a/src/mcp/tools/simulator/build_run_sim.ts b/src/mcp/tools/simulator/build_run_sim.ts index d4721c80..cd22a486 100644 --- a/src/mcp/tools/simulator/build_run_sim.ts +++ b/src/mcp/tools/simulator/build_run_sim.ts @@ -51,6 +51,10 @@ const baseOptions = { .describe( 'If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.', ), + suppressWarnings: z + .boolean() + .optional() + .describe('If true, suppresses warning messages from build output to reduce context usage'), }; const baseSchemaObject = z.object({ diff --git a/src/mcp/tools/simulator/build_sim.ts b/src/mcp/tools/simulator/build_sim.ts index 6d357d2e..fc8cff87 100644 --- a/src/mcp/tools/simulator/build_sim.ts +++ b/src/mcp/tools/simulator/build_sim.ts @@ -49,6 +49,10 @@ const baseOptions = { .describe( 'If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.', ), + suppressWarnings: z + .boolean() + .optional() + .describe('If true, suppresses warning messages from build output to reduce context usage'), }; const baseSchemaObject = z.object({ diff --git a/src/mcp/tools/simulator/test_sim.ts b/src/mcp/tools/simulator/test_sim.ts index c6061c30..0ca64710 100644 --- a/src/mcp/tools/simulator/test_sim.ts +++ b/src/mcp/tools/simulator/test_sim.ts @@ -64,6 +64,10 @@ const baseSchemaObject = z.object({ .describe( 'Environment variables to pass to the test runner (TEST_RUNNER_ prefix added automatically)', ), + suppressWarnings: z + .boolean() + .optional() + .describe('If true, suppresses warning messages from build output to reduce context usage'), }); // Apply preprocessor to handle empty strings @@ -113,6 +117,7 @@ export async function test_simLogic( preferXcodebuild: params.preferXcodebuild ?? false, platform: XcodePlatform.iOSSimulator, testRunnerEnv: params.testRunnerEnv, + suppressWarnings: params.suppressWarnings, }, executor, ); diff --git a/src/mcp/tools/utilities/clean.ts b/src/mcp/tools/utilities/clean.ts index f59b288b..8687923f 100644 --- a/src/mcp/tools/utilities/clean.ts +++ b/src/mcp/tools/utilities/clean.ts @@ -51,6 +51,10 @@ const baseOptions = { .describe( 'Optional: Platform to clean for (defaults to iOS). Choose from macOS, iOS, iOS Simulator, watchOS, watchOS Simulator, tvOS, tvOS Simulator, visionOS, visionOS Simulator', ), + suppressWarnings: z + .boolean() + .optional() + .describe('If true, suppresses warning messages from build output to reduce context usage'), }; const baseSchemaObject = z.object({ diff --git a/src/types/common.ts b/src/types/common.ts index 96ff1914..2150993a 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -94,6 +94,7 @@ export interface SharedBuildParams { configuration: string; derivedDataPath?: string; extraArgs?: string[]; + suppressWarnings?: boolean; } /** diff --git a/src/utils/__tests__/build-utils-suppress-warnings.test.ts b/src/utils/__tests__/build-utils-suppress-warnings.test.ts new file mode 100644 index 00000000..bb310bdd --- /dev/null +++ b/src/utils/__tests__/build-utils-suppress-warnings.test.ts @@ -0,0 +1,70 @@ +import { describe, it, expect, vi } from 'vitest'; +import { executeXcodeBuildCommand } from '../build-utils.ts'; +import { XcodePlatform } from '../../types/common.ts'; + +describe('executeXcodeBuildCommand - suppressWarnings', () => { + it('should include warnings when suppressWarnings is false', async () => { + const mockExecutor = vi.fn().mockResolvedValue({ + success: true, + output: 'warning: Some warning\nerror: Some error', + error: '', + exitCode: 0, + }); + + const result = await executeXcodeBuildCommand( + { + projectPath: '/test/project.xcodeproj', + scheme: 'TestScheme', + configuration: 'Debug', + suppressWarnings: false, + }, + { + platform: XcodePlatform.macOS, + logPrefix: 'Test', + }, + false, + 'build', + mockExecutor, + ); + + expect(result.content).toBeDefined(); + const textContent = result.content + ?.filter((c) => c.type === 'text') + .map((c) => (c as { text: string }).text) + .join('\n'); + expect(textContent).toContain('⚠️ Warning:'); + }); + + it('should suppress warnings when suppressWarnings is true', async () => { + const mockExecutor = vi.fn().mockResolvedValue({ + success: true, + output: 'warning: Some warning\nerror: Some error', + error: '', + exitCode: 0, + }); + + const result = await executeXcodeBuildCommand( + { + projectPath: '/test/project.xcodeproj', + scheme: 'TestScheme', + configuration: 'Debug', + suppressWarnings: true, + }, + { + platform: XcodePlatform.macOS, + logPrefix: 'Test', + }, + false, + 'build', + mockExecutor, + ); + + expect(result.content).toBeDefined(); + const textContent = result.content + ?.filter((c) => c.type === 'text') + .map((c) => (c as { text: string }).text) + .join('\n'); + expect(textContent).not.toContain('⚠️ Warning:'); + expect(textContent).toContain('❌ Error:'); + }); +}); diff --git a/src/utils/build-utils.ts b/src/utils/build-utils.ts index 66c769ee..4b717815 100644 --- a/src/utils/build-utils.ts +++ b/src/utils/build-utils.ts @@ -232,6 +232,9 @@ export async function executeXcodeBuildCommand( // Grep warnings and errors from stdout (build output) const warningOrErrorLines = grepWarningsAndErrors(result.output); warningOrErrorLines.forEach(({ type, content }) => { + if (type === 'warning' && params.suppressWarnings) { + return; + } buildMessages.push({ type: 'text', text: type === 'warning' ? `⚠️ Warning: ${content}` : `❌ Error: ${content}`, diff --git a/src/utils/test-common.ts b/src/utils/test-common.ts index cc9333e6..eea02cdb 100644 --- a/src/utils/test-common.ts +++ b/src/utils/test-common.ts @@ -159,6 +159,7 @@ export async function handleTestLogic( preferXcodebuild?: boolean; platform: XcodePlatform; testRunnerEnv?: Record; + suppressWarnings?: boolean; }, executor?: CommandExecutor, ): Promise { From 5da681465aa054359d5c6fdfe7125b2bf86eea3f Mon Sep 17 00:00:00 2001 From: Cody Vandermyn Date: Wed, 10 Dec 2025 12:46:06 -0800 Subject: [PATCH 2/5] test: update schema key assertions to include suppressWarnings --- src/mcp/tools/device/__tests__/build_device.test.ts | 2 +- src/mcp/tools/device/__tests__/test_device.test.ts | 1 + src/mcp/tools/macos/__tests__/build_macos.test.ts | 2 +- src/mcp/tools/macos/__tests__/build_run_macos.test.ts | 2 +- src/mcp/tools/macos/__tests__/test_macos.test.ts | 2 +- src/mcp/tools/simulator/__tests__/build_run_sim.test.ts | 2 +- src/mcp/tools/simulator/__tests__/test_sim.test.ts | 2 +- src/mcp/tools/utilities/__tests__/clean.test.ts | 2 +- 8 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/mcp/tools/device/__tests__/build_device.test.ts b/src/mcp/tools/device/__tests__/build_device.test.ts index 8d937d36..66bc57b7 100644 --- a/src/mcp/tools/device/__tests__/build_device.test.ts +++ b/src/mcp/tools/device/__tests__/build_device.test.ts @@ -37,7 +37,7 @@ describe('build_device plugin', () => { expect(schema.safeParse({ projectPath: '/path/to/MyProject.xcodeproj' }).success).toBe(false); const schemaKeys = Object.keys(buildDevice.schema).sort(); - expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild']); + expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings']); }); }); diff --git a/src/mcp/tools/device/__tests__/test_device.test.ts b/src/mcp/tools/device/__tests__/test_device.test.ts index ce6e0bb4..6543dc06 100644 --- a/src/mcp/tools/device/__tests__/test_device.test.ts +++ b/src/mcp/tools/device/__tests__/test_device.test.ts @@ -52,6 +52,7 @@ describe('test_device plugin', () => { 'extraArgs', 'platform', 'preferXcodebuild', + 'suppressWarnings', 'testRunnerEnv', ]); }); diff --git a/src/mcp/tools/macos/__tests__/build_macos.test.ts b/src/mcp/tools/macos/__tests__/build_macos.test.ts index 24a31713..44f3dc43 100644 --- a/src/mcp/tools/macos/__tests__/build_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/build_macos.test.ts @@ -46,7 +46,7 @@ describe('build_macos plugin', () => { expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); const schemaKeys = Object.keys(buildMacOS.schema).sort(); - expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild'].sort()); + expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings'].sort()); }); }); diff --git a/src/mcp/tools/macos/__tests__/build_run_macos.test.ts b/src/mcp/tools/macos/__tests__/build_run_macos.test.ts index c0aa4133..f211dd18 100644 --- a/src/mcp/tools/macos/__tests__/build_run_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/build_run_macos.test.ts @@ -39,7 +39,7 @@ describe('build_run_macos', () => { expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); const schemaKeys = Object.keys(tool.schema).sort(); - expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild'].sort()); + expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings'].sort()); }); }); diff --git a/src/mcp/tools/macos/__tests__/test_macos.test.ts b/src/mcp/tools/macos/__tests__/test_macos.test.ts index d51157fd..4074b30d 100644 --- a/src/mcp/tools/macos/__tests__/test_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/test_macos.test.ts @@ -47,7 +47,7 @@ describe('test_macos plugin (unified)', () => { const schemaKeys = Object.keys(testMacos.schema).sort(); expect(schemaKeys).toEqual( - ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'testRunnerEnv'].sort(), + ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings', 'testRunnerEnv'].sort(), ); }); }); diff --git a/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts b/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts index 0a8e4bff..b73cd661 100644 --- a/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts @@ -45,7 +45,7 @@ describe('build_run_sim tool', () => { expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); const schemaKeys = Object.keys(buildRunSim.schema).sort(); - expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild'].sort()); + expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings'].sort()); expect(schemaKeys).not.toContain('scheme'); expect(schemaKeys).not.toContain('simulatorName'); expect(schemaKeys).not.toContain('projectPath'); diff --git a/src/mcp/tools/simulator/__tests__/test_sim.test.ts b/src/mcp/tools/simulator/__tests__/test_sim.test.ts index fed2ff2c..f18c608e 100644 --- a/src/mcp/tools/simulator/__tests__/test_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/test_sim.test.ts @@ -46,7 +46,7 @@ describe('test_sim tool', () => { const schemaKeys = Object.keys(testSim.schema).sort(); expect(schemaKeys).toEqual( - ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'testRunnerEnv'].sort(), + ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings', 'testRunnerEnv'].sort(), ); }); }); diff --git a/src/mcp/tools/utilities/__tests__/clean.test.ts b/src/mcp/tools/utilities/__tests__/clean.test.ts index 2e4b0269..8b149509 100644 --- a/src/mcp/tools/utilities/__tests__/clean.test.ts +++ b/src/mcp/tools/utilities/__tests__/clean.test.ts @@ -28,7 +28,7 @@ describe('clean (unified) tool', () => { const schemaKeys = Object.keys(tool.schema).sort(); expect(schemaKeys).toEqual( - ['derivedDataPath', 'extraArgs', 'platform', 'preferXcodebuild'].sort(), + ['derivedDataPath', 'extraArgs', 'platform', 'preferXcodebuild', 'suppressWarnings'].sort(), ); }); From b4a382ce3635a72375e8b9a16c06fa814ae1ff54 Mon Sep 17 00:00:00 2001 From: Cody Vandermyn Date: Wed, 10 Dec 2025 13:05:22 -0800 Subject: [PATCH 3/5] fix: pass suppressWarnings through to executeXcodeBuildCommand in test_macos, test_device, and clean --- src/mcp/tools/device/__tests__/build_device.test.ts | 7 ++++++- src/mcp/tools/device/test_device.ts | 1 + src/mcp/tools/macos/__tests__/build_macos.test.ts | 4 +++- src/mcp/tools/macos/__tests__/build_run_macos.test.ts | 4 +++- src/mcp/tools/macos/__tests__/test_macos.test.ts | 8 +++++++- src/mcp/tools/macos/test_macos.ts | 1 + src/mcp/tools/simulator/__tests__/build_run_sim.test.ts | 4 +++- src/mcp/tools/simulator/__tests__/test_sim.test.ts | 8 +++++++- src/mcp/tools/utilities/clean.ts | 1 + 9 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/mcp/tools/device/__tests__/build_device.test.ts b/src/mcp/tools/device/__tests__/build_device.test.ts index 66bc57b7..34377f97 100644 --- a/src/mcp/tools/device/__tests__/build_device.test.ts +++ b/src/mcp/tools/device/__tests__/build_device.test.ts @@ -37,7 +37,12 @@ describe('build_device plugin', () => { expect(schema.safeParse({ projectPath: '/path/to/MyProject.xcodeproj' }).success).toBe(false); const schemaKeys = Object.keys(buildDevice.schema).sort(); - expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings']); + expect(schemaKeys).toEqual([ + 'derivedDataPath', + 'extraArgs', + 'preferXcodebuild', + 'suppressWarnings', + ]); }); }); diff --git a/src/mcp/tools/device/test_device.ts b/src/mcp/tools/device/test_device.ts index f6efe974..309dae5c 100644 --- a/src/mcp/tools/device/test_device.ts +++ b/src/mcp/tools/device/test_device.ts @@ -225,6 +225,7 @@ export async function testDeviceLogic( configuration: params.configuration ?? 'Debug', derivedDataPath: params.derivedDataPath, extraArgs, + suppressWarnings: params.suppressWarnings, }, { platform: (params.platform as XcodePlatform) || XcodePlatform.iOS, diff --git a/src/mcp/tools/macos/__tests__/build_macos.test.ts b/src/mcp/tools/macos/__tests__/build_macos.test.ts index 44f3dc43..a3047f95 100644 --- a/src/mcp/tools/macos/__tests__/build_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/build_macos.test.ts @@ -46,7 +46,9 @@ describe('build_macos plugin', () => { expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); const schemaKeys = Object.keys(buildMacOS.schema).sort(); - expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings'].sort()); + expect(schemaKeys).toEqual( + ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings'].sort(), + ); }); }); diff --git a/src/mcp/tools/macos/__tests__/build_run_macos.test.ts b/src/mcp/tools/macos/__tests__/build_run_macos.test.ts index f211dd18..75e597d2 100644 --- a/src/mcp/tools/macos/__tests__/build_run_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/build_run_macos.test.ts @@ -39,7 +39,9 @@ describe('build_run_macos', () => { expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); const schemaKeys = Object.keys(tool.schema).sort(); - expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings'].sort()); + expect(schemaKeys).toEqual( + ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings'].sort(), + ); }); }); diff --git a/src/mcp/tools/macos/__tests__/test_macos.test.ts b/src/mcp/tools/macos/__tests__/test_macos.test.ts index 4074b30d..b4065cdb 100644 --- a/src/mcp/tools/macos/__tests__/test_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/test_macos.test.ts @@ -47,7 +47,13 @@ describe('test_macos plugin (unified)', () => { const schemaKeys = Object.keys(testMacos.schema).sort(); expect(schemaKeys).toEqual( - ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings', 'testRunnerEnv'].sort(), + [ + 'derivedDataPath', + 'extraArgs', + 'preferXcodebuild', + 'suppressWarnings', + 'testRunnerEnv', + ].sort(), ); }); }); diff --git a/src/mcp/tools/macos/test_macos.ts b/src/mcp/tools/macos/test_macos.ts index f20516ff..51a1283b 100644 --- a/src/mcp/tools/macos/test_macos.ts +++ b/src/mcp/tools/macos/test_macos.ts @@ -268,6 +268,7 @@ export async function testMacosLogic( configuration: params.configuration ?? 'Debug', derivedDataPath: params.derivedDataPath, extraArgs, + suppressWarnings: params.suppressWarnings, }, { platform: XcodePlatform.macOS, diff --git a/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts b/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts index b73cd661..8211a248 100644 --- a/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts @@ -45,7 +45,9 @@ describe('build_run_sim tool', () => { expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); const schemaKeys = Object.keys(buildRunSim.schema).sort(); - expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings'].sort()); + expect(schemaKeys).toEqual( + ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings'].sort(), + ); expect(schemaKeys).not.toContain('scheme'); expect(schemaKeys).not.toContain('simulatorName'); expect(schemaKeys).not.toContain('projectPath'); diff --git a/src/mcp/tools/simulator/__tests__/test_sim.test.ts b/src/mcp/tools/simulator/__tests__/test_sim.test.ts index f18c608e..dcaba237 100644 --- a/src/mcp/tools/simulator/__tests__/test_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/test_sim.test.ts @@ -46,7 +46,13 @@ describe('test_sim tool', () => { const schemaKeys = Object.keys(testSim.schema).sort(); expect(schemaKeys).toEqual( - ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings', 'testRunnerEnv'].sort(), + [ + 'derivedDataPath', + 'extraArgs', + 'preferXcodebuild', + 'suppressWarnings', + 'testRunnerEnv', + ].sort(), ); }); }); diff --git a/src/mcp/tools/utilities/clean.ts b/src/mcp/tools/utilities/clean.ts index 8687923f..ecfb4b2e 100644 --- a/src/mcp/tools/utilities/clean.ts +++ b/src/mcp/tools/utilities/clean.ts @@ -127,6 +127,7 @@ export async function cleanLogic( configuration: params.configuration ?? 'Debug', derivedDataPath: params.derivedDataPath, extraArgs: params.extraArgs, + suppressWarnings: params.suppressWarnings, }; // For clean operations, simulator platforms should be mapped to their device equivalents From 713a8215936e3eb7cc6f99e948c4ac7f6f5d3425 Mon Sep 17 00:00:00 2001 From: Cody Vandermyn Date: Tue, 16 Dec 2025 08:53:48 -0800 Subject: [PATCH 4/5] refactor: move suppressWarnings to session-aware default Per PR feedback, suppressWarnings is now a session-level setting instead of a per-tool parameter. This reduces tool schema surface area and aligns with the session-aware architecture. Changes: - Add suppressWarnings to SessionDefaults type and session_set_defaults - Remove suppressWarnings from all individual build/test tool schemas - Update build-utils.ts to read from sessionStore - Update tests to reflect the new session-based approach --- src/mcp/tools/device/__tests__/build_device.test.ts | 7 +------ src/mcp/tools/device/__tests__/test_device.test.ts | 1 - src/mcp/tools/device/build_device.ts | 4 ---- src/mcp/tools/device/test_device.ts | 5 ----- src/mcp/tools/macos/__tests__/build_macos.test.ts | 4 +--- .../tools/macos/__tests__/build_run_macos.test.ts | 4 +--- src/mcp/tools/macos/__tests__/test_macos.test.ts | 8 +------- src/mcp/tools/macos/build_macos.ts | 4 ---- src/mcp/tools/macos/build_run_macos.ts | 4 ---- src/mcp/tools/macos/test_macos.ts | 5 ----- .../session-management/session_set_defaults.ts | 4 ++++ .../tools/simulator/__tests__/build_run_sim.test.ts | 4 +--- src/mcp/tools/simulator/__tests__/test_sim.test.ts | 8 +------- src/mcp/tools/simulator/build_run_sim.ts | 4 ---- src/mcp/tools/simulator/build_sim.ts | 4 ---- src/mcp/tools/simulator/test_sim.ts | 5 ----- src/mcp/tools/utilities/__tests__/clean.test.ts | 2 +- src/mcp/tools/utilities/clean.ts | 5 ----- src/types/common.ts | 1 - .../__tests__/build-utils-suppress-warnings.test.ts | 13 ++++++++++--- src/utils/build-utils.ts | 4 +++- src/utils/session-store.ts | 1 + src/utils/test-common.ts | 1 - 23 files changed, 25 insertions(+), 77 deletions(-) diff --git a/src/mcp/tools/device/__tests__/build_device.test.ts b/src/mcp/tools/device/__tests__/build_device.test.ts index 34377f97..8d937d36 100644 --- a/src/mcp/tools/device/__tests__/build_device.test.ts +++ b/src/mcp/tools/device/__tests__/build_device.test.ts @@ -37,12 +37,7 @@ describe('build_device plugin', () => { expect(schema.safeParse({ projectPath: '/path/to/MyProject.xcodeproj' }).success).toBe(false); const schemaKeys = Object.keys(buildDevice.schema).sort(); - expect(schemaKeys).toEqual([ - 'derivedDataPath', - 'extraArgs', - 'preferXcodebuild', - 'suppressWarnings', - ]); + expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild']); }); }); diff --git a/src/mcp/tools/device/__tests__/test_device.test.ts b/src/mcp/tools/device/__tests__/test_device.test.ts index 6543dc06..ce6e0bb4 100644 --- a/src/mcp/tools/device/__tests__/test_device.test.ts +++ b/src/mcp/tools/device/__tests__/test_device.test.ts @@ -52,7 +52,6 @@ describe('test_device plugin', () => { 'extraArgs', 'platform', 'preferXcodebuild', - 'suppressWarnings', 'testRunnerEnv', ]); }); diff --git a/src/mcp/tools/device/build_device.ts b/src/mcp/tools/device/build_device.ts index 7b351a68..cbb799db 100644 --- a/src/mcp/tools/device/build_device.ts +++ b/src/mcp/tools/device/build_device.ts @@ -25,10 +25,6 @@ const baseSchemaObject = z.object({ derivedDataPath: z.string().optional().describe('Path to derived data directory'), extraArgs: z.array(z.string()).optional().describe('Additional arguments to pass to xcodebuild'), preferXcodebuild: z.boolean().optional().describe('Prefer xcodebuild over faster alternatives'), - suppressWarnings: z - .boolean() - .optional() - .describe('If true, suppresses warning messages from build output to reduce context usage'), }); const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); diff --git a/src/mcp/tools/device/test_device.ts b/src/mcp/tools/device/test_device.ts index 309dae5c..15dbb8df 100644 --- a/src/mcp/tools/device/test_device.ts +++ b/src/mcp/tools/device/test_device.ts @@ -47,10 +47,6 @@ const baseSchemaObject = z.object({ .describe( 'Environment variables to pass to the test runner (TEST_RUNNER_ prefix added automatically)', ), - suppressWarnings: z - .boolean() - .optional() - .describe('If true, suppresses warning messages from build output to reduce context usage'), }); const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); @@ -225,7 +221,6 @@ export async function testDeviceLogic( configuration: params.configuration ?? 'Debug', derivedDataPath: params.derivedDataPath, extraArgs, - suppressWarnings: params.suppressWarnings, }, { platform: (params.platform as XcodePlatform) || XcodePlatform.iOS, diff --git a/src/mcp/tools/macos/__tests__/build_macos.test.ts b/src/mcp/tools/macos/__tests__/build_macos.test.ts index a3047f95..24a31713 100644 --- a/src/mcp/tools/macos/__tests__/build_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/build_macos.test.ts @@ -46,9 +46,7 @@ describe('build_macos plugin', () => { expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); const schemaKeys = Object.keys(buildMacOS.schema).sort(); - expect(schemaKeys).toEqual( - ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings'].sort(), - ); + expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild'].sort()); }); }); diff --git a/src/mcp/tools/macos/__tests__/build_run_macos.test.ts b/src/mcp/tools/macos/__tests__/build_run_macos.test.ts index 75e597d2..c0aa4133 100644 --- a/src/mcp/tools/macos/__tests__/build_run_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/build_run_macos.test.ts @@ -39,9 +39,7 @@ describe('build_run_macos', () => { expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); const schemaKeys = Object.keys(tool.schema).sort(); - expect(schemaKeys).toEqual( - ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings'].sort(), - ); + expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild'].sort()); }); }); diff --git a/src/mcp/tools/macos/__tests__/test_macos.test.ts b/src/mcp/tools/macos/__tests__/test_macos.test.ts index b4065cdb..d51157fd 100644 --- a/src/mcp/tools/macos/__tests__/test_macos.test.ts +++ b/src/mcp/tools/macos/__tests__/test_macos.test.ts @@ -47,13 +47,7 @@ describe('test_macos plugin (unified)', () => { const schemaKeys = Object.keys(testMacos.schema).sort(); expect(schemaKeys).toEqual( - [ - 'derivedDataPath', - 'extraArgs', - 'preferXcodebuild', - 'suppressWarnings', - 'testRunnerEnv', - ].sort(), + ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'testRunnerEnv'].sort(), ); }); }); diff --git a/src/mcp/tools/macos/build_macos.ts b/src/mcp/tools/macos/build_macos.ts index 684f36f5..80d41bd7 100644 --- a/src/mcp/tools/macos/build_macos.ts +++ b/src/mcp/tools/macos/build_macos.ts @@ -46,10 +46,6 @@ const baseSchemaObject = z.object({ .boolean() .optional() .describe('If true, prefers xcodebuild over the experimental incremental build system'), - suppressWarnings: z - .boolean() - .optional() - .describe('If true, suppresses warning messages from build output to reduce context usage'), }); const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); diff --git a/src/mcp/tools/macos/build_run_macos.ts b/src/mcp/tools/macos/build_run_macos.ts index 458b002e..78249bae 100644 --- a/src/mcp/tools/macos/build_run_macos.ts +++ b/src/mcp/tools/macos/build_run_macos.ts @@ -37,10 +37,6 @@ const baseSchemaObject = z.object({ .boolean() .optional() .describe('If true, prefers xcodebuild over the experimental incremental build system'), - suppressWarnings: z - .boolean() - .optional() - .describe('If true, suppresses warning messages from build output to reduce context usage'), }); const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); diff --git a/src/mcp/tools/macos/test_macos.ts b/src/mcp/tools/macos/test_macos.ts index 51a1283b..393aa3ce 100644 --- a/src/mcp/tools/macos/test_macos.ts +++ b/src/mcp/tools/macos/test_macos.ts @@ -48,10 +48,6 @@ const baseSchemaObject = z.object({ .describe( 'Environment variables to pass to the test runner (TEST_RUNNER_ prefix added automatically)', ), - suppressWarnings: z - .boolean() - .optional() - .describe('If true, suppresses warning messages from build output to reduce context usage'), }); const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); @@ -268,7 +264,6 @@ export async function testMacosLogic( configuration: params.configuration ?? 'Debug', derivedDataPath: params.derivedDataPath, extraArgs, - suppressWarnings: params.suppressWarnings, }, { platform: XcodePlatform.macOS, diff --git a/src/mcp/tools/session-management/session_set_defaults.ts b/src/mcp/tools/session-management/session_set_defaults.ts index 220a8886..6526a143 100644 --- a/src/mcp/tools/session-management/session_set_defaults.ts +++ b/src/mcp/tools/session-management/session_set_defaults.ts @@ -14,6 +14,10 @@ const baseSchema = z.object({ deviceId: z.string().optional(), useLatestOS: z.boolean().optional(), arch: z.enum(['arm64', 'x86_64']).optional(), + suppressWarnings: z + .boolean() + .optional() + .describe('When true, warning messages are filtered from build output to conserve context'), }); const schemaObj = baseSchema diff --git a/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts b/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts index 8211a248..0a8e4bff 100644 --- a/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/build_run_sim.test.ts @@ -45,9 +45,7 @@ describe('build_run_sim tool', () => { expect(schema.safeParse({ preferXcodebuild: 'yes' }).success).toBe(false); const schemaKeys = Object.keys(buildRunSim.schema).sort(); - expect(schemaKeys).toEqual( - ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'suppressWarnings'].sort(), - ); + expect(schemaKeys).toEqual(['derivedDataPath', 'extraArgs', 'preferXcodebuild'].sort()); expect(schemaKeys).not.toContain('scheme'); expect(schemaKeys).not.toContain('simulatorName'); expect(schemaKeys).not.toContain('projectPath'); diff --git a/src/mcp/tools/simulator/__tests__/test_sim.test.ts b/src/mcp/tools/simulator/__tests__/test_sim.test.ts index dcaba237..fed2ff2c 100644 --- a/src/mcp/tools/simulator/__tests__/test_sim.test.ts +++ b/src/mcp/tools/simulator/__tests__/test_sim.test.ts @@ -46,13 +46,7 @@ describe('test_sim tool', () => { const schemaKeys = Object.keys(testSim.schema).sort(); expect(schemaKeys).toEqual( - [ - 'derivedDataPath', - 'extraArgs', - 'preferXcodebuild', - 'suppressWarnings', - 'testRunnerEnv', - ].sort(), + ['derivedDataPath', 'extraArgs', 'preferXcodebuild', 'testRunnerEnv'].sort(), ); }); }); diff --git a/src/mcp/tools/simulator/build_run_sim.ts b/src/mcp/tools/simulator/build_run_sim.ts index cd22a486..d4721c80 100644 --- a/src/mcp/tools/simulator/build_run_sim.ts +++ b/src/mcp/tools/simulator/build_run_sim.ts @@ -51,10 +51,6 @@ const baseOptions = { .describe( 'If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.', ), - suppressWarnings: z - .boolean() - .optional() - .describe('If true, suppresses warning messages from build output to reduce context usage'), }; const baseSchemaObject = z.object({ diff --git a/src/mcp/tools/simulator/build_sim.ts b/src/mcp/tools/simulator/build_sim.ts index fc8cff87..6d357d2e 100644 --- a/src/mcp/tools/simulator/build_sim.ts +++ b/src/mcp/tools/simulator/build_sim.ts @@ -49,10 +49,6 @@ const baseOptions = { .describe( 'If true, prefers xcodebuild over the experimental incremental build system, useful for when incremental build system fails.', ), - suppressWarnings: z - .boolean() - .optional() - .describe('If true, suppresses warning messages from build output to reduce context usage'), }; const baseSchemaObject = z.object({ diff --git a/src/mcp/tools/simulator/test_sim.ts b/src/mcp/tools/simulator/test_sim.ts index 0ca64710..c6061c30 100644 --- a/src/mcp/tools/simulator/test_sim.ts +++ b/src/mcp/tools/simulator/test_sim.ts @@ -64,10 +64,6 @@ const baseSchemaObject = z.object({ .describe( 'Environment variables to pass to the test runner (TEST_RUNNER_ prefix added automatically)', ), - suppressWarnings: z - .boolean() - .optional() - .describe('If true, suppresses warning messages from build output to reduce context usage'), }); // Apply preprocessor to handle empty strings @@ -117,7 +113,6 @@ export async function test_simLogic( preferXcodebuild: params.preferXcodebuild ?? false, platform: XcodePlatform.iOSSimulator, testRunnerEnv: params.testRunnerEnv, - suppressWarnings: params.suppressWarnings, }, executor, ); diff --git a/src/mcp/tools/utilities/__tests__/clean.test.ts b/src/mcp/tools/utilities/__tests__/clean.test.ts index 8b149509..2e4b0269 100644 --- a/src/mcp/tools/utilities/__tests__/clean.test.ts +++ b/src/mcp/tools/utilities/__tests__/clean.test.ts @@ -28,7 +28,7 @@ describe('clean (unified) tool', () => { const schemaKeys = Object.keys(tool.schema).sort(); expect(schemaKeys).toEqual( - ['derivedDataPath', 'extraArgs', 'platform', 'preferXcodebuild', 'suppressWarnings'].sort(), + ['derivedDataPath', 'extraArgs', 'platform', 'preferXcodebuild'].sort(), ); }); diff --git a/src/mcp/tools/utilities/clean.ts b/src/mcp/tools/utilities/clean.ts index ecfb4b2e..f59b288b 100644 --- a/src/mcp/tools/utilities/clean.ts +++ b/src/mcp/tools/utilities/clean.ts @@ -51,10 +51,6 @@ const baseOptions = { .describe( 'Optional: Platform to clean for (defaults to iOS). Choose from macOS, iOS, iOS Simulator, watchOS, watchOS Simulator, tvOS, tvOS Simulator, visionOS, visionOS Simulator', ), - suppressWarnings: z - .boolean() - .optional() - .describe('If true, suppresses warning messages from build output to reduce context usage'), }; const baseSchemaObject = z.object({ @@ -127,7 +123,6 @@ export async function cleanLogic( configuration: params.configuration ?? 'Debug', derivedDataPath: params.derivedDataPath, extraArgs: params.extraArgs, - suppressWarnings: params.suppressWarnings, }; // For clean operations, simulator platforms should be mapped to their device equivalents diff --git a/src/types/common.ts b/src/types/common.ts index 2150993a..96ff1914 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -94,7 +94,6 @@ export interface SharedBuildParams { configuration: string; derivedDataPath?: string; extraArgs?: string[]; - suppressWarnings?: boolean; } /** diff --git a/src/utils/__tests__/build-utils-suppress-warnings.test.ts b/src/utils/__tests__/build-utils-suppress-warnings.test.ts index bb310bdd..dbd75620 100644 --- a/src/utils/__tests__/build-utils-suppress-warnings.test.ts +++ b/src/utils/__tests__/build-utils-suppress-warnings.test.ts @@ -1,9 +1,16 @@ -import { describe, it, expect, vi } from 'vitest'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; import { executeXcodeBuildCommand } from '../build-utils.ts'; import { XcodePlatform } from '../../types/common.ts'; +import { sessionStore } from '../session-store.ts'; describe('executeXcodeBuildCommand - suppressWarnings', () => { + beforeEach(() => { + sessionStore.clear(); + }); + it('should include warnings when suppressWarnings is false', async () => { + sessionStore.setDefaults({ suppressWarnings: false }); + const mockExecutor = vi.fn().mockResolvedValue({ success: true, output: 'warning: Some warning\nerror: Some error', @@ -16,7 +23,6 @@ describe('executeXcodeBuildCommand - suppressWarnings', () => { projectPath: '/test/project.xcodeproj', scheme: 'TestScheme', configuration: 'Debug', - suppressWarnings: false, }, { platform: XcodePlatform.macOS, @@ -36,6 +42,8 @@ describe('executeXcodeBuildCommand - suppressWarnings', () => { }); it('should suppress warnings when suppressWarnings is true', async () => { + sessionStore.setDefaults({ suppressWarnings: true }); + const mockExecutor = vi.fn().mockResolvedValue({ success: true, output: 'warning: Some warning\nerror: Some error', @@ -48,7 +56,6 @@ describe('executeXcodeBuildCommand - suppressWarnings', () => { projectPath: '/test/project.xcodeproj', scheme: 'TestScheme', configuration: 'Debug', - suppressWarnings: true, }, { platform: XcodePlatform.macOS, diff --git a/src/utils/build-utils.ts b/src/utils/build-utils.ts index 4b717815..565c4127 100644 --- a/src/utils/build-utils.ts +++ b/src/utils/build-utils.ts @@ -30,6 +30,7 @@ import { doesMakefileExist, doesMakeLogFileExist, } from './xcodemake.ts'; +import { sessionStore } from './session-store.ts'; import path from 'path'; /** @@ -231,8 +232,9 @@ export async function executeXcodeBuildCommand( // Grep warnings and errors from stdout (build output) const warningOrErrorLines = grepWarningsAndErrors(result.output); + const suppressWarnings = sessionStore.get('suppressWarnings'); warningOrErrorLines.forEach(({ type, content }) => { - if (type === 'warning' && params.suppressWarnings) { + if (type === 'warning' && suppressWarnings) { return; } buildMessages.push({ diff --git a/src/utils/session-store.ts b/src/utils/session-store.ts index 9df96c7c..e61c691b 100644 --- a/src/utils/session-store.ts +++ b/src/utils/session-store.ts @@ -10,6 +10,7 @@ export type SessionDefaults = { deviceId?: string; useLatestOS?: boolean; arch?: 'arm64' | 'x86_64'; + suppressWarnings?: boolean; }; class SessionStore { diff --git a/src/utils/test-common.ts b/src/utils/test-common.ts index eea02cdb..cc9333e6 100644 --- a/src/utils/test-common.ts +++ b/src/utils/test-common.ts @@ -159,7 +159,6 @@ export async function handleTestLogic( preferXcodebuild?: boolean; platform: XcodePlatform; testRunnerEnv?: Record; - suppressWarnings?: boolean; }, executor?: CommandExecutor, ): Promise { From bf35f004a3e3055564e534107ce67ca76f2c8300 Mon Sep 17 00:00:00 2001 From: Cody Vandermyn Date: Fri, 19 Dec 2025 14:31:36 -0800 Subject: [PATCH 5/5] fix: replace vi.fn() with createMockExecutor in build-utils-suppress-warnings.test.ts --- src/utils/__tests__/build-utils-suppress-warnings.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/utils/__tests__/build-utils-suppress-warnings.test.ts b/src/utils/__tests__/build-utils-suppress-warnings.test.ts index dbd75620..61dd69a8 100644 --- a/src/utils/__tests__/build-utils-suppress-warnings.test.ts +++ b/src/utils/__tests__/build-utils-suppress-warnings.test.ts @@ -1,7 +1,8 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { describe, it, expect, beforeEach } from 'vitest'; import { executeXcodeBuildCommand } from '../build-utils.ts'; import { XcodePlatform } from '../../types/common.ts'; import { sessionStore } from '../session-store.ts'; +import { createMockExecutor } from '../../test-utils/mock-executors.ts'; describe('executeXcodeBuildCommand - suppressWarnings', () => { beforeEach(() => { @@ -11,7 +12,7 @@ describe('executeXcodeBuildCommand - suppressWarnings', () => { it('should include warnings when suppressWarnings is false', async () => { sessionStore.setDefaults({ suppressWarnings: false }); - const mockExecutor = vi.fn().mockResolvedValue({ + const mockExecutor = createMockExecutor({ success: true, output: 'warning: Some warning\nerror: Some error', error: '', @@ -44,7 +45,7 @@ describe('executeXcodeBuildCommand - suppressWarnings', () => { it('should suppress warnings when suppressWarnings is true', async () => { sessionStore.setDefaults({ suppressWarnings: true }); - const mockExecutor = vi.fn().mockResolvedValue({ + const mockExecutor = createMockExecutor({ success: true, output: 'warning: Some warning\nerror: Some error', error: '',