Skip to content

Commit d5c9e4e

Browse files
Refactor: Consolidate schema helpers and update tool references
Co-authored-by: web <web@cameroncooke.com>
1 parent da42459 commit d5c9e4e

22 files changed

Lines changed: 94 additions & 223 deletions

docs/MANUAL_TESTING.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,8 @@ fi
244244
- `discover_projs` - Project/workspace paths
245245

246246
2. **Discovery Tools** (provide metadata for build tools):
247-
- `list_schems_proj` / `list_schems_ws` - Scheme names
248-
- `show_build_set_proj` / `show_build_set_ws` - Build settings
247+
- `list_schemes` - Scheme names
248+
- `show_build_settings` - Build settings
249249

250250
3. **Build Tools** (create artifacts for install tools):
251251
- `build_*` tools - Create app bundles
@@ -518,7 +518,7 @@ done < /tmp/project_paths.txt
518518
while IFS= read -r workspace_path; do
519519
if [ -n "$workspace_path" ]; then
520520
echo "Getting schemes for: $workspace_path"
521-
npx reloaderoo@latest inspect call-tool "list_schems_ws" --params "{\"workspacePath\": \"$workspace_path\"}" -- node build/index.js 2>/dev/null > /tmp/ws_schemes_$$.json
521+
npx reloaderoo@latest inspect call-tool "list_schemes" --params "{\"workspacePath\": \"$workspace_path\"}" -- node build/index.js 2>/dev/null > /tmp/ws_schemes_$$.json
522522
SCHEMES=$(jq -r '.content[1].text' /tmp/ws_schemes_$$.json 2>/dev/null || echo "NoScheme")
523523
echo "$workspace_path|$SCHEMES" >> /tmp/workspace_schemes.txt
524524
echo "Schemes captured for $workspace_path: $SCHEMES"
@@ -557,7 +557,7 @@ npx reloaderoo@latest inspect call-tool "list_schems_proj" --params '{"projectPa
557557
# [Record scheme names from response for build tools]
558558

559559
# STEP 3: Test workspace tools (use discovered workspace paths)
560-
npx reloaderoo@latest inspect call-tool "list_schems_ws" --params '{"workspacePath": "/actual/path/from/discover_projs.xcworkspace"}' -- node build/index.js
560+
npx reloaderoo@latest inspect call-tool "list_schemes" --params '{"workspacePath": "/actual/path/from/discover_projs.xcworkspace"}' -- node build/index.js
561561
# [Record scheme names from response for build tools]
562562

563563
# STEP 4: Test simulator tools (use captured simulator UUIDs from step 1)

docs/TESTING.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -737,8 +737,8 @@ fi
737737
- `discover_projs` - Project/workspace paths
738738

739739
2. **Discovery Tools** (provide metadata for build tools):
740-
- `list_schems_proj` / `list_schems_ws` - Scheme names
741-
- `show_build_set_proj` / `show_build_set_ws` - Build settings
740+
- `list_schemes` - Scheme names
741+
- `show_build_settings` - Build settings
742742

743743
3. **Build Tools** (create artifacts for install tools):
744744
- `build_*` tools - Create app bundles
@@ -1010,7 +1010,7 @@ done < /tmp/project_paths.txt
10101010
while IFS= read -r workspace_path; do
10111011
if [ -n "$workspace_path" ]; then
10121012
echo "Getting schemes for: $workspace_path"
1013-
npx reloaderoo@latest inspect call-tool "list_schems_ws" --params "{\"workspacePath\": \"$workspace_path\"}" -- node build/index.js 2>/dev/null > /tmp/ws_schemes_$$.json
1013+
npx reloaderoo@latest inspect call-tool "list_schemes" --params "{\"workspacePath\": \"$workspace_path\"}" -- node build/index.js 2>/dev/null > /tmp/ws_schemes_$$.json
10141014
SCHEMES=$(jq -r '.content[1].text' /tmp/ws_schemes_$$.json 2>/dev/null || echo "NoScheme")
10151015
echo "$workspace_path|$SCHEMES" >> /tmp/workspace_schemes.txt
10161016
echo "Schemes captured for $workspace_path: $SCHEMES"
@@ -1049,7 +1049,7 @@ npx reloaderoo@latest inspect call-tool "list_schems_proj" --params '{"projectPa
10491049
# [Record scheme names from response for build tools]
10501050

