Skip to content

Commit 244f618

Browse files
lapfelixclaude
andcommitted
Add preferred scheme and xcodeproj support
This update implements configurable preferred values for scheme and xcodeproj parameters, reducing repetition when working with single projects while maintaining full backward compatibility. Key Features: - Preferred values configurable via environment variables (XCODE_MCP_PREFERRED_SCHEME, XCODE_MCP_PREFERRED_XCODEPROJ) or CLI args (--preferred-scheme, --preferred-xcodeproj) - Tool parameters become optional when preferred values are provided - Tool descriptions dynamically show default values (e.g., "defaults to MyApp.xcodeproj") - CLI help displays active preferred values in header - Full backward compatibility - existing usage unchanged Technical Implementation: - Updated XcodeServer constructor to accept preferred values - Modified getToolDefinitions() to conditionally make parameters optional - Enhanced tool descriptions with dynamic default value indicators - Added comprehensive test suite (14 test cases) - Parameter resolution applies preferred values before validation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent a44e557 commit 244f618

22 files changed

+558
-123
lines changed
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { getToolDefinitions } from '../src/shared/toolDefinitions.js';
3+
import { XcodeServer } from '../src/XcodeServer.js';
4+
5+
describe('Preferred Values', () => {
6+
describe('Tool Definitions', () => {
7+
it('should make xcodeproj optional when preferredXcodeproj is provided', () => {
8+
const tools = getToolDefinitions({
9+
preferredXcodeproj: 'MyApp.xcodeproj'
10+
});
11+
12+
const buildTool = tools.find(t => t.name === 'xcode_build');
13+
expect(buildTool).toBeDefined();
14+
expect(buildTool!.inputSchema.required).not.toContain('xcodeproj');
15+
expect(buildTool!.inputSchema.properties.xcodeproj.description).toContain('defaults to MyApp.xcodeproj');
16+
});
17+
18+
it('should make scheme optional when preferredScheme is provided', () => {
19+
const tools = getToolDefinitions({
20+
preferredScheme: 'MyAppScheme'
21+
});
22+
23+
const buildTool = tools.find(t => t.name === 'xcode_build');
24+
expect(buildTool).toBeDefined();
25+
expect(buildTool!.inputSchema.required).not.toContain('scheme');
26+
expect(buildTool!.inputSchema.properties.scheme.description).toContain('defaults to MyAppScheme');
27+
});
28+
29+
it('should make both optional when both preferred values are provided', () => {
30+
const tools = getToolDefinitions({
31+
preferredXcodeproj: 'MyApp.xcodeproj',
32+
preferredScheme: 'MyAppScheme'
33+
});
34+
35+
const buildTool = tools.find(t => t.name === 'xcode_build');
36+
expect(buildTool).toBeDefined();
37+
expect(buildTool!.inputSchema.required).toEqual([]);
38+
expect(buildTool!.inputSchema.properties.xcodeproj.description).toContain('defaults to MyApp.xcodeproj');
39+
expect(buildTool!.inputSchema.properties.scheme.description).toContain('defaults to MyAppScheme');
40+
});
41+
42+
it('should keep parameters required when no preferred values are provided', () => {
43+
const tools = getToolDefinitions({});
44+
45+
const buildTool = tools.find(t => t.name === 'xcode_build');
46+
expect(buildTool).toBeDefined();
47+
expect(buildTool!.inputSchema.required).toContain('xcodeproj');
48+
expect(buildTool!.inputSchema.required).toContain('scheme');
49+
expect(buildTool!.inputSchema.properties.xcodeproj.description).not.toContain('defaults to');
50+
expect(buildTool!.inputSchema.properties.scheme.description).not.toContain('defaults to');
51+
});
52+
53+
it('should update descriptions for all tools that use xcodeproj', () => {
54+
const tools = getToolDefinitions({
55+
preferredXcodeproj: 'MyApp.xcodeproj'
56+
});
57+
58+
const toolsWithXcodeproj = [
59+
'xcode_open_project',
60+
'xcode_close_project',
61+
'xcode_build',
62+
'xcode_get_schemes',
63+
'xcode_set_active_scheme',
64+
'xcode_test',
65+
'xcode_build_and_run',
66+
'xcode_debug',
67+
'xcode_stop',
68+
'find_xcresults',
69+
'xcode_get_run_destinations',
70+
'xcode_get_workspace_info',
71+
'xcode_get_projects'
72+
];
73+
74+
toolsWithXcodeproj.forEach(toolName => {
75+
const tool = tools.find(t => t.name === toolName);
76+
expect(tool, `Tool ${toolName} should exist`).toBeDefined();
77+
if (tool && tool.inputSchema.properties.xcodeproj) {
78+
expect(tool.inputSchema.properties.xcodeproj.description, `Tool ${toolName} should have updated description`).toContain('defaults to MyApp.xcodeproj');
79+
80+
// Most tools should have xcodeproj not required when preferred value is set
81+
if (toolName !== 'xcode_set_active_scheme') { // This one still requires scheme_name
82+
expect(tool.inputSchema.required).not.toContain('xcodeproj');
83+
}
84+
}
85+
});
86+
});
87+
88+
it('should handle xcode_clean when includeClean is true', () => {
89+
const tools = getToolDefinitions({
90+
includeClean: true,
91+
preferredXcodeproj: 'MyApp.xcodeproj'
92+
});
93+
94+
const cleanTool = tools.find(t => t.name === 'xcode_clean');
95+
expect(cleanTool).toBeDefined();
96+
expect(cleanTool!.inputSchema.required).toEqual([]);
97+
expect(cleanTool!.inputSchema.properties.xcodeproj.description).toContain('defaults to MyApp.xcodeproj');
98+
});
99+
100+
it('should not include xcode_clean when includeClean is false', () => {
101+
const tools = getToolDefinitions({
102+
includeClean: false,
103+
preferredXcodeproj: 'MyApp.xcodeproj'
104+
});
105+
106+
const cleanTool = tools.find(t => t.name === 'xcode_clean');
107+
expect(cleanTool).toBeUndefined();
108+
});
109+
});
110+
111+
describe('XcodeServer', () => {
112+
let server: XcodeServer;
113+
114+
beforeEach(() => {
115+
vi.clearAllMocks();
116+
});
117+
118+
it('should store preferred values when initialized', () => {
119+
server = new XcodeServer({
120+
preferredScheme: 'MyScheme',
121+
preferredXcodeproj: 'MyProject.xcodeproj'
122+
});
123+
124+
// Check that the server was created successfully
125+
expect(server).toBeDefined();
126+
expect(server.server).toBeDefined();
127+
});
128+
129+
it('should work without preferred values', () => {
130+
server = new XcodeServer({});
131+
132+
// Check that the server was created successfully
133+
expect(server).toBeDefined();
134+
expect(server.server).toBeDefined();
135+
});
136+
137+
it('should handle only preferredScheme', () => {
138+
server = new XcodeServer({
139+
preferredScheme: 'MyScheme'
140+
});
141+
142+
// Check that the server was created successfully
143+
expect(server).toBeDefined();
144+
expect(server.server).toBeDefined();
145+
});
146+
147+
it('should handle only preferredXcodeproj', () => {
148+
server = new XcodeServer({
149+
preferredXcodeproj: 'MyProject.xcodeproj'
150+
});
151+
152+
// Check that the server was created successfully
153+
expect(server).toBeDefined();
154+
expect(server.server).toBeDefined();
155+
});
156+
});
157+
158+
describe('Tool Parameter Required Arrays', () => {
159+
it('should correctly build required array for xcode_build', () => {
160+
// No preferred values - both required
161+
let tools = getToolDefinitions({});
162+
let buildTool = tools.find(t => t.name === 'xcode_build');
163+
expect(buildTool!.inputSchema.required).toEqual(['xcodeproj', 'scheme']);
164+
165+
// Only preferred xcodeproj - scheme still required
166+
tools = getToolDefinitions({ preferredXcodeproj: 'MyApp.xcodeproj' });
167+
buildTool = tools.find(t => t.name === 'xcode_build');
168+
expect(buildTool!.inputSchema.required).toEqual(['scheme']);
169+
170+
// Only preferred scheme - xcodeproj still required
171+
tools = getToolDefinitions({ preferredScheme: 'MyScheme' });
172+
buildTool = tools.find(t => t.name === 'xcode_build');
173+
expect(buildTool!.inputSchema.required).toEqual(['xcodeproj']);
174+
175+
// Both preferred - nothing required
176+
tools = getToolDefinitions({
177+
preferredXcodeproj: 'MyApp.xcodeproj',
178+
preferredScheme: 'MyScheme'
179+
});
180+
buildTool = tools.find(t => t.name === 'xcode_build');
181+
expect(buildTool!.inputSchema.required).toEqual([]);
182+
});
183+
184+
it('should correctly build required array for xcode_test', () => {
185+
// No preferred values - xcodeproj and destination required
186+
let tools = getToolDefinitions({});
187+
let testTool = tools.find(t => t.name === 'xcode_test');
188+
expect(testTool!.inputSchema.required).toEqual(['xcodeproj', 'destination']);
189+
190+
// With preferred xcodeproj - only destination required
191+
tools = getToolDefinitions({ preferredXcodeproj: 'MyApp.xcodeproj' });
192+
testTool = tools.find(t => t.name === 'xcode_test');
193+
expect(testTool!.inputSchema.required).toEqual(['destination']);
194+
});
195+
196+
it('should correctly handle xcode_set_active_scheme', () => {
197+
// No preferred values - both required
198+
let tools = getToolDefinitions({});
199+
let schemeTool = tools.find(t => t.name === 'xcode_set_active_scheme');
200+
expect(schemeTool!.inputSchema.required).toEqual(['xcodeproj', 'scheme_name']);
201+
202+
// With preferred xcodeproj - only scheme_name required
203+
tools = getToolDefinitions({ preferredXcodeproj: 'MyApp.xcodeproj' });
204+
schemeTool = tools.find(t => t.name === 'xcode_set_active_scheme');
205+
expect(schemeTool!.inputSchema.required).toEqual(['scheme_name']);
206+
});
207+
});
208+
});

__tests__/setup.js

Lines changed: 0 additions & 27 deletions
This file was deleted.

dist/XcodeServer.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ export declare class XcodeServer {
88
private isValidated;
99
private canOperateInDegradedMode;
1010
private includeClean;
11+
private preferredScheme;
12+
private preferredXcodeproj;
1113
constructor(options?: {
1214
includeClean?: boolean;
15+
preferredScheme?: string;
16+
preferredXcodeproj?: string;
1317
});
1418
/**
1519
* Validates the environment and sets up the server accordingly

dist/XcodeServer.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/XcodeServer.js

Lines changed: 27 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/XcodeServer.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/cli.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)