-
-
Notifications
You must be signed in to change notification settings - Fork 193
feat: Unify tools #93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Fix isError assertion to use toBeUndefined() instead of toBe(false) - Update error message expectations to match 'iOS Simulator Build' prefix - Correct invalid simulatorId test to check for destination error message - Update exception handling tests with proper error message format All 25 tests now pass. Full test suite (1495 tests) passing.
|
I refactored a widely duplicated schema helper function into a shared utility, significantly improving code maintainability. Additionally, I unified several project discovery tool names for better consistency and updated all relevant documentation and tests to reflect these changes.
6 tasks completed
Learn more about Cursor Agents |
Co-authored-by: web <web@cameroncooke.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🔭 Outside diff range comments (6)
src/mcp/tools/device/test_device.ts (1)
255-268: Inconsistent parameter handling in createTypedTool callback.The
createTypedToolfunction is designed to handle parameter validation and pass validated params directly to the logic function with the executor. However, the current implementation:
- Takes 3 parameters in its callback but
createTypedToolonly provides 2 (params and executor)- Manually calls
getDefaultCommandExecutor()andgetDefaultFileSystemExecutor()instead of using the injected executor- The third parameter to
createTypedToolshould be the executor getter, not used within the callbackhandler: createTypedTool<TestDeviceParams>( testDeviceSchema, - (params: TestDeviceParams) => { + (params: TestDeviceParams, executor: CommandExecutor) => { return testDeviceLogic( { ...params, platform: params.platform ?? 'iOS', }, - getDefaultCommandExecutor(), + executor, getDefaultFileSystemExecutor(), ); }, getDefaultCommandExecutor, ),Note: The
testDeviceLogicfunction expects bothCommandExecutorandFileSystemExecutor. You may need to update thecreateTypedToolsignature to support multiple executors, or create a composite executor type.docs/MANUAL_TESTING.md (3)
510-515: Inconsistent tool reference with unified architecture.The code still references
list_schems_projwhich should be updated to the unifiedlist_schemestool based on the PR's unification effort.- npx reloaderoo@latest inspect call-tool "list_schems_proj" --params "{\"projectPath\": \"$project_path\"}" -- node build/index.js 2>/dev/null > /tmp/schemes_$$.json + npx reloaderoo@latest inspect call-tool "list_schemes" --params "{\"projectPath\": \"$project_path\"}" -- node build/index.js 2>/dev/null > /tmp/schemes_$$.json
556-557: Inconsistent tool reference with unified architecture.Line 556 still references
list_schems_projwhich should be updated tolist_schemesto match the unified tool architecture described in the PR objectives.-npx reloaderoo@latest inspect call-tool "list_schems_proj" --params '{"projectPath": "/actual/path/from/discover_projs.xcodeproj"}' -- node build/index.js +npx reloaderoo@latest inspect call-tool "list_schemes" --params '{"projectPath": "/actual/path/from/discover_projs.xcodeproj"}' -- node build/index.js
657-662: Inconsistent tool references in error testing section.The error testing examples still use
list_schems_projwhich should be updated to the unifiedlist_schemestool.-npx reloaderoo@latest inspect call-tool list_schems_proj --params '{"projectPath": 123}' -- node build/index.js 2>/dev/null +npx reloaderoo@latest inspect call-tool list_schemes --params '{"projectPath": 123}' -- node build/index.js 2>/dev/null # Test with non-existent paths echo "Testing non-existent paths..." -npx reloaderoo@latest inspect call-tool list_schems_proj --params '{"projectPath": "/nonexistent/path.xcodeproj"}' -- node build/index.js 2>/dev/null +npx reloaderoo@latest inspect call-tool list_schemes --params '{"projectPath": "/nonexistent/path.xcodeproj"}' -- node build/index.js 2>/dev/nullsrc/mcp/tools/macos/build_run_macos.ts (1)
212-224: Use injected executor instead of calling getDefaultCommandExecutor in handlerThe handler’s logic function ignores the executor injected by createTypedTool and calls getDefaultCommandExecutor directly. This breaks the DI pattern and can trip the test guard that forbids default executor usage in tests.
Apply this diff:
handler: createTypedTool<BuildRunMacOSParams>( buildRunMacOSSchema, - (params: BuildRunMacOSParams) => + (params: BuildRunMacOSParams, executor: CommandExecutor) => buildRunMacOSLogic( { ...params, configuration: params.configuration ?? 'Debug', preferXcodebuild: params.preferXcodebuild ?? false, }, - getDefaultCommandExecutor(), + executor, ), getDefaultCommandExecutor, ),Optional follow-up: move the defaulting of configuration/prefer into buildRunMacOSLogic (like buildMacOSLogic) to keep handler thin and reduce duplication.
src/mcp/tools/device/get_device_app_path.ts (1)
45-55: Avoid duplicating XcodePlatform constants; import shared enum insteadDefining a local XcodePlatform risks drift and inconsistencies. Reuse the shared enum exported from types/common.js (as other tools do).
Apply this diff:
-import { ToolResponse } from '../../../types/common.js'; +import { ToolResponse, XcodePlatform } from '../../../types/common.js'; @@ -const XcodePlatform = { - iOS: 'iOS', - watchOS: 'watchOS', - tvOS: 'tvOS', - visionOS: 'visionOS', - iOSSimulator: 'iOS Simulator', - watchOSSimulator: 'watchOS Simulator', - tvOSSimulator: 'tvOS Simulator', - visionOSSimulator: 'visionOS Simulator', - macOS: 'macOS', -}; +// Use shared XcodePlatform from types/common.js
♻️ Duplicate comments (3)
scripts/analysis/tools-analysis.ts (1)
149-159: Consider the existing comment about the comment filtering logic.The comment filtering logic has an issue that was previously raised - it could incorrectly handle edge cases like inline comments and multi-line comments that span multiple lines. While the current implementation attempts to handle this, it may still have issues with complex cases.
src/mcp/tools/project-scaffolding/__tests__/scaffold_macos_project.test.ts (1)
288-293: Repeat: fix second test block to unified build_run_macos styleMirror the change in this block as well for consistency.
Apply this diff:
nextSteps: [ 'Important: Before working on the project make sure to read the README.md file in the workspace root directory.', 'Build for macOS: build_macos({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject" })', - 'Run and run on macOS: build_run_mac_ws --workspace-path "/tmp/test-projects/MyProject.xcworkspace" --scheme "MyProject"', + 'Build & Run on macOS: build_run_macos({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject" })', ],src/mcp/tools/device/__tests__/get_device_app_path.test.ts (1)
9-9: Import path extension needs correction.The import uses
.tsextension which is correct for TypeScript imports in this project, but there was a previous comment about inconsistent extension usage. The current.tsextension is actually correct and consistent with TypeScript module resolution.
🧹 Nitpick comments (11)
src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts (5)
28-33: Add positive validation for workspacePath (symmetry with projectPath)Minor coverage gap: validate the schema accepts a workspacePath-only input as valid, mirroring the projectPath case.
Apply this diff to add the missing assertion:
it('should validate schema with valid inputs', () => { const schema = z.object(plugin.schema); expect(schema.safeParse({ projectPath: '/path/to/MyProject.xcodeproj' }).success).toBe(true); expect(schema.safeParse({ projectPath: '/Users/dev/App.xcodeproj' }).success).toBe(true); + expect(schema.safeParse({ workspacePath: '/path/to/MyProject.xcworkspace' }).success).toBe(true); });
34-41: Strengthen invalid schema tests for workspacePath nullsYou verify number types are rejected for both paths; also reject null for workspacePath for parity with projectPath.
Apply this diff:
expect(schema.safeParse({ projectPath: null }).success).toBe(false); - expect(schema.safeParse({ workspacePath: 123 }).success).toBe(false); + expect(schema.safeParse({ workspacePath: 123 }).success).toBe(false); + expect(schema.safeParse({ workspacePath: null }).success).toBe(false);
144-160: Avoid emitting an empty “Next Steps” content block when no schemes existWhen no schemes are found (but the Schemes: section exists), the response currently includes a trailing empty text block. Prefer omitting the block to reduce noise.
Apply this diff to adjust the test expectation (logic change shown below):
expect(result).toEqual({ content: [ { type: 'text', text: '✅ Available schemes:', }, { type: 'text', text: '', }, - { - type: 'text', - text: '', - }, ], isError: false, });Outside this file, change listSchemesLogic to conditionally include the “Next Steps” block only when populated:
// In src/mcp/tools/project-discovery/list_schemes.ts const content = [ { type: 'text', text: '✅ Available schemes:' as const }, { type: 'text', text: schemes.join('\n') as const }, ]; if (nextStepsText) { content.push({ type: 'text', text: nextStepsText as const }); } return { content, isError: false };
233-241: Clarify test name to reflect unified XOR requirementThe handler validates both projectPath and workspacePath; rename for precision.
Apply this diff:
-it('should handle validation when testing with missing projectPath via plugin handler', async () => { +it('should error when neither projectPath nor workspacePath provided (plugin handler)', async () => {
259-266: Also treat whitespace-only strings as “not provided” in XOR validationYou cover empty strings; add a case for whitespace-only input to prevent accidental bypasses.
Apply this diff to extend the test:
it('should handle empty strings as undefined', async () => { const result = await plugin.handler({ projectPath: '', workspacePath: '', }); expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Either projectPath or workspacePath is required'); }); + +it('should handle whitespace-only strings as undefined', async () => { + const result = await plugin.handler({ + projectPath: ' ', + workspacePath: ' ', + }); + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('Either projectPath or workspacePath is required'); +});If not already implemented, ensure the schema uses z.string().trim() before XOR refinements so whitespace becomes empty.
src/mcp/tools/device/test_device.ts (2)
156-164: Consider updating the internal log message for clarity.The log message mentions "(internal)" which could be confusing as this is no longer an internal/private implementation detail but the unified public-facing logic.
log( 'info', - `Starting test run for scheme ${params.scheme} on platform ${params.platform ?? 'iOS'} (internal)`, + `Starting test run for scheme ${params.scheme} on platform ${params.platform ?? 'iOS'}`, );
253-253: Update tool description to match unified naming convention.Based on the PR objectives, the description should be updated to reflect the unified tool naming. The example should also show both projectPath and workspacePath options to make the XOR relationship clearer.
description: - 'Runs tests for an Apple project or workspace on a physical device (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro) using xcodebuild test and parses xcresult output. Provide exactly one of projectPath or workspacePath. IMPORTANT: Requires scheme and deviceId. Example: test_device({ projectPath: "/path/to/MyProject.xcodeproj", scheme: "MyScheme", deviceId: "device-uuid" })', + 'Runs tests for an Apple project or workspace on a physical device (iPhone, iPad, Apple Watch, Apple TV, Apple Vision Pro) using xcodebuild test and parses xcresult output. Provide exactly one of projectPath or workspacePath. IMPORTANT: Requires scheme and deviceId. Examples: test_device({ projectPath: "/path/to/MyProject.xcodeproj", scheme: "MyScheme", deviceId: "device-uuid" }) OR test_device({ workspacePath: "/path/to/MyWorkspace.xcworkspace", scheme: "MyScheme", deviceId: "device-uuid" })',src/mcp/tools/project-discovery/show_build_settings.ts (1)
53-56: Simplify conditional logic for path selection.The code unnecessarily uses non-null assertions when the values are already type-checked through the schema validation.
- if (hasProjectPath) { - command.push('-project', params.projectPath!); - } else { - command.push('-workspace', params.workspacePath!); - } + if (hasProjectPath && params.projectPath) { + command.push('-project', params.projectPath); + } else if (params.workspacePath) { + command.push('-workspace', params.workspacePath); + }src/mcp/tools/macos/build_run_macos.ts (1)
53-73: Minor: defaulting duplicated across layersconfiguration is defaulted here and again in _handleMacOSBuildLogic. Not harmful, but consolidating defaults in one place reduces maintenance overhead.
src/mcp/tools/device/get_device_app_path.ts (2)
42-44: Export the params type for consistency with other toolsOther unified tools export their Params types (e.g., BuildMacOSParams). Exporting this supports reuse in tests and downstream code.
Apply this diff:
-// Use z.infer for type safety -type GetDeviceAppPathParams = z.infer<typeof getDeviceAppPathSchema>; +// Use z.infer for type safety and export for reuse +export type GetDeviceAppPathParams = z.infer<typeof getDeviceAppPathSchema>;
12-13: Consistency: import CommandExecutor from utils/commandElsewhere (e.g., build_macos.ts) CommandExecutor is imported from utils/command.js. Align imports to reduce confusion.
Apply this diff:
-import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; +import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js';
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
docs/MANUAL_TESTING.md(3 hunks)docs/TESTING.md(3 hunks)scripts/analysis/tools-analysis.ts(1 hunks)src/mcp/tools/device/__tests__/get_device_app_path.test.ts(9 hunks)src/mcp/tools/device/build_device.ts(1 hunks)src/mcp/tools/device/get_device_app_path.ts(5 hunks)src/mcp/tools/device/test_device.ts(6 hunks)src/mcp/tools/macos/build_macos.ts(5 hunks)src/mcp/tools/macos/build_run_macos.ts(6 hunks)src/mcp/tools/macos/get_macos_app_path.ts(6 hunks)src/mcp/tools/macos/test_macos.ts(10 hunks)src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts(11 hunks)src/mcp/tools/project-discovery/list_schemes.ts(1 hunks)src/mcp/tools/project-discovery/show_build_settings.ts(1 hunks)src/mcp/tools/project-scaffolding/__tests__/scaffold_macos_project.test.ts(2 hunks)src/mcp/tools/project-scaffolding/scaffold_macos_project.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- docs/TESTING.md
🚧 Files skipped from review as they are similar to previous changes (2)
- src/mcp/tools/device/build_device.ts
- src/mcp/tools/project-discovery/list_schemes.ts
🧰 Additional context used
🧬 Code Graph Analysis (10)
src/mcp/tools/project-discovery/show_build_settings.ts (5)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/logger.ts (1)
log(33-53)src/utils/validation.ts (1)
createTextResponse(35-45)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)src/utils/command.ts (1)
getDefaultCommandExecutor(232-243)
src/mcp/tools/macos/get_macos_app_path.ts (2)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
src/mcp/tools/macos/test_macos.ts (5)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/test-common.ts (1)
parseXcresultBundle(60-75)src/utils/command.ts (3)
getDefaultCommandExecutor(232-243)FileSystemExecutor(41-52)getDefaultFileSystemExecutor(249-260)src/utils/logger.ts (1)
log(33-53)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
scripts/analysis/tools-analysis.ts (3)
scripts/check-code-patterns.js (2)
__filename(31-31)__dirname(32-32)src/core/plugin-registry.ts (1)
getWorkflowMetadata(90-109)build-plugins/plugin-discovery.js (3)
indexPath(40-40)workflowDirs(31-33)metadata(51-51)
src/mcp/tools/device/get_device_app_path.ts (2)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts (3)
build-plugins/plugin-discovery.js (1)
result(172-175)src/mcp/tools/project-discovery/list_schemes.ts (1)
listSchemesLogic(38-109)src/utils/command.ts (1)
createMockExecutor(267-305)
src/mcp/tools/device/test_device.ts (3)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/command.ts (3)
getDefaultCommandExecutor(232-243)FileSystemExecutor(41-52)getDefaultFileSystemExecutor(249-260)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
src/mcp/tools/device/__tests__/get_device_app_path.test.ts (2)
build-plugins/plugin-discovery.js (1)
result(172-175)src/utils/command.ts (1)
createMockExecutor(267-305)
src/mcp/tools/macos/build_run_macos.ts (3)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/command.ts (1)
getDefaultCommandExecutor(232-243)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
src/mcp/tools/macos/build_macos.ts (3)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)src/utils/command.ts (1)
getDefaultCommandExecutor(232-243)
🔇 Additional comments (36)
src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts (3)
10-16: Unification rename reflected correctlyImport path and plugin.name assertion align with the unified tool naming (“list_schemes”). Looks good.
19-21: Description literal matches unified API expectationsThe exact description string (with XOR guidance and example) looks correct for the unified tool. Keep it in sync with any future refinements to inputs/examples.
If you’re planning to further unify downstream tool names (per the Phase 2 note in the logic), make sure to update this description’s example accordingly when that change lands.
221-231: Command construction assertion is solidYou assert the full command shape and metadata (“List Schemes”), including showOutput and workingDir. This guards against regressions in the executor integration.
src/mcp/tools/device/test_device.ts (2)
21-21: Good job consolidating the duplicate helper function!The
nullifyEmptyStringsfunction has been properly extracted to the shared utilities module as suggested in the past review. This resolves the DRY principle violation.
39-47: LGTM! Well-implemented XOR validation for unified parameters.The schema correctly enforces mutual exclusivity between
projectPathandworkspacePathwhile ensuring at least one is provided. The error messages are clear and actionable.docs/MANUAL_TESTING.md (1)
247-248: Update tool references to match unified variants.Based on the PR context where tools are being unified, these references to
list_schemesandshow_build_settingsare now correct after unification. The manual testing documentation has been properly updated to reflect the new unified tool names.src/mcp/tools/project-discovery/show_build_settings.ts (4)
1-7: LGTM! Clear documentation for the unified tool.The file header clearly describes this as the unified version that accepts mutually exclusive parameters, which aligns well with the PR's unification objectives.
16-31: Well-implemented XOR validation pattern.The schema correctly implements the XOR pattern between
projectPathandworkspacePathwith clear error messages. The use ofnullifyEmptyStringspreprocessor ensures consistent handling of empty string inputs.
83-91: LGTM! Helpful next steps guidance.The conditional next steps section for workspace users is a nice touch that provides actionable guidance with correct tool references to the unified
list_schemestool.
104-114: Clear and accurate tool export configuration.The default export correctly exposes the unified tool with an appropriate description that emphasizes the XOR requirement between project and workspace paths. The schema is properly exposed for MCP client discovery.
src/mcp/tools/macos/build_macos.ts (3)
26-56: Good: Unified XOR validation with empty-string normalizationThe schema cleanly enforces exactly one of projectPath/workspacePath and normalizes empty strings to undefined. This avoids ambiguous inputs and matches the unified tools pattern.
63-87: Good: Logic composes params and defers to shared executorDefaulting configuration/prefer flags and delegating to executeXcodeBuildCommand with platform context is clear and testable via dependency injection.
89-99: Handler wiring matches typed-tool factory patternUsing buildMacOSSchema for validation with baseSchemaObject.shape for MCP compatibility is consistent with the codebase. No issues.
src/mcp/tools/macos/build_run_macos.ts (1)
17-47: Good: Unified XOR schema with nullifyEmptyStringsSolid input model: mutually exclusive project/workspace with preprocessing to avoid accidental empty-string inputs.
src/mcp/tools/device/get_device_app_path.ts (1)
16-41: Good: Unified schema with XOR and normalizationInput validation and normalization are consistent with the rest of the unified tools. Defaults to iOS are sensible.
src/mcp/tools/macos/get_macos_app_path.ts (8)
1-6: LGTM: Updated documentation reflects unified approach.The header comments correctly describe the unified functionality that accepts either project or workspace paths with mutual exclusivity.
13-13: Good addition of schema helper import.The import of
nullifyEmptyStringssupports the preprocessing functionality for handling empty string inputs.
15-41: Excellent XOR validation implementation.The schema design correctly implements mutual exclusivity between
projectPathandworkspacePathusing:
nullifyEmptyStringspreprocessing to normalize empty strings toundefined- Two refinement checks to ensure exactly one path is provided
- Clear error messages for validation failures
This follows the unified tool pattern mentioned in the PR objectives.
44-44: Good type safety with inferred types.Using
z.infer<typeof getMacosAppPathSchema>provides compile-time type safety that stays in sync with the schema definition.
58-60: Updated function signature reflects unified approach.The function name change from
get_mac_app_path_projLogictoget_macos_app_pathLogicand parameter type change toGetMacosAppPathParamscorrectly reflect the unified tool implementation.
70-78: Proper XOR logic implementation.The conditional logic correctly handles either project or workspace paths, with a defensive error case that should never be reached due to schema validation. This is a clean implementation of the unified approach.
150-154: Enhanced user experience with next steps guidance.The addition of next steps guidance provides clear actionable instructions for users after successfully retrieving the app path. This improves the tool's usability.
183-192: Updated export configuration aligns with unified naming.The tool name change from
get_mac_app_path_projtoget_macos_app_pathand the updated description properly reflect the unified functionality. The schema and handler configuration correctly use the new types and validation.src/mcp/tools/macos/test_macos.ts (8)
1-6: LGTM: Updated documentation reflects unified approach.The header comments correctly describe the unified functionality that accepts either project or workspace paths with mutual exclusivity, consistent with other unified tools in this PR.
23-48: Excellent XOR validation implementation.The schema design correctly implements the unified approach with:
- Base schema object defining both
projectPathandworkspacePathas optionalnullifyEmptyStringspreprocessing to handle empty string normalization- Two refinement checks ensuring exactly one path is provided
- Clear, descriptive error messages
This follows the same pattern as other unified tools in the PR.
50-50: Good type safety with exported type.The exported
TestMacosParamstype provides proper type safety and aligns with the unified naming convention.
216-220: Enhanced function signature with dependency injection.The updated function signature correctly:
- Uses the unified
TestMacosParamstype- Accepts both
CommandExecutorandFileSystemExecutorfor testability- Provides default executors for production use
- Exports the function for direct testing
This follows the dependency injection pattern mentioned in the relevant code snippets.
225-227: Proper filesystem executor usage.The migration from direct filesystem calls to
fileSystemExecutor.mkdtempandfileSystemExecutor.tmpdir()enables proper mocking in tests and follows the executor pattern used throughout the codebase.
234-250: Clean parameter passing to build command.The explicit parameter passing (
projectPath,workspacePath,scheme, etc.) instead of spreadingparamsprovides better control and clarity over what parameters are passed to the build system.
259-259: Consistent filesystem executor usage.All filesystem operations (
stat,rm) have been migrated to use thefileSystemExecutor, maintaining consistency with the dependency injection pattern.Also applies to: 270-270, 289-289
304-315: Updated export configuration reflects unified tool.The tool configuration correctly:
- Updates name from
test_macos_projtotest_macos- Provides comprehensive description with XOR guidance and examples
- Uses
baseSchemaObject.shapefor MCP SDK compatibility- Implements proper typed tool wiring with both executors
src/mcp/tools/device/__tests__/get_device_app_path.test.ts (5)
11-26: Updated test descriptions and validations reflect unified tool.The test suite correctly:
- Updates plugin name to
get_device_app_path- Updates description to reflect XOR behavior between
projectPathandworkspacePath- Tests both import references (
getDeviceAppPathandget_device_app_pathLogic)
27-54: Comprehensive schema validation tests.The schema validation tests correctly cover:
- Both
projectPathandworkspacePathfields- Required
schemefield- Optional fields like
configurationand platform validation- Invalid input handling
This ensures the unified tool properly validates XOR requirements.
56-74: Excellent XOR validation test coverage.The new test suite properly validates:
- Error when neither path is provided with expected error message
- Error when both paths are provided with "mutually exclusive" message
- Proper error response structure with
isError: trueThis thoroughly tests the core XOR functionality of the unified tool.
185-235: Good test coverage for workspace functionality.The new workspace test correctly verifies:
- Command generation with
-workspaceparameter- Proper argument structure for workspace builds
- Expected output format and mock executor usage
This ensures the unified tool works correctly for both project and workspace inputs.
104-110: All function calls updated to unified logic function.All test calls have been correctly updated from the old project-specific function to
get_device_app_pathLogic, maintaining consistent test coverage for the unified functionality.Also applies to: 156-163, 209-215, 244-250, 272-278, 297-303, 340-347, 374-380, 398-404
| // Add optional derived data path (only for projects) | ||
| if (params.derivedDataPath && params.projectPath) { | ||
| command.push('-derivedDataPath', params.derivedDataPath); | ||
| } | ||
|
|
||
| // Add extra arguments if provided | ||
| if (params.extraArgs && Array.isArray(params.extraArgs)) { | ||
| // Handle destination for macOS (only for workspaces) | ||
| if (params.workspacePath) { | ||
| let destinationString = 'platform=macOS'; | ||
| if (params.arch) { | ||
| destinationString += `,arch=${params.arch}`; | ||
| } | ||
| command.push('-destination', destinationString); | ||
| } | ||
|
|
||
| // Add extra arguments if provided (only for projects) | ||
| if (params.extraArgs && Array.isArray(params.extraArgs) && params.projectPath) { | ||
| command.push(...params.extraArgs); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify workspace-specific parameter restrictions.
The logic applies derivedDataPath and extraArgs only to projects, while destination is only applied to workspaces. However, the schema allows these parameters for both project and workspace inputs.
🏁 Script executed:
#!/bin/bash
# Description: Verify if workspace builds support derivedDataPath and extraArgs parameters
# Search for documentation or examples showing workspace parameter usage
rg -A 5 -B 5 "workspace.*derivedDataPath|derivedDataPath.*workspace"
rg -A 5 -B 5 "workspace.*extraArgs|extraArgs.*workspace"
# Look for xcodebuild documentation references
rg -A 10 -B 5 "xcodebuild.*workspace.*derivedDataPath"Length of output: 186
🌐 Web query:
Does `xcodebuild -derivedDataPath` apply to workspace builds?
xcodebuild -workspace -derivedDataPath
xcodebuild -destination workspace macOS
xcodebuild derivedDataPath workspace vs -project
xcodebuild extra arguments workspace - how to pass custom args to xcodebuild -workspace
💡 Result:
Yes, the -derivedDataPath argument for xcodebuild applies to workspace builds as well as project builds. When you provide the -workspace flag (e.g., xcodebuild -workspace MyWorkspace.xcworkspace ... -derivedDataPath ./DerivedData), xcodebuild will use the specified directory for all build products and derived data instead of the default ~/Library/Developer/Xcode/DerivedData location[3][5][6].
Key points:
- The
-derivedDataPathflag can be used with both-projectand-workspaceinvocations of xcodebuild[3][6]. - This argument sets the location where Xcode will place derived data, such as build intermediates, products, and other generated files[3].
- This is confirmed by both documentation and multiple examples where
-derivedDataPathis specified in commands that include-workspace[3][5][6].
Example usage with workspace:
xcodebuild -workspace MyWorkspace.xcworkspace -scheme MyScheme -configuration Debug -derivedDataPath ./buildThis command will place all build output under ./build rather than under the default Xcode derived data location[5].
Important caveats:
- The syntax must be
-derivedDataPath ./pathand not-derivedDataPath=./path; using an equal sign may cause the flag to be ignored, especially in more recent Xcode versions[1]. - Some bugs and integration issues have been observed when using
-derivedDataPathin combination with certain dependencies or features (e.g., Swift Package Manager resolution in Xcode 13+ or third-party integrations like Flipper), causing builds to fail or behave unexpectedly[4][5]. These are not universal, but worth noting for CI/CD setups. - The output directories' structure may also be affected by workspace vs. project naming and by the configured build location style (unique, shared, custom), but the root for derived data will always be the one specified by
-derivedDataPath[2].
Custom arguments to xcodebuild:
- You can pass additional arguments (e.g.,
-destination,-scheme, environment overrides) in the same command as usual with-workspaceand-derivedDataPath[3]. - The placement of
-derivedDataPathin the argument list doesn't matter, but its value must follow as a separate argument (not with=)[1][3].
Summary Table:
| xcodebuild Option | Applies to Workspace? | Notes |
|---|---|---|
-derivedDataPath <path> |
Yes | Use as -derivedDataPath ./foo, not with =. |
-project <.xcodeproj> |
Yes | Traditional project usage. |
-workspace <.xcworkspace> |
Yes | Workspace usage supports -derivedDataPath. |
If you need to pass custom (user-defined) build settings, set them as environment variables or append them as KEY=VALUE pairs after the main flags. This works identically on both project and workspace commands[3][6].
Citations:
- 1: https://developer.apple.com/forums/thread/675778
- 2: https://pewpewthespells.com/blog/xcode_build_locations.html
- 3: https://fig.io/manual/xcodebuild
- 4: https://forums.swift.org/t/swiftpm-package-build-via-xcodebuild-with-specified-derived-data-path/59055
- 5: iOS Build Fails when -derivedDataPath is specified facebook/flipper#4381
- 6: https://keith.github.io/xcode-man-pages/xcodebuild.1.html
- 7: https://discuss.circleci.com/t/sharing-xcode-derived-data-folder-across-jobs-workflows/31363
- 8: https://discuss.bitrise.io/t/how-to-determine-xccoverage-or-deriveddata-location/3660
Apply -derivedDataPath and extraArgs for workspace builds; don't restrict -destination to workspaces
xcodebuild accepts -derivedDataPath and arbitrary extra args with -workspace, so the current guards that only apply those flags when params.projectPath is set are incorrect. Likewise -destination can be used with both project and workspace invocations.
- File to change: src/mcp/tools/macos/get_macos_app_path.ts (lines ~84-101)
- Change required:
- Remove the project-only checks for derivedDataPath and extraArgs.
- Allow the destination logic for both workspace and project builds (e.g. use params.workspacePath || params.projectPath).
Suggested replacement snippet:
// Add optional derived data path
if (params.derivedDataPath) {
command.push('-derivedDataPath', params.derivedDataPath);
}
// Handle destination for macOS (workspace or project)
if (params.workspacePath || params.projectPath) {
let destinationString = 'platform=macOS';
if (params.arch) {
destinationString += `,arch=${params.arch}`;
}
command.push('-destination', destinationString);
}
// Add extra arguments if provided
if (params.extraArgs && Array.isArray(params.extraArgs)) {
command.push(...params.extraArgs);
}
🤖 Prompt for AI Agents
In src/mcp/tools/macos/get_macos_app_path.ts around lines 84 to 101, the code
currently only applies -derivedDataPath and extraArgs when params.projectPath is
set and only applies -destination for workspaces; update it to apply
-derivedDataPath whenever params.derivedDataPath is provided, apply -destination
when either params.workspacePath or params.projectPath is present (building
destinationString with optional arch), and apply extraArgs whenever
params.extraArgs is a valid array—remove the project-only guards so these flags
work for both workspace and project invocations.
src/mcp/tools/project-scaffolding/__tests__/scaffold_macos_project.test.ts
Show resolved
Hide resolved
Co-authored-by: web <web@cameroncooke.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🔭 Outside diff range comments (7)
src/mcp/tools/device/test_device.ts (2)
255-266: Forward the injected executor instead of instantiating a new default (avoid double-default and ease testing)The wrapper currently ignores the executor that createTypedTool injects and calls getDefaultCommandExecutor() again. Forward the injected executor to testDeviceLogic and only source the default file system executor here. This avoids redundant default creation and aligns with the factory’s intent.
Apply this diff:
- handler: createTypedTool<TestDeviceParams>( - testDeviceSchema as z.ZodType<TestDeviceParams>, - (params: TestDeviceParams) => { - return testDeviceLogic( - { - ...params, - platform: params.platform ?? 'iOS', - }, - getDefaultCommandExecutor(), - getDefaultFileSystemExecutor(), - ); - }, - getDefaultCommandExecutor, - ), + handler: createTypedTool<TestDeviceParams>( + testDeviceSchema as z.ZodType<TestDeviceParams>, + (params: TestDeviceParams, executor: CommandExecutor) => { + return testDeviceLogic( + { + ...params, + platform: params.platform ?? 'iOS', + }, + executor, // use the injected executor from the factory + getDefaultFileSystemExecutor(), + ); + }, + getDefaultCommandExecutor, + ),
166-175: Ensure temp directory cleanup happens even on early failures (use finally)If executeXcodeBuildCommand throws before xcresult parsing, the temporary directory is never removed. Move cleanup to a finally block and guard it to avoid leaking temp dirs on early errors.
One way to implement:
export async function testDeviceLogic( params: TestDeviceParams, executor: CommandExecutor = getDefaultCommandExecutor(), fileSystemExecutor: FileSystemExecutor = getDefaultFileSystemExecutor(), ): Promise<ToolResponse> { log( 'info', `Starting test run for scheme ${params.scheme} on platform ${params.platform ?? 'iOS'} (internal)`, ); - try { - // Create temporary directory for xcresult bundle - const tempDir = await fileSystemExecutor.mkdtemp( - join(fileSystemExecutor.tmpdir(), 'xcodebuild-test-'), - ); - const resultBundlePath = join(tempDir, 'TestResults.xcresult'); + let tempDir: string | undefined; + const cleanup = async () => { + if (!tempDir) return; + try { + await fileSystemExecutor.rm(tempDir, { recursive: true, force: true }); + } catch (cleanupError) { + log('warn', `Failed to clean up temporary directory: ${cleanupError}`); + } + }; + + try { + tempDir = await fileSystemExecutor.mkdtemp( + join(fileSystemExecutor.tmpdir(), 'xcodebuild-test-'), + ); + const resultBundlePath = join(tempDir, 'TestResults.xcresult'); @@ - // Clean up temporary directory - await fileSystemExecutor.rm(tempDir, { recursive: true, force: true }); + // Clean up temporary directory + await cleanup(); @@ - // Clean up temporary directory even if parsing fails - try { - await fileSystemExecutor.rm(tempDir, { recursive: true, force: true }); - } catch (cleanupError) { - log('warn', `Failed to clean up temporary directory: ${cleanupError}`); - } + await cleanup(); @@ } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); log('error', `Error during test run: ${errorMessage}`); return createTextResponse(`Error during test run: ${errorMessage}`, true); + } finally { + await cleanup(); }Also applies to: 216-241
src/mcp/tools/macos/build_macos.ts (1)
26-55: XOR validation looks correct; suggest adding path extension checks and schema defaults
- The XOR checks are correct and the preprocess step handles empty strings.
- Recommend also validating file extensions to catch common misconfigurations early.
- Consider moving defaults into the schema to avoid duplicating defaults in code and make them visible to callers.
Apply these diffs:
- Add extension checks with superRefine:
const buildMacOSSchema = baseSchema .refine((val) => val.projectPath !== undefined || val.workspacePath !== undefined, { message: 'Either projectPath or workspacePath is required.', }) .refine((val) => !(val.projectPath !== undefined && val.workspacePath !== undefined), { message: 'projectPath and workspacePath are mutually exclusive. Provide only one.', - }); + }) + .superRefine((val, ctx) => { + if (val.projectPath && !val.projectPath.endsWith('.xcodeproj')) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ['projectPath'], + message: 'projectPath must point to a .xcodeproj file', + }); + } + if (val.workspacePath && !val.workspacePath.endsWith('.xcworkspace')) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: ['workspacePath'], + message: 'workspacePath must point to a .xcworkspace file', + }); + } + });
- Move defaults into the schema:
const baseSchemaObject = z.object({ projectPath: z.string().optional().describe('Path to the .xcodeproj file'), workspacePath: z.string().optional().describe('Path to the .xcworkspace file'), scheme: z.string().describe('The scheme to use'), - configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), + configuration: z + .string() + .default('Debug') + .describe('Build configuration (Debug, Release, etc.). Defaults to "Debug".'), derivedDataPath: z .string() .optional() .describe('Path where build products and other derived data will go'), arch: z .enum(['arm64', 'x86_64']) .optional() .describe('Architecture to build for (arm64 or x86_64). For macOS only.'), - extraArgs: z.array(z.string()).optional().describe('Additional xcodebuild arguments'), + extraArgs: z + .array(z.string()) + .default([]) + .describe('Additional xcodebuild arguments'), - preferXcodebuild: z - .boolean() - .optional() - .describe('If true, prefers xcodebuild over the experimental incremental build system'), + preferXcodebuild: z + .boolean() + .default(false) + .describe('If true, prefers xcodebuild over the experimental incremental build system. Defaults to false.'), });Also applies to: 47-47
src/mcp/tools/macos/build_run_macos.ts (4)
212-224: Don’t ignore the injected executor; current handler bypasses mocks and will trip test guard
createTypedToolinjects an executor (often a mock in tests). The inline logic function here discards that executor and callsgetDefaultCommandExecutor()directly, defeating test isolation and violating the pattern used across tools. Fix by threading the provided executor through.Apply this diff:
handler: createTypedTool<BuildRunMacOSParams>( buildRunMacOSSchema as z.ZodType<BuildRunMacOSParams>, - (params: BuildRunMacOSParams) => - buildRunMacOSLogic( - { - ...params, - configuration: params.configuration ?? 'Debug', - preferXcodebuild: params.preferXcodebuild ?? false, - }, - getDefaultCommandExecutor(), - ), + (params: BuildRunMacOSParams, executor) => + buildRunMacOSLogic( + { + ...params, + configuration: params.configuration ?? 'Debug', + preferXcodebuild: params.preferXcodebuild ?? false, + }, + executor, + ), getDefaultCommandExecutor, ),
53-73: Avoid defaulting to the real system executor in helpers; enforce explicit injectionBoth helpers should require an executor parameter to prevent accidental use of the real executor in tests. Also coalesce
preferXcodebuildto a boolean before passing toexecuteXcodeBuildCommand.async function _handleMacOSBuildLogic( - params: BuildRunMacOSParams, - executor: CommandExecutor = getDefaultCommandExecutor(), + params: BuildRunMacOSParams, + executor: CommandExecutor, ): Promise<ToolResponse> { log('info', `Starting macOS build for scheme ${params.scheme} (internal)`); return executeXcodeBuildCommand( { ...params, configuration: params.configuration ?? 'Debug', }, { platform: XcodePlatform.macOS, arch: params.arch, logPrefix: 'macOS Build', }, - params.preferXcodebuild, + params.preferXcodebuild ?? false, 'build', executor, ); }
75-129: Return a discriminated union from _getAppPathFromBuildSettings to eliminate non-null assertions and improve type narrowingThis strengthens type-safety and avoids the
!assertion at launch time.async function _getAppPathFromBuildSettings( - params: BuildRunMacOSParams, - executor: CommandExecutor = getDefaultCommandExecutor(), -): Promise<{ success: boolean; appPath?: string; error?: string }> { + params: BuildRunMacOSParams, + executor: CommandExecutor, +): Promise<{ success: true; appPath: string } | { success: false; error: string }> { try { // Create the command array for xcodebuild const command = ['xcodebuild', '-showBuildSettings']; // Add the project or workspace if (params.projectPath) { command.push('-project', params.projectPath); } else if (params.workspacePath) { command.push('-workspace', params.workspacePath); } // Add the scheme and configuration command.push('-scheme', params.scheme); command.push('-configuration', params.configuration ?? 'Debug'); // Add derived data path if provided if (params.derivedDataPath) { command.push('-derivedDataPath', params.derivedDataPath); } // Add extra args if provided if (params.extraArgs && params.extraArgs.length > 0) { command.push(...params.extraArgs); } // Execute the command directly const result = await executor(command, 'Get Build Settings for Launch', true, undefined); if (!result.success) { return { - success: false, - error: result.error ?? 'Failed to get build settings', + success: false, + error: result.error ?? 'Failed to get build settings', }; } // Parse the output to extract the app path const buildSettingsOutput = result.output; const builtProductsDirMatch = buildSettingsOutput.match(/BUILT_PRODUCTS_DIR = (.+)$/m); const fullProductNameMatch = buildSettingsOutput.match(/FULL_PRODUCT_NAME = (.+)$/m); if (!builtProductsDirMatch || !fullProductNameMatch) { - return { success: false, error: 'Could not extract app path from build settings' }; + return { success: false, error: 'Could not extract app path from build settings' }; } const appPath = `${builtProductsDirMatch[1].trim()}/${fullProductNameMatch[1].trim()}`; - return { success: true, appPath }; + return { success: true, appPath }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); - return { success: false, error: errorMessage }; + return { success: false, error: errorMessage }; } }
166-171: Drop the non-null assertion by leveraging the discriminated unionWith the union return type, TS narrows
appPathtostringwhensuccessis true.- const appPath = appPathResult.appPath; // We know this is a valid string now + const appPath = appPathResult.appPath; // success === true narrows to string log('info', `App path determined as: ${appPath}`); // 4. Launch the app using CommandExecutor - const launchResult = await executor(['open', appPath!], 'Launch macOS App', true); + const launchResult = await executor(['open', appPath], 'Launch macOS App', true);
🧹 Nitpick comments (7)
src/mcp/tools/device/get_device_app_path.ts (3)
9-9: Deduplicate XcodePlatform: import from shared types instead of redefining locallyAvoid drifting constants by reusing the canonical XcodePlatform export from types/common. This keeps platform strings consistent across tools and prevents future mismatch.
Apply this diff:
- import { ToolResponse } from '../../../types/common.js'; + import { ToolResponse, XcodePlatform } from '../../../types/common.js';-const XcodePlatform = { - iOS: 'iOS', - watchOS: 'watchOS', - tvOS: 'tvOS', - visionOS: 'visionOS', - iOSSimulator: 'iOS Simulator', - watchOSSimulator: 'watchOS Simulator', - tvOSSimulator: 'tvOS Simulator', - visionOSSimulator: 'visionOS Simulator', - macOS: 'macOS', -};Also applies to: 45-55
119-122: Make build settings parsing more robust to whitespace variationsThe current regexes assume exact spacing. Xcodebuild output often includes leading spaces and variable spacing around “=”. Slightly loosening the patterns reduces false negatives.
Apply this diff:
- const builtProductsDirMatch = buildSettingsOutput.match(/BUILT_PRODUCTS_DIR = (.+)$/m); - const fullProductNameMatch = buildSettingsOutput.match(/FULL_PRODUCT_NAME = (.+)$/m); + const builtProductsDirMatch = buildSettingsOutput.match(/^\s*BUILT_PRODUCTS_DIR\s*=\s*(.+)$/m); + const fullProductNameMatch = buildSettingsOutput.match(/^\s*FULL_PRODUCT_NAME\s*=\s*(.+)$/m);
12-13: Import consistency: prefer importing CommandExecutor from utils/command for symmetryElsewhere (e.g., test_device.ts) CommandExecutor and getDefaultCommandExecutor are imported from utils/command. Consider aligning imports here for consistency and clarity on provenance.
Example:
- import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; + import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js';src/mcp/tools/device/test_device.ts (1)
69-76: Guard against empty xcresulttool output before JSON.parseIf the command succeeds but produces no output, JSON.parse will throw a confusing error. Provide a clearer failure mode.
Apply this diff:
- if (!result.success) { + if (!result.success) { throw new Error(result.error ?? 'Failed to execute xcresulttool'); } - - // Parse JSON response and format as human-readable - const summaryData = JSON.parse(result.output) as Record<string, unknown>; + if (!result.output || result.output.trim().length === 0) { + throw new Error('xcresulttool returned no output'); + } + // Parse JSON response and format as human-readable + const summaryData = JSON.parse(result.output) as Record<string, unknown>;src/mcp/tools/macos/build_macos.ts (2)
63-86: Remove manual defaults; let Zod handle themIf you adopt schema-level defaults, you can drop processedParams and use params directly. This reduces duplication and keeps behavior centralized in validation.
Apply this diff:
export async function buildMacOSLogic( params: BuildMacOSParams, executor: CommandExecutor, buildUtilsDeps: BuildUtilsDependencies = defaultBuildUtilsDependencies, ): Promise<ToolResponse> { log('info', `Starting macOS build for scheme ${params.scheme} (internal)`); - const processedParams = { - ...params, - configuration: params.configuration ?? 'Debug', - preferXcodebuild: params.preferXcodebuild ?? false, - }; - return buildUtilsDeps.executeXcodeBuildCommand( - processedParams, + params, { platform: XcodePlatform.macOS, arch: params.arch, logPrefix: 'macOS Build', }, - processedParams.preferXcodebuild ?? false, + params.preferXcodebuild, 'build', executor, ); }
90-99: Schema drift: published schema vs. runtime schemaThe exported schema for MCP is baseSchemaObject.shape (no preprocess/refine/defaults), while handler validation uses buildMacOSSchema (with preprocess, XOR, and future defaults if applied). This can surprise clients/docs that rely on the published schema.
Options:
- Expose the actual Zod schema in the descriptor if your MCP layer supports it (preferred).
- Or generate the MCP schema from buildMacOSSchema (including defaults) via a converter to keep docs/metadata in sync.
- At minimum, ensure the description explicitly calls out the XOR constraint and defaults (you already note XOR; consider adding defaults after adopting them).
Would you like me to add a utility to derive the MCP schema from a Zod schema (e.g., via zod-to-json-schema) and update all unified tools to use it?
src/mcp/tools/macos/build_run_macos.ts (1)
40-46: Attach XOR validation errors to the specific fields (not root) for better UXCurrent
refinemessages land atroot, which can be less actionable. UsesuperRefineto targetprojectPath/workspacePathdirectly.-const buildRunMacOSSchema = baseSchema - .refine((val) => val.projectPath !== undefined || val.workspacePath !== undefined, { - message: 'Either projectPath or workspacePath is required.', - }) - .refine((val) => !(val.projectPath !== undefined && val.workspacePath !== undefined), { - message: 'projectPath and workspacePath are mutually exclusive. Provide only one.', - }); +const buildRunMacOSSchema = baseSchema.superRefine((val, ctx) => { + const hasProject = val.projectPath !== undefined; + const hasWorkspace = val.workspacePath !== undefined; + if (!hasProject && !hasWorkspace) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Either projectPath or workspacePath is required.', + path: ['projectPath'], + }); + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Either projectPath or workspacePath is required.', + path: ['workspacePath'], + }); + } + if (hasProject && hasWorkspace) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'projectPath and workspacePath are mutually exclusive. Provide only one.', + path: ['projectPath'], + }); + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'projectPath and workspacePath are mutually exclusive. Provide only one.', + path: ['workspacePath'], + }); + } +});
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (9)
src/mcp/tools/device/build_device.ts(1 hunks)src/mcp/tools/device/get_device_app_path.ts(5 hunks)src/mcp/tools/device/test_device.ts(6 hunks)src/mcp/tools/macos/build_macos.ts(5 hunks)src/mcp/tools/macos/build_run_macos.ts(6 hunks)src/mcp/tools/macos/get_macos_app_path.ts(6 hunks)src/mcp/tools/macos/test_macos.ts(10 hunks)src/mcp/tools/project-discovery/list_schemes.ts(1 hunks)src/mcp/tools/project-discovery/show_build_settings.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- src/mcp/tools/device/build_device.ts
- src/mcp/tools/project-discovery/list_schemes.ts
- src/mcp/tools/project-discovery/show_build_settings.ts
- src/mcp/tools/macos/get_macos_app_path.ts
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/mcp/tools/device/test_device.ts (4)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/command.ts (3)
getDefaultCommandExecutor(232-243)FileSystemExecutor(41-52)getDefaultFileSystemExecutor(249-260)src/utils/logger.ts (1)
log(33-53)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
src/mcp/tools/device/get_device_app_path.ts (2)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
src/mcp/tools/macos/build_macos.ts (3)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)src/utils/command.ts (1)
getDefaultCommandExecutor(232-243)
src/mcp/tools/macos/test_macos.ts (4)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/test-common.ts (1)
parseXcresultBundle(60-75)src/utils/command.ts (3)
getDefaultCommandExecutor(232-243)FileSystemExecutor(41-52)getDefaultFileSystemExecutor(249-260)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
src/mcp/tools/macos/build_run_macos.ts (3)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/command.ts (1)
getDefaultCommandExecutor(232-243)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
🔇 Additional comments (17)
src/mcp/tools/device/get_device_app_path.ts (1)
134-137: Verify “Next Steps” tool names reflect unified surfacesEnsure get_app_bundle_id, install_app_device, and launch_app_device match the current unified tool names and paths post-consolidation. If any were renamed during unification, update these references to prevent user confusion.
Would you like me to scan the repo for any lingering references to pre-unified tool names and suggest fixes?
src/mcp/tools/device/test_device.ts (2)
253-254: Confirm references to upstream tools reflect unified namesThe description mentions “deviceId (obtained from list_devices)”. Given this PR unified ID/name simulator tools and project/workspace variants, please verify this reference is still accurate and update if the discovery tool name changed.
I can scan the repo for lingering references to pre-unified names (e.g., *_ws, *_proj, *_by_id, *_by_name) and surface a patch if you want.
21-21: Nice: shared nullifyEmptyStrings utility is being reusedGood reuse of the shared schema helper to normalize empty strings prior to validation. This keeps XOR validation behavior predictable.
src/mcp/tools/macos/build_macos.ts (2)
2-6: Good: clear unified tool description and XOR guidanceThe docstring accurately reflects the unified interface and mutual exclusivity. Nice.
14-14: Preprocessing empty strings is the right callUsing nullifyEmptyStrings ensures '' behaves like “unset” before validation. This avoids surprising validation passes on whitespace. Good choice.
src/mcp/tools/macos/test_macos.ts (10)
1-6: LGTM: Header comment correctly updated to reflect unified functionality.The header documentation now accurately describes the unified nature of the tool, mentioning mutual exclusivity between
projectPathandworkspacePath.
23-50: Excellent unified schema implementation with proper XOR validation.The schema design correctly implements the unification pattern:
- Uses
nullifyEmptyStringspreprocessing to normalize input- Implements proper XOR validation ensuring exactly one of
projectPathorworkspacePathis provided- Exports the unified
TestMacosParamstype for external use
68-104: Good addition of executor parameter for testability.The
parseXcresultBundlefunction now accepts an optionalCommandExecutorparameter, which improves testability and follows the established pattern in the codebase.
216-220: Well-designed function signature with proper executor injection.The
testMacosLogicfunction signature correctly:
- Uses the unified
TestMacosParamstype- Accepts both
CommandExecutorandFileSystemExecutorwith proper defaults- Maintains backward compatibility through default parameter values
225-227: Proper filesystem abstraction usage.The temporary directory creation correctly uses the
FileSystemExecutorinterface, which improves testability and follows the established abstraction pattern.
234-250: Correct parameter passing to executeXcodeBuildCommand.The function correctly passes the explicit
projectPathandworkspacePathparameters instead of spreading the entire params object, which aligns with the unified approach.
259-260: Good filesystem executor usage for file existence check.Using
fileSystemExecutor.stat()instead of direct filesystem calls maintains consistency with the abstraction layer.
270-270: Proper cleanup using filesystem executor.The cleanup operations correctly use the
FileSystemExecutorinterface for removing temporary directories, maintaining consistency with the abstraction pattern.Also applies to: 289-289
304-315: Tool export correctly implements unified interface.The default export properly:
- Updates the tool name from
test_macos_projtotest_macos- Provides clear description explaining XOR requirement and includes usage example
- Uses the unified schema and proper typed tool creation
- Correctly wires both command and filesystem executors
308-314: Verify schema compatibility assertion — safe (no change required)testMacosSchema is defined in the same file and
export type TestMacosParams = z.infer<typeof testMacosSchema>, so the casttestMacosSchema as z.ZodType<TestMacosParams>is consistent and not introducing an immediate type-safety issue. The repository uses the same pattern in multiple places (examples):
- src/mcp/tools/macos/test_macos.ts — exports TestMacosParams from testMacosSchema
- src/mcp/tools/macos/build_macos.ts — buildMacOSSchema as z.ZodType
- src/mcp/tools/device/test_device.ts — testDeviceSchema as z.ZodType
- src/mcp/tools/simulator/get_simulator_app_path.ts — getSimulatorAppPathSchema as z.ZodType
Optional recommendation: to avoid masking future mismatches, prefer typing the schema variable directly (declare it as z.ZodType<...>) instead of using
ascasts.src/mcp/tools/macos/build_run_macos.ts (2)
17-39: Unified XOR schema and empty-string normalization looks goodThe base schema with
nullifyEmptyStringsand the mutually exclusive fields are aligned with the unification effort.
209-213: Description updated to reflect unified project/workspace usage—LGTMClear guidance on providing exactly one path aligns with the PR objectives to unify tool variants.
| name: 'build_macos', | ||
| description: | ||
| "Builds a macOS app using xcodebuild from a project or workspace. Provide exactly one of projectPath or workspacePath. Example: build_macos({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' })", | ||
| schema: baseSchemaObject.shape, // MCP SDK compatibility | ||
| handler: createTypedTool<BuildMacOSParams>( | ||
| buildMacOSSchema as z.ZodType<BuildMacOSParams>, | ||
| buildMacOSLogic, | ||
| getDefaultCommandExecutor, | ||
| ), | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Tool naming is consistent with unification goals; check for outdated references elsewhere
build_macos aligns with the unified naming. Make sure any docs/tests that referenced pre-unified variants are updated.
🏁 Script executed:
#!/bin/bash
# Find outdated references to pre-unified tool names/usages.
# Adjust/extend the patterns as needed for your codebase.
set -euo pipefail
echo "Searching for pre-unified tool names (e.g., *_ws, *_proj, *_id, *_name)..."
rg -n --hidden -g '!.git' -E '\b(list|build|run|test)_[a-z]+_(ws|proj|id|name)\b|list_schemes_ws|projectPath.+workspacePath.+both' || true
echo
echo "Searching for old descriptions mentioning project/workspace variants..."
rg -n --hidden -g '!.git' -E 'project vs\. workspace|workspace vs\. project|workspace-only|project-only' || trueLength of output: 1387
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Searching tracked files for pre-unified tool name patterns..."
git ls-files -z | xargs -0 grep -n -E --binary-files=without-match '\b(list|build|run|test)_[a-z]+_(ws|proj|id|name)\b|list_schemes_ws|projectPath.+workspacePath.+both' || true
echo
echo "Searching for references to build_macos / buildMacOS / build-macos..."
git ls-files -z | xargs -0 grep -n -E --binary-files=without-match 'build_macos|buildMacOS|build-macos' || true
echo
echo "Searching for descriptions mentioning project/workspace variants (project-only, workspace-only, project vs. workspace)..."
git ls-files -z | xargs -0 grep -n -E --binary-files=without-match 'project vs\. workspace|workspace vs\. project|workspace-only|project-only|workspace only|project only|projectPath|workspacePath' || trueLength of output: 89199
build_macos name is correct — docs/tests still contain pre‑unified tool names; please update or confirm re‑exports
Confirmed build_macos is named correctly, but a repo search found lingering legacy identifiers (patterns like *_proj, *_ws, *_id, *_name) in docs and tests. Please either update these to the unified names or confirm they intentionally reference legacy re-exports.
Files/locations with representative occurrences:
- docs/PHASE1-TASKS.md
- docs/MANUAL_TESTING.md
- docs/TESTING.md
- docs/TOOLS.md
- scripts/update-tools-docs.ts
- src/mcp/tools/project-discovery/tests/list_schemes.test.ts
- src/mcp/tools/project-discovery/tests/get_mac_bundle_id.test.ts
- src/mcp/tools/project-discovery/list_schemes.ts
- src/mcp/tools/project-discovery/show_build_settings.ts
- src/mcp/tools/device/tests/list_devices.test.ts
Requested action: update those docs/tests to use the unified tool names (or keep them only if you maintain backwards-compatible re-exports and adjust tests to expect that).
🤖 Prompt for AI Agents
In src/mcp/tools/macos/build_macos.ts around lines 90–99 the tool name
build_macos is correct, but the repo contains legacy identifiers (e.g. *_proj,
*_ws, *_id, *_name) in docs and tests; search the listed files and the repo for
those legacy patterns and either (A) update docs and test fixtures to use the
unified tool names (e.g. build_macos and unified param keys) or (B) add explicit
backwards-compatible re-exports/aliases in the tool index and change tests to
expect those aliases; update scripts/update-tools-docs.ts to emit the unified
names, adjust the tests under src/mcp/tools/* to use the new names (or new
expectations for aliases), and run the test/docs build to ensure nothing else
breaks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cursoragent fix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⌛ Working on 5 tasks
- Update list_schemes test to use build_simulator instead of build_simulator_name
- Update scaffold_macos_project to use build_run_macos instead of build_run_mac_ws
- Remove workspace restrictions for derivedDataPath and extraArgs in get_macos_app_path.ts
- Fix executor forwarding issues in build_run_macos and test_device handlers
- Improve error handling and cleanup in test functions
| schema: baseSchemaObject.shape, // MCP SDK compatibility | ||
| handler: createTypedTool<BuildRunMacOSParams>( | ||
| buildRunMacOSSchema as z.ZodType<BuildRunMacOSParams>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Double-check MCP-facing schema metadata and repo-wide descriptions for pre-unified names
You’re exposing baseSchemaObject.shape to MCP for compatibility, which won’t encode the XOR constraint. Ensure docs and any consumer-side validations communicate that constraint. Also verify no references to old tool names remain (e.g., list_schemes_ws).
Run this script to find outdated references and spot-check docs:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Searching for pre-unified tool names..."
rg -n --hidden --glob '!*node_modules*' --glob '!*.lock' \
-e 'list_schemes_ws' \
-e 'list_schemes_proj' \
-e 'build_run_mac_proj' \
-e 'build_run_macos_ws' \
-e 'get_macos_app_path_proj' \
-e 'get_macos_app_path_ws' \
-e 'run_ios_simulator_by_name' \
-e 'run_ios_simulator_by_id' \
-e 'boot_simulator_by_name' \
-e 'boot_simulator_by_id' \
|| echo "No pre-unified names found."
echo
echo "Spot-checking descriptions mentioning old variants or missing XOR guidance..."
rg -n --hidden --glob '!*node_modules*' --glob '!*.lock' \
-e 'Provide exactly one of projectPath or workspacePath' \
-e 'projectPath.*workspacePath' \
docs \
src/mcp/tools \
|| trueLength of output: 31497
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Showing src/mcp/tools/macos/build_run_macos.ts (first 320 lines) for inspection..."
sed -n '1,320p' src/mcp/tools/macos/build_run_macos.ts || trueLength of output: 8367
Action: Ensure MCP-facing schema encodes the XOR constraint and remove remaining pre-unified names
baseSchemaObject.shape is being exported for MCP compatibility — that shape omits the .refine() XOR (projectPath vs workspacePath), so MCP consumers won't see/enforce the mutually-exclusive requirement. The repo search also found leftover pre-unified name references in tests.
Files/locations needing attention:
-
src/mcp/tools/macos/build_run_macos.ts — default export currently uses:
schema: baseSchemaObject.shape, // MCP SDK compatibility handler: createTypedTool<BuildRunMacOSParams>( buildRunMacOSSchema as z.ZodType<BuildRunMacOSParams>,Recommendation: export metadata that includes the refinements (e.g. expose the refined schema to MCP if the SDK supports it), or else add explicit, machine-readable XOR metadata / clearer schema descriptions so MCP consumers know to enforce "either projectPath OR workspacePath (not both)".
-
Replace/update snippet (example):
// Option A — if MCP can accept the full Zod schema: schema: buildRunMacOSSchema, // includes XOR refinements // Option B — if MCP requires a plain shape: schema: baseSchemaObject.shape, // keep for compatibility // — and also add explicit XOR metadata in the exported description/metadata so consumers can validate/enforce it
Outdated/pre-unified name references found (examples — update to canonical names):
- src/mcp/tools/macos/tests/build_macos.test.ts:82,144 — references to "get_macos_app_path_project"
- src/mcp/tools/macos/tests/build_run_macos.test.ts:201,378,441 — references to "get_macos_app_path_project"
Please update tests/docs to use the canonical unified tool names (or make sure these strings are intentionally left as legacy messages), and ensure MCP-facing schema metadata either encodes the XOR or clearly communicates it to consumers.
🤖 Prompt for AI Agents
In src/mcp/tools/macos/build_run_macos.ts around lines 211-213, the exported
MCP-facing schema currently uses baseSchemaObject.shape which omits the
.refine() XOR constraint (projectPath vs workspacePath) and there are leftover
pre-unified tool name references in tests; fix by either exporting the full
refined schema (schema: buildRunMacOSSchema) if the MCP SDK accepts Zod schemas,
or if MCP requires a plain shape keep baseSchemaObject.shape but augment the
exported metadata/description with explicit machine-readable XOR metadata (e.g.,
a field that states "xor": ["projectPath","workspacePath"] or a clear
description so consumers can enforce it); additionally update tests and docs to
replace pre-unified names (e.g., "get_macos_app_path_project") with the
canonical unified tool names across the listed test files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cursoragent fix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I enhanced the robustness of device testing by ensuring proper cleanup of temporary files and improved dependency injection for command execution. I also streamlined macOS app path retrieval logic and updated tool names for consistency across various "Next Steps" suggestions.
- I refactored
testDeviceLogicinsrc/mcp/tools/device/test_device.tsto use afinallyblock fortempDircleanup, ensuring temporary files are always removed even if errors occur during test parsing or execution. - I added a check in
parseXcresultBundlewithinsrc/mcp/tools/device/test_device.tsto throw an error ifxcresulttoolreturns no output, improving error handling. - I updated the
testDeviceandbuildRunMacOStool handlers insrc/mcp/tools/device/test_device.tsandsrc/mcp/tools/macos/build_run_macos.tsrespectively, to directly pass theCommandExecutorinstance to their logic functions, improving dependency injection. - I simplified the
get_macos_app_pathLogicfunction insrc/mcp/tools/macos/get_macos_app_path.tsby removing conditional logic forderivedDataPath,destination, andextraArgsbased onprojectPathorworkspacePath, making these parameters universally applicable. - I updated "Next Steps" suggestions across
src/utils/build-utils.tsand several test files to reflect unified tool names likeget_device_app_path,get_macos_app_path,build_simulator, andbuild_run_macos.
Learn more about Cursor Agents
…erences Co-authored-by: web <web@cameroncooke.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (2)
src/mcp/tools/macos/build_run_macos.ts (1)
114-125: Fix app path detection: select the .app target and match the correct BUILT_PRODUCTS_DIRUsing the first match for
FULL_PRODUCT_NAME/BUILT_PRODUCTS_DIRcan select a framework target or mismatched sections when multiple targets exist, causing launch failures. Parse all matches, select the.appproduct, and pair it with the nearest precedingBUILT_PRODUCTS_DIR.Apply this diff:
- // Parse the output to extract the app path - const buildSettingsOutput = result.output; - const builtProductsDirMatch = buildSettingsOutput.match(/BUILT_PRODUCTS_DIR = (.+)$/m); - const fullProductNameMatch = buildSettingsOutput.match(/FULL_PRODUCT_NAME = (.+)$/m); - - if (!builtProductsDirMatch || !fullProductNameMatch) { - return { success: false, error: 'Could not extract app path from build settings' }; - } - - const appPath = `${builtProductsDirMatch[1].trim()}/${fullProductNameMatch[1].trim()}`; - return { success: true, appPath }; + // Parse the output to extract the app path (robustly pick the application target) + const buildSettingsOutput = result.output; + const appNameMatches = [...buildSettingsOutput.matchAll(/^FULL_PRODUCT_NAME = (.+)$/gm)]; + const appNameMatch = [...appNameMatches].reverse().find((m) => m[1].trim().endsWith('.app')); + if (!appNameMatch || appNameMatch.index === undefined) { + return { success: false, error: 'Could not find FULL_PRODUCT_NAME ending with .app' }; + } + // Find the nearest BUILT_PRODUCTS_DIR before the selected FULL_PRODUCT_NAME line + const beforeAppText = buildSettingsOutput.slice(0, appNameMatch.index); + const builtDirMatchesBefore = [...beforeAppText.matchAll(/^BUILT_PRODUCTS_DIR = (.+)$/gm)]; + let builtProductsDir = builtDirMatchesBefore[builtDirMatchesBefore.length - 1]?.[1]?.trim(); + // Fallback: take the last BUILT_PRODUCTS_DIR in the entire output + if (!builtProductsDir) { + const allBuiltDirMatches = [...buildSettingsOutput.matchAll(/^BUILT_PRODUCTS_DIR = (.+)$/gm)]; + builtProductsDir = allBuiltDirMatches[allBuiltDirMatches.length - 1]?.[1]?.trim(); + } + if (!builtProductsDir) { + return { success: false, error: 'Could not extract BUILT_PRODUCTS_DIR from build settings' }; + } + const appPath = `${builtProductsDir}/${appNameMatch[1].trim()}`; + return { success: true, appPath };src/mcp/tools/device/test_device.ts (1)
15-19: Wrong import source for FileSystemExecutor (compile/runtime break).FileSystemExecutor and getDefaultFileSystemExecutor are defined in src/utils/file-system-executor.ts (see relevant snippet), not in utils/command.js. As written, this will break type resolution and likely fail at runtime.
Apply this diff to import from the correct module:
-import { - CommandExecutor, - getDefaultCommandExecutor, - FileSystemExecutor, - getDefaultFileSystemExecutor, -} from '../../../utils/command.js'; +import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js'; +import { + FileSystemExecutor, + getDefaultFileSystemExecutor, +} from '../../../utils/file-system-executor.js';
♻️ Duplicate comments (5)
src/mcp/tools/macos/build_run_macos.ts (1)
208-215: MCP-facing schema currently omits XOR; export refined schema or add explicit constraint metadataThis mirrors a prior review concern. Exporting
baseSchemaObject.shapehides the XOR constraint from MCP consumers, even though runtime validation uses the refined schema. Prefer exposing the refined schema if the MCP SDK accepts it. Otherwise, keep the shape but add explicit machine-readable metadata so clients can enforce XOR.Option A — expose refined schema (preferred if supported):
- schema: baseSchemaObject.shape, // MCP SDK compatibility + // Expose the refined schema so MCP consumers see XOR constraints + schema: buildRunMacOSSchema,Option B — keep shape and add constraints metadata (if SDK limitation exists):
name: 'build_run_macos', description: "Builds and runs a macOS app from a project or workspace in one step. Provide exactly one of projectPath or workspacePath. Example: build_run_macos({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' })", - schema: baseSchemaObject.shape, // MCP SDK compatibility + schema: baseSchemaObject.shape, // MCP SDK compatibility + // Non-standard extension to communicate XOR to consumers + constraints: { xor: ['projectPath', 'workspacePath'] },Also double-check repo-wide descriptions/tests for pre-unified tool names.
Run this verification to catch stragglers:
#!/bin/bash set -euo pipefail echo "Searching for pre-unified tool names..." rg -n --hidden --glob '!*node_modules*' --glob '!*.lock' \ -e 'list_schemes_ws' \ -e 'list_schemes_proj' \ -e 'build_run_mac_proj' \ -e 'build_run_macos_ws' \ -e 'get_macos_app_path_proj' \ -e 'get_macos_app_path_ws' \ -e 'run_ios_simulator_by_name' \ -e 'run_ios_simulator_by_id' \ -e 'boot_simulator_by_name' \ -e 'boot_simulator_by_id' \ || echo "No pre-unified names found." echo echo "Spot-check mentions of XOR guidance..." rg -n --hidden --glob '!*node_modules*' --glob '!*.lock' \ -e 'Provide exactly one of projectPath or workspacePath' \ -e 'projectPath.*workspacePath' \ docs \ src/mcp/tools \ || truesrc/mcp/tools/macos/get_macos_app_path.ts (3)
84-88: Derived data path now applies to both project and workspace — good fixThis addresses the earlier restriction and aligns with xcodebuild capabilities.
95-98: extraArgs no longer gated by project/workspace — goodAccepting extraArgs regardless of project/workspace fixes the earlier limitation.
89-94: Always pass -destination platform=macOS; add arch only when providedPreviously this flag was applied only for workspaces; now it’s gated on arch. For consistency and to ensure settings resolve deterministically (especially with multi-platform workspaces), pass -destination platform=macOS always and append ,arch=… only when params.arch is set.
- // Handle destination for macOS when arch is specified - if (params.arch) { - const destinationString = `platform=macOS,arch=${params.arch}`; - command.push('-destination', destinationString); - } + // Always target macOS; optionally include arch + { + let destinationString = 'platform=macOS'; + if (params.arch) { + destinationString += `,arch=${params.arch}`; + } + command.push('-destination', destinationString); + }src/mcp/tools/device/test_device.ts (1)
21-21: Good: duplication removed for nullifyEmptyStrings.Importing the shared helper conforms to DRY and resolves the prior duplication issue flagged earlier.
🧹 Nitpick comments (9)
src/mcp/tools/macos/build_run_macos.ts (3)
17-21: Strengthen input validation: trim/validate scheme and ensure correct file extensionsTo prevent subtle input issues:
- Trim and require non-empty
scheme.- Enforce proper extensions for
projectPathandworkspacePath.Apply this diff:
- projectPath: z.string().optional().describe('Path to the .xcodeproj file'), - workspacePath: z.string().optional().describe('Path to the .xcworkspace file'), - scheme: z.string().describe('The scheme to use'), + projectPath: z + .string() + .transform((s) => s.trim()) + .refine((p) => p.endsWith('.xcodeproj'), { message: 'projectPath must end with .xcodeproj' }) + .optional() + .describe('Path to the .xcodeproj file'), + workspacePath: z + .string() + .transform((s) => s.trim()) + .refine((p) => p.endsWith('.xcworkspace'), { message: 'workspacePath must end with .xcworkspace' }) + .optional() + .describe('Path to the .xcworkspace file'), + scheme: z + .string() + .transform((s) => s.trim()) + .min(1, 'Scheme is required and cannot be blank') + .describe('The scheme to use'),
54-56: Avoid double-defaulting configuration and preferXcodebuildDefaults for
configurationandpreferXcodebuildare set both in the wrapper (default export handler) and in the internal logic (_handleMacOSBuildLogic). Keep the defaults in a single place (the logic) to reduce drift.Apply this diff to the handler wrapper:
handler: createTypedTool<BuildRunMacOSParams>( buildRunMacOSSchema as z.ZodType<BuildRunMacOSParams>, - (params: BuildRunMacOSParams, executor) => - buildRunMacOSLogic( - { - ...params, - configuration: params.configuration ?? 'Debug', - preferXcodebuild: params.preferXcodebuild ?? false, - }, - executor, - ), + (params: BuildRunMacOSParams, executor) => buildRunMacOSLogic(params, executor), getDefaultCommandExecutor, ),Also applies to: 69-70, 213-221
166-171: Launching viaopenlooks fineLaunching the resolved .app bundle directly is appropriate for macOS. If you later need to block until app termination, consider
open -W, but current behavior is reasonable.src/mcp/tools/macos/get_macos_app_path.ts (3)
10-11: Consolidate duplicate imports from utils/index.jsMinor cleanup: merge these two imports into one to avoid duplication.
-import { log } from '../../../utils/index.js'; - -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js'; +import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js';
70-78: Unreachable branch handled defensively; consider simplifyingThe else branch throwing when neither path is provided should be unreachable due to schema validation. Keeping it is okay for defense-in-depth, but a short comment noting “unreachable due to schema” would reduce future confusion.
} else { - // This should never happen due to schema validation + // Unreachable due to schema validation; retained for defense-in-depth throw new Error('Neither projectPath nor workspacePath provided'); }
127-130: Make regexes resilient to varying whitespacexcodebuild -showBuildSettings output can differ in spacing/indentation. Slightly harden the patterns to reduce false negatives.
- const builtProductsDirMatch = buildSettingsOutput.match(/BUILT_PRODUCTS_DIR = (.+)$/m); - const fullProductNameMatch = buildSettingsOutput.match(/FULL_PRODUCT_NAME = (.+)$/m); + const builtProductsDirMatch = buildSettingsOutput.match(/^\s*BUILT_PRODUCTS_DIR\s*=\s*(.+)\s*$/m); + const fullProductNameMatch = buildSettingsOutput.match(/^\s*FULL_PRODUCT_NAME\s*=\s*(.+)\s*$/m);src/mcp/tools/device/test_device.ts (3)
23-27: XOR schema validation looks correct; consider superRefine for clearer, single-point errors.Current refine rules work, but produce two separate root-level errors in some cases. superRefine can emit a single, clearer message in one pass.
Optional improvement:
-const testDeviceSchema = baseSchema - .refine((val) => val.projectPath !== undefined || val.workspacePath !== undefined, { - message: 'Either projectPath or workspacePath is required.', - }) - .refine((val) => !(val.projectPath !== undefined && val.workspacePath !== undefined), { - message: 'projectPath and workspacePath are mutually exclusive. Provide only one.', - }); +const testDeviceSchema = baseSchema.superRefine((val, ctx) => { + const hasProject = val.projectPath !== undefined; + const hasWorkspace = val.workspacePath !== undefined; + if (!hasProject && !hasWorkspace) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'Either projectPath or workspacePath is required.', + path: [], // root + }); + } else if (hasProject && hasWorkspace) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'projectPath and workspacePath are mutually exclusive. Provide only one.', + path: [], // root + }); + } +});Also applies to: 39-47, 49-49
169-178: Make cleanup idempotent and avoid redundant invocations.cleanup() is called in success, error, and finally blocks. That’s functionally safe but redundant and can mask cleanup failures. Make cleanup idempotent and call it only in finally.
Apply this diff:
let tempDir: string | undefined; const cleanup = async (): Promise<void> => { if (!tempDir) return; try { await fileSystemExecutor.rm(tempDir, { recursive: true, force: true }); + tempDir = undefined; // prevent repeat attempts } catch (cleanupError) { log('warn', `Failed to clean up temporary directory: ${cleanupError}`); } }; @@ - // Clean up temporary directory - await cleanup(); + // cleanup handled in finally @@ - await cleanup(); + // cleanup handled in finally @@ } finally { await cleanup(); }Also applies to: 230-231, 247-248, 255-257
266-274: Avoid double-defaulting platform (set default in one place).Handler applies a default for platform, and testDeviceLogic/execution also defaults to iOS. Centralize to a single place to reduce drift.
Apply this diff:
handler: createTypedTool<TestDeviceParams>( testDeviceSchema as z.ZodType<TestDeviceParams>, (params: TestDeviceParams, executor: CommandExecutor) => { - return testDeviceLogic( - { - ...params, - platform: params.platform ?? 'iOS', - }, - executor, - getDefaultFileSystemExecutor(), - ); + return testDeviceLogic(params, executor, getDefaultFileSystemExecutor()); }, getDefaultCommandExecutor, ),
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (7)
src/mcp/tools/device/test_device.ts(8 hunks)src/mcp/tools/macos/build_run_macos.ts(7 hunks)src/mcp/tools/macos/get_macos_app_path.ts(7 hunks)src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts(11 hunks)src/mcp/tools/project-discovery/list_schemes.ts(1 hunks)src/mcp/tools/project-scaffolding/__tests__/scaffold_macos_project.test.ts(2 hunks)src/mcp/tools/project-scaffolding/scaffold_macos_project.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- src/mcp/tools/project-scaffolding/tests/scaffold_macos_project.test.ts
- src/mcp/tools/project-scaffolding/scaffold_macos_project.ts
- src/mcp/tools/project-discovery/list_schemes.ts
- src/mcp/tools/project-discovery/tests/list_schemes.test.ts
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/mcp/tools/macos/build_run_macos.ts (2)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
src/mcp/tools/device/test_device.ts (6)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/command.ts (3)
getDefaultCommandExecutor(232-243)FileSystemExecutor(41-52)getDefaultFileSystemExecutor(249-260)src/utils/logger.ts (1)
log(33-53)src/utils/template-manager.ts (1)
cleanup(135-143)src/utils/validation.ts (1)
createTextResponse(35-45)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
src/mcp/tools/macos/get_macos_app_path.ts (2)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
🔇 Additional comments (10)
src/mcp/tools/macos/build_run_macos.ts (4)
2-2: Unified tool docstrings read clearly — LGTMThe top-level description cleanly communicates the unified behavior and XOR input requirement.
Also applies to: 4-5
15-15: DRY win: shared nullifyEmptyStrings helper import — LGTMImporting the shared helper eliminates duplication and keeps preprocessing consistent across tools.
38-46: XOR validation is correctly enforced at runtime — LGTMThe preprocess + dual refine sequence correctly ensures “exactly one of projectPath or workspacePath”.
83-88: LGTM: Properly selects project vs workspace for showBuildSettingsThe branching correctly appends either -project or -workspace based on validated inputs.
src/mcp/tools/macos/get_macos_app_path.ts (3)
15-41: XOR schema with input normalization looks solidGood use of nullifyEmptyStrings preprocessing plus XOR validation to ensure exactly one of projectPath/workspacePath is provided. This prevents a whole class of edge cases with empty strings and ambiguous inputs.
147-151: Next steps text is correct — no rename detectedVerified: the launcher is named
launch_mac_appandget_app_bundle_idexists. No renaming tolaunch_macos_appwas found, so the Next Steps strings (and snapshots) don’t need updating.
- src/mcp/tools/macos/launch_mac_app.ts — name: 'launch_mac_app' (line ~81)
- src/mcp/tools/project-discovery/get_app_bundle_id.ts — name: 'get_app_bundle_id' (line ~129)
- src/mcp/tools/project-discovery/get_mac_bundle_id.ts — name: 'get_mac_bundle_id' (line ~125)
183-186: Confirm MCP “schema” export shape is correct for docs/SDK consumersVerified: src/mcp/tools/macos/get_macos_app_path.ts exports schema: baseSchemaObject.shape (losing the .refine XOR checks), while the handler uses the refined getMacosAppPathSchema.
- File to check:
- src/mcp/tools/macos/get_macos_app_path.ts
- getMacosAppPathSchema (with two .refine calls enforcing "exactly one of projectPath/workspacePath") — defined earlier in the file.
- Exported schema: schema: baseSchemaObject.shape (line ~183) — this does not include the refinements.
- Repo-wide note: This .shape export pattern is used in many tools (search shows numerous occurrences), so changing behavior may affect MCP SDK/docs pipelines that expect shape-only exports.
Recommendation (pick one based on your docs/SDK pipeline capabilities):
- If your docs generator/SDK supports full Zod schemas, export the refined schema so docs can reflect the XOR constraint:
- schema: getMacosAppPathSchema as unknown as z.ZodTypeAny
- Otherwise (if you must keep .shape for SDK compatibility), ensure the docs/descriptions explicitly state the XOR requirement (the current description already says "Provide exactly one of projectPath or workspacePath" — confirm your docs renderer surfaces that text).
src/mcp/tools/device/test_device.ts (3)
72-74: Good defensive check for empty xcresult output.Guarding against empty output avoids JSON.parse errors and returns actionable feedback earlier.
2-6: Confirmed: unified tool names (list_devices / list_schemes) are in use — no legacy variants foundSearches of src/ and docs/ show only the unified tool names; I found no occurrences of list_devices_proj, list_devices_ws, list_schemes_ws, or list_schemes_proj.
Key hits (representative)
- src/mcp/tools/device/list_devices.ts — defines name: 'list_devices'
- src/mcp/tools/project-discovery/list_schemes.ts — defines name: 'list_schemes'
- src/mcp/tools/device/test_device.ts — deviceId description references list_devices
- src/mcp/tools/device/list_schemes.ts, src/mcp/tools/simulator/list_schemes.ts, src/mcp/tools/macos/list_schemes.ts — re-exports to unified list_schemes
- docs/TOOLS.md, docs/MANUAL_TESTING.md, docs/TESTING.md — examples and usage referencing list_devices / list_schemes
Notes
- Some docs (docs/PLUGIN_DEVELOPMENT.md, docs/ESLINT_TYPE_SAFETY.md, docs/PHASE1-TASKS.md) mention the _proj/_ws suffixes as naming conventions or historical examples — these are explanatory and not active legacy tool names.
Action
- No changes required to src/mcp/tools/device/test_device.ts description; referencing list_devices is correct.
262-265: Do not change just this file —schema: baseSchemaObject.shapeis intentional for SDK compatibilityThe repo consistently exports baseSchemaObject.shape for the exported
schema(MCP SDK compatibility) and core/plugin-types.ts expects a shape-like mapping. Replacing this single file with the Zod effect (testDeviceSchema) would be inconsistent and could break consumers/docs tooling. Either update the docs generator to handle ZodEffects (preprocess/refine) or change the plugin typing and update all tools — the latter is a large breaking change.Files that use baseSchemaObject.shape (representative):
- src/mcp/tools/device/test_device.ts (current file)
- src/mcp/tools/device/build_device.ts
- src/mcp/tools/device/get_device_app_path.ts
- src/mcp/tools/simulator/test_simulator.ts
- src/mcp/tools/simulator/build_run_simulator.ts
- src/mcp/tools/macos/test_macos.ts
- src/mcp/tools/macos/build_run_macos.ts
- src/mcp/tools/project-discovery/list_schemes.ts
- src/core/plugin-types.ts
If you still want to expose the full Zod schema to docs/consumers, you can apply this change across the codebase (and update plugin types/docs generator). Example diff (single-file change — not recommended without broader changes):
export default { name: 'test_device', description: 'Runs tests for an Apple project or workspace ...', - schema: baseSchemaObject.shape, + schema: testDeviceSchema,Recommendation: Keep schema as baseSchemaObject.shape for compatibility and either (A) enhance the docs generator to understand ZodEffects (preprocess/refine) or (B) plan a coordinated, repo-wide migration to export full ZodTypes and update plugin typings and consumers.
Likely an incorrect or invalid review comment.
Co-authored-by: web <web@cameroncooke.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (12)
src/mcp/tools/macos/__tests__/build_macos.test.ts (4)
31-56: Strengthen schema tests with empty and mixed-type validationCurrent validation is solid. Consider adding negative cases for empty strings and mixed-type arrays in
extraArgsto catch more real-world invalid inputs early.Apply within this test block:
// Test invalid inputs expect(buildMacOS.schema.projectPath.safeParse(null).success).toBe(false); expect(buildMacOS.schema.workspacePath.safeParse(null).success).toBe(false); expect(buildMacOS.schema.scheme.safeParse(null).success).toBe(false); + expect(buildMacOS.schema.projectPath.safeParse('').success).toBe(false); + expect(buildMacOS.schema.workspacePath.safeParse('').success).toBe(false); + expect(buildMacOS.schema.scheme.safeParse('').success).toBe(false); expect(buildMacOS.schema.arch.safeParse('invalidArch').success).toBe(false); expect(buildMacOS.schema.extraArgs.safeParse('not-array').success).toBe(false); + // Mixed-type array should fail + expect(buildMacOS.schema.extraArgs.safeParse(['--valid', 123 as any]).success).toBe(false); expect(buildMacOS.schema.preferXcodebuild.safeParse('not-boolean').success).toBe(false);
66-86: Success response expectations consistent; minor wording nitThe success assertion matches the expected ToolResponse. However, the phrase "macOS Build build" reads awkwardly (duplicated "build"). Consider simplifying the underlying message to "macOS build succeeded for scheme ..." and update tests accordingly to reduce redundancy.
If you opt to adjust messaging, I can propose a coordinated change across the underlying builder utility and tests.
381-413: Workspace minimal command generation added—greatValidates switch from
-projectto-workspacewhile retaining defaults and destination. Consider adding a companion test for workspace with all parameters to mirror the project variant.Here’s a test you can append in this section:
+ it('should generate correct xcodebuild workspace command with all parameters', async () => { + let capturedCommand: string[] = []; + const mockExecutor = createMockExecutor({ success: true, output: 'BUILD SUCCEEDED' }); + const spyExecutor = async (command: string[]) => { + capturedCommand = command; + return mockExecutor(command); + }; + + await buildMacOSLogic( + { + workspacePath: '/path/to/workspace.xcworkspace', + scheme: 'MyScheme', + configuration: 'Release', + arch: 'x86_64', + derivedDataPath: '/custom/derived', + extraArgs: ['--verbose'], + preferXcodebuild: true, + }, + spyExecutor, + ); + + expect(capturedCommand).toEqual([ + 'xcodebuild', + '-workspace', + '/path/to/workspace.xcworkspace', + '-scheme', + 'MyScheme', + '-configuration', + 'Release', + '-skipMacroValidation', + '-destination', + 'platform=macOS,arch=x86_64', + '-derivedDataPath', + '/custom/derived', + '--verbose', + 'build', + ]); + });
432-465: XOR success paths validated via logicBoth project and workspace success paths return non-error responses. Consider adding handler-based success tests as well to verify handler wiring (parsing + delegation) end-to-end.
I can add small handler success tests if you want parity with the error-path handler tests.
src/mcp/tools/device/__tests__/build_device.test.ts (2)
18-20: Reduce brittleness of description assertion; ensure it reflects unified namingAsserting the entire description string is fragile and will cause churn if copy changes slightly. Prefer checking essential phrases to keep tests stable while still enforcing correctness.
Apply this diff to assert key parts while allowing non-breaking edits:
- it('should have correct description', () => { - expect(buildDevice.description).toBe( - "Builds an app from a project or workspace for a physical Apple device. Provide exactly one of projectPath or workspacePath. Example: build_device({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' })", - ); - }); + it('should have correct description', () => { + const { description } = buildDevice; + expect(description).toContain('Builds an app from a project or workspace'); + expect(description).toContain('exactly one of projectPath or workspacePath'); + expect(description).toContain( + "build_device({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' })", + ); + });
55-74: Align XOR validation messages with the description and reduce coupling to exact phrasingThe error text checks are coupled to specific phrasing (“Either ... is required”, “mutually exclusive”). The description already uses “Provide exactly one of projectPath or workspacePath”. Consider aligning error messaging to the same phrasing and loosening the assertion to key phrases.
For example:
- expect(result.content[0].text).toContain('Either projectPath or workspacePath is required'); + expect(result.content[0].text).toContain('exactly one of projectPath or workspacePath'); - expect(result.content[0].text).toContain('mutually exclusive'); + expect(result.content[0].text).toContain('exactly one of projectPath or workspacePath');If you prefer keeping both checks, assert both key terms (“projectPath” and “workspacePath”) rather than the exact sentence.
src/mcp/tools/macos/__tests__/get_macos_app_path.test.ts (6)
156-197: DRY the manual executor capturing (optional)The repeated inline mock executors/call arrays across command-generation tests are clear but repetitive. Consider extracting a tiny helper to reduce duplication and ease future updates.
Example helper:
function makeCapturingExecutor(calls: unknown[]): CommandExecutor { return async (...args) => { calls.push(args); return { success: true, output: 'BUILT_PRODUCTS_DIR = /path/to/build\nFULL_PRODUCT_NAME = MyApp.app', error: undefined, process: { pid: 12345 } }; }; }Also applies to: 199-240, 242-285, 287-327
199-240: Command generation (workspace, x86_64) — LGTMCorrect destination string for x86_64. Suggest adding one more test for derivedDataPath/extraArgs with workspace to mirror parity with the project path case.
242-285: Command generation (project, derivedDataPath + extraArgs) — LGTM; add an extraArgs edge caseThis covers derivedDataPath and a simple flag. Consider adding an extra arg that takes a value (e.g., '-sdk', 'macosx14.2') to ensure correct tokenization and ordering when appending extraArgs.
347-376: Happy path (workspace) — LGTM; consider a variant with spaces in product nameSuccess payload and “Next Steps” guidance match the logic contract. Add a case where FULL_PRODUCT_NAME includes spaces (e.g., "My App.app") to guard parsing robustness.
440-447: Missing app-path keys in output — LGTM; add “no output” variantCovers the “settings present but not the required keys” case. Consider adding a “no output at all” scenario to cover that branch explicitly.
Also applies to: 448-456
464-471: Exception handling — LGTM; add string-throw variant (optional)Covers thrown Error objects. For completeness with
createMockExecutorsemantics, add a variant where the executor rejects with a string to ensure uniform error formatting.Also applies to: 472-480
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (4)
src/mcp/tools/device/__tests__/build_device.test.ts(9 hunks)src/mcp/tools/macos/__tests__/build_macos.test.ts(15 hunks)src/mcp/tools/macos/__tests__/build_run_macos.test.ts(13 hunks)src/mcp/tools/macos/__tests__/get_macos_app_path.test.ts(15 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/mcp/tools/macos/tests/build_run_macos.test.ts
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/mcp/tools/macos/__tests__/build_macos.test.ts (3)
build-plugins/plugin-discovery.js (1)
result(172-175)src/mcp/tools/macos/build_macos.ts (1)
buildMacOSLogic(63-87)src/utils/command.ts (1)
createMockExecutor(267-305)
src/mcp/tools/device/__tests__/build_device.test.ts (3)
build-plugins/plugin-discovery.js (1)
result(172-175)src/utils/command.ts (1)
createMockExecutor(267-305)src/mcp/tools/device/build_device.ts (1)
buildDeviceLogic(42-61)
src/mcp/tools/macos/__tests__/get_macos_app_path.test.ts (2)
src/mcp/tools/macos/get_macos_app_path.ts (1)
get_macos_app_pathLogic(58-177)src/utils/command.ts (1)
createMockExecutor(267-305)
🔇 Additional comments (27)
src/mcp/tools/macos/__tests__/build_macos.test.ts (10)
11-11: Import updated to unified tool looks correctSwitch to
build_macos.jsdefault export (buildMacOS) and named export (buildMacOSLogic) aligns with the unification. The ESM.jsextension is appropriate for Vitest + TS in ESM mode.
88-115: Failure response expectations look goodExact stderr forwarding and the failure summary are asserted precisely, which is valuable for deterministic UX.
123-148: Optional-params path validated; good coverageCovers
configuration,arch,derivedDataPath,extraArgs, andpreferXcodebuild. This ensures command construction branches are exercised without relying on mocks.
157-174: Exception and spawn-error handling coveredBoth thrown-error paths are validated. This is important for MCP runtime stability.
Also applies to: 183-200
214-235: Project minimal command generation—arguments order and defaults verifiedThe exact
xcodebuildargv array, defaultDebug,-skipMacroValidation, and macOS destination are correctly asserted. Good.
247-276: All-params command generation for project is correctDestination with
arch,-derivedDataPath, and passthroughextraArgsverified. This matches expected normalization.
288-312: Edge cases (derivedDataPath-only, arm64-only, spaces in path) are well coveredThese cases frequently regress; good to see exact argv checks.
Also applies to: 324-346, 358-379
415-430: XOR validation error paths covered via handlerGreat: tests assert both "neither provided" and "both provided" error conditions via the public handler, which matches the user-facing contract.
11-11: No syntax error found — build_macos.ts log line is validI searched for "Starting macOS build for scheme" and found the correctly-closed template string and parentheses:
- src/mcp/tools/macos/build_macos.ts — line 68:
log('info',Starting macOS build for scheme ${params.scheme} (internal));The suggested patch is unnecessary — no changes required.
16-16: Plugin metadata matches tests — no action requiredVerified src/mcp/tools/macos/build_macos.ts contains the expected metadata: name is 'build_macos' and the description exactly matches the test string (includes the "Provide exactly one of projectPath or workspacePath" XOR note and example).
- File: src/mcp/tools/macos/build_macos.ts — lines ~90–92 contain:
- name: 'build_macos'
- description: "Builds a macOS app using xcodebuild from a project or workspace. Provide exactly one of projectPath or workspacePath. Example: build_macos({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' })"
src/mcp/tools/device/__tests__/build_device.test.ts (8)
14-15: LGTM: unified tool name assertion matches the new APIThe export’s name check aligns with the unified tool surface (
build_device). No issues here.
76-97: LGTM: handler parameter validation tests are precise and cover common failure pathsThe tests exercise missing required fields and invalid types through the handler, ensuring Zod feedback is surfaced. Looks good.
100-117: LGTM: happy path (project) via logic with injected executorDeterministic executor injection, defaulting configuration to Debug, and success assertions are all solid.
161-188: LGTM: command args verification for project-based build is thoroughEnsures the exact xcodebuild invocation, including defaults and destination. Nicely deterministic.
218-245: LGTM: failure response assertion is exact and validates stderr surfacingThe test covers the failure contract precisely, ensuring error output is exposed and the error flag is set.
247-303: LGTM: optional parameters propagate into the command as expectedValidates configuration override, derived data path, and extra args injection. Solid coverage.
196-215: Verified: canonical tool names unchanged — no test update requiredBoth tool names exist in the repo and match the test expectation.
- src/mcp/tools/device/get_device_app_path.ts — exports name 'get_device_app_path'
- src/utils/build-utils.ts — contains "Get App Path: get_device_app_path" and "Get Bundle ID: get_ios_bundle_id"
- src/mcp/tools/device/tests/build_device.test.ts — expected Next Steps string includes both names
Conclusion: no changes needed to the implementation or this test. (Optional: if you want to reduce brittleness, assert the presence of "Get App Path" and "Get Bundle ID" rather than the full literal.)
28-51: No change required — exported schema already exposes field validators (tests are correct)I checked src/mcp/tools/device/build_device.ts:
baseSchemaObjectis defined as a z.object (includes projectPath, workspacePath, scheme, etc.) and the module export setsschema: baseSchemaObject.shape(around line 67). That meansbuildDevice.schema.projectPath.safeParse(...)is valid and the suggested.shapechanges in the original comment are unnecessary.
- File: src/mcp/tools/device/build_device.ts
- baseSchemaObject defined (projectPath, workspacePath, scheme...) — ~lines 16–19
- export uses
schema: baseSchemaObject.shape— line 67Action: ignore the diff in the original comment; no test changes required.
Likely an incorrect or invalid review comment.
src/mcp/tools/macos/__tests__/get_macos_app_path.test.ts (9)
9-9: Public API rename wired correctly (imports and name assertion) — LGTMImport path and exported names align with the unified tool. The name assertion matches the expected public API identifier.
Also applies to: 11-15
117-154: Command generation (project, minimal params) — LGTMCovers the
-projectflow with default Debug configuration. Good parity with the workspace test.
156-197: Command generation (workspace, all params) — LGTMCorrect inclusion of
-configuration Releaseand-destination platform=macOS,arch=arm64.
287-327: Default configuration fallback — LGTMAsserts ‘Debug’ when configuration is omitted, with
-destinationpresent for arch=arm64. Good coverage.
378-407: Happy path (project) — LGTMMirrors the workspace case, ensuring consistent behavior for projects.
415-422: Build settings failure path — LGTMAsserts the exact error text from a failed xcodebuild invocation. Matches the described behavior.
Also applies to: 423-431
96-115: Command generation (workspace, minimal params) — LGTMLabel "Get App Path" confirmed in src/mcp/tools/macos/get_macos_app_path.ts:101. No changes required.
27-52: Schema exposes per-field Zod validators — no change requiredVerified: the default export sets
schema: baseSchemaObject.shape, andbaseSchemaObjectdefinesprojectPath,workspacePath,scheme,arch,derivedDataPath, andextraArgsas Zod types. So calls likegetMacosAppPath.schema.workspacePath.safeParse(...)are valid.
- File: src/mcp/tools/macos/get_macos_app_path.ts
- baseSchemaObject definition (lines ~27–31)
- default export uses
schema: baseSchemaObject.shape(line ~183)
18-20: Description matches source — no action requiredConfirmed the description string in src/mcp/tools/macos/get_macos_app_path.ts exactly matches the literal asserted in the test.
- src/mcp/tools/macos/get_macos_app_path.ts — description at line 182 matches the test literal.
- src/mcp/tools/macos/tests/get_macos_app_path.test.ts — assertion at lines 18–20 matches the source.
| describe('XOR Validation', () => { | ||
| it('should error when neither projectPath nor workspacePath provided', async () => { | ||
| const result = await getMacosAppPath.handler({ | ||
| scheme: 'MyScheme', | ||
| }); | ||
|
|
||
| expect(result.isError).toBe(true); | ||
| expect(result.content[0].text).toContain('Either projectPath or workspacePath is required'); | ||
| }); | ||
|
|
||
| it('should error when both projectPath and workspacePath provided', async () => { | ||
| const result = await getMacosAppPath.handler({ | ||
| projectPath: '/path/to/project.xcodeproj', | ||
| workspacePath: '/path/to/workspace.xcworkspace', | ||
| scheme: 'MyScheme', | ||
| }); | ||
|
|
||
| expect(result.isError).toBe(true); | ||
| expect(result.content[0].text).toContain('mutually exclusive'); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
XOR validation messages — confirm exact strings surfaced by handler
The tests assert substrings: “Either projectPath or workspacePath is required” and “mutually exclusive”. Verify the handler emits these exact messages (the logic function throws “Neither … provided” per snippet, which may differ).
If messages differ, update the handler to standardize on the phrasing used in tests and documentation.
🏁 Script executed:
#!/bin/bash
# Check for user-facing XOR messages
rg -n -A2 -B2 $'Either projectPath or workspacePath is required|mutually exclusive|Neither projectPath nor workspacePath provided' src/mcp/tools/macos/get_macos_app_path.tsLength of output: 924
XOR validation messages — verified and minor consistency fix recommended
Schema refinements already contain the substrings used in tests, but the runtime fallback throw uses a different phrase — align them for consistency.
- File: src/mcp/tools/macos/get_macos_app_path.ts
- Schema refinements (≈ lines 35–41):
- "Either projectPath or workspacePath is required."
- "projectPath and workspacePath are mutually exclusive. Provide only one."
- Runtime fallback (≈ lines 75–78):
- throw new Error('Neither projectPath nor workspacePath provided');
- Schema refinements (≈ lines 35–41):
Suggested change:
Replace
throw new Error('Neither projectPath nor workspacePath provided');
with
throw new Error('Either projectPath or workspacePath is required.');
🤖 Prompt for AI Agents
In src/mcp/tools/macos/get_macos_app_path.ts around lines 75–78, the runtime
fallback error message ('Neither projectPath nor workspacePath provided') is
inconsistent with the schema refinement message; update the thrown Error text to
exactly "Either projectPath or workspacePath is required." so runtime and schema
messages match the tests and ensure consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cursoragent please address
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| describe('Handler Behavior (Complete Literal Returns)', () => { | ||
| it('should return Zod validation error for missing workspacePath', async () => { | ||
| const result = await getMacAppPathWs.handler({ | ||
| scheme: 'MyScheme', | ||
| it('should return Zod validation error for missing scheme', async () => { | ||
| const result = await getMacosAppPath.handler({ | ||
| workspacePath: '/path/to/MyProject.xcworkspace', | ||
| }); | ||
|
|
||
| expect(result).toEqual({ | ||
| content: [ | ||
| { | ||
| type: 'text', | ||
| text: 'Error: Parameter validation failed\nDetails: Invalid parameters:\nworkspacePath: Required', | ||
| text: 'Error: Parameter validation failed\nDetails: Invalid parameters:\nscheme: Required', | ||
| }, | ||
| ], | ||
| isError: true, | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Zod error literal on missing scheme — verify exact formatting and standardize across tools
The exact, multi-line literal is asserted. Ensure the handler uses a consistent zod-to-text error formatting utility across tools to prevent drift.
If not centralized, consider extracting a reusable “formatZodError” helper to keep messaging consistent.
🏁 Script executed:
#!/bin/bash
# Verify standardized zod formatting utility usage and the exact text
rg -n -A3 -B3 $'Parameter validation failed|Invalid parameters' src/mcp/tools/macos/get_macos_app_path.ts
# Optional: find a shared error formatting util if one exists
rg -n $'zod|Zod' src | sed -n '1,200p'Length of output: 18600
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== src/utils/typed-tool-factory.ts ==="
sed -n '1,240p' src/utils/typed-tool-factory.ts || true
echo
echo "=== src/mcp/tools/macos/get_macos_app_path.ts ==="
sed -n '1,400p' src/mcp/tools/macos/get_macos_app_path.ts || true
echo
echo "=== Files with `if (error instanceof z.ZodError)` ==="
rg -n "if \\(error instanceof z.ZodError\\)" src || true
echo
echo "=== Show excerpts for files that handle ZodError (first 200 lines each) ==="
for f in $(rg -n "if \\(error instanceof z.ZodError\\)" src -l || true); do
echo "---- $f ----"
sed -n '1,200p' "$f" || true
echo
doneLength of output: 31891
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== List src/utils ==="
ls -la src/utils || true
echo
echo "=== Show src/utils/index.ts ==="
sed -n '1,240p' src/utils/index.ts || sed -n '1,240p' src/utils/index.js || trueLength of output: 2197
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for createErrorResponse/createTextResponse helpers ==="
rg -n "createErrorResponse|createTextResponse" src || true
echo
echo "=== src/utils/errors.ts ==="
sed -n '1,240p' src/utils/errors.ts || true
echo
echo "=== src/utils/validation.ts ==="
sed -n '1,240p' src/utils/validation.ts || true
echo
echo "=== Occurrences of 'Invalid parameters' across repo ==="
rg -n "Invalid parameters" || true
echo
echo "=== Show snippets around Zod error formatting in files that handle ZodError ==="
for f in $(rg -n "if \\(error instanceof z.ZodError\\)" src -l || true); do
echo "---- $f ----"
rg -n -A2 -B2 "if \\(error instanceof z.ZodError\\)" "$f" || true
sed -n '1,220p' "$f" | sed -n '1,220p' || true
echo
doneLength of output: 57356
Standardize Zod validation error formatting across handlers
Short: createTypedTool already formats Zod errors via createErrorResponse (producing the "Error: \nDetails:
Details
" literal). Several handlers catch ZodError and build a different literal (Parameter validation failed. Invalid parameters:\n...), causing inconsistent output and brittle tests.
Files to update:
- src/mcp/tools/simulator/build_simulator.ts — local Zod catch builds
Parameter validation failed. Invalid parameters:… - src/mcp/tools/simulator/build_run_simulator.ts — same
- src/mcp/tools/simulator/test_simulator.ts — same
(typed-tool-factory.ts is already the canonical implementation)
Recommended fixes (pick one):
- Replace the manual Zod catch in the files above with the same call used by createTypedTool:
return createErrorResponse(
'Parameter validation failed',
Invalid parameters:\n${errorMessages.join('\n')},
); - OR wrap those tools with createTypedTool so Zod handling is centralized.
- Optionally, extract a small helper (e.g., formatZodErrorResponse) into src/utils/ (validation.ts or errors.ts) to produce the errorMessages and call createErrorResponse from a single place.
Because tests assert the current "Error: ...\nDetails: ..." literal in many places (e.g. src/mcp/tools/macos/tests/get_macos_app_path.test.ts), apply one of the fixes above to avoid future drift.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cursoragent what do you think we should do here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I enhanced the build_device tool's testing and improved the robustness of xcodebuild output parsing across several tools. I also refactored a common type definition for better code organization.
- I added a new test case in
src/mcp/tools/device/__tests__/build_device.test.tsto verify the correctxcodebuildcommand generation when a workspace path is provided, ensuring proper-workspaceand-schemearguments. - I refactored the
XcodePlatformenum by moving its definition fromsrc/mcp/tools/device/get_device_app_path.tstosrc/types/common.js, promoting reusability and centralizing type definitions. - I updated the regex patterns for extracting
BUILT_PRODUCTS_DIRandFULL_PRODUCT_NAMEinsrc/mcp/tools/device/get_device_app_path.ts,src/mcp/tools/macos/build_run_macos.ts,src/mcp/tools/macos/get_macos_app_path.ts,src/mcp/tools/simulator/build_run_simulator.ts, andsrc/mcp/tools/simulator/get_simulator_app_path.ts. The new patterns (/^\s*KEY\s*=\s*(.+)$/m) are more robust against varying whitespace inxcodebuildoutput. - I standardized error messages in
get_device_app_path.tsandget_macos_app_path.tstoEither projectPath or workspacePath is required.for clarity.
4 tasks completed
- Add workspace command generation test to build_device.test.ts to verify -workspace flag usage
- Update XOR validation error message consistency in get_macos_app_path.ts and get_device_app_path.ts
- Make build settings parsing more robust to whitespace variations
- Remove duplicate XcodePlatform definition in get_device_app_path.ts
Learn more about Cursor Agents
Co-authored-by: web <web@cameroncooke.com>
Co-authored-by: web <web@cameroncooke.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🔭 Outside diff range comments (1)
src/mcp/tools/device/__tests__/build_device.test.ts (1)
256-266: Test labels OK; docs contain stale pre-unified tool name(s) — please updateChecked the repo for pre-unified tool names. The test snippet in src/mcp/tools/device/tests/build_device.test.ts (lines 256–266) already uses the unified names (get_device_app_path, get_ios_bundle_id). I only found stale/typo references in documentation:
- docs/MANUAL_TESTING.md: lines 510, 556, 657, 661 — "list_schems_proj"
- docs/TESTING.md: lines 1002, 1048, 1149, 1153 — "list_schems_proj"
Please update those docs to the unified name (e.g., list_schemes) and fix the typo. No code/test changes required otherwise.
♻️ Duplicate comments (2)
src/mcp/tools/device/__tests__/build_device.test.ts (1)
138-188: Workspace command-generation test added — addresses prior reviewThis directly validates the -workspace path and ensures correct flag composition. It resolves the earlier ask to assert the generated workspace command.
src/mcp/tools/macos/get_macos_app_path.ts (1)
84-99: Addressed earlier review: derivedDataPath/extraArgs now apply to both project and workspaceThe previous limitation has been removed; both options are now passed regardless of project/workspace. This resolves the earlier concern.
🧹 Nitpick comments (8)
src/mcp/tools/device/__tests__/build_device.test.ts (3)
17-21: Description assertion aligns with unified surface; consider showing both XOR examplesGood that the description reflects the unified API and XOR requirement. As a minor enhancement, consider including a workspace-based example as well to communicate parity explicitly.
299-355: Add the “optional params for workspace” symmetry testYou assert optional parameters are included in the project command. Mirror this for workspace to guarantee parity and prevent regressions across both entry points.
Apply this diff to add the workspace-optional-params test:
@@ describe('Handler Behavior (Complete Literal Returns)', () => { @@ it('should include optional parameters in command', async () => { @@ }); }); + + it('should include optional parameters in workspace command', async () => { + const commandCalls: Array<{ + args: string[]; + logPrefix: string; + silent: boolean; + timeout: number | undefined; + }> = []; + + const stubExecutor = async ( + args: string[], + logPrefix: string, + silent: boolean, + timeout?: number, + ) => { + commandCalls.push({ args, logPrefix, silent, timeout }); + return { + success: true, + output: 'Build succeeded', + error: undefined, + process: { pid: 12345 }, + }; + }; + + await buildDeviceLogic( + { + workspacePath: '/path/to/MyProject.xcworkspace', + scheme: 'MyScheme', + configuration: 'Release', + derivedDataPath: '/tmp/derived-data', + extraArgs: ['--verbose'], + }, + stubExecutor, + ); + + expect(commandCalls).toHaveLength(1); + expect(commandCalls[0]).toEqual({ + args: [ + 'xcodebuild', + '-workspace', + '/path/to/MyProject.xcworkspace', + '-scheme', + 'MyScheme', + '-configuration', + 'Release', + '-skipMacroValidation', + '-destination', + 'generic/platform=iOS', + '-derivedDataPath', + '/tmp/derived-data', + '--verbose', + 'build', + ], + logPrefix: 'iOS Device Build', + silent: true, + timeout: undefined, + }); + });
114-117: Nit: Message wording reads as “Build build succeeded”The prefix “iOS Device Build” followed by action “build succeeded” reads as “iOS Device Build build succeeded.” Consider rephrasing the status strings to avoid repetition (e.g., “iOS Device build succeeded” or “iOS Device Build succeeded”).
Also applies to: 256-262
src/mcp/tools/device/get_device_app_path.ts (2)
16-24: Prefer schema-level default for platform (simplifies logic and metadata)Set the default platform to iOS at the schema level to avoid sprinkling defaults in logic. This also documents the default closer to the input contract.
Apply this diff to baseOptions:
- platform: z - .enum(['iOS', 'watchOS', 'tvOS', 'visionOS']) - .optional() - .describe('Target platform (defaults to iOS)'), + platform: z + .enum(['iOS', 'watchOS', 'tvOS', 'visionOS']) + .default('iOS') + .describe('Target platform (defaults to iOS)'),Optionally, you can then remove the fallback in logic (see Line 56).
96-99: Minor consistency: align executor invocation signature with macOS toolmacOS version passes a fourth argument (
undefined). IfCommandExecutor’s signature expects four params, align here for consistency (no behavior change).Outside the shown range, adjust:
// Before: const result = await executor(command, 'Get App Path', true); // After: const result = await executor(command, 'Get App Path', true, undefined);src/mcp/tools/macos/get_macos_app_path.ts (3)
15-25: Capture defaults at the schema layer (configuration; optional: arch rationale remains)You already default configuration in logic. Prefer declaring defaults in the schema for a tighter contract and cleaner logic.
Apply this diff to set the default for configuration:
- configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), + configuration: z.string().default('Debug').describe('Build configuration (Debug, Release, etc.)'),You can then remove the fallback in logic (Line 62).
89-94: Always pass a macOS destination; makearchan additive qualifierTo reduce host-dependent variability, pass
-destination platform=macOSeven whenarchisn’t specified, and append,arch=…only when provided.Apply this diff:
- // Handle destination for macOS when arch is specified - if (params.arch) { - const destinationString = `platform=macOS,arch=${params.arch}`; - command.push('-destination', destinationString); - } + // Always specify destination for macOS; add arch qualifier when provided + { + let destinationString = 'platform=macOS'; + if (params.arch) { + destinationString += `,arch=${params.arch}`; + } + command.push('-destination', destinationString); + }
46-56: Avoid duplicating platform constants; import shared XcodePlatformLocal XcodePlatform duplicates values available in shared types. Importing the shared enum/const ensures consistency across tools.
Example change (outside the shown range):
// Replace local XcodePlatform const with: import { XcodePlatform } from '../../../types/common.js'; // And use XcodePlatform.macOS in logging
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (3)
src/mcp/tools/device/__tests__/build_device.test.ts(9 hunks)src/mcp/tools/device/get_device_app_path.ts(4 hunks)src/mcp/tools/macos/get_macos_app_path.ts(8 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/mcp/tools/device/__tests__/build_device.test.ts (3)
build-plugins/plugin-discovery.js (1)
result(172-175)src/utils/command.ts (1)
createMockExecutor(267-305)src/mcp/tools/device/build_device.ts (1)
buildDeviceLogic(42-61)
src/mcp/tools/macos/get_macos_app_path.ts (2)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
src/mcp/tools/device/get_device_app_path.ts (2)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
🔇 Additional comments (10)
src/mcp/tools/device/__tests__/build_device.test.ts (2)
54-73: XOR validation coverage is solidNice coverage for neither-provided and both-provided scenarios. This guards the unified surface contract well.
119-136: Workspace happy-path test adds parity with project flowThis validates the workspace execution path successfully. Complements the project path test and ensures both variants behave consistently.
src/mcp/tools/device/get_device_app_path.ts (4)
2-6: Docs accurately reflect unified/XOR inputs — LGTMThe header comment clearly states the unified tool behavior and XOR requirement between projectPath/workspacePath.
34-41: XOR validation between projectPath/workspacePath — LGTMThe refine checks enforce exactly one path is provided. This matches the unification goal and prevents ambiguous invocations.
65-73: Project/workspace flag selection — LGTMCorrectly chooses -project or -workspace and guards against the impossible state post-validation.
108-110: Anchored parsing of build settings — LGTMThe anchored/multiline regex for BUILT_PRODUCTS_DIR and FULL_PRODUCT_NAME is robust for xcodebuild output.
src/mcp/tools/macos/get_macos_app_path.ts (4)
2-6: Docs accurately reflect unified/XOR inputs — LGTMThe header comment aligns with the consolidation goals and clarifies the mutually exclusive inputs.
35-42: XOR validation between projectPath/workspacePath — LGTMThe refine checks ensure exactly one path is provided. Matches the unified tool contract.
70-78: Project/workspace flag selection — LGTMCorrect branching to select -project vs -workspace, with a guard for impossible state after validation.
128-130: Anchored parsing of build settings — LGTMAnchored, multiline regex for BUILT_PRODUCTS_DIR and FULL_PRODUCT_NAME is robust for xcodebuild output.
| export default { | ||
| name: 'get_device_app_path_ws', | ||
| name: 'get_device_app_path', | ||
| description: | ||
| "Gets the app bundle path for a physical device application (iOS, watchOS, tvOS, visionOS) using a workspace. IMPORTANT: Requires workspacePath and scheme. Example: get_device_app_path_ws({ workspacePath: '/path/to/workspace', scheme: 'MyScheme' })", | ||
| schema: getDeviceAppPathWsSchema.shape, // MCP SDK compatibility | ||
| handler: createTypedTool( | ||
| getDeviceAppPathWsSchema, | ||
| get_device_app_path_wsLogic, | ||
| "Gets the app bundle path for a physical device application (iOS, watchOS, tvOS, visionOS) using either a project or workspace. Provide exactly one of projectPath or workspacePath. Example: get_device_app_path({ projectPath: '/path/to/project.xcodeproj', scheme: 'MyScheme' })", | ||
| schema: baseSchemaObject.shape, // MCP SDK compatibility | ||
| handler: createTypedTool<GetDeviceAppPathParams>( | ||
| getDeviceAppPathSchema as z.ZodType<GetDeviceAppPathParams>, | ||
| get_device_app_pathLogic, | ||
| getDefaultCommandExecutor, | ||
| ), | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Schema metadata vs runtime schema: XOR not reflected in exported schema
You export schema: baseSchemaObject.shape (for MCP SDK compatibility), but the handler validates against getDeviceAppPathSchema (with preprocess + XOR). This is fine functionally, but the metadata won’t encode the XOR requirement, which can mislead UIs/docs generated from schema.
- If the MCP layer now supports richer schemas, consider exporting a metadata form that reflects XOR.
- At minimum, keep the description explicit (you already do) and ensure external docs/tests don’t rely solely on
schemato infer XOR.
Run this script to locate any lingering references to pre-unified tool names in docs/tests and double-check descriptions for accuracy:
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
# Scan for old variant names to update references (examples, docs, tests)
rg -nH --no-ignore -e '(_ws|_proj)\b' -e '\blist_schemes_ws\b' -e '\bget_device_app_path_ws\b' -e '\bget_mac(?:os)?_app_path_proj\b' -e '\blaunch_.*_(sim|device)_id\b' -e '\blaunch_.*_(sim|device)_name\b' -e '\binstall_.*_(sim|device)_id\b' -e '\binstall_.*_(sim|device)_name\b' || true
# Specifically check this tool’s mentions
rg -nH --no-ignore -e '\bget_device_app_path_ws\b' -e '\bget_device_app_path_proj\b' || trueLength of output: 10600
Exported schema omits the XOR constraint — update metadata or docs; legacy _ws/_proj references remain
Short: The handler validates using getDeviceAppPathSchema (preprocess + XOR) but the exported metadata is just schema: baseSchemaObject.shape, which does not encode the XOR requirement and can mislead UIs/docs. Either export richer metadata that reflects the XOR or ensure external tooling/docs/tests don’t rely solely on schema.
Files to address
- Primary (change metadata or docs/tests):
- src/mcp/tools/device/get_device_app_path.ts — replace or augment
schema: baseSchemaObject.shapewith metadata that encodes the XOR for projectPath vs workspacePath, or clearly document that the runtime schema enforces XOR.
- src/mcp/tools/device/get_device_app_path.ts — replace or augment
- Confirmed no direct leftover tool variants for this tool:
- No occurrences of get_device_app_path_ws or get_device_app_path_proj were found.
- Legacy _ws/_proj references found elsewhere (examples to update to canonical unified names):
- src/mcp/tools/simulator/list_sims.ts
- src/mcp/tools/simulator/tests/list_sims.test.ts
- src/mcp/tools/simulator/launch_app_sim_name.ts
- src/mcp/tools/device/list_devices.ts
- src/mcp/tools/device/tests/list_devices.test.ts
- src/mcp/tools/project-scaffolding/scaffold_ios_project.ts
- src/mcp/resources/tests/simulators.test.ts
- docs/TOOLS.md
- docs/PHASE1-TASKS.md
- docs/MANUAL_TESTING.md
- docs/PLUGIN_DEVELOPMENT.md
- docs/ARCHITECTURE.md
- docs/TESTING.md
Recommendation (pick one)
- Export a metadata object for the tool that mirrors getDeviceAppPathSchema (including XOR) so generated UIs/docs reflect runtime validation; or
- Keep the current lightweight
schemaexport but make the XOR requirement explicit in the exported description and update any doc-generation tooling/tests to consult the runtime schema instead of metadata.
| export default { | ||
| name: 'get_mac_app_path_proj', | ||
| name: 'get_macos_app_path', | ||
| description: | ||
| "Gets the app bundle path for a macOS application using a project file. IMPORTANT: Requires projectPath and scheme. Example: get_mac_app_path_proj({ projectPath: '/path/to/project.xcodeproj', scheme: 'MyScheme' })", | ||
| schema: getMacAppPathProjSchema.shape, // MCP SDK compatibility | ||
| handler: createTypedTool( | ||
| getMacAppPathProjSchema, | ||
| get_mac_app_path_projLogic, | ||
| "Gets the app bundle path for a macOS application using either a project or workspace. Provide exactly one of projectPath or workspacePath. Example: get_macos_app_path({ projectPath: '/path/to/project.xcodeproj', scheme: 'MyScheme' })", | ||
| schema: baseSchemaObject.shape, // MCP SDK compatibility | ||
| handler: createTypedTool<GetMacosAppPathParams>( | ||
| getMacosAppPathSchema as z.ZodType<GetMacosAppPathParams>, | ||
| get_macos_app_pathLogic, | ||
| getDefaultCommandExecutor, | ||
| ), | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Exported schema shape vs handler schema (XOR not encoded in metadata)
As with the device tool, schema: baseSchemaObject.shape omits preprocess/XOR in metadata. Functionally fine (handler validates with the refined schema), but UIs/docs relying on schema won’t reflect XOR.
Ensure external docs/tests communicate the XOR requirement; consider richer schema metadata if the MCP SDK supports it.
Run this to catch lingering pre-unified names and mismatched descriptions/tests:
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
# Pre-unified macOS tool names
rg -nH --no-ignore -e '\bget_mac_app_path_proj\b' -e '\bget_mac_app_path_ws\b' || true
# General project/workspace and id/name variants across repo
rg -nH --no-ignore -e '(_ws|_proj)\b' -e '\blist_schemes_ws\b' -e '\blist_schemes_proj\b' -e '\bget_device_app_path_ws\b' -e '\blaunch_.*_(sim|device)_id\b' -e '\blaunch_.*_(sim|device)_name\b' || trueLength of output: 10623
Exported schema omits XOR metadata — docs contain pre‑unified names; fixes required
The exported schema uses baseSchemaObject.shape (so UIs/docs won’t see the projectPath vs workspacePath XOR), while the handler still validates the refined schema. A repository search also shows lingering pre‑unified _ws/_proj names in docs/tests that should be cleaned or documented.
Files/locations to address
- src/mcp/tools/macos/get_macos_app_path.ts (lines ~179–189): exports
schema: baseSchemaObject.shape— loses XOR metadata. - docs/ARCHITECTURE.md:300 — references
get_mac_app_path_ws(pre‑unified name). - Representative other hits (sweep required): src/mcp/tools/simulator/list_sims.ts (lines ~120,122), src/mcp/tools/device/list_devices.ts (lines ~392–396), docs/PHASE1-TASKS.md, docs/PLUGIN_DEVELOPMENT.md, docs/MANUAL_TESTING.md, and related tests shown in the search output.
Recommended fixes
- Prefer exporting the refined schema (or attach richer metadata) so the projectPath/workspacePath XOR is encoded in the exported schema if the MCP SDK supports it — e.g., expose the same schema used by the handler.
- If the SDK cannot support richer schema metadata, update external docs/CLI help and tests to explicitly require “exactly one of projectPath or workspacePath.”
- Sweep docs/tests to remove or update pre‑unified
_ws/_projtool names (or add compatibility notes/aliases) so examples and docs match the unified tool interface.
🤖 Prompt for AI Agents
In src/mcp/tools/macos/get_macos_app_path.ts around lines 179–189, the exported
schema uses baseSchemaObject.shape which strips the XOR metadata for projectPath
vs workspacePath; replace the exported schema with the refined Zod schema used
by the handler (the same schema passed into createTypedTool — e.g., export
getMacosAppPathSchema or attach that richer schema/metadata) so the XOR
constraint is preserved for UIs/docs; if the MCP SDK cannot consume richer Zod
metadata, add explicit metadata or a wrapper that documents "exactly one of
projectPath or workspacePath" and then sweep and update docs/tests to remove
pre‑unified names (change get_mac_app_path_ws -> get_macos_app_path and update
examples/tests in docs/ARCHITECTURE.md, PHASE1-TASKS.md, PLUGIN_DEVELOPMENT.md,
MANUAL_TESTING.md and related test files) or add compatibility aliases where
necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (1)
src/mcp/tools/macos/build_run_macos.ts (1)
114-124: Make app path extraction resilient when multiple targets are printedxcodebuild -showBuildSettings often prints settings for multiple targets/configurations. The first match might not belong to the app target. Prefer selecting the last BUILT_PRODUCTS_DIR and pick a FULL_PRODUCT_NAME that ends with .app.
- const buildSettingsOutput = result.output; - const builtProductsDirMatch = buildSettingsOutput.match(/^\s*BUILT_PRODUCTS_DIR\s*=\s*(.+)$/m); - const fullProductNameMatch = buildSettingsOutput.match(/^\s*FULL_PRODUCT_NAME\s*=\s*(.+)$/m); - - if (!builtProductsDirMatch || !fullProductNameMatch) { + const buildSettingsOutput = result.output; + const builtProductsDirMatches = [ + ...buildSettingsOutput.matchAll(/^\s*BUILT_PRODUCTS_DIR\s*=\s*(.+)$/mg), + ]; + const fullProductNameMatches = [ + ...buildSettingsOutput.matchAll(/^\s*FULL_PRODUCT_NAME\s*=\s*(.+)$/mg), + ]; + const builtProductsDir = builtProductsDirMatches.at(-1)?.[1].trim(); + // Prefer an .app product; fall back to the last occurrence if none matches + const fullProductName = + fullProductNameMatches.map((m) => m[1].trim()).find((n) => n.endsWith('.app')) ?? + fullProductNameMatches.at(-1)?.[1].trim(); + + if (!builtProductsDir || !fullProductName) { return { success: false, error: 'Could not extract app path from build settings' }; } - const appPath = `${builtProductsDirMatch[1].trim()}/${fullProductNameMatch[1].trim()}`; + const appPath = `${builtProductsDir}/${fullProductName}`;If you want to be even more precise, consider parsing per-target “Build settings for action … target …” blocks and selecting the one with WRAPPER_EXTENSION = app.
♻️ Duplicate comments (1)
src/mcp/tools/macos/build_run_macos.ts (1)
211-214: Expose XOR to MCP consumers (shape omits refine) — choose Option A or BYou export baseSchemaObject.shape for MCP compatibility, which drops the XOR refinements. This can mislead clients and defer errors to runtime handler validation.
Option A — Preferable if MCP accepts Zod schemas:
- Export the refined schema to encode XOR to consumers.
- schema: baseSchemaObject.shape, // MCP SDK compatibility + schema: buildRunMacOSSchema, // includes XOR refinementsOption B — If MCP requires a plain shape:
- Keep shape, but add explicit machine-readable metadata alongside a stronger description. For example, add a metadata field your MCP layer can read:
metadata: { constraints: { xor: [['projectPath', 'workspacePath']] } }- If adding metadata isn’t supported, at minimum, keep the current explicit description (good) and surface XOR in any generated docs.
To verify SDK capability and catch any remaining pre-unified names across the repo, run:
#!/bin/bash set -euo pipefail echo "Can MCP accept full Zod schemas? Grep for prior usage patterns:" rg -n --hidden --glob '!*node_modules*' --glob '!*.lock' -e 'schema:\s*buildRunMacOSSchema' -e 'schema:\s*\w+Schema' src || true echo echo "Search for pre-unified tool names still lingering:" rg -n --hidden --glob '!*node_modules*' --glob '!*.lock' \ -e 'list_schemes_ws' \ -e 'list_schemes_proj' \ -e 'build_run_mac_proj' \ -e 'build_run_macos_ws' \ -e 'get_macos_app_path_proj' \ -e 'get_macos_app_path_ws' \ -e 'run_ios_simulator_by_name' \ -e 'run_ios_simulator_by_id' \ -e 'boot_simulator_by_name' \ -e 'boot_simulator_by_id' \ || echo "No pre-unified names found."
🧹 Nitpick comments (3)
src/mcp/tools/macos/build_run_macos.ts (3)
17-36: Strengthen schema: enforce non-empty scheme, add safe defaults, and validate path extensionsCurrent validation allows empty scheme and defers defaults to call sites. Making these constraints explicit in the schema improves UX and removes duplication elsewhere. Also consider validating file extensions to catch common input errors early.
Apply this diff to the base schema:
-const baseSchemaObject = z.object({ - projectPath: z.string().optional().describe('Path to the .xcodeproj file'), - workspacePath: z.string().optional().describe('Path to the .xcworkspace file'), - scheme: z.string().describe('The scheme to use'), - configuration: z.string().optional().describe('Build configuration (Debug, Release, etc.)'), +const baseSchemaObject = z.object({ + projectPath: z + .string() + .optional() + .refine((p) => p === undefined || p.endsWith('.xcodeproj'), { + message: 'projectPath must point to a .xcodeproj file', + }) + .describe('Path to the .xcodeproj file'), + workspacePath: z + .string() + .optional() + .refine((p) => p === undefined || p.endsWith('.xcworkspace'), { + message: 'workspacePath must point to a .xcworkspace file', + }) + .describe('Path to the .xcworkspace file'), + scheme: z.string().min(1, 'scheme cannot be empty').describe('The scheme to use'), + configuration: z.string().default('Debug').describe('Build configuration (Debug, Release, etc.)'), derivedDataPath: z .string() .optional() .describe('Path where build products and other derived data will go'), arch: z .enum(['arm64', 'x86_64']) .optional() .describe('Architecture to build for (arm64 or x86_64). For macOS only.'), extraArgs: z.array(z.string()).optional().describe('Additional xcodebuild arguments'), - preferXcodebuild: z - .boolean() - .optional() - .describe('If true, prefers xcodebuild over the experimental incremental build system'), + preferXcodebuild: z + .boolean() + .default(false) + .describe('If true, prefers xcodebuild over the experimental incremental build system'), });
59-69: Remove duplicated defaults; rely on schema defaultsConfiguration and preferXcodebuild defaults are set both in the schema and again in logic/wrapper. Centralize defaults in the schema to reduce drift.
return executeXcodeBuildCommand( { - ...params, - configuration: params.configuration ?? 'Debug', + ...params, }, { platform: XcodePlatform.macOS, arch: params.arch, logPrefix: 'macOS Build', }, - params.preferXcodebuild ?? false, + params.preferXcodebuild, 'build', executor, );- buildRunMacOSLogic( - { - ...params, - configuration: params.configuration ?? 'Debug', - preferXcodebuild: params.preferXcodebuild ?? false, - }, - executor, - ), + buildRunMacOSLogic(params, executor),Also applies to: 215-222
170-170: Minor: consider logging stdout/stderr on launch failure for diagnosticsIf executor provides separate stdout/stderr, appending them to the failure response can speed up troubleshooting when launch fails.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these settings in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/mcp/tools/macos/build_run_macos.ts(8 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/mcp/tools/macos/build_run_macos.ts (2)
src/utils/schema-helpers.ts (1)
nullifyEmptyStrings(14-24)src/utils/typed-tool-factory.ts (1)
createTypedTool(29-60)
🔇 Additional comments (3)
src/mcp/tools/macos/build_run_macos.ts (3)
15-15: Good reuse: shared nullifyEmptyStrings helperImporting the shared nullifyEmptyStrings removes duplication and aligns with DRY. Nice.
38-46: LGTM: XOR (one-of-one) validation is enforcedThe two-step refine accurately encodes “exactly one of projectPath or workspacePath.” This is correct at runtime.
2-6: Docs: unified-name usage verified — no action requiredSearch found updated examples and test hints using the unified names (build_run_macos, build_macos, get_macos_app_path); no remaining outdated references detected.
Key locations checked:
- docs/TOOLS.md, docs/PHASE1-TASKS.md
- src/utils/build-utils.ts
- src/mcp/tools/macos/{build_run_macos.ts, build_macos.ts, get_macos_app_path.ts}
- src/mcp/tools/macos/tests/{build_run_macos.test.ts, build_macos.test.ts, get_macos_app_path.test.ts}
- src/mcp/tools/project-scaffolding/scaffold_macos_project.ts and its tests
This commit implements comprehensive tool hint optimization and naming consistency improvements: ## Major Tool Hint Optimizations (~75% context reduction) - **build-utils.ts**: Reduced verbose Next Steps from ~15 lines to 4 concise lines - **Removed redundant explanations**: Eliminated mutual exclusivity descriptions already covered in schemas - **Parameter consistency**: Dynamic parameter detection respects user's original choices (simulatorId vs simulatorName, projectPath vs workspacePath) - **Context efficiency**: Focused hints on actionable next steps without repeating validation rules ## Tool Naming Consistency & Unification - **Simulator tools**: Renamed to shorter, consistent patterns: - `build_simulator` → `build_sim` - `build_run_simulator` → `build_run_sim` - `test_simulator` → `test_sim` - `get_simulator_app_path` → `get_sim_app_path` - `reset_simulator_location` → `reset_sim_location` - `set_simulator_location` → `set_sim_location` - **macOS tools**: Unified naming conventions: - `get_macos_app_path` → `get_mac_app_path` - **Removed duplicate tools**: Eliminated redundant name-based variants (`launch_app_sim_name`, `stop_app_sim_name`) ## Parameter Consistency Fixes - **launch_app_sim**: Fixed to use dynamic parameter names in hints matching user input - **list_devices**: Verified parameter-neutral hints without static preferences - **build-utils**: Implemented dynamic simulator parameter detection ## Test Updates & Validation - Updated 8+ test files to match new concise hint patterns - Added parameter consistency test for launch_app_sim - All 1046 tests passing - Build verification successful ## Tool Documentation Artifacts - Added comprehensive tool naming verification documentation - Maintains compatibility with existing MCP protocol interfaces This optimization significantly reduces context consumption while maintaining full functionality and improving user experience through consistent, focused tool hints.
|
Claude finished @cameroncooke's task —— View job I'll analyze this and get back to you. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
🔭 Outside diff range comments (4)
src/mcp/tools/macos/__tests__/build_run_macos.test.ts (1)
469-476: Mismatch with implementation: error label should read “macOS build and run”, not “macOS Build build”.buildRunMacOSLogic wraps the entire flow and on thrown errors produces:
"Error during macOS build and run: "Update the expectation accordingly.
Apply this diff:
- expect(result).toEqual({ - content: [ - { type: 'text', text: 'Error during macOS Build build: spawn xcodebuild ENOENT' }, - ], - isError: true, - }); + expect(result).toEqual({ + content: [ + { type: 'text', text: 'Error during macOS build and run: spawn xcodebuild ENOENT' }, + ], + isError: true, + });src/mcp/tools/simulator-management/set_sim_location.ts (1)
1-126: Docs & help-texts still reference pre-unified tool names — please fix.Short: I ran a repo-wide search for legacy tool names and found leftover references in documentation and some CLI/help descriptions. The code tools themselves appear renamed, but the docs and a few help strings need updating.
Files to update (examples from search):
- docs/TOOLS.md (lines ~33–44) — mentions build_run_simulator, build_simulator, get_simulator_app_path, get_macos_app_path, reset_simulator_location, set_simulator_location, launch_app_sim_name, stop_app_sim_name, test_simulator.
- docs/PHASE1-TASKS.md — task list still references several old names (build_simulator*, build_run_simulator*, get_macos_app_path, get_simulator_app_path*, test_simulator*, etc.).
- TOOL_NAMING_VERIFICATION_2025-08-14_22-13.md(.bak) — verification artifact contains the old names and the mapping to new names (useful as a mapping reference).
- src (help/describe strings) — update if the referenced helper tool was renamed:
- src/mcp/tools/logging/start_sim_log_cap.ts (line ~17)
- src/mcp/tools/simulator/boot_sim.ts (line ~10)
- src/mcp/tools/simulator/launch_app_sim.ts (line ~13)
- src/mcp/tools/simulator/install_app_sim.ts (line ~16)
- src/mcp/tools/simulator/stop_app_sim.ts (line ~12)
- src/mcp/tools/simulator/launch_app_logs_sim.ts (line ~24)
- src/mcp/tools/simulator-management/reset_sim_location.ts (line ~11)
- src/mcp/tools/simulator-management/set_sim_location.ts (line ~10)
- src/mcp/tools/simulator-management/sim_statusbar.ts (line ~10)
- src/mcp/tools/simulator-management/set_sim_appearance.ts (line ~10)
Actionable next steps:
- Replace old names in docs with the unified names (e.g., build_sim, build_run_sim, test_sim, get_sim_app_path, get_mac_app_path, reset_sim_location, set_sim_location) and remove references to deleted tools (launch_app_sim_name, stop_app_sim_name). See TOOL_NAMING_VERIFICATION_2025-08-14_22-13.md for mapping.
- Update or confirm user-facing .describe(...) strings if the referenced helper tool (e.g., list_simulators) was renamed.
eslint.config.js (2)
30-33: 'never' patterns unintentionally ignore variables containing “never”argsIgnorePattern/varsIgnorePattern are regexes. Using 'never' will ignore any variable whose name contains “never” (e.g., neverUsed), which is likely accidental. Use a regex that matches nothing.
- '@typescript-eslint/no-unused-vars': ['error', { - argsIgnorePattern: 'never', - varsIgnorePattern: 'never' - }], + '@typescript-eslint/no-unused-vars': ['error', { + argsIgnorePattern: '^$', + varsIgnorePattern: '^$' + }],
97-116: Unify formatting and environment in tests
- Apply Prettier in tests to keep formatting enforcement consistent.
- Add ecmaVersion/sourceType for parity with other blocks.
- Add Node globals if tests rely on Node (common in CLI/tooling repos).
{ files: ['**/*.test.ts'], languageOptions: { - parser: tseslint.parser, + parser: tseslint.parser, + ecmaVersion: 2020, + sourceType: 'module', + globals: { ...globals.node }, parserOptions: { project: './tsconfig.test.json', }, }, + plugins: { + 'prettier': prettierPlugin, + }, rules: { + 'prettier/prettier': 'error', '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-unused-vars': 'off', '@typescript-eslint/explicit-function-return-type': 'off', 'prefer-const': 'off',Note: The added globals import is already suggested above.
♻️ Duplicate comments (4)
src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts (2)
434-436: NextSteps updated to unified sim tooling — keep naming/param consistency across the suiteSame verification as above applies here for the customized names path. Ensure
build_sim/build_run_simandsimulatorNamealign with the unified tool API and docs.
469-471: NextSteps updated for customizeNames=false — verify against unified API and docsSame checks as Lines 391–393: confirm canonical tool names and parameter naming are consistent and that no legacy tool names persist in code or documentation.
src/mcp/tools/device/__tests__/build_device.test.ts (1)
138-188: Workspace command-generation test added as requested — LGTMThis directly addresses the earlier review request to assert -workspace usage and expected args. Thanks for adding this; it increases confidence in the unified path.
src/mcp/tools/project-discovery/show_build_settings.ts (1)
86-90: Past typo resolved: references the unified list_schemes toolThe previous “list_schems_ws” typo flagged earlier is corrected here to the unified list_schemes reference in the Next Steps.
🧹 Nitpick comments (41)
src/mcp/tools/project-discovery/get_mac_bundle_id.ts (2)
97-99: Escape appPath in the example to avoid malformed output if the path contains quotesMinor robustness: render the appPath using JSON.stringify so sample command stays valid even when the path contains quotes/spaces.
Apply this diff:
- text: `Next Steps: -- Launch: launch_mac_app({ appPath: "${appPath}" }) -- Build again: build_macos({ scheme: "SCHEME_NAME" })`, + text: `Next Steps: + - Launch: launch_mac_app({ appPath: ${JSON.stringify(appPath)} }) + - Build again: build_macos({ scheme: "SCHEME_NAME" })`,
125-127: Nit: description mentions mcp0_get_macos_bundle_id (macos) while the tool is get_mac_bundle_id (mac)For consistency, prefer “…prefixed as mcp0_get_mac_bundle_id.”
Apply this diff:
- "Extracts the bundle identifier from a macOS app bundle (.app). IMPORTANT: You MUST provide the appPath parameter. Example: get_mac_bundle_id({ appPath: '/path/to/your/app.app' }) Note: In some environments, this tool may be prefixed as mcp0_get_macos_bundle_id.", + "Extracts the bundle identifier from a macOS app bundle (.app). IMPORTANT: You MUST provide the appPath parameter. Example: get_mac_bundle_id({ appPath: '/path/to/your/app.app' }) Note: In some environments, this tool may be prefixed as mcp0_get_mac_bundle_id.",If you want, I can run a repo-wide scan and patch any remaining “macos” vs “mac” inconsistencies in tool names/descriptions.
src/mcp/tools/device/__tests__/index.test.ts (1)
1-3: Update test naming to drop “device-project” terminologyThe file header and describe title still reference the pre-unification variant.
- * Tests for device-project workflow metadata + * Tests for device workflow metadata-describe('device-project workflow metadata', () => { +describe('device workflow metadata', () => {Also applies to: 7-7
src/mcp/tools/device/list_devices.ts (4)
2-6: Header still says “Device Workspace Plugin”Doc header should reflect the unified device tooling.
- * Device Workspace Plugin: List Devices + * Device Tool: List Devices
336-342: Remove unreachable “Paired but Not Connected” sectionState is never set to “Paired (not connected)”, so this group stays empty. Either reintroduce that state or remove the block. Simplest: delete the group.
- const pairedDevices = uniqueDevices.filter((d) => d.state === 'Paired (not connected)');- if (pairedDevices.length > 0) { - responseText += '🔗 Paired but Not Connected:\n'; - for (const device of pairedDevices) { - responseText += `\n📱 ${device.name}\n`; - responseText += ` UDID: ${device.identifier}\n`; - responseText += ` Model: ${device.model ?? 'Unknown'}\n`; - responseText += ` Platform: ${device.platform} ${device.osVersion ?? ''}\n`; - } - responseText += '\n'; - }Also applies to: 364-373
321-324: Deduplicate in O(n) using a MapThe current filter+findIndex approach is O(n^2). Use a Map for linear-time deduplication.
- const uniqueDevices = devices.filter( - (device, index, self) => index === self.findIndex((d) => d.identifier === device.identifier), - ); + const uniqueDevices = Array.from(new Map(devices.map((d) => [d.identifier, d])).values());
43-43: Add a type to the devices collection for stronger safetyAvoids any[]. Keeps downstream formatting code type-safe.
- const devices = []; + const devices: Array<{ + name: string; + identifier: string; + platform: string; + model?: string; + osVersion?: string; + state: string; + connectionType?: string; + trustState?: string; + developerModeStatus?: string; + productType?: string; + cpuArchitecture?: string; + }> = [];src/mcp/tools/device/__tests__/list_devices.test.ts (1)
3-7: Modernize test comments to remove “device-workspace” referencesComments still reference pre-unified modules and re-export paths.
- * Tests for list_devices plugin (device-shared) - * This tests the re-exported plugin from device-workspace + * Tests for list_devices plugin (device) + * Validates the exported tool metadata and command generation @@ - * Note: This is a re-export test. Comprehensive handler tests are in device-workspace/list_devices.test.ts + * Note: Handler logic is covered in dedicated unit tests for the device toolsetIf there are other test headers referencing “device-workspace”, I can sweep and update them consistently.
src/mcp/tools/macos/__tests__/build_macos.test.ts (2)
74-82: Nit: user-facing text reads “macOS Build build …” (duplicate “build”)Messages like “✅ macOS Build build succeeded …” and “Error during macOS Build build …” are awkward. Consider changing either the logPrefix or the message template to avoid the duplication (e.g., “macOS build succeeded …” or “macOS Build succeeded …”). If you adopt the change in the implementation, update these expectations accordingly.
Apply this test diff if you decide to remove the duplication in the implementation:
- text: '✅ macOS Build build succeeded for scheme MyScheme.', + text: '✅ macOS build succeeded for scheme MyScheme.', @@ - text: '❌ macOS Build build failed for scheme MyScheme.', + text: '❌ macOS build failed for scheme MyScheme.', @@ - text: '✅ macOS Build build succeeded for scheme MyScheme.', + text: '✅ macOS build succeeded for scheme MyScheme.', @@ - text: 'Error during macOS Build build: Network error', + text: 'Error during macOS build: Network error', @@ - text: 'Error during macOS Build build: Spawn error', + text: 'Error during macOS build: Spawn error',Also applies to: 106-111, 168-171, 194-196
381-412: Add a workspace “all parameters” command-generation case (parity with project flow)You have a minimal workspace case. For parity, add a test that exercises workspace + configuration + arch + derivedDataPath + extraArgs to ensure both code paths stay in sync.
Apply this diff to extend the workspace coverage:
it('should generate correct xcodebuild workspace command with minimal parameters', async () => { @@ expect(capturedCommand).toEqual([ @@ ]); }); + + it('should generate correct xcodebuild workspace command with all parameters', async () => { + let capturedCommand: string[] = []; + const mockExecutor = createMockExecutor({ success: true, output: 'BUILD SUCCEEDED' }); + + const spyExecutor = async (command: string[]) => { + capturedCommand = command; + return mockExecutor(command); + }; + + await buildMacOSLogic( + { + workspacePath: '/path/to/workspace.xcworkspace', + scheme: 'MyScheme', + configuration: 'Release', + arch: 'arm64', + derivedDataPath: '/custom/derived', + extraArgs: ['--verbose'], + preferXcodebuild: true, + }, + spyExecutor, + ); + + expect(capturedCommand).toEqual([ + 'xcodebuild', + '-workspace', + '/path/to/workspace.xcworkspace', + '-scheme', + 'MyScheme', + '-configuration', + 'Release', + '-skipMacroValidation', + '-destination', + 'platform=macOS,arch=arm64', + '-derivedDataPath', + '/custom/derived', + '--verbose', + 'build', + ]); + });src/mcp/tools/device/__tests__/build_device.test.ts (4)
18-20: Consider expanding the description example to include workspace usageThe description is accurate for the unified surface. Optionally, include a workspace example to make both flows explicit.
If you choose to update the plugin description accordingly, mirror that here:
- Example: build_device({ workspacePath: '/path/to/MyProject.xcworkspace', scheme: 'MyScheme' })
28-51: Schema coverage is solid; consider validating file extensions for project/workspace pathsYou validate presence and types. Consider enforcing valid Xcode file extensions at the schema level for stronger guarantees (e.g., projectPath must end with .xcodeproj, workspacePath must end with .xcworkspace). This would catch misconfigurations earlier.
Outside this test file, you could implement something akin to:
// In build_device.ts schema (illustrative) projectPath: z .string() .endsWith('.xcodeproj', { message: 'projectPath must point to a .xcodeproj' }) .optional(), workspacePath: z .string() .endsWith('.xcworkspace', { message: 'workspacePath must point to a .xcworkspace' }) .optional(),If you add these constraints, I can update tests to cover invalid extensions as well.
322-355: Optional params (Release, derivedDataPath, extraArgs) are asserted — add workspace parityYou’ve covered optional params for the -project path. Consider a symmetric test for the -workspace path to fully exercise both branches.
Apply the following to add a workspace variant right after this test:
+ it('should include optional parameters in command (workspace)', async () => { + const commandCalls: Array<{ + args: string[]; + logPrefix: string; + silent: boolean; + timeout: number | undefined; + }> = []; + + const stubExecutor = async ( + args: string[], + logPrefix: string, + silent: boolean, + timeout?: number, + ) => { + commandCalls.push({ args, logPrefix, silent, timeout }); + return { + success: true, + output: 'Build succeeded', + error: undefined, + process: { pid: 12345 }, + }; + }; + + await buildDeviceLogic( + { + workspacePath: '/path/to/MyProject.xcworkspace', + scheme: 'MyScheme', + configuration: 'Release', + derivedDataPath: '/tmp/derived-data', + extraArgs: ['--verbose'], + }, + stubExecutor, + ); + + expect(commandCalls).toHaveLength(1); + expect(commandCalls[0]).toEqual({ + args: [ + 'xcodebuild', + '-workspace', + '/path/to/MyProject.xcworkspace', + '-scheme', + 'MyScheme', + '-configuration', + 'Release', + '-skipMacroValidation', + '-destination', + 'generic/platform=iOS', + '-derivedDataPath', + '/tmp/derived-data', + '--verbose', + 'build', + ], + logPrefix: 'iOS Device Build', + silent: true, + timeout: undefined, + }); + });
138-159: DRY the capturing stub executor across testsThe stub executor used to capture args/log settings is repeated in three places. Extract a tiny helper within this file (or a shared test util) to reduce duplication and improve readability.
For example:
function createCapturingExecutor() { const calls: Array<{ args: string[]; logPrefix: string; silent: boolean; timeout: number | undefined; }> = []; const executor = async (args: string[], logPrefix: string, silent: boolean, timeout?: number) => { calls.push({ args, logPrefix, silent, timeout }); return { success: true, output: 'Build succeeded', error: undefined, process: { pid: 12345 } }; }; return { calls, executor }; } // Usage: // const { calls, executor } = createCapturingExecutor(); // await buildDeviceLogic({ ... }, executor); // expect(calls[0]).toEqual({ ... });Also applies to: 198-212, 307-320
src/mcp/tools/project-discovery/show_build_settings.ts (1)
68-75: Unify the success header to always include the schemeCurrently, the header omits the scheme when using a workspace. For consistency, include the scheme in both cases.
Apply this diff:
- const content: Array<{ type: 'text'; text: string }> = [ - { - type: 'text', - text: hasProjectPath - ? `✅ Build settings for scheme ${params.scheme}:` - : '✅ Build settings retrieved successfully', - }, + const content: Array<{ type: 'text'; text: string }> = [ + { + type: 'text', + text: `✅ Build settings for scheme ${params.scheme}:`, + },src/mcp/tools/project-discovery/list_schemes.ts (3)
65-74: Guard against undefined output before regex matchCommandExecutor results generally include output, but to be defensive (and avoid a potential TypeError on match), compute against result.output ?? ''.
Apply this diff:
- // Extract schemes from the output - const schemesMatch = result.output.match(/Schemes:([\s\S]*?)(?=\n\n|$)/); + // Extract schemes from the output + const output = result.output ?? ''; + const schemesMatch = output.match(/Schemes:([\s\S]*?)(?=\n\n|$)/);
80-81: Remove outdated comment about future unificationThese references are already unified in this PR. The comment is now stale.
Apply this diff:
- // Note: After Phase 2, these will be unified tool names too
111-121: Enhance description with a workspace example to reflect unificationMinor docs polish: add a workspacePath example to match the tool’s unified nature.
Apply this diff:
- description: - "Lists available schemes for either a project or a workspace. Provide exactly one of projectPath or workspacePath. Example: list_schemes({ projectPath: '/path/to/MyProject.xcodeproj' })", + description: + "Lists available schemes for either a project or a workspace. Provide exactly one of projectPath or workspacePath. Examples: list_schemes({ projectPath: '/path/to/MyProject.xcodeproj' }) and list_schemes({ workspacePath: '/path/to/MyProject.xcworkspace' })",src/mcp/tools/project-discovery/__tests__/list_schemes.test.ts (1)
269-335: Workspace flow test coverage is solid; consider adding a negative caseYou have positive workspace flow and command-generation tests. Consider a negative workspace case with an output containing no “Schemes:” section to mirror the project negative test, ensuring consistent behavior across both inputs.
src/mcp/tools/macos/__tests__/build_run_macos.test.ts (5)
116-175: Success path builds: verified command line for project flow; consider asserting the launch step.The first two executor calls are asserted (build + showBuildSettings). Since buildRunMacOSLogic launches the app via open, also assert the 3rd call to ensure we verify the final action.
Apply this diff to add the assertion after the build settings check:
// Verify build settings command was called expect(executorCalls[1]).toEqual({ @@ }); + + // Verify app launch command was called + expect(executorCalls[2]).toEqual({ + command: ['open', '/path/to/build/MyApp.app'], + description: 'Launch macOS App', + logOutput: true, + timeout: undefined, + });
193-209: Overly brittle full-structure equality and “Next Steps” messaging.
- Asserting the entire ToolResponse structure makes tests fragile to benign text tweaks. Prefer partial/contains assertions for messages to reduce churn.
- The presence of “Next Steps” in a combined build+run tool can be confusing (they look like follow-up actions, yet the tool already launches the app). If intentional (coming from the build step), this is fine—but consider dropping or tailoring those messages for the unified flow.
No action required if this is by design; flagging for awareness.
212-306: Success path builds (workspace flow): assert the launch step as well.Same rationale as the project flow—add an assertion for the third executor call (open).
Apply this diff after the build settings assertion:
// Verify build settings command was called expect(executorCalls[1]).toEqual({ @@ }); + + // Verify app launch command was called + expect(executorCalls[2]).toEqual({ + command: ['open', '/path/to/build/MyApp.app'], + description: 'Launch macOS App', + logOutput: true, + timeout: undefined, + });
479-537: Test name vs. setup mismatch: you pass configuration despite the title saying “when not provided”.To actually test the defaulting behavior, omit configuration from args.
Apply this diff:
- const args = { - projectPath: '/path/to/project.xcodeproj', - scheme: 'MyApp', - configuration: 'Debug', - preferXcodebuild: false, - }; + const args = { + projectPath: '/path/to/project.xcodeproj', + scheme: 'MyApp', + preferXcodebuild: false, + };(Keep the expectation the same if the default is 'Debug'.)
116-210: Optional: strengthen coverage for arch, derivedDataPath, extraArgs, and preferXcodebuild.Right now command assertions don’t exercise these flags. Consider adding a case that supplies:
- arch: 'arm64'
- derivedDataPath: '/tmp/DerivedData'
- extraArgs: ['-UseNewBuildSystem=YES']
- preferXcodebuild: true (if this changes invocation behavior)
Then assert the exact build and showBuildSettings commands for both project and workspace flows.
Also applies to: 212-306
TOOL_NAMING_VERIFICATION_2025-08-14_22-13.md (2)
43-45: Fix duplicate “Detailed Test Results” heading (MD024).Remove the first duplicate heading to satisfy markdownlint and improve readability.
Apply this diff:
-## Detailed Test Results -[Updated as tests are completed] -
1-209: Move timestamped verification report under docs/ or reports/ to avoid repo root churn.Placing time-stamped verification artifacts at the repo root increases noise over time. Consider relocating to
docs/reports/(or similar) and/or adding a stable, rolling report file.src/mcp/tools/simulator-management/set_sim_location.ts (2)
8-12: Update schema description to reference unified tool names.The description mentions “list_simulators”; per this PR, it should reference the unified tool name (likely
list_sims).Apply this diff:
- simulatorUuid: z - .string() - .describe('UUID of the simulator to use (obtained from list_simulators)'), + simulatorUuid: z + .string() + .describe('UUID of the simulator to use (obtained from list_sims)'),
18-28: Optional: prefer Zod range constraints over custom extraValidation.Encoding latitude/longitude ranges in the Zod schema would centralize validation and produce consistent errors via
createTypedTool. This is optional since tests assert current custom messages.If you choose to migrate, here’s a minimal schema tweak (will require updating tests to expect Zod error formatting):
-const setSimulatorLocationSchema = z.object({ - simulatorUuid: z - .string() - .describe('UUID of the simulator to use (obtained from list_sims)'), - latitude: z.number().describe('The latitude for the custom location.'), - longitude: z.number().describe('The longitude for the custom location.'), -}); +const setSimulatorLocationSchema = z.object({ + simulatorUuid: z + .string() + .describe('UUID of the simulator to use (obtained from list_sims)'), + latitude: z.number().min(-90).max(90).describe('The latitude for the custom location.'), + longitude: z.number().min(-180).max(180).describe('The longitude for the custom location.'), +});If you proceed, run tests to capture and update any message expectations that rely on the current custom validation.
Also applies to: 71-98, 104-114
src/mcp/tools/simulator-management/reset_sim_location.ts (3)
9-12: Update schema description to reference the unified tool name.Swap “list_simulators” for the unified “list_sims” in the param description.
Apply this diff:
- simulatorUuid: z - .string() - .describe('UUID of the simulator to use (obtained from list_simulators)'), + simulatorUuid: z + .string() + .describe('UUID of the simulator to use (obtained from list_sims)'),
3-5: Minor: consolidate imports from utils into a single statement.Not a functional issue, but merging these two imports improves consistency (matches set_sim_location.ts).
Apply this diff:
-import { log } from '../../../utils/index.ts'; -import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.ts'; +import { log, CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.ts';
17-27: Align helper signature with set_sim_location.ts for consistency.Make
extraValidationreturnToolResponse | nulland (optionally) default theexecutortogetDefaultCommandExecutor()for parity with the other module.Apply this diff:
-async function executeSimctlCommandAndRespond( +async function executeSimctlCommandAndRespond( params: ResetSimulatorLocationParams, simctlSubCommand: string[], operationDescriptionForXcodeCommand: string, successMessage: string, failureMessagePrefix: string, operationLogContext: string, - executor: CommandExecutor, - extraValidation?: () => ToolResponse | undefined, + executor: CommandExecutor = getDefaultCommandExecutor(), + extraValidation?: () => ToolResponse | null, ): Promise<ToolResponse> { if (extraValidation) { const validationResult = extraValidation(); if (validationResult) { return validationResult; } }src/mcp/tools/simulator-management/__tests__/set_sim_location.test.ts (1)
12-76: Optional: add an integration-style test for the default export’s handler.You already validate the schema shape and call the logic directly. Add a small test invoking
setSimLocation.handler({...})to exercise thecreateTypedToolpath (runtime parse and execution).Example:
it('handler should parse and call logic successfully', async () => { const mockExecutor = createMockExecutor({ success: true, output: 'ok' }); const res = await setSimLocation.handler({ simulatorUuid: 'uuid', latitude: 1, longitude: 2, }); expect(res).toEqual({ content: [{ type: 'text', text: 'Successfully set simulator uuid location to 1,2' }], }); });eslint.config.js (3)
13-13: Future-proof src glob to include TSX (if it appears later)If any TSX sneaks into src (even non-React), this will miss it. Safe to include now.
- files: ['src/**/*.ts'], + files: ['src/**/*.{ts,tsx}'],
75-94: Optional: avoid parsing .js with the TS parser for performance/accuracyUsing @typescript-eslint/parser for .js works but is slower and can be surprising. Consider splitting scripts into two blocks: one for TS with the TS parser and one for JS using the default parser (or rely on @eslint/js recommended). This keeps JS on the fastest path and TS on the type-aware path you intend.
12-20: Consider leveraging typescript-eslint’s type-checked presetsSince you’re opting into type info in src and opting out in scripts, the following could reduce maintenance:
- In src block: start from tseslint.configs.recommendedTypeChecked (or strictTypeChecked if desired) and then override deltas.
- In scripts block: start from tseslint.configs.disableTypeChecked to ensure no type-aware rules sneak in.
This preserves your intent but with less manual rule curation.
Also applies to: 61-95
src/mcp/tools/macos/__tests__/get_mac_app_path.test.ts (4)
11-11: Prefer “tool” over “plugin” for naming consistency.Project-wide terminology uses “tool”; update the describe label to avoid confusion.
-describe('get_mac_app_path plugin', () => { +describe('get_mac_app_path tool', () => {
17-21: Literal description equality is brittle; consider partial match.Exact string equality on descriptions tends to break on minor copy edits. Using a contains or regex match keeps intent while reducing churn.
- expect(getMacAppPath.description).toBe( - "Gets the app bundle path for a macOS application using either a project or workspace. Provide exactly one of projectPath or workspacePath. Example: get_mac_app_path({ projectPath: '/path/to/project.xcodeproj', scheme: 'MyScheme' })", - ); + expect(getMacAppPath.description).toContain('Gets the app bundle path for a macOS application'); + expect(getMacAppPath.description).toContain('projectPath or workspacePath');
154-195: Workspace all-params command: consider adding derivedDataPath/extraArgs coverage.This case validates arch and configuration. To fully mirror the project “all parameters” test, add a workspace variant that includes derivedDataPath and extraArgs to ensure parity.
it('should generate correct command with workspace all parameters', async () => { @@ - const args = { - workspacePath: '/path/to/MyProject.xcworkspace', - scheme: 'MyScheme', - configuration: 'Release', - arch: 'arm64', - }; + const args = { + workspacePath: '/path/to/MyProject.xcworkspace', + scheme: 'MyScheme', + configuration: 'Release', + arch: 'arm64', + derivedDataPath: '/path/to/derived', + extraArgs: ['--verbose'], + }; @@ - [ + [ 'xcodebuild', '-showBuildSettings', '-workspace', '/path/to/MyProject.xcworkspace', '-scheme', 'MyScheme', '-configuration', 'Release', '-destination', 'platform=macOS,arch=arm64', + '-derivedDataPath', + '/path/to/derived', + '--verbose', ],
329-343: Literal equality on validation error is fragile.This asserts the entire error block. If createTypedTool formats change (e.g., prefix wording), this test will fail. Consider narrowing to essential substrings.
- expect(result).toEqual({ - content: [ - { - type: 'text', - text: 'Error: Parameter validation failed\nDetails: Invalid parameters:\nscheme: Required', - }, - ], - isError: true, - }); + expect(result.isError).toBe(true); + expect(result.content[0].type).toBe('text'); + expect(result.content[0].text).toContain('Parameter validation failed'); + expect(result.content[0].text).toContain('scheme: Required');src/mcp/tools/macos/get_mac_app_path.ts (2)
27-41: Prefer a union-based XOR for stronger type inference (nice-to-have).Refining a single object works, but modeling XOR as a union yields better inferred types (e.g., in logic, TS knows only one of projectPath/workspacePath exists). You can still wrap with preprocess for empty-string nullification.
-const baseSchemaObject = z.object({ - projectPath: z.string().optional().describe('Path to the .xcodeproj file'), - workspacePath: z.string().optional().describe('Path to the .xcworkspace file'), - ...baseOptions, -}); - -const baseSchema = z.preprocess(nullifyEmptyStrings, baseSchemaObject); - -const getMacosAppPathSchema = baseSchema - .refine((val) => val.projectPath !== undefined || val.workspacePath !== undefined, { - message: 'Either projectPath or workspacePath is required.', - }) - .refine((val) => !(val.projectPath !== undefined && val.workspacePath !== undefined), { - message: 'projectPath and workspacePath are mutually exclusive. Provide only one.', - }); +const projectVariant = z.object({ + projectPath: z.string().describe('Path to the .xcodeproj file'), + workspacePath: z.undefined().optional(), + ...baseOptions, +}); + +const workspaceVariant = z.object({ + projectPath: z.undefined().optional(), + workspacePath: z.string().describe('Path to the .xcworkspace file'), + ...baseOptions, +}); + +// Preprocess to nullify empty strings before union validation +const getMacosAppPathSchema = z.preprocess( + nullifyEmptyStrings, + z.union([projectVariant, workspaceVariant]), +);Note: If you keep schema exposure for MCP as
schema: baseSchemaObject.shape, you can continue doing so for UI/docs while using the union for runtime validation.
64-66: Remove unused XcodePlatform entries to reduce noise.Only macOS is used. Consider inlining or scoping to avoid dead constants.
-const XcodePlatform = { - iOS: 'iOS', - watchOS: 'watchOS', - tvOS: 'tvOS', - visionOS: 'visionOS', - iOSSimulator: 'iOS Simulator', - watchOSSimulator: 'watchOS Simulator', - tvOSSimulator: 'tvOS Simulator', - visionOSSimulator: 'visionOS Simulator', - macOS: 'macOS', -}; +const MACOS_PLATFORM = 'macOS'; @@ - log('info', `Getting app path for scheme ${params.scheme} on platform ${XcodePlatform.macOS}`); + log('info', `Getting app path for scheme ${params.scheme} on platform ${MACOS_PLATFORM}`);Also applies to: 179-183
| { | ||
| // JavaScript and TypeScript files outside the main project (scripts/, etc.) | ||
| files: ['**/*.{js,ts}'], | ||
| ignores: ['src/**/*', '**/*.test.ts'], | ||
| languageOptions: { | ||
| ecmaVersion: 2020, | ||
| sourceType: 'module', | ||
| parser: tseslint.parser, | ||
| // No project reference for scripts - use standalone parsing | ||
| }, | ||
| plugins: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add Node globals for scripts to avoid false positives
Scripts often use Node globals (process, __dirname, etc.). Without globals, core rules like no-undef (on JS) or tooling may misbehave. Add Node globals here.
languageOptions: {
ecmaVersion: 2020,
sourceType: 'module',
parser: tseslint.parser,
+ globals: { ...globals.node },
// No project reference for scripts - use standalone parsing
},Add the import at the top of the file:
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import prettierPlugin from 'eslint-plugin-prettier';
+import globals from 'globals';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| { | |
| // JavaScript and TypeScript files outside the main project (scripts/, etc.) | |
| files: ['**/*.{js,ts}'], | |
| ignores: ['src/**/*', '**/*.test.ts'], | |
| languageOptions: { | |
| ecmaVersion: 2020, | |
| sourceType: 'module', | |
| parser: tseslint.parser, | |
| // No project reference for scripts - use standalone parsing | |
| }, | |
| plugins: { | |
| import eslint from '@eslint/js'; | |
| import tseslint from 'typescript-eslint'; | |
| import prettierPlugin from 'eslint-plugin-prettier'; | |
| import globals from 'globals'; | |
| { | |
| // JavaScript and TypeScript files outside the main project (scripts/, etc.) | |
| files: ['**/*.{js,ts}'], | |
| ignores: ['src/**/*', '**/*.test.ts'], | |
| languageOptions: { | |
| ecmaVersion: 2020, | |
| sourceType: 'module', | |
| parser: tseslint.parser, | |
| globals: { ...globals.node }, | |
| // No project reference for scripts - use standalone parsing | |
| }, | |
| plugins: { |
🤖 Prompt for AI Agents
In eslint.config.js around lines 61 to 71, the configuration for scripts lacks
Node globals which causes false positives for builtins like process and
__dirname; add the Node environment/globals by importing the Node globals or
setting env: { node: true } (and/or globals: { process: 'readonly', __dirname:
'readonly', __filename: 'readonly' }) at the top of the file and inside this
files block so scripts are parsed with Node globals available; also add the
requested import statement at the top of the file as indicated in the review
comment.
| '@typescript-eslint/no-unused-vars': ['warn', { | ||
| argsIgnorePattern: 'never', | ||
| varsIgnorePattern: 'never' | ||
| }], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same 'never' regex bug for scripts’ no-unused-vars
Mirror the fix from the src block to avoid accidentally ignoring names containing “never”.
- '@typescript-eslint/no-unused-vars': ['warn', {
- argsIgnorePattern: 'never',
- varsIgnorePattern: 'never'
- }],
+ '@typescript-eslint/no-unused-vars': ['warn', {
+ argsIgnorePattern: '^$',
+ varsIgnorePattern: '^$'
+ }],📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| '@typescript-eslint/no-unused-vars': ['warn', { | |
| argsIgnorePattern: 'never', | |
| varsIgnorePattern: 'never' | |
| }], | |
| '@typescript-eslint/no-unused-vars': ['warn', { | |
| argsIgnorePattern: '^$', | |
| varsIgnorePattern: '^$' | |
| }], |
🤖 Prompt for AI Agents
In eslint.config.js around lines 80 to 83, the
argsIgnorePattern/varsIgnorePattern are set to 'never' which matches any
identifier containing "never"; change both patterns to the stricter regex used
in the src block (e.g. '^never$') so only identifiers exactly named "never" are
ignored, mirroring the src fix.
| it('should have correct description', () => { | ||
| expect(buildMacProj.description).toBe( | ||
| 'Builds a macOS app using xcodebuild from a project file.', | ||
| expect(buildMacOS.description).toBe( | ||
| "Builds a macOS app using xcodebuild from a project or workspace. Provide exactly one of projectPath or workspacePath. Example: build_macos({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' })", | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify all docs/tests no longer reference pre-unified tool names
The tool description and “Next Steps” strings reflect the unified naming. Please ensure there are no lingering references to legacy variants (e.g., build_mac_proj/build_mac_ws, list_schemes_ws/list_schemes_proj, _by_id/_by_name) across code, tests, and docs.
Run this repo-wide check to find stale names and confirm the referenced follow-up tools exist:
Also applies to: 82-82, 144-145
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Searching for pre-unified names…"
rg -n -S -e 'build_mac_(proj|ws)\b' -e 'list_schemes_(ws|proj)\b' -e '_by_id\b' -e '_by_name\b' -e '\b(list|get|boot|erase|launch)_(sim|simulator)_(id|name)\b' -e 'get_device_(id|name)\b' || true
echo
echo "Verifying unified macOS tool references in tests/docs point to real tool modules…"
# Look for references used in this test and ensure they exist in src
rg -n -S "get_mac_app_path|get_mac_bundle_id|launch_mac_app" | cat
echo
echo "If hits were found above for legacy names, please update those files to the unified names and adjust tests accordingly."Length of output: 13467
Fix stale test strings: replace get_app_bundle_id → get_mac_bundle_id
Ran the repo-wide check you requested. No matches were found for the legacy patterns (build_mac_proj/build_mac_ws, list_schemes_*, _by_id/_by_name, simulator id/name patterns), but I found lingering pre-unified name(s) in tests that need updating:
- src/mcp/tools/macos/tests/get_mac_app_path.test.ts
- ~line 370 and ~line 401: "Next Steps" strings contain
get_app_bundle_id({ appPath: ... })— replace withget_mac_bundle_id({ appPath: ... }).
- ~line 370 and ~line 401: "Next Steps" strings contain
Docs that mention mcp0_* prefixes (notes about environment-specific prefixes) are informational and can remain as-is unless you want to remove those notes.
🤖 Prompt for AI Agents
In src/mcp/tools/macos/__tests__/build_macos.test.ts around lines 19 to 22 and
also update src/mcp/tools/macos/__tests__/get_mac_app_path.test.ts around lines
~370 and ~401, replace the stale test strings referencing get_app_bundle_id(...)
with the unified get_mac_bundle_id(...) call; search for any
"get_app_bundle_id({ appPath:" occurrences in these tests and update the
function name in the example/Next Steps strings (keeping the rest of the text
identical) so test messages reflect the new API name.
| it('should reject inputs with both projectPath and workspacePath', () => { | ||
| const invalidInput = { | ||
| projectPath: '/path/to/project.xcodeproj', | ||
| workspacePath: '/path/to/workspace.xcworkspace', | ||
| scheme: 'MyApp', | ||
| }; | ||
| const schema = z.object(tool.schema); | ||
| expect(schema.safeParse(invalidInput).success).toBe(true); // Base schema passes, but runtime validation should fail | ||
| }); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Test titles contradict expectations; add runtime XOR tests or enforce XOR in Zod schema.
- The titles say “should reject…”, but the assertions expect Zod to pass. This is confusing, even with the inline comment. Either:
- Keep base-schema permissive and rename these tests to reflect that runtime validation handles XOR; and add runtime tests that call the handler/logic and assert rejection. Or
- Enforce XOR in the Zod schema so these become hard validation failures and change the expectations to .toBe(false).
If you choose option 1, rename titles as below and add two handler/logic tests that assert an error.
If you choose option 2, adopt a refined/union schema (see code snippet after the diff) and update these assertions to expect .success === false.
Suggested minimal change (Option 1: rename tests to avoid confusion):
- it('should reject inputs with both projectPath and workspacePath', () => {
+ it('should pass base schema for both projectPath and workspacePath (runtime should reject)', () => {
@@
- expect(schema.safeParse(invalidInput).success).toBe(true); // Base schema passes, but runtime validation should fail
+ expect(schema.safeParse(invalidInput).success).toBe(true); // Base schema intentionally passes; runtime validation should reject
});
@@
- it('should reject inputs with neither projectPath nor workspacePath', () => {
+ it('should pass base schema for neither projectPath nor workspacePath (runtime should reject)', () => {
@@
- expect(schema.safeParse(invalidInput).success).toBe(true); // Base schema passes, but runtime validation should fail
+ expect(schema.safeParse(invalidInput).success).toBe(true); // Base schema intentionally passes; runtime validation should reject
});Option 1 follow-up (add runtime XOR tests near here):
it('should reject at runtime when both projectPath and workspacePath are provided', async () => {
const result = await tool.handler({
projectPath: '/p.xcodeproj',
workspacePath: '/w.xcworkspace',
scheme: 'MyApp',
}, createMockExecutor({ success: true, output: '', error: '' }));
expect(result.isError).toBe(true);
expect(result.content?.some(c => c.type === 'text' && c.text.includes('Provide exactly one'))).toBe(true);
});
it('should reject at runtime when neither projectPath nor workspacePath is provided', async () => {
const result = await tool.handler({ scheme: 'MyApp' } as any, createMockExecutor({ success: true, output: '', error: '' }));
expect(result.isError).toBe(true);
expect(result.content?.some(c => c.type === 'text' && c.text.includes('Provide exactly one'))).toBe(true);
});Optional (Option 2: enforce XOR at schema-level). In build_run_macos.ts, define mutually exclusive schemas:
const Base = {
scheme: z.string(),
configuration: z.string().optional(),
derivedDataPath: z.string().optional(),
arch: z.enum(['arm64', 'x86_64']).optional(),
extraArgs: z.array(z.string()).optional(),
preferXcodebuild: z.boolean().optional(),
};
const ProjectSchema = z.object({ ...Base, projectPath: z.string() });
const WorkspaceSchema = z.object({ ...Base, workspacePath: z.string() });
export const schema = z.union([ProjectSchema, WorkspaceSchema]); // tool.schema should be this Zod schema, not a raw shapeIf you adopt Option 2, also update tests to use tool.schema.safeParse(input) directly (not z.object(tool.schema)).
Also applies to: 78-85
🏁 Script executed:
#!/bin/bash
set -euo pipefail
test_path="src/mcp/tools/macos/__tests__/build_run_macos.test.ts"
tool_path="src/mcp/tools/macos/build_run_macos.ts"
echo "=== Checking test file ==="
if [ -f "$test_path" ]; then
sed -n '1,240p' "$test_path"
else
echo "NOT FOUND: $test_path"
fi
echo
echo "=== Checking tool file ==="
if [ -f "$tool_path" ]; then
sed -n '1,240p' "$tool_path"
else
echo "NOT FOUND: $tool_path"
fi
echo
echo "=== Searching repo for occurrences of projectPath/workspacePath/schema/handler ==="
rg --hidden -n "projectPath|workspacePath|schema|handler" || trueLength of output: 244209
Fix tests: title/assertion mismatch in build_run_macos tests (handler enforces XOR but tests validate base shape).
Verified: src/mcp/tools/macos/build_run_macos.ts uses a refined Zod schema (buildRunMacOSSchema) with XOR checks in the handler but exports baseSchemaObject.shape as tool.schema. The tests at src/mcp/tools/macos/tests/build_run_macos.test.ts assert z.object(tool.schema).safeParse(...) for the both/none cases (they pass the base shape) while the test titles say "should reject" — this is confusing and needs fixing.
Files to update
- src/mcp/tools/macos/tests/build_run_macos.test.ts (lines ~68-85) — fix test titles/assertions and add runtime handler tests.
- (Optional) src/mcp/tools/macos/build_run_macos.ts — if you prefer schema-level enforcement, update exported schema to a union/refined shape.
Suggested fixes
Option 1 (recommended, minimal): keep exported base shape for MCP compatibility, rename the two misleading tests and add handler-level runtime tests that assert the handler rejects invalid XOR cases.
Rename diff (tests):
- it('should reject inputs with both projectPath and workspacePath', () => {
+ it('should pass base schema for both projectPath and workspacePath (handler should reject at runtime)', () => {
@@
- it('should reject inputs with neither projectPath nor workspacePath', () => {
+ it('should pass base schema for neither projectPath nor workspacePath (handler should reject at runtime)', () => {Add runtime XOR tests (near the schema tests):
it('should reject at runtime when both projectPath and workspacePath are provided', async () => {
const result = await tool.handler(
{
projectPath: '/p.xcodeproj',
workspacePath: '/w.xcworkspace',
scheme: 'MyApp',
} as any,
createMockExecutor({ success: true, output: '', error: '' }),
);
expect(result.isError).toBe(true);
expect(result.content?.some(c => c.type === 'text' && (c.text as string).includes('Either projectPath or workspacePath is required') || (c.text as string).includes('mutually exclusive'))).toBe(true);
});
it('should reject at runtime when neither projectPath nor workspacePath is provided', async () => {
const result = await tool.handler({ scheme: 'MyApp' } as any, createMockExecutor({ success: true, output: '', error: '' }));
expect(result.isError).toBe(true);
expect(result.content?.some(c => c.type === 'text' && (c.text as string).includes('Either projectPath or workspacePath is required'))).toBe(true);
});Option 2 (alternative): enforce XOR at schema-level and update schema export so z.object(tool.schema).safeParse(...) returns false for invalid combos. Example (in build_run_macos.ts):
const Base = {
scheme: z.string(),
configuration: z.string().optional(),
derivedDataPath: z.string().optional(),
arch: z.enum(['arm64', 'x86_64']).optional(),
extraArgs: z.array(z.string()).optional(),
preferXcodebuild: z.boolean().optional(),
};
const ProjectSchema = z.object({ ...Base, projectPath: z.string() });
const WorkspaceSchema = z.object({ ...Base, workspacePath: z.string() });
export const schema = z.union([ProjectSchema, WorkspaceSchema]).shape; // or adjust to match MCP schema shape expectationsIf you take Option 2, update the two tests to expect .success === false.
Summary: current repo pattern is to export the raw shape for MCP compatibility and enforce refinements in the handler — so the simplest, consistent fix is Option 1: rename tests and add two handler-level runtime XOR tests.
🤖 Prompt for AI Agents
In src/mcp/tools/macos/__tests__/build_run_macos.test.ts around lines 68-77, the
test titles/assertions are mismatched: the file currently validates the exported
base schema (z.object(tool.schema)) and therefore the existing assertions that
.safeParse(...).success is true are correct for the base shape, but the test
titles claim the input "should reject". Rename those two schema tests to reflect
that the base shape accepts both/none (e.g., "base schema allows both
projectPath and workspacePath" and "base schema allows neither"), and add two
new runtime handler tests that call tool.handler with the invalid XOR combos
(both set, and neither set) and assert result.isError === true and that
result.content includes a text error mentioning "Either projectPath or
workspacePath is required" or "mutually exclusive"; alternatively, if you prefer
schema-level enforcement, update build_run_macos.ts to export a union/refined
schema enforcing XOR and then change the existing tests to expect .success ===
false for invalid combos.
| export default { | ||
| name: 'get_mac_app_path_proj', | ||
| name: 'get_mac_app_path', | ||
| description: | ||
| "Gets the app bundle path for a macOS application using a project file. IMPORTANT: Requires projectPath and scheme. Example: get_mac_app_path_proj({ projectPath: '/path/to/project.xcodeproj', scheme: 'MyScheme' })", | ||
| schema: getMacAppPathProjSchema.shape, // MCP SDK compatibility | ||
| handler: createTypedTool( | ||
| getMacAppPathProjSchema, | ||
| get_mac_app_path_projLogic, | ||
| "Gets the app bundle path for a macOS application using either a project or workspace. Provide exactly one of projectPath or workspacePath. Example: get_mac_app_path({ projectPath: '/path/to/project.xcodeproj', scheme: 'MyScheme' })", | ||
| schema: baseSchemaObject.shape, // MCP SDK compatibility | ||
| handler: createTypedTool<GetMacosAppPathParams>( | ||
| getMacosAppPathSchema as z.ZodType<GetMacosAppPathParams>, | ||
| get_mac_app_pathLogic, | ||
| getDefaultCommandExecutor, | ||
| ), | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
End-to-end rename audit for unified tools.
Given the goal to unify tool variants, ensure no stale references (e.g., get_mac_app_path_ws, *_proj/_ws, simulator *_id/_name variants) remain in code/docs/tests.
🏁 Script executed:
#!/bin/bash
# Find pre-unified tool name patterns and common legacy suffixes
rg -n --fixed-strings \
"get_mac_app_path_ws" \
"get_mac_app_path_proj" \
"list_schemes_ws" \
"list_schemes_proj" \
"launch_simulator_id" \
"launch_simulator_name" \
"boot_simulator_id" \
"boot_simulator_name" \
"shutdown_simulator_id" \
"shutdown_simulator_name" \
| sed 's/^/Stale reference: /'Length of output: 798
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for explicit legacy tool names
echo "=== Explicit legacy names ==="
rg -n -F \
-e "get_mac_app_path_ws" \
-e "get_mac_app_path_proj" \
-e "list_schemes_ws" \
-e "list_schemes_proj" \
-e "launch_simulator_id" \
-e "launch_simulator_name" \
-e "boot_simulator_id" \
-e "boot_simulator_name" \
-e "shutdown_simulator_id" \
-e "shutdown_simulator_name" \
| sed 's/^/Stale reference: /' || true
# Broader search for common legacy suffixes in likely locations
echo
echo "=== Suffix matches (_proj/_ws/_id/_name) in src/docs/tests ==="
rg -n --hidden -S -g 'src/**' -g 'docs/**' -g 'test/**' -g 'tests/**' -e '_proj\b' -e '_ws\b' -e '_id\b' -e '_name\b' \
| sed 's/^/Suffix match: /' || trueLength of output: 30251
Action required — remove or alias stale legacy tool names in docs/tests
Brief: the repo still contains legacy *_proj/_ws/_id/_name tool references in documentation and testing material that must be updated or aliased for compatibility.
Files/locations to fix (from grep):
- docs/ARCHITECTURE.md (e.g. line ~300:
get_mac_app_path_ws) - docs/TOOLS.md (mentions like
launch_app_sim_name,stop_app_sim_name,get_mac_bundle_id, etc.) - docs/PHASE1-TASKS.md (cleanup/task list referencing
tool_proj/tool_wsand many legacy variants) - docs/TESTING.md (many example commands:
list_schems_proj,build_sim_id_proj, etc.) - docs/MANUAL_TESTING.md (same kinds of example commands)
- docs/PLUGIN_DEVELOPMENT.md (templates/examples referencing
_ws/__projvariants) - Misc docs/comments in src/* that include legacy examples / test commands (see docs and testing files above)
Recommended fixes:
- Update documentation and test examples to use unified tool names (e.g.,
get_mac_app_path) or add documented backward-compatible aliases/re-exports if workflows expect old names. - Remove/replace leftover TODOs and examples that reference legacy variants.
- After changes, re-run the same repository-wide search to confirm no stale references remain.
🤖 Prompt for AI Agents
In src/mcp/tools/macos/get_mac_app_path.ts around lines 179 to 189,
documentation and tests across the repo still reference legacy tool name
variants (e.g., *_proj, *_ws, *_id, *_name such as get_mac_app_path_ws), which
must be removed or aliased; update the listed docs (docs/ARCHITECTURE.md,
docs/TOOLS.md, docs/PHASE1-TASKS.md, docs/TESTING.md, docs/MANUAL_TESTING.md,
docs/PLUGIN_DEVELOPMENT.md) and any comments/examples in src/* to use the
unified tool names (e.g., get_mac_app_path) OR add backward-compatible
aliases/re-exports for the legacy names with clear docs pointing to the
canonical name, replace/remove leftover TODOs referencing legacy variants, and
then run a repo-wide search (grep) to verify no stale references remain.
| const hasProjectPath = typeof params.projectPath === 'string'; | ||
| const path = hasProjectPath ? params.projectPath : params.workspacePath; | ||
|
|
||
| if (hasProjectPath) { | ||
| command.push('-project', params.projectPath!); | ||
| } else { | ||
| command.push('-workspace', params.workspacePath!); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Expose projectOrWorkspace for consistent “Next Steps” and messages
You already compute hasProjectPath and path. Add a projectOrWorkspace discriminator to reuse in messages and “Next Steps” construction.
Apply this diff:
- const hasProjectPath = typeof params.projectPath === 'string';
- const path = hasProjectPath ? params.projectPath : params.workspacePath;
+ const hasProjectPath = typeof params.projectPath === 'string';
+ const projectOrWorkspace = hasProjectPath ? 'project' : 'workspace';
+ const path = hasProjectPath ? params.projectPath : params.workspacePath;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const hasProjectPath = typeof params.projectPath === 'string'; | |
| const path = hasProjectPath ? params.projectPath : params.workspacePath; | |
| if (hasProjectPath) { | |
| command.push('-project', params.projectPath!); | |
| } else { | |
| command.push('-workspace', params.workspacePath!); | |
| } | |
| const hasProjectPath = typeof params.projectPath === 'string'; | |
| const projectOrWorkspace = hasProjectPath ? 'project' : 'workspace'; | |
| const path = hasProjectPath ? params.projectPath : params.workspacePath; | |
| if (hasProjectPath) { | |
| command.push('-project', params.projectPath!); | |
| } else { | |
| command.push('-workspace', params.workspacePath!); | |
| } |
🤖 Prompt for AI Agents
In src/mcp/tools/project-discovery/show_build_settings.ts around lines 49 to 56,
introduce a projectOrWorkspace discriminator so the code can consistently refer
to whether we used a project or workspace in messages and Next Steps. After
computing hasProjectPath and path, add const projectOrWorkspace = hasProjectPath
? 'project' : 'workspace' and then use that variable wherever you build
user-facing strings or Next Steps (instead of re-checking hasProjectPath or
interpolating path directly); leave the existing command.push logic as-is but
replace any message/Next Step construction to reference projectOrWorkspace for
clear, consistent wording.
| // Add next steps for workspace (similar to original workspace implementation) | ||
| if (!hasProjectPath && path) { | ||
| content.push({ | ||
| type: 'text', | ||
| text: `Next Steps: | ||
| - Build the workspace: build_macos({ workspacePath: "${path}", scheme: "${params.scheme}" }) | ||
| - For iOS: build_sim({ workspacePath: "${path}", scheme: "${params.scheme}", simulatorName: "iPhone 16" }) | ||
| - List schemes: list_schemes({ workspacePath: "${path}" })`, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Show “Next Steps” for both project and workspace (unification goal)
“Next Steps” are only shown for workspaces. To align with the unification objective and with list_schemes’ behavior, generate them for projects too, using a single template keyed off projectOrWorkspace.
Apply this diff:
- // Add next steps for workspace (similar to original workspace implementation)
- if (!hasProjectPath && path) {
- content.push({
- type: 'text',
- text: `Next Steps:
-- Build the workspace: build_macos({ workspacePath: "${path}", scheme: "${params.scheme}" })
-- For iOS: build_sim({ workspacePath: "${path}", scheme: "${params.scheme}", simulatorName: "iPhone 16" })
-- List schemes: list_schemes({ workspacePath: "${path}" })`,
- });
- }
+ // Add next steps for both project and workspace cases
+ if (path) {
+ content.push({
+ type: 'text',
+ text: `Next Steps:
+- Build the ${projectOrWorkspace}: build_macos({ ${projectOrWorkspace}Path: "${path}", scheme: "${params.scheme}" })
+- For iOS: build_sim({ ${projectOrWorkspace}Path: "${path}", scheme: "${params.scheme}", simulatorName: "iPhone 16" })
+- List schemes: list_schemes({ ${projectOrWorkspace}Path: "${path}" })`,
+ });
+ }Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/mcp/tools/project-discovery/show_build_settings.ts around lines 82 to 91,
the current code only pushes "Next Steps" when hasProjectPath is false
(workspace only); change it to generate the same Next Steps for both projects
and workspaces by using a single template keyed off projectOrWorkspace (the
variable used elsewhere to represent either projectPath or workspacePath).
Replace the hasProjectPath conditional with a check for path (existing path
variable) and build the content.text using projectOrWorkspace as the key name
and params.scheme for scheme (e.g., build_macos({ projectOrWorkspace: "${path}",
scheme: "${params.scheme}" }) etc.), ensuring the three commands (build_macos,
build_sim, list_schemes) are emitted the same way for projects and workspaces.
| export default { | ||
| name: 'show_build_settings', | ||
| description: | ||
| "Shows build settings from either a project or workspace using xcodebuild. Provide exactly one of projectPath or workspacePath, plus scheme. Example: show_build_settings({ projectPath: '/path/to/MyProject.xcodeproj', scheme: 'MyScheme' })", | ||
| schema: baseSchemaObject.shape, | ||
| handler: createTypedTool<ShowBuildSettingsParams>( | ||
| showBuildSettingsSchema as z.ZodType<ShowBuildSettingsParams>, | ||
| showBuildSettingsLogic, | ||
| getDefaultCommandExecutor, | ||
| ), | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Double-check repo for any lingering pre-unified tool names in docs/tests
Given the unification objective, verify there are no leftover references (e.g., list_schems_ws, list_schems_proj, show_build_set_proj, build_simulator_name/id).
Run:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Scanning for pre-unified discovery tool names..."
rg -n -A 1 -B 1 $'list_schems_ws|list_schems_proj|show_build_set_proj'
echo
echo "Scanning for legacy simulator tool variants..."
rg -n -A 1 -B 1 $'build_simulator_name|build_simulator_id|run_simulator_name|run_simulator_id'
echo
echo "Scanning for unified names to ensure consistent usage..."
rg -n -A 1 -B 1 $'list_schemes\\b|show_build_settings\\b|build_macos\\b|build_sim\\b'Length of output: 30570
Fix docs/tests: replace lingering pre-unified tool names
Verification found leftover legacy names in documentation and a verification backup. Please update these to the unified names.
- docs/TESTING.md — replace occurrences of
list_schems_proj→list_schemes(around lines ~1002, 1048, 1149, 1153; includesnpx reloaderoo ... call-tool "list_schems_proj"usages). - docs/MANUAL_TESTING.md — replace
list_schems_proj→list_schemes(around lines ~510, 556, 657, 661). - docs/PHASE1-TASKS.md — replace
show_build_set→show_build_settings(tool listing at top of file). Note:build_simulator_name/build_simulator_idappear here as task history (no code impact), but review if you want them normalized in the task doc. - TOOL_NAMING_VERIFICATION_2025-08-14_22-13.md.bak — contains old tool-name references (and duplicated entries); either update the file to use unified names or remove the stale backup.
Suggested quick grep to re-check before merging:
rg -n $'list_schems|show_build_set|build_simulator_name|build_simulator_id'
🤖 Prompt for AI Agents
In src/mcp/tools/project-discovery/show_build_settings.ts around lines 104 to
114 and across the repo update lingering legacy tool names to the unified names:
in docs/TESTING.md replace all occurrences of `list_schems_proj` with
`list_schemes` (notably near lines ~1002, 1048, 1149, 1153 including any npx
reloaderoo call-tool invocations), in docs/MANUAL_TESTING.md replace
`list_schems_proj` → `list_schemes` (around ~510, 556, 657, 661), in
docs/PHASE1-TASKS.md replace `show_build_set` → `show_build_settings` in the
tool listing, and either update or remove
TOOL_NAMING_VERIFICATION_2025-08-14_22-13.md.bak to remove stale legacy names;
after edits run the suggested grep (rg -n
$'list_schems|show_build_set|build_simulator_name|build_simulator_id') to verify
no leftover references remain.
| 'Build for simulator: build_sim({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject", simulatorName: "iPhone 16" })', | ||
| 'Build and run on simulator: build_run_sim({ workspacePath: "/tmp/test-projects/MyProject.xcworkspace", scheme: "MyProject", simulatorName: "iPhone 16" })', | ||
| ], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Switched to unified function-style sim tools in nextSteps — verify canonical names and params
Good move replacing legacy CLI-style strings with build_sim/build_run_sim. Please double-check that:
build_simandbuild_run_simare the canonical, unified tool names exported by the tool registry.- The parameter name
simulatorNamematches the unified schema (if the PR standardized on a singlesimulatorparam that accepts either name or ID, update the tests accordingly). - Any references to pre-unified names (e.g.,
*_ws,*_proj,*_id,*_name) are fully removed from code and docs.
Run this script to verify there are no lingering references and that the unified tools exist:
If anything pops, I can draft the necessary code/doc/test updates.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) Searching for legacy simulator tool names that should be removed..."
legacy_names=(
'build_ios_sim_name_ws'
'build_run_ios_sim_name_ws'
'build_ios_sim_id_ws'
'build_run_ios_sim_id_ws'
'build_ios_sim_name_proj'
'build_run_ios_sim_name_proj'
'build_ios_sim_id_proj'
'build_run_ios_sim_id_proj'
'list_schemes_ws'
'list_schemes_proj'
)
for n in "${legacy_names[@]}"; do
if rg -n --no-heading -S "$n"; then
echo "Found legacy reference: $n"
fi
done
echo
echo "2) Confirming unified tool names are defined somewhere (by export name field or usage)..."
rg -n --no-heading -S $'name:\\s*[\'"]build_sim[\'"]|name:\\s*[\'"]build_run_sim[\'"]' -A 2 -B 2 || true
rg -n --no-heading -S $'\\bbuild_sim\\b|\\bbuild_run_sim\\b' -A 2 -B 2 || true
echo
echo "3) Auditing parameter naming for simulator unification..."
rg -n --no-heading -S $'simulatorName|simulatorId|\\bsimulator\\s*:' -A 3 -B 2 || true
echo
echo "4) Grep for any doc or description references to pre-unified tool names..."
fd -t f README* docs --exec rg -n --no-heading -S 'list_schemes_ws|_sim_name_|_sim_id_|_proj\\b|_ws\\b' {} || true
echo
echo "Done. Review any matches above; ideally there should be 0 hits for legacy names."Length of output: 154834
Verified: build_sim / build_run_sim are canonical — update docs with legacy names
Short summary: I ran repository-wide checks. The unified tools are implemented and tests use the unified params (simulatorId / simulatorName). However there are leftover doc/test examples referencing pre‑unified tool names that should be updated.
Key findings (actionable):
-
Confirmed canonical tools and schema (no code fixes needed):
- src/mcp/tools/simulator/build_sim.ts — exports name: 'build_sim'
- src/mcp/tools/simulator/build_run_sim.ts — exports name: 'build_run_sim'
- Types and helpers use simulatorId / simulatorName (e.g. src/types/common.ts, src/utils/simulator-utils.ts)
- Tests and scaffold nextSteps already use simulatorName (e.g. src/mcp/tools/project-scaffolding/tests/scaffold_ios_project.test.ts lines ~391-393) — these are correct.
-
Files that need doc/test updates (replace pre‑unified names with canonical names and clarify XOR param guidance):
- docs/TOOLS.md — contains build_run_simulator, build_simulator, get_simulator_app_path, launch_app_sim_name, stop_app_sim_name, test_simulator; update to build_run_sim, build_sim, get_sim_app_path, launch_app_sim, stop_app_sim, test_sim and correct parameter descriptions (use simulatorId OR simulatorName).
- docs/MANUAL_TESTING.md — contains example calling build_sim_id_proj; update examples to use build_sim and the unified param names.
- docs/TESTING.md — same outdated example (build_sim_id_proj) — update.
- Any other docs/snippets that reference id, name, *_proj/_ws variants (quick grep shows these in the docs set) — replace with unified forms and XOR guidance.
If you want, I can draft a patch to update those docs/examples and adjust wording to reflect "Provide either simulatorId OR simulatorName (mutually exclusive)."
🤖 Prompt for AI Agents
In src/mcp/tools/project-scaffolding/__tests__/scaffold_ios_project.test.ts
around lines 391-393, update any remaining legacy tool names and parameter forms
in docs/tests to the canonical unified names and params: replace
build_run_simulator → build_run_sim, build_simulator → build_sim,
get_simulator_app_path → get_sim_app_path, launch_app_sim_name → launch_app_sim,
stop_app_sim_name → stop_app_sim, test_simulator → test_sim, and change any
examples using simulatorId/simulatorName variants or *_proj/_ws variants to use
simulatorId OR simulatorName (mutually exclusive) and canonical workspace/scheme
naming; apply the same replacements in docs/TOOLS.md, docs/MANUAL_TESTING.md,
docs/TESTING.md and any other doc snippets found by grep to ensure
examples/tests consistently use build_sim / build_run_sim and the XOR parameter
guidance.
|
✅ RESOLVED - Already Fixed This comment filtering issue has already been addressed in the current codebase. The logic now properly handles both multi-line comments and inline comments:
The current implementation is robust and correctly handles the scenarios mentioned. No further action needed. |
|
❌ INCORRECT - TypeScript ESM Requirement This suggestion is technically incorrect for this project's configuration. The project uses:
In TypeScript ESM projects, import statements MUST use
The current `.js" imports are correct and required for this project configuration. |
|
❌ INCORRECT - Already Extracted This is a false positive. The
The code correctly follows DRY principles. No duplication exists. |
|
❌ CANNOT VERIFY - Typo Not Found I cannot locate the alleged typo If this typo existed, it appears to have been already corrected. The current code uses the correct spelling `list_schemes" throughout. |
|
✅ RESOLVED - Already Fixed This parameter restriction issue has been resolved. The current code in
The |
|
✅ RESOLVED - Tool Names Updated The tool references have been updated to use unified naming conventions. The current code correctly uses:
All "Next Steps" text and tool references reflect the current unified tool names. The migration from legacy tool variants to the canonical unified tools is complete. |
|
✅ RESOLVED - Test Expectations Updated Test expectations have been updated to reflect unified tool names and function-style invocations. The current tests properly expect:
The test suite now validates the correct unified tool interfaces. |
|
✅ ACKNOWLEDGED - Design Decision This is a valid observation about MCP protocol limitations. The XOR constraint ( This is an intentional design tradeoff:
The tool description clearly states "Provide exactly one of projectPath or workspacePath" which helps LLMs understand the constraint. This approach balances MCP compatibility with API usability. |
📋 CodeRabbit Review SummaryAfter comprehensive analysis of all inline comments, here's the status: ✅ Issues Already Resolved (3)
❌ Incorrect Suggestions (4)
✅ Acknowledged Design Decisions (1)
Result: All legitimate issues have been proactively addressed. The codebase is in excellent shape with robust error handling, proper tool unification, and optimized hint patterns. |
This PR unifies the project/workspace tool variants and the ID/name simulator tool variants.
Summary by CodeRabbit
New Features
Refactor
Documentation
Chores