10511051
# STEP 3: Test workspace tools (use discovered workspace paths)
1052-
npx reloaderoo@latest inspect call-tool "list_schems_ws" --params '{"workspacePath": "/actual/path/from/discover_projs.xcworkspace"}' -- node build/index.js
1052+
npx reloaderoo@latest inspect call-tool "list_schemes" --params '{"workspacePath": "/actual/path/from/discover_projs.xcworkspace"}' -- node build/index.js
10531053
# [Record scheme names from response for build tools]
10541054

10551055
# STEP 4: Test simulator tools (use captured simulator UUIDs from step 1)

scripts/analysis/tools-analysis.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,17 @@ function isReExportFile(filePath: string): boolean {
146146
const content = fs.readFileSync(filePath, 'utf-8');
147147

148148
// Remove comments and empty lines, then check for re-export pattern
149-
const cleanedLines = content
149+
// First remove multi-line comments
150+
let contentWithoutBlockComments = content.replace(/\/\*[\s\S]*?\*\//g, '');
151+
152+
const cleanedLines = contentWithoutBlockComments
150153
.split('\n')
151-
.map((line) => line.trim())
152-
.filter((line) => line.length > 0 && !line.startsWith('//') && !line.startsWith('/*'));
154+
.map((line) => {
155+
// Remove inline comments but preserve the code before them
156+
const codeBeforeComment = line.split('//')[0].trim();
157+
return codeBeforeComment;
158+
})
159+
.filter((line) => line.length > 0);
153160

154161
// Should have exactly one line: export { default } from '...';
155162
if (cleanedLines.length !== 1) {

src/mcp/tools/device/__tests__/get_device_app_path.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import { describe, it, expect } from 'vitest';
88
import { createMockExecutor } from '../../../../utils/command.js';
9-
import getDeviceAppPath, { get_device_app_pathLogic } from '../get_device_app_path.js';
9+
import getDeviceAppPath, { get_device_app_pathLogic } from '../get_device_app_path.ts';
1010

1111
describe('get_device_app_path plugin', () => {
1212
describe('Export Field Validation (Literal)', () => {

src/mcp/tools/device/build_device.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,7 @@ import { ToolResponse, XcodePlatform } from '../../../types/common.js';
1010
import { executeXcodeBuildCommand } from '../../../utils/index.js';
1111
import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js';
1212
import { createTypedTool } from '../../../utils/typed-tool-factory.js';
13-
14-
// Helper: convert empty strings to undefined (shallow) so optional fields don't trip validation
15-
function nullifyEmptyStrings(value: unknown): unknown {
16-
if (value && typeof value === 'object' && !Array.isArray(value)) {
17-
const copy: Record<string, unknown> = { ...(value as Record<string, unknown>) };
18-
for (const key of Object.keys(copy)) {
19-
const v = copy[key];
20-
if (typeof v === 'string' && v.trim() === '') copy[key] = undefined;
21-
}
22-
return copy;
23-
}
24-
return value;
25-
}
13+
import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js';
2614

2715
// Unified schema: XOR between projectPath and workspacePath
2816
const baseSchemaObject = z.object({
@@ -78,7 +66,7 @@ export default {
7866
"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' })",
7967
schema: baseSchemaObject.shape,
8068
handler: createTypedTool<BuildDeviceParams>(
81-
buildDeviceSchema as unknown as z.ZodType<BuildDeviceParams>,
69+
buildDeviceSchema,
8270
buildDeviceLogic,
8371
getDefaultCommandExecutor,
8472
),

src/mcp/tools/device/get_device_app_path.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,7 @@ import { log } from '../../../utils/index.js';
1111
import { createTextResponse } from '../../../utils/index.js';
1212
import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js';
1313
import { createTypedTool } from '../../../utils/typed-tool-factory.js';
14-
15-
// Helper: convert empty strings to undefined (shallow) so optional fields don't trip validation
16-
function nullifyEmptyStrings(value: unknown): unknown {
17-
if (value && typeof value === 'object' && !Array.isArray(value)) {
18-
const copy: Record<string, unknown> = { ...(value as Record<string, unknown>) };
19-
for (const key of Object.keys(copy)) {
20-
const v = copy[key];
21-
if (typeof v === 'string' && v.trim() === '') copy[key] = undefined;
22-
}
23-
return copy;
24-
}
25-
return value;
26-
}
14+
import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js';
2715

2816
// Unified schema: XOR between projectPath and workspacePath, sharing common options
2917
const baseOptions = {
@@ -89,8 +77,11 @@ export async function get_device_app_pathLogic(
8977
// Add the project or workspace
9078
if (params.projectPath) {
9179
command.push('-project', params.projectPath);
80+
} else if (params.workspacePath) {
81+
command.push('-workspace', params.workspacePath);
9282
} else {
93-
command.push('-workspace', params.workspacePath!);
83+
// This should never happen due to schema validation
84+
throw new Error('Neither projectPath nor workspacePath provided');
9485
}
9586

9687
// Add the scheme and configuration
@@ -170,7 +161,7 @@ export default {
170161
"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' })",
171162
schema: baseSchemaObject.shape, // MCP SDK compatibility
172163
handler: createTypedTool<GetDeviceAppPathParams>(
173-
getDeviceAppPathSchema as unknown as z.ZodType<GetDeviceAppPathParams>,
164+
getDeviceAppPathSchema,
174165
get_device_app_pathLogic,
175166
getDefaultCommandExecutor,
176167
),

src/mcp/tools/device/test_device.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,7 @@ import {
1818
getDefaultFileSystemExecutor,
1919
} from '../../../utils/command.js';
2020
import { createTypedTool } from '../../../utils/typed-tool-factory.js';
21-
22-
// Helper: convert empty strings to undefined (shallow) so optional fields don't trip validation
23-
function nullifyEmptyStrings(value: unknown): unknown {
24-
if (value && typeof value === 'object' && !Array.isArray(value)) {
25-
const copy: Record<string, unknown> = { ...(value as Record<string, unknown>) };
26-
for (const key of Object.keys(copy)) {
27-
const v = copy[key];
28-
if (typeof v === 'string' && v.trim() === '') copy[key] = undefined;
29-
}
30-
return copy;
31-
}
32-
return value;
33-
}
21+
import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js';
3422

3523
// Unified schema: XOR between projectPath and workspacePath
3624
const baseSchemaObject = z.object({
@@ -265,7 +253,7 @@ export default {
265253
'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" })',
266254
schema: baseSchemaObject.shape,
267255
handler: createTypedTool<TestDeviceParams>(
268-
testDeviceSchema as unknown as z.ZodType<TestDeviceParams>,
256+
testDeviceSchema,
269257
(params: TestDeviceParams) => {
270258
return testDeviceLogic(
271259
{

src/mcp/tools/macos/build_macos.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { executeXcodeBuildCommand } from '../../../utils/index.js';
1111
import { ToolResponse, XcodePlatform } from '../../../types/common.js';
1212
import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js';
1313
import { createTypedTool } from '../../../utils/typed-tool-factory.js';
14+
import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js';
1415

1516
// Types for dependency injection
1617
export interface BuildUtilsDependencies {
@@ -22,19 +23,6 @@ const defaultBuildUtilsDependencies: BuildUtilsDependencies = {
2223
executeXcodeBuildCommand,
2324
};
2425

25-
// Helper: convert empty strings to undefined (shallow) so optional fields don't trip validation
26-
function nullifyEmptyStrings(value: unknown): unknown {
27-
if (value && typeof value === 'object' && !Array.isArray(value)) {
28-
const copy: Record<string, unknown> = { ...(value as Record<string, unknown>) };
29-
for (const key of Object.keys(copy)) {
30-
const v = copy[key];
31-
if (typeof v === 'string' && v.trim() === '') copy[key] = undefined;
32-
}
33-
return copy;
34-
}
35-
return value;
36-
}
37-
3826
// Unified schema: XOR between projectPath and workspacePath
3927
const baseSchemaObject = z.object({
4028
projectPath: z.string().optional().describe('Path to the .xcodeproj file'),
@@ -104,7 +92,7 @@ export default {
10492
"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' })",
10593
schema: baseSchemaObject.shape, // MCP SDK compatibility
10694
handler: createTypedTool<BuildMacOSParams>(
107-
buildMacOSSchema as unknown as z.ZodType<BuildMacOSParams>,
95+
buildMacOSSchema,
10896
buildMacOSLogic,
10997
getDefaultCommandExecutor,
11098
),

src/mcp/tools/macos/build_run_macos.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,7 @@ import { executeXcodeBuildCommand } from '../../../utils/index.js';
1212
import { ToolResponse, XcodePlatform } from '../../../types/common.js';
1313
import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/command.js';
1414
import { createTypedTool } from '../../../utils/typed-tool-factory.js';
15-
16-
// Helper: convert empty strings to undefined (shallow) so optional fields don't trip validation
17-
function nullifyEmptyStrings(value: unknown): unknown {
18-
if (value && typeof value === 'object' && !Array.isArray(value)) {
19-
const copy: Record<string, unknown> = { ...(value as Record<string, unknown>) };
20-
for (const key of Object.keys(copy)) {
21-
const v = copy[key];
22-
if (typeof v === 'string' && v.trim() === '') copy[key] = undefined;
23-
}
24-
return copy;
25-
}
26-
return value;
27-
}
15+
import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js';
2816

2917
// Unified schema: XOR between projectPath and workspacePath
3018
const baseSchemaObject = z.object({
@@ -222,7 +210,7 @@ export default {
222210
"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' })",
223211
schema: baseSchemaObject.shape, // MCP SDK compatibility
224212
handler: createTypedTool<BuildRunMacOSParams>(
225-
buildRunMacOSSchema as unknown as z.ZodType<BuildRunMacOSParams>,
213+
buildRunMacOSSchema,
226214
(params: BuildRunMacOSParams) =>
227215
buildRunMacOSLogic(
228216
{

src/mcp/tools/macos/get_macos_app_path.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,7 @@ import { ToolResponse } from '../../../types/common.js';
1010
import { log } from '../../../utils/index.js';
1111
import { CommandExecutor, getDefaultCommandExecutor } from '../../../utils/index.js';
1212
import { createTypedTool } from '../../../utils/typed-tool-factory.js';
13-
14-
// Helper: convert empty strings to undefined (shallow) so optional fields don't trip validation
15-
function nullifyEmptyStrings(value: unknown): unknown {
16-
if (value && typeof value === 'object' && !Array.isArray(value)) {
17-
const copy: Record<string, unknown> = { ...(value as Record<string, unknown>) };
18-
for (const key of Object.keys(copy)) {
19-
const v = copy[key];
20-
if (typeof v === 'string' && v.trim() === '') copy[key] = undefined;
21-
}
22-
return copy;
23-
}
24-
return value;
25-
}
13+
import { nullifyEmptyStrings } from '../../../utils/schema-helpers.js';
2614

2715
// Unified schema: XOR between projectPath and workspacePath, sharing common options
2816
const baseOptions = {
@@ -82,8 +70,11 @@ export async function get_macos_app_pathLogic(
8270
// Add the project or workspace
8371
if (params.projectPath) {
8472
command.push('-project', params.projectPath);
73+
} else if (params.workspacePath) {
74+
command.push('-workspace', params.workspacePath);
8575
} else {
86-
command.push('-workspace', params.workspacePath!);
76+
// This should never happen due to schema validation
77+
throw new Error('Neither projectPath nor workspacePath provided');
8778
}
8879

8980
// Add the scheme and configuration
@@ -194,7 +185,7 @@ export default {
194185
"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' })",
195186
schema: baseSchemaObject.shape, // MCP SDK compatibility
196187
handler: createTypedTool<GetMacosAppPathParams>(
197-
getMacosAppPathSchema as unknown as z.ZodType<GetMacosAppPathParams>,
188+
getMacosAppPathSchema,
198189
get_macos_app_pathLogic,
199190
getDefaultCommandExecutor,
200191
),

0 commit comments

Comments
 (0)