xcode-mcp-server 1.3.7__tar.gz → 1.3.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/PKG-INFO +1 -1
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/TODO.md +2 -1
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/dev.sh +0 -4
- xcode_mcp_server-1.3.9/fools-errand.md +80 -0
- xcode_mcp_server-1.3.9/test_projects/fromXcode/iosEmptySwiftUIApp/iosEmptySwiftUIApp.xcodeproj/project.xcworkspace/xcuserdata/andrew.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- {xcode_mcp_server-1.3.7/test_projects/templates/BrokenApp/BrokenApp.xcodeproj → xcode_mcp_server-1.3.9/test_projects/fromXcode/iosEmptySwiftUIApp/iosEmptySwiftUIApp.xcodeproj}/xcuserdata/andrew.xcuserdatad/xcschemes/xcschememanagement.plist +1 -1
- xcode_mcp_server-1.3.9/test_projects/fromXcode/macosEmptyCommandLineApp/macosEmptyCommandLineApp.xcodeproj/project.xcworkspace/xcuserdata/andrew.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- xcode_mcp_server-1.3.9/test_projects/fromXcode/macosEmptyCommandLineApp/macosEmptyCommandLineApp.xcodeproj/xcuserdata/andrew.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- xcode_mcp_server-1.3.9/test_projects/fromXcode/macosEmptySwiftUIApp/macosEmptySwiftUIApp.xcodeproj/project.xcworkspace/xcuserdata/andrew.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- xcode_mcp_server-1.3.9/test_projects/fromXcode/macosEmptySwiftUIApp/macosEmptySwiftUIApp.xcodeproj/xcuserdata/andrew.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- xcode_mcp_server-1.3.9/test_projects/templates/BrokenApp/BrokenApp.xcodeproj/project.xcworkspace/xcuserdata/andrew.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- xcode_mcp_server-1.3.9/test_projects/templates/BrokenApp/BrokenApp.xcodeproj/xcuserdata/andrew.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- xcode_mcp_server-1.3.9/test_projects/templates/ConsoleApp/ConsoleApp.xcodeproj/project.xcworkspace/xcuserdata/andrew.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- xcode_mcp_server-1.3.9/test_projects/templates/ConsoleApp/ConsoleApp.xcodeproj/xcuserdata/andrew.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- xcode_mcp_server-1.3.9/test_projects/templates/SimpleApp/SimpleApp.xcodeproj/project.xcworkspace/xcuserdata/andrew.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- xcode_mcp_server-1.3.9/test_projects/templates/SimpleApp/SimpleApp.xcodeproj/xcuserdata/andrew.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- xcode_mcp_server-1.3.9/test_projects/templates/SwiftUITestApp/SwiftUITestApp.xcodeproj/project.xcworkspace/xcuserdata/andrew.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- xcode_mcp_server-1.3.9/test_projects/templates/SwiftUITestApp/SwiftUITestApp.xcodeproj/xcuserdata/andrew.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- xcode_mcp_server-1.3.9/test_projects/templates/iOSTestApp/iOSTestApp.xcodeproj/project.xcworkspace/xcuserdata/andrew.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- xcode_mcp_server-1.3.9/test_projects/templates/iOSTestApp/iOSTestApp.xcodeproj/xcuserdata/andrew.xcuserdatad/xcschemes/xcschememanagement.plist +19 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/tests/test_basic.py +24 -15
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/tests/test_runner.py +10 -17
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/__init__.py +1 -1
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/security.py +65 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/__init__.py +4 -0
- xcode_mcp_server-1.3.9/xcode_mcp_server/tools/create_project.py +100 -0
- xcode_mcp_server-1.3.9/xcode_mcp_server/tools/get_active_run_destination.py +199 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/get_xcode_projects.py +16 -0
- xcode_mcp_server-1.3.9/xcode_mcp_server/tools/list_run_destinations.py +155 -0
- xcode_mcp_server-1.3.9/xcode_mcp_server/tools/set_run_destination.py +103 -0
- xcode_mcp_server-1.3.9/xcode_mcp_server/utils/decode_active_destination.swift +104 -0
- xcode_mcp_server-1.3.9/xcode_mcp_server/utils/project_templates.py +658 -0
- xcode_mcp_server-1.3.7/test_projects/templates/BrokenApp/BrokenApp.xcodeproj/project.xcworkspace/xcuserdata/andrew.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/.github/workflows/claude-code.yml +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/.github/workflows/claude-self-review.yml +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/.gitignore +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/CLAUDE.md +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/LICENSE +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/README.md +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/_venv-setup.sh +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/deploy-beta.sh +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/deploy.sh +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/info.sh +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/pyproject.toml +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/requirements.txt +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/run_local_for_claude.sh +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/scripts/cleanup_tests.sh +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/scripts/create_projects.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/scripts/create_real_projects.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/scripts/create_spm_projects.sh +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/scripts/create_xcode_projects.sh +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/scripts/run_all_tests.sh +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/scripts/setup_test_projects.sh +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/scripts/setup_tests.sh +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/iosEmptySwiftUIApp/iosEmptySwiftUIApp/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/iosEmptySwiftUIApp/iosEmptySwiftUIApp/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/iosEmptySwiftUIApp/iosEmptySwiftUIApp/Assets.xcassets/Contents.json +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/iosEmptySwiftUIApp/iosEmptySwiftUIApp/ContentView.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/iosEmptySwiftUIApp/iosEmptySwiftUIApp/iosEmptySwiftUIAppApp.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/iosEmptySwiftUIApp/iosEmptySwiftUIApp.xcodeproj/project.pbxproj +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/iosEmptySwiftUIApp/iosEmptySwiftUIApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/macosEmptyCommandLineApp/macosEmptyCommandLineApp/main.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/macosEmptyCommandLineApp/macosEmptyCommandLineApp.xcodeproj/project.pbxproj +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/macosEmptyCommandLineApp/macosEmptyCommandLineApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/macosEmptySwiftUIApp/macosEmptySwiftUIApp/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/macosEmptySwiftUIApp/macosEmptySwiftUIApp/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/macosEmptySwiftUIApp/macosEmptySwiftUIApp/Assets.xcassets/Contents.json +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/macosEmptySwiftUIApp/macosEmptySwiftUIApp/ContentView.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/macosEmptySwiftUIApp/macosEmptySwiftUIApp/macosEmptySwiftUIAppApp.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/macosEmptySwiftUIApp/macosEmptySwiftUIApp.xcodeproj/project.pbxproj +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/fromXcode/macosEmptySwiftUIApp/macosEmptySwiftUIApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/BrokenApp/BrokenApp/main.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/BrokenApp/BrokenApp.xcodeproj/project.pbxproj +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/BrokenApp/BrokenApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/ConsoleApp/ConsoleApp/main.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/ConsoleApp/ConsoleApp.xcodeproj/project.pbxproj +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/ConsoleApp/ConsoleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/SimpleApp/SimpleApp/main.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/SimpleApp/SimpleApp.xcodeproj/project.pbxproj +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/SimpleApp/SimpleApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/SwiftUITestApp/SwiftUITestApp/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/SwiftUITestApp/SwiftUITestApp/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/SwiftUITestApp/SwiftUITestApp/Assets.xcassets/Contents.json +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/SwiftUITestApp/SwiftUITestApp/ContentView.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/SwiftUITestApp/SwiftUITestApp/macosEmptySwiftUIAppApp.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/SwiftUITestApp/SwiftUITestApp.xcodeproj/project.pbxproj +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/SwiftUITestApp/SwiftUITestApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/iOSTestApp/iOSTestApp/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/iOSTestApp/iOSTestApp/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/iOSTestApp/iOSTestApp/Assets.xcassets/Contents.json +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/iOSTestApp/iOSTestApp/ContentView.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/iOSTestApp/iOSTestApp/iosEmptySwiftUIAppApp.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/iOSTestApp/iOSTestApp.xcodeproj/project.pbxproj +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/test_projects/templates/iOSTestApp/iOSTestApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/tests/debug_build.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/tests/debug_test.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/tests/test_build.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/tests/test_fixed.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/tests/test_minimal.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/__main__.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/cli.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/config_manager.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/config_ui.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/exceptions.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/server.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/build_project.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/clean_project.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/debug_list_notification_history.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/get_build_errors.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/get_build_results.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/get_directory_listing.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/get_directory_tree.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/get_latest_test_results.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/get_project_schemes.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/get_runtime_output.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/list_booted_simulators.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/list_mac_app_windows.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/list_project_tests.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/list_running_mac_apps.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/run_project_tests.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/run_project_unmonitored.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/run_project_until_terminated.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/run_project_with_user_interaction.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/stop_project.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/take_app_screenshot.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/take_simulator_screenshot.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/take_window_screenshot.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/take_xcode_screenshot.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/tools/version.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/utils/__init__.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/utils/applescript.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/utils/build_log_parser.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/utils/decode_xcode_recents.swift +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/utils/screenshot.py +0 -0
- {xcode_mcp_server-1.3.7 → xcode_mcp_server-1.3.9}/xcode_mcp_server/utils/xcresult.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xcode-mcp-server
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.9
|
|
4
4
|
Summary: Drew's MCP server for Xcode integration
|
|
5
5
|
Project-URL: Homepage, https://github.com/drewster99/xcode-mcp-server
|
|
6
6
|
Project-URL: Repository, https://github.com/drewster99/xcode-mcp-server
|
|
@@ -192,4 +192,5 @@ xcrun xcresulttool get --path <xcresult> --format json
|
|
|
192
192
|
- `ALLOWED_FOLDERS` environment variable or CLI args
|
|
193
193
|
- All paths validated: absolute, exist, directory, no '..'
|
|
194
194
|
- Default to `$HOME` if not specified
|
|
195
|
-
- Every tool validates project path against allowed folders
|
|
195
|
+
- Every tool validates project path against allowed folders
|
|
196
|
+
|
|
@@ -59,11 +59,7 @@ echo ""
|
|
|
59
59
|
echo ""
|
|
60
60
|
echo "If you need to test with Claude, do this:"
|
|
61
61
|
echo " claude mcp remove xcode-mcp-server"
|
|
62
|
-
echo " claude mcp add --transport stdio --scope user xcode-mcp-server -- python3 -m `pwd`/xcode_mcp_server"
|
|
63
|
-
echo ""
|
|
64
|
-
echo " or maybe..."
|
|
65
62
|
echo " claude mcp add --transport stdio --scope user xcode-mcp-server `pwd`/run_local_for_claude.sh"
|
|
66
|
-
echo " claude mcp add --transport stdio --scope user xcode-mcp-server -- /opt/homebrew/Caskroom/miniconda/base/envs/xcode-mcp-dev/bin/python -m `pwd`/xcode_mcp_server"
|
|
67
63
|
echo ""
|
|
68
64
|
|
|
69
65
|
# Run the MCP inspector
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
How Xcode Instantiates Projects Internally
|
|
3
|
+
|
|
4
|
+
The process is handled by private framework classes in IDEFoundation.framework:
|
|
5
|
+
|
|
6
|
+
1. IDETemplateKind — knows the different kinds of templates (projectTemplateKind,
|
|
7
|
+
fileTemplateKind, targetTemplateKind, playgroundTemplateKind)
|
|
8
|
+
2. IDETemplate — loads TemplateInfo.plist, resolves the Ancestors inheritance chain, and
|
|
9
|
+
merges all options/definitions/nodes
|
|
10
|
+
3. IDETemplateFactory — the workhorse: instantiateTemplateForContext:options:whenDone:
|
|
11
|
+
takes a template + user's option choices and creates the project on disk
|
|
12
|
+
4. IDETemplateMacroEngine (Swift class) — performs variable substitution on filenames and
|
|
13
|
+
file contents:
|
|
14
|
+
- ___PACKAGENAME___ → project name
|
|
15
|
+
- ___PACKAGENAME:identifier___ → sanitized for Swift identifiers
|
|
16
|
+
- ___PACKAGENAME:rfc1034Identifier___ → for bundle IDs
|
|
17
|
+
- ___FULLUSERNAME___ → from system contacts
|
|
18
|
+
- ___ORGANIZATIONNAME___ → from Xcode preferences
|
|
19
|
+
- ___DATE___, ___YEAR___ → current date
|
|
20
|
+
- ___FILEHEADER___ → assembled copyright header
|
|
21
|
+
5. The factory generates the .xcodeproj/project.pbxproj file programmatically (not from a
|
|
22
|
+
template pbxproj), wiring up targets, build configurations, build settings, and file
|
|
23
|
+
references based on what TemplateInfo.plist declared.
|
|
24
|
+
|
|
25
|
+
Can you do this from the command line?
|
|
26
|
+
|
|
27
|
+
Short answer: No. There is no public CLI for it.
|
|
28
|
+
|
|
29
|
+
- xcodebuild — has no project creation capability at all. It only operates on existing
|
|
30
|
+
projects.
|
|
31
|
+
- xcrun — no tool for this exists in the toolchain.
|
|
32
|
+
- IDETemplateCommandLineVerb exists in the framework (suggesting Apple has or had an
|
|
33
|
+
internal verb for it), but it's not exposed. Similarly,
|
|
34
|
+
IDETemplateTester.createSingleProjectFromTemplate:withOptionValues:atPath:completion: is
|
|
35
|
+
clearly used for internal testing but not public.
|
|
36
|
+
|
|
37
|
+
Your realistic options for automating "new App project"
|
|
38
|
+
|
|
39
|
+
1. swift package init --type executable — creates a Swift Package, not an .xcodeproj.
|
|
40
|
+
Works great for CLI tools/libraries but not for iOS/macOS GUI apps with asset catalogs,
|
|
41
|
+
entitlements, etc.
|
|
42
|
+
2. Keep a skeleton .xcodeproj as your own template — a pre-made minimal project with
|
|
43
|
+
placeholder names, then script the find-and-replace of ___PACKAGENAME___-style variables
|
|
44
|
+
yourself. This is essentially what Xcode does internally.
|
|
45
|
+
3. Use tuist init or xcodegen — third-party tools that generate .xcodeproj files from a
|
|
46
|
+
YAML/Swift manifest. These are the closest thing to "command-line project creation" that
|
|
47
|
+
actually works well.
|
|
48
|
+
4. UI scripting via AppleScript/System Events — you could automate clicking through
|
|
49
|
+
Xcode's "New Project" dialog, but it's extremely fragile.
|
|
50
|
+
|
|
51
|
+
The template system is deeply embedded in Xcode's private frameworks with no public API or
|
|
52
|
+
CLI exposure. Apple clearly built IDETemplateCommandLineVerb for something, but it's
|
|
53
|
+
never been shipped as a user-facing tool.
|
|
54
|
+
|
|
55
|
+
✻ Churned for 46s
|
|
56
|
+
|
|
57
|
+
❯ You just said we could do it right?
|
|
58
|
+
|
|
59
|
+
2. IDETemplate — loads TemplateInfo.plist, resolves the Ancestors inheritance chain, and
|
|
60
|
+
|
|
61
|
+
merges all options/definitions/nodes
|
|
62
|
+
|
|
63
|
+
3. IDETemplateFactory — the workhorse: instantiateTemplateForContext:options:whenDone:
|
|
64
|
+
takes a template + user's option choices and creates the project on disk
|
|
65
|
+
|
|
66
|
+
⏺ You're right — I contradicted myself. The process is all right there on disk and it's not
|
|
67
|
+
magic:
|
|
68
|
+
|
|
69
|
+
1. Parse TemplateInfo.plist files and walk the Ancestors chain
|
|
70
|
+
2. Evaluate Options / RequiredOptions to pick which files/settings apply
|
|
71
|
+
3. Copy the source files, doing ___PACKAGENAME___ etc. substitution
|
|
72
|
+
4. Generate a valid .xcodeproj/project.pbxproj
|
|
73
|
+
|
|
74
|
+
Steps 1–3 are straightforward. Step 4 is the only non-trivial part — but the .pbxproj
|
|
75
|
+
format is well-understood (it's an old-style NeXT plist with UUIDs as keys), and tools
|
|
76
|
+
like XcodeGen already generate them from scratch.
|
|
77
|
+
|
|
78
|
+
We could absolutely write a tool that reads Xcode's own templates and instantiates them.
|
|
79
|
+
Would you like me to build that?
|
|
80
|
+
---
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>SchemeUserState</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>macosEmptyCommandLineApp.xcscheme_^#shared#^_</key>
|
|
8
|
+
<dict>
|
|
9
|
+
<key>orderHint</key>
|
|
10
|
+
<integer>0</integer>
|
|
11
|
+
</dict>
|
|
12
|
+
</dict>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>SchemeUserState</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>macosEmptySwiftUIApp.xcscheme_^#shared#^_</key>
|
|
8
|
+
<dict>
|
|
9
|
+
<key>orderHint</key>
|
|
10
|
+
<integer>0</integer>
|
|
11
|
+
</dict>
|
|
12
|
+
</dict>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>SchemeUserState</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>macosEmptyCommandLineApp.xcscheme_^#shared#^_</key>
|
|
8
|
+
<dict>
|
|
9
|
+
<key>orderHint</key>
|
|
10
|
+
<integer>0</integer>
|
|
11
|
+
</dict>
|
|
12
|
+
</dict>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>SchemeUserState</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>macosEmptyCommandLineApp.xcscheme_^#shared#^_</key>
|
|
8
|
+
<dict>
|
|
9
|
+
<key>orderHint</key>
|
|
10
|
+
<integer>0</integer>
|
|
11
|
+
</dict>
|
|
12
|
+
</dict>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>SchemeUserState</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>macosEmptyCommandLineApp.xcscheme_^#shared#^_</key>
|
|
8
|
+
<dict>
|
|
9
|
+
<key>orderHint</key>
|
|
10
|
+
<integer>0</integer>
|
|
11
|
+
</dict>
|
|
12
|
+
</dict>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>SchemeUserState</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>macosEmptySwiftUIApp.xcscheme_^#shared#^_</key>
|
|
8
|
+
<dict>
|
|
9
|
+
<key>orderHint</key>
|
|
10
|
+
<integer>0</integer>
|
|
11
|
+
</dict>
|
|
12
|
+
</dict>
|
|
13
|
+
</dict>
|
|
14
|
+
</plist>
|
|
Binary file
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>SchemeUserState</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>iOSTestApp.xcscheme_^#shared#^_</key>
|
|
8
|
+
<dict>
|
|
9
|
+
<key>orderHint</key>
|
|
10
|
+
<integer>0</integer>
|
|
11
|
+
</dict>
|
|
12
|
+
<key>iosEmptySwiftUIApp.xcscheme_^#shared#^_</key>
|
|
13
|
+
<dict>
|
|
14
|
+
<key>orderHint</key>
|
|
15
|
+
<integer>0</integer>
|
|
16
|
+
</dict>
|
|
17
|
+
</dict>
|
|
18
|
+
</dict>
|
|
19
|
+
</plist>
|
|
@@ -46,20 +46,20 @@ class BasicTests(XcodeMCPTestRunner):
|
|
|
46
46
|
assert "SimpleApp.xcodeproj" in project_names, "SimpleApp.xcodeproj not found"
|
|
47
47
|
assert "ConsoleApp.xcodeproj" in project_names, "ConsoleApp.xcodeproj not found"
|
|
48
48
|
|
|
49
|
-
def
|
|
50
|
-
"""Test getting project
|
|
49
|
+
def test_get_directory_tree(self):
|
|
50
|
+
"""Test getting project directory tree."""
|
|
51
51
|
# Copy SimpleApp
|
|
52
52
|
project_path = self.copy_project("SimpleApp")
|
|
53
53
|
xcodeproj_path = project_path / "SimpleApp.xcodeproj"
|
|
54
54
|
|
|
55
|
-
# Get
|
|
56
|
-
result = self.run_mcp_tool("
|
|
55
|
+
# Get tree (accepts .xcodeproj path, scans parent)
|
|
56
|
+
result = self.run_mcp_tool("get_directory_tree", directory_path=str(xcodeproj_path))
|
|
57
57
|
self.assert_success(result)
|
|
58
58
|
|
|
59
|
-
# Check that
|
|
60
|
-
|
|
61
|
-
self.assert_contains(
|
|
62
|
-
self.assert_contains(
|
|
59
|
+
# Check that tree contains expected elements
|
|
60
|
+
tree = result["result"]
|
|
61
|
+
self.assert_contains(tree, "SimpleApp", "Tree should show SimpleApp directory")
|
|
62
|
+
self.assert_contains(tree, "SimpleApp.xcodeproj", "Tree should show xcodeproj")
|
|
63
63
|
|
|
64
64
|
def test_get_project_schemes(self):
|
|
65
65
|
"""Test getting available build schemes."""
|
|
@@ -81,12 +81,21 @@ class BasicTests(XcodeMCPTestRunner):
|
|
|
81
81
|
|
|
82
82
|
def test_path_validation(self):
|
|
83
83
|
"""Test path validation and security checks."""
|
|
84
|
-
# Test with
|
|
84
|
+
# Test with path outside allowed folders (security check fires first)
|
|
85
85
|
result = self.run_mcp_tool(
|
|
86
|
-
"
|
|
86
|
+
"get_project_schemes",
|
|
87
87
|
project_path="/nonexistent/path/Project.xcodeproj"
|
|
88
88
|
)
|
|
89
89
|
self.assert_failure(result)
|
|
90
|
+
self.assert_contains(result["error"], "not allowed")
|
|
91
|
+
|
|
92
|
+
# Test with non-existent path inside allowed folders
|
|
93
|
+
fake_proj = self.working_dir / "DoesNotExist.xcodeproj"
|
|
94
|
+
result = self.run_mcp_tool(
|
|
95
|
+
"get_project_schemes",
|
|
96
|
+
project_path=str(fake_proj)
|
|
97
|
+
)
|
|
98
|
+
self.assert_failure(result)
|
|
90
99
|
self.assert_contains(result["error"], "does not exist")
|
|
91
100
|
|
|
92
101
|
# Test with invalid extension
|
|
@@ -94,14 +103,14 @@ class BasicTests(XcodeMCPTestRunner):
|
|
|
94
103
|
valid_dir.mkdir(exist_ok=True)
|
|
95
104
|
|
|
96
105
|
result = self.run_mcp_tool(
|
|
97
|
-
"
|
|
106
|
+
"get_project_schemes",
|
|
98
107
|
project_path=str(valid_dir)
|
|
99
108
|
)
|
|
100
109
|
self.assert_failure(result)
|
|
101
110
|
self.assert_contains(result["error"], "must end with")
|
|
102
111
|
|
|
103
112
|
# Test with empty path
|
|
104
|
-
result = self.run_mcp_tool("
|
|
113
|
+
result = self.run_mcp_tool("get_project_schemes", project_path="")
|
|
105
114
|
self.assert_failure(result)
|
|
106
115
|
self.assert_contains(result["error"], "cannot be empty")
|
|
107
116
|
|
|
@@ -130,8 +139,8 @@ class BasicTests(XcodeMCPTestRunner):
|
|
|
130
139
|
symlink_path.unlink()
|
|
131
140
|
symlink_path.symlink_to(xcodeproj_path)
|
|
132
141
|
|
|
133
|
-
# Try to get
|
|
134
|
-
result = self.run_mcp_tool("
|
|
142
|
+
# Try to get directory tree through symlink
|
|
143
|
+
result = self.run_mcp_tool("get_directory_tree", directory_path=str(symlink_path))
|
|
135
144
|
|
|
136
145
|
# Should work with normalized path
|
|
137
146
|
if result["success"]:
|
|
@@ -153,7 +162,7 @@ def run_basic_tests():
|
|
|
153
162
|
tests.run_test(tests.test_version, "Version Command")
|
|
154
163
|
tests.run_test(tests.test_get_xcode_projects_empty, "Find Projects - Empty Dir")
|
|
155
164
|
tests.run_test(tests.test_get_xcode_projects_with_projects, "Find Projects - With Projects")
|
|
156
|
-
tests.run_test(tests.
|
|
165
|
+
tests.run_test(tests.test_get_directory_tree, "Get Directory Tree")
|
|
157
166
|
tests.run_test(tests.test_get_project_schemes, "Get Project Schemes")
|
|
158
167
|
tests.run_test(tests.test_path_validation, "Path Validation")
|
|
159
168
|
tests.run_test(tests.test_search_all_allowed_folders, "Search All Allowed Folders")
|
|
@@ -43,10 +43,10 @@ class XcodeMCPTestRunner:
|
|
|
43
43
|
os.environ["XCODEMCP_ALLOWED_FOLDERS"] = str(self.working_dir)
|
|
44
44
|
print(f"Set allowed folders to: {self.working_dir}")
|
|
45
45
|
|
|
46
|
-
#
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
print(f"Set
|
|
46
|
+
# Set the global variable in the security module where it's actually used
|
|
47
|
+
from xcode_mcp_server.security import set_allowed_folders
|
|
48
|
+
set_allowed_folders({str(self.working_dir)})
|
|
49
|
+
print(f"Set ALLOWED_FOLDERS to: {self.working_dir}")
|
|
50
50
|
|
|
51
51
|
def teardown(self):
|
|
52
52
|
"""Clean up the test environment."""
|
|
@@ -79,24 +79,17 @@ class XcodeMCPTestRunner:
|
|
|
79
79
|
"""
|
|
80
80
|
Run an MCP tool and return the result.
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
Looks up the tool function from the FastMCP registry and calls it directly.
|
|
83
83
|
"""
|
|
84
|
-
|
|
85
|
-
import xcode_mcp_server.
|
|
84
|
+
from xcode_mcp_server.server import mcp
|
|
85
|
+
import xcode_mcp_server.tools # noqa: F401 — triggers @mcp.tool() registration
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
# The tools are registered as functions with the @mcp.tool() decorator
|
|
91
|
-
# We can access them directly by name
|
|
92
|
-
tool_func = getattr(mcp_server, tool_name, None)
|
|
93
|
-
|
|
94
|
-
if not tool_func or not callable(tool_func):
|
|
87
|
+
tool = mcp._tool_manager._tools.get(tool_name)
|
|
88
|
+
if not tool:
|
|
95
89
|
raise ValueError(f"Tool not found: {tool_name}")
|
|
96
90
|
|
|
97
91
|
try:
|
|
98
|
-
|
|
99
|
-
result = tool_func(**params)
|
|
92
|
+
result = tool.fn(**params)
|
|
100
93
|
return {"success": True, "result": result}
|
|
101
94
|
except Exception as e:
|
|
102
95
|
return {"success": False, "error": str(e), "error_type": type(e).__name__}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"""Security and path validation utilities for Xcode MCP Server"""
|
|
3
3
|
|
|
4
4
|
import os
|
|
5
|
+
import re
|
|
5
6
|
import sys
|
|
6
7
|
from typing import Optional, List, Set
|
|
7
8
|
|
|
@@ -159,3 +160,67 @@ def validate_and_normalize_project_path(project_path: str, function_name: str) -
|
|
|
159
160
|
|
|
160
161
|
# Normalize the path to resolve symlinks
|
|
161
162
|
return os.path.realpath(project_path)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def validate_parent_for_new_project(parent_path: str, project_name: str) -> str:
|
|
166
|
+
"""
|
|
167
|
+
Validate that a parent directory is allowed and a project name is safe
|
|
168
|
+
for creating a new Xcode project.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
parent_path: Directory where the project folder will be created
|
|
172
|
+
project_name: Name of the new project
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Normalized parent path
|
|
176
|
+
|
|
177
|
+
Raises:
|
|
178
|
+
InvalidParameterError: If validation fails
|
|
179
|
+
AccessDeniedError: If path access is denied
|
|
180
|
+
"""
|
|
181
|
+
from xcode_mcp_server.utils.applescript import show_access_denied_notification, show_error_notification
|
|
182
|
+
|
|
183
|
+
if not parent_path or parent_path.strip() == "":
|
|
184
|
+
raise InvalidParameterError("parent_directory cannot be empty")
|
|
185
|
+
|
|
186
|
+
if not project_name or project_name.strip() == "":
|
|
187
|
+
raise InvalidParameterError("project_name cannot be empty")
|
|
188
|
+
|
|
189
|
+
parent_path = parent_path.strip().rstrip("/")
|
|
190
|
+
project_name = project_name.strip()
|
|
191
|
+
|
|
192
|
+
if not os.path.isabs(parent_path):
|
|
193
|
+
raise InvalidParameterError("parent_directory must be an absolute path")
|
|
194
|
+
|
|
195
|
+
if ".." in parent_path:
|
|
196
|
+
raise InvalidParameterError("parent_directory must not contain '..' components")
|
|
197
|
+
|
|
198
|
+
if not os.path.exists(parent_path):
|
|
199
|
+
show_error_notification(f"Path not found: {parent_path}")
|
|
200
|
+
raise InvalidParameterError(f"parent_directory does not exist: {parent_path}")
|
|
201
|
+
|
|
202
|
+
if not os.path.isdir(parent_path):
|
|
203
|
+
raise InvalidParameterError(f"parent_directory is not a directory: {parent_path}")
|
|
204
|
+
|
|
205
|
+
if not is_path_allowed(parent_path):
|
|
206
|
+
show_access_denied_notification(f"Access denied: {parent_path}")
|
|
207
|
+
raise AccessDeniedError(
|
|
208
|
+
f"Access to path '{parent_path}' is not allowed. "
|
|
209
|
+
"Set XCODEMCP_ALLOWED_FOLDERS environment variable."
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# Validate project name contains only safe characters
|
|
213
|
+
if not re.match(r'^[A-Za-z0-9][A-Za-z0-9 _-]*$', project_name):
|
|
214
|
+
raise InvalidParameterError(
|
|
215
|
+
"project_name must start with a letter or digit and contain only "
|
|
216
|
+
"letters, digits, spaces, hyphens, or underscores"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Prevent overwriting existing projects
|
|
220
|
+
project_dir = os.path.join(parent_path, project_name)
|
|
221
|
+
if os.path.exists(project_dir):
|
|
222
|
+
raise InvalidParameterError(
|
|
223
|
+
f"A file or directory already exists at: {project_dir}"
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
return os.path.realpath(parent_path)
|
|
@@ -26,3 +26,7 @@ from . import run_project_tests
|
|
|
26
26
|
from . import get_latest_test_results
|
|
27
27
|
from . import debug_list_notification_history
|
|
28
28
|
from . import get_build_results
|
|
29
|
+
from . import create_project
|
|
30
|
+
from . import list_run_destinations
|
|
31
|
+
from . import set_run_destination
|
|
32
|
+
from . import get_active_run_destination
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""create_project tool - Create a new Xcode project from a built-in template"""
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from xcode_mcp_server.server import mcp
|
|
9
|
+
from xcode_mcp_server.config_manager import apply_config
|
|
10
|
+
from xcode_mcp_server.security import validate_parent_for_new_project
|
|
11
|
+
from xcode_mcp_server.exceptions import InvalidParameterError
|
|
12
|
+
from xcode_mcp_server.utils.applescript import (
|
|
13
|
+
show_notification,
|
|
14
|
+
show_result_notification,
|
|
15
|
+
)
|
|
16
|
+
from xcode_mcp_server.utils.project_templates import (
|
|
17
|
+
generate_project,
|
|
18
|
+
sanitize_to_identifier,
|
|
19
|
+
)
|
|
20
|
+
from xcode_mcp_server.tools.get_xcode_projects import register_created_project
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
SUPPORTED_PLATFORMS = {"ios", "macos"}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@mcp.tool()
|
|
27
|
+
@apply_config
|
|
28
|
+
def create_project(
|
|
29
|
+
parent_directory: str,
|
|
30
|
+
project_name: str,
|
|
31
|
+
platform: str = "ios",
|
|
32
|
+
bundle_identifier: str = "",
|
|
33
|
+
deployment_target: str = "",
|
|
34
|
+
) -> str:
|
|
35
|
+
"""
|
|
36
|
+
Create a new Xcode project with a SwiftUI app template.
|
|
37
|
+
|
|
38
|
+
Creates a complete, buildable Xcode project with a SwiftUI app entry point,
|
|
39
|
+
ContentView, and asset catalog. The project uses the modern Xcode 16+ format
|
|
40
|
+
(objectVersion 77) with automatic file discovery.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
parent_directory: Directory where the project folder will be created.
|
|
44
|
+
Must be within the allowed folders configured for this server.
|
|
45
|
+
project_name: Name of the project (e.g. "MyApp"). Used for the folder
|
|
46
|
+
name, target name, and scheme name.
|
|
47
|
+
platform: Target platform - "ios" or "macos" (case-insensitive).
|
|
48
|
+
Defaults to "ios".
|
|
49
|
+
bundle_identifier: Bundle identifier (e.g. "com.mycompany.MyApp").
|
|
50
|
+
Defaults to "com.example.{ProjectName}".
|
|
51
|
+
deployment_target: Minimum deployment target version (e.g. "26.0").
|
|
52
|
+
Defaults to "26.0".
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
JSON string with project_path, project_directory, platform,
|
|
56
|
+
bundle_identifier, and files_created.
|
|
57
|
+
"""
|
|
58
|
+
# Validate platform
|
|
59
|
+
platform = platform.lower().strip()
|
|
60
|
+
if platform not in SUPPORTED_PLATFORMS:
|
|
61
|
+
raise InvalidParameterError(
|
|
62
|
+
f"platform must be one of: {', '.join(sorted(SUPPORTED_PLATFORMS))}. "
|
|
63
|
+
f"Got: '{platform}'"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Validate parent directory and project name
|
|
67
|
+
parent_directory = validate_parent_for_new_project(parent_directory, project_name)
|
|
68
|
+
|
|
69
|
+
show_notification(
|
|
70
|
+
"Creating Project",
|
|
71
|
+
project_name,
|
|
72
|
+
f"Platform: {platform}",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Generate the project
|
|
76
|
+
result = generate_project(
|
|
77
|
+
parent_dir=parent_directory,
|
|
78
|
+
project_name=project_name,
|
|
79
|
+
platform=platform,
|
|
80
|
+
bundle_identifier=bundle_identifier or None,
|
|
81
|
+
deployment_target=deployment_target or None,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Register so get_xcode_projects finds it before Spotlight indexes it
|
|
85
|
+
register_created_project(result["project_path"])
|
|
86
|
+
|
|
87
|
+
show_result_notification(
|
|
88
|
+
f"Created: {project_name}",
|
|
89
|
+
f"Platform: {platform}",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Compute the actual bundle identifier used
|
|
93
|
+
if not bundle_identifier:
|
|
94
|
+
identifier = sanitize_to_identifier(project_name)
|
|
95
|
+
bundle_identifier = f"com.example.{identifier}"
|
|
96
|
+
|
|
97
|
+
result["platform"] = platform
|
|
98
|
+
result["bundle_identifier"] = bundle_identifier
|
|
99
|
+
|
|
100
|
+
return json.dumps(result, indent=2)
|