hatch-xclam 0.7.1.dev3__py3-none-any.whl → 0.8.0.dev1__py3-none-any.whl

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.
Files changed (81) hide show
  1. hatch/__init__.py +1 -1
  2. hatch/cli/__init__.py +71 -0
  3. hatch/cli/__main__.py +1035 -0
  4. hatch/cli/cli_env.py +865 -0
  5. hatch/cli/cli_mcp.py +1965 -0
  6. hatch/cli/cli_package.py +566 -0
  7. hatch/cli/cli_system.py +136 -0
  8. hatch/cli/cli_utils.py +1289 -0
  9. hatch/cli_hatch.py +160 -2838
  10. hatch/mcp_host_config/__init__.py +10 -10
  11. hatch/mcp_host_config/adapters/__init__.py +34 -0
  12. hatch/mcp_host_config/adapters/base.py +170 -0
  13. hatch/mcp_host_config/adapters/claude.py +105 -0
  14. hatch/mcp_host_config/adapters/codex.py +104 -0
  15. hatch/mcp_host_config/adapters/cursor.py +83 -0
  16. hatch/mcp_host_config/adapters/gemini.py +75 -0
  17. hatch/mcp_host_config/adapters/kiro.py +78 -0
  18. hatch/mcp_host_config/adapters/lmstudio.py +79 -0
  19. hatch/mcp_host_config/adapters/registry.py +149 -0
  20. hatch/mcp_host_config/adapters/vscode.py +83 -0
  21. hatch/mcp_host_config/backup.py +5 -3
  22. hatch/mcp_host_config/fields.py +126 -0
  23. hatch/mcp_host_config/models.py +161 -456
  24. hatch/mcp_host_config/reporting.py +57 -16
  25. hatch/mcp_host_config/strategies.py +155 -87
  26. hatch/template_generator.py +1 -1
  27. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/METADATA +3 -2
  28. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/RECORD +52 -43
  29. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/WHEEL +1 -1
  30. hatch_xclam-0.8.0.dev1.dist-info/entry_points.txt +2 -0
  31. tests/cli_test_utils.py +280 -0
  32. tests/integration/cli/__init__.py +14 -0
  33. tests/integration/cli/test_cli_reporter_integration.py +2439 -0
  34. tests/integration/mcp/__init__.py +0 -0
  35. tests/integration/mcp/test_adapter_serialization.py +173 -0
  36. tests/regression/cli/__init__.py +16 -0
  37. tests/regression/cli/test_color_logic.py +268 -0
  38. tests/regression/cli/test_consequence_type.py +298 -0
  39. tests/regression/cli/test_error_formatting.py +328 -0
  40. tests/regression/cli/test_result_reporter.py +586 -0
  41. tests/regression/cli/test_table_formatter.py +211 -0
  42. tests/regression/mcp/__init__.py +0 -0
  43. tests/regression/mcp/test_field_filtering.py +162 -0
  44. tests/test_cli_version.py +7 -5
  45. tests/test_data/fixtures/cli_reporter_fixtures.py +184 -0
  46. tests/unit/__init__.py +0 -0
  47. tests/unit/mcp/__init__.py +0 -0
  48. tests/unit/mcp/test_adapter_protocol.py +138 -0
  49. tests/unit/mcp/test_adapter_registry.py +158 -0
  50. tests/unit/mcp/test_config_model.py +146 -0
  51. hatch_xclam-0.7.1.dev3.dist-info/entry_points.txt +0 -2
  52. tests/integration/test_mcp_kiro_integration.py +0 -153
  53. tests/regression/test_mcp_codex_backup_integration.py +0 -162
  54. tests/regression/test_mcp_codex_host_strategy.py +0 -163
  55. tests/regression/test_mcp_codex_model_validation.py +0 -117
  56. tests/regression/test_mcp_kiro_backup_integration.py +0 -241
  57. tests/regression/test_mcp_kiro_cli_integration.py +0 -141
  58. tests/regression/test_mcp_kiro_decorator_registration.py +0 -71
  59. tests/regression/test_mcp_kiro_host_strategy.py +0 -214
  60. tests/regression/test_mcp_kiro_model_validation.py +0 -116
  61. tests/regression/test_mcp_kiro_omni_conversion.py +0 -104
  62. tests/test_mcp_atomic_operations.py +0 -276
  63. tests/test_mcp_backup_integration.py +0 -308
  64. tests/test_mcp_cli_all_host_specific_args.py +0 -496
  65. tests/test_mcp_cli_backup_management.py +0 -295
  66. tests/test_mcp_cli_direct_management.py +0 -456
  67. tests/test_mcp_cli_discovery_listing.py +0 -582
  68. tests/test_mcp_cli_host_config_integration.py +0 -823
  69. tests/test_mcp_cli_package_management.py +0 -360
  70. tests/test_mcp_cli_partial_updates.py +0 -859
  71. tests/test_mcp_environment_integration.py +0 -520
  72. tests/test_mcp_host_config_backup.py +0 -257
  73. tests/test_mcp_host_configuration_manager.py +0 -331
  74. tests/test_mcp_host_registry_decorator.py +0 -348
  75. tests/test_mcp_pydantic_architecture_v4.py +0 -603
  76. tests/test_mcp_server_config_models.py +0 -242
  77. tests/test_mcp_server_config_type_field.py +0 -221
  78. tests/test_mcp_sync_functionality.py +0 -316
  79. tests/test_mcp_user_feedback_reporting.py +0 -359
  80. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/licenses/LICENSE +0 -0
  81. {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/top_level.txt +0 -0
@@ -1,496 +0,0 @@
1
- """
2
- Tests for ALL host-specific CLI arguments in MCP configure command.
3
-
4
- This module tests that:
5
- 1. All host-specific arguments are accepted for all hosts
6
- 2. Unsupported fields are reported as "UNSUPPORTED" in conversion reports
7
- 3. All new arguments (httpUrl, includeTools, excludeTools, inputs) work correctly
8
- """
9
-
10
- import unittest
11
- from unittest.mock import patch, MagicMock
12
- from io import StringIO
13
-
14
- from hatch.cli_hatch import handle_mcp_configure, parse_input
15
- from hatch.mcp_host_config import MCPHostType
16
- from hatch.mcp_host_config.models import (
17
- MCPServerConfigGemini, MCPServerConfigCursor, MCPServerConfigVSCode,
18
- MCPServerConfigClaude, MCPServerConfigCodex
19
- )
20
-
21
-
22
- class TestAllGeminiArguments(unittest.TestCase):
23
- """Test ALL Gemini-specific CLI arguments."""
24
-
25
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
26
- @patch('sys.stdout', new_callable=StringIO)
27
- def test_all_gemini_arguments_accepted(self, mock_stdout, mock_manager_class):
28
- """Test that all Gemini arguments are accepted and passed to model."""
29
- mock_manager = MagicMock()
30
- mock_manager_class.return_value = mock_manager
31
-
32
- mock_result = MagicMock()
33
- mock_result.success = True
34
- mock_result.backup_path = None
35
- mock_manager.configure_server.return_value = mock_result
36
-
37
- result = handle_mcp_configure(
38
- host='gemini',
39
- server_name='test-server',
40
- command='python',
41
- args=['server.py'],
42
- timeout=30000,
43
- trust=True,
44
- cwd='/workspace',
45
- http_url='https://api.example.com/mcp',
46
- include_tools=['tool1', 'tool2'],
47
- exclude_tools=['dangerous_tool'],
48
- auto_approve=True
49
- )
50
-
51
- self.assertEqual(result, 0)
52
-
53
- # Verify all fields were passed to Gemini model
54
- call_args = mock_manager.configure_server.call_args
55
- server_config = call_args.kwargs['server_config']
56
- self.assertIsInstance(server_config, MCPServerConfigGemini)
57
- self.assertEqual(server_config.timeout, 30000)
58
- self.assertEqual(server_config.trust, True)
59
- self.assertEqual(server_config.cwd, '/workspace')
60
- self.assertEqual(server_config.httpUrl, 'https://api.example.com/mcp')
61
- self.assertEqual(server_config.includeTools, ['tool1', 'tool2'])
62
- self.assertEqual(server_config.excludeTools, ['dangerous_tool'])
63
-
64
-
65
- class TestUnsupportedFieldReporting(unittest.TestCase):
66
- """Test that unsupported fields are reported correctly, not rejected."""
67
-
68
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
69
- @patch('sys.stdout', new_callable=StringIO)
70
- def test_gemini_args_on_vscode_show_unsupported(self, mock_stdout, mock_manager_class):
71
- """Test that Gemini-specific args on VS Code show as UNSUPPORTED."""
72
- mock_manager = MagicMock()
73
- mock_manager_class.return_value = mock_manager
74
-
75
- mock_result = MagicMock()
76
- mock_result.success = True
77
- mock_result.backup_path = None
78
- mock_manager.configure_server.return_value = mock_result
79
-
80
- result = handle_mcp_configure(
81
- host='vscode',
82
- server_name='test-server',
83
- command='python',
84
- args=['server.py'],
85
- timeout=30000, # Gemini-only field
86
- trust=True, # Gemini-only field
87
- auto_approve=True
88
- )
89
-
90
- # Should succeed (not return error code 1)
91
- self.assertEqual(result, 0)
92
-
93
- # Check that output contains "UNSUPPORTED" for Gemini fields
94
- output = mock_stdout.getvalue()
95
- self.assertIn('UNSUPPORTED', output)
96
- self.assertIn('timeout', output)
97
- self.assertIn('trust', output)
98
-
99
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
100
- @patch('sys.stdout', new_callable=StringIO)
101
- def test_vscode_inputs_on_gemini_show_unsupported(self, mock_stdout, mock_manager_class):
102
- """Test that VS Code inputs on Gemini show as UNSUPPORTED."""
103
- mock_manager = MagicMock()
104
- mock_manager_class.return_value = mock_manager
105
-
106
- mock_result = MagicMock()
107
- mock_result.success = True
108
- mock_result.backup_path = None
109
- mock_manager.configure_server.return_value = mock_result
110
-
111
- result = handle_mcp_configure(
112
- host='gemini',
113
- server_name='test-server',
114
- command='python',
115
- args=['server.py'],
116
- input=['promptString,api-key,API Key,password=true'], # VS Code-only field
117
- auto_approve=True
118
- )
119
-
120
- # Should succeed (not return error code 1)
121
- self.assertEqual(result, 0)
122
-
123
- # Check that output contains "UNSUPPORTED" for inputs field
124
- output = mock_stdout.getvalue()
125
- self.assertIn('UNSUPPORTED', output)
126
- self.assertIn('inputs', output)
127
-
128
-
129
- class TestVSCodeInputsParsing(unittest.TestCase):
130
- """Test VS Code inputs parsing."""
131
-
132
- def test_parse_input_basic(self):
133
- """Test basic input parsing."""
134
- input_list = ['promptString,api-key,GitHub Personal Access Token']
135
- result = parse_input(input_list)
136
-
137
- self.assertIsNotNone(result)
138
- self.assertEqual(len(result), 1)
139
- self.assertEqual(result[0]['type'], 'promptString')
140
- self.assertEqual(result[0]['id'], 'api-key')
141
- self.assertEqual(result[0]['description'], 'GitHub Personal Access Token')
142
- self.assertNotIn('password', result[0])
143
-
144
- def test_parse_input_with_password(self):
145
- """Test input parsing with password flag."""
146
- input_list = ['promptString,api-key,API Key,password=true']
147
- result = parse_input(input_list)
148
-
149
- self.assertIsNotNone(result)
150
- self.assertEqual(len(result), 1)
151
- self.assertEqual(result[0]['password'], True)
152
-
153
- def test_parse_input_multiple(self):
154
- """Test parsing multiple inputs."""
155
- input_list = [
156
- 'promptString,api-key,API Key,password=true',
157
- 'promptString,db-url,Database URL'
158
- ]
159
- result = parse_input(input_list)
160
-
161
- self.assertIsNotNone(result)
162
- self.assertEqual(len(result), 2)
163
-
164
- def test_parse_input_none(self):
165
- """Test parsing None inputs."""
166
- result = parse_input(None)
167
- self.assertIsNone(result)
168
-
169
- def test_parse_input_empty(self):
170
- """Test parsing empty inputs list."""
171
- result = parse_input([])
172
- self.assertIsNone(result)
173
-
174
-
175
- class TestVSCodeInputsIntegration(unittest.TestCase):
176
- """Test VS Code inputs integration with configure command."""
177
-
178
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
179
- def test_vscode_inputs_passed_to_model(self, mock_manager_class):
180
- """Test that parsed inputs are passed to VS Code model."""
181
- mock_manager = MagicMock()
182
- mock_manager_class.return_value = mock_manager
183
-
184
- mock_result = MagicMock()
185
- mock_result.success = True
186
- mock_result.backup_path = None
187
- mock_manager.configure_server.return_value = mock_result
188
-
189
- result = handle_mcp_configure(
190
- host='vscode',
191
- server_name='test-server',
192
- command='python',
193
- args=['server.py'],
194
- input=['promptString,api-key,API Key,password=true'],
195
- auto_approve=True
196
- )
197
-
198
- self.assertEqual(result, 0)
199
-
200
- # Verify inputs were passed to VS Code model
201
- call_args = mock_manager.configure_server.call_args
202
- server_config = call_args.kwargs['server_config']
203
- self.assertIsInstance(server_config, MCPServerConfigVSCode)
204
- self.assertIsNotNone(server_config.inputs)
205
- self.assertEqual(len(server_config.inputs), 1)
206
- self.assertEqual(server_config.inputs[0]['id'], 'api-key')
207
-
208
-
209
- class TestHttpUrlArgument(unittest.TestCase):
210
- """Test --http-url argument for Gemini."""
211
-
212
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
213
- def test_http_url_passed_to_gemini(self, mock_manager_class):
214
- """Test that httpUrl is passed to Gemini model."""
215
- mock_manager = MagicMock()
216
- mock_manager_class.return_value = mock_manager
217
-
218
- mock_result = MagicMock()
219
- mock_result.success = True
220
- mock_result.backup_path = None
221
- mock_manager.configure_server.return_value = mock_result
222
-
223
- result = handle_mcp_configure(
224
- host='gemini',
225
- server_name='test-server',
226
- command='python',
227
- args=['server.py'],
228
- http_url='https://api.example.com/mcp',
229
- auto_approve=True
230
- )
231
-
232
- self.assertEqual(result, 0)
233
-
234
- # Verify httpUrl was passed to Gemini model
235
- call_args = mock_manager.configure_server.call_args
236
- server_config = call_args.kwargs['server_config']
237
- self.assertIsInstance(server_config, MCPServerConfigGemini)
238
- self.assertEqual(server_config.httpUrl, 'https://api.example.com/mcp')
239
-
240
-
241
- class TestToolFilteringArguments(unittest.TestCase):
242
- """Test --include-tools and --exclude-tools arguments for Gemini."""
243
-
244
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
245
- def test_include_tools_passed_to_gemini(self, mock_manager_class):
246
- """Test that includeTools is passed to Gemini model."""
247
- mock_manager = MagicMock()
248
- mock_manager_class.return_value = mock_manager
249
-
250
- mock_result = MagicMock()
251
- mock_result.success = True
252
- mock_result.backup_path = None
253
- mock_manager.configure_server.return_value = mock_result
254
-
255
- result = handle_mcp_configure(
256
- host='gemini',
257
- server_name='test-server',
258
- command='python',
259
- args=['server.py'],
260
- include_tools=['tool1', 'tool2', 'tool3'],
261
- auto_approve=True
262
- )
263
-
264
- self.assertEqual(result, 0)
265
-
266
- # Verify includeTools was passed to Gemini model
267
- call_args = mock_manager.configure_server.call_args
268
- server_config = call_args.kwargs['server_config']
269
- self.assertIsInstance(server_config, MCPServerConfigGemini)
270
- self.assertEqual(server_config.includeTools, ['tool1', 'tool2', 'tool3'])
271
-
272
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
273
- def test_exclude_tools_passed_to_gemini(self, mock_manager_class):
274
- """Test that excludeTools is passed to Gemini model."""
275
- mock_manager = MagicMock()
276
- mock_manager_class.return_value = mock_manager
277
-
278
- mock_result = MagicMock()
279
- mock_result.success = True
280
- mock_result.backup_path = None
281
- mock_manager.configure_server.return_value = mock_result
282
-
283
- result = handle_mcp_configure(
284
- host='gemini',
285
- server_name='test-server',
286
- command='python',
287
- args=['server.py'],
288
- exclude_tools=['dangerous_tool'],
289
- auto_approve=True
290
- )
291
-
292
- self.assertEqual(result, 0)
293
-
294
- # Verify excludeTools was passed to Gemini model
295
- call_args = mock_manager.configure_server.call_args
296
- server_config = call_args.kwargs['server_config']
297
- self.assertIsInstance(server_config, MCPServerConfigGemini)
298
- self.assertEqual(server_config.excludeTools, ['dangerous_tool'])
299
-
300
-
301
- class TestAllCodexArguments(unittest.TestCase):
302
- """Test ALL Codex-specific CLI arguments."""
303
-
304
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
305
- @patch('sys.stdout', new_callable=StringIO)
306
- def test_all_codex_arguments_accepted(self, mock_stdout, mock_manager_class):
307
- """Test that all Codex arguments are accepted and passed to model."""
308
- mock_manager = MagicMock()
309
- mock_manager_class.return_value = mock_manager
310
-
311
- mock_result = MagicMock()
312
- mock_result.success = True
313
- mock_result.backup_path = None
314
- mock_manager.configure_server.return_value = mock_result
315
-
316
- # Test STDIO server with Codex-specific STDIO fields
317
- result = handle_mcp_configure(
318
- host='codex',
319
- server_name='test-server',
320
- command='npx',
321
- args=['-y', '@upstash/context7-mcp'],
322
- env_vars=['PATH', 'HOME'],
323
- cwd='/workspace',
324
- startup_timeout=15,
325
- tool_timeout=120,
326
- enabled=True,
327
- include_tools=['read', 'write'],
328
- exclude_tools=['delete'],
329
- auto_approve=True
330
- )
331
-
332
- # Verify success
333
- self.assertEqual(result, 0)
334
-
335
- # Verify configure_server was called
336
- mock_manager.configure_server.assert_called_once()
337
-
338
- # Verify server_config is MCPServerConfigCodex
339
- call_args = mock_manager.configure_server.call_args
340
- server_config = call_args.kwargs['server_config']
341
- self.assertIsInstance(server_config, MCPServerConfigCodex)
342
-
343
- # Verify Codex-specific STDIO fields
344
- self.assertEqual(server_config.env_vars, ['PATH', 'HOME'])
345
- self.assertEqual(server_config.cwd, '/workspace')
346
- self.assertEqual(server_config.startup_timeout_sec, 15)
347
- self.assertEqual(server_config.tool_timeout_sec, 120)
348
- self.assertTrue(server_config.enabled)
349
- self.assertEqual(server_config.enabled_tools, ['read', 'write'])
350
- self.assertEqual(server_config.disabled_tools, ['delete'])
351
-
352
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
353
- @patch('sys.stdout', new_callable=StringIO)
354
- def test_codex_env_vars_list(self, mock_stdout, mock_manager_class):
355
- """Test that env_vars accepts multiple values as a list."""
356
- mock_manager = MagicMock()
357
- mock_manager_class.return_value = mock_manager
358
-
359
- mock_result = MagicMock()
360
- mock_result.success = True
361
- mock_result.backup_path = None
362
- mock_manager.configure_server.return_value = mock_result
363
-
364
- result = handle_mcp_configure(
365
- host='codex',
366
- server_name='test-server',
367
- command='npx',
368
- args=['-y', 'package'],
369
- env_vars=['PATH', 'HOME', 'USER'],
370
- auto_approve=True
371
- )
372
-
373
- self.assertEqual(result, 0)
374
- call_args = mock_manager.configure_server.call_args
375
- server_config = call_args.kwargs['server_config']
376
- self.assertEqual(server_config.env_vars, ['PATH', 'HOME', 'USER'])
377
-
378
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
379
- @patch('sys.stdout', new_callable=StringIO)
380
- def test_codex_env_header_parsing(self, mock_stdout, mock_manager_class):
381
- """Test that env_header parses KEY=ENV_VAR format correctly."""
382
- mock_manager = MagicMock()
383
- mock_manager_class.return_value = mock_manager
384
-
385
- mock_result = MagicMock()
386
- mock_result.success = True
387
- mock_result.backup_path = None
388
- mock_manager.configure_server.return_value = mock_result
389
-
390
- result = handle_mcp_configure(
391
- host='codex',
392
- server_name='test-server',
393
- command='npx',
394
- args=['-y', 'package'],
395
- env_header=['X-API-Key=API_KEY', 'Authorization=AUTH_TOKEN'],
396
- auto_approve=True
397
- )
398
-
399
- self.assertEqual(result, 0)
400
- call_args = mock_manager.configure_server.call_args
401
- server_config = call_args.kwargs['server_config']
402
- self.assertEqual(server_config.env_http_headers, {
403
- 'X-API-Key': 'API_KEY',
404
- 'Authorization': 'AUTH_TOKEN'
405
- })
406
-
407
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
408
- @patch('sys.stdout', new_callable=StringIO)
409
- def test_codex_timeout_fields(self, mock_stdout, mock_manager_class):
410
- """Test that timeout fields are passed as integers."""
411
- mock_manager = MagicMock()
412
- mock_manager_class.return_value = mock_manager
413
-
414
- mock_result = MagicMock()
415
- mock_result.success = True
416
- mock_result.backup_path = None
417
- mock_manager.configure_server.return_value = mock_result
418
-
419
- result = handle_mcp_configure(
420
- host='codex',
421
- server_name='test-server',
422
- command='npx',
423
- args=['-y', 'package'],
424
- startup_timeout=30,
425
- tool_timeout=180,
426
- auto_approve=True
427
- )
428
-
429
- self.assertEqual(result, 0)
430
- call_args = mock_manager.configure_server.call_args
431
- server_config = call_args.kwargs['server_config']
432
- self.assertEqual(server_config.startup_timeout_sec, 30)
433
- self.assertEqual(server_config.tool_timeout_sec, 180)
434
-
435
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
436
- @patch('sys.stdout', new_callable=StringIO)
437
- def test_codex_enabled_flag(self, mock_stdout, mock_manager_class):
438
- """Test that enabled flag works as boolean."""
439
- mock_manager = MagicMock()
440
- mock_manager_class.return_value = mock_manager
441
-
442
- mock_result = MagicMock()
443
- mock_result.success = True
444
- mock_result.backup_path = None
445
- mock_manager.configure_server.return_value = mock_result
446
-
447
- result = handle_mcp_configure(
448
- host='codex',
449
- server_name='test-server',
450
- command='npx',
451
- args=['-y', 'package'],
452
- enabled=True,
453
- auto_approve=True
454
- )
455
-
456
- self.assertEqual(result, 0)
457
- call_args = mock_manager.configure_server.call_args
458
- server_config = call_args.kwargs['server_config']
459
- self.assertTrue(server_config.enabled)
460
-
461
- @patch('hatch.cli_hatch.MCPHostConfigurationManager')
462
- @patch('sys.stdout', new_callable=StringIO)
463
- def test_codex_reuses_shared_arguments(self, mock_stdout, mock_manager_class):
464
- """Test that Codex reuses shared arguments (cwd, include-tools, exclude-tools)."""
465
- mock_manager = MagicMock()
466
- mock_manager_class.return_value = mock_manager
467
-
468
- mock_result = MagicMock()
469
- mock_result.success = True
470
- mock_result.backup_path = None
471
- mock_manager.configure_server.return_value = mock_result
472
-
473
- result = handle_mcp_configure(
474
- host='codex',
475
- server_name='test-server',
476
- command='npx',
477
- args=['-y', 'package'],
478
- cwd='/workspace',
479
- include_tools=['tool1', 'tool2'],
480
- exclude_tools=['tool3'],
481
- auto_approve=True
482
- )
483
-
484
- self.assertEqual(result, 0)
485
- call_args = mock_manager.configure_server.call_args
486
- server_config = call_args.kwargs['server_config']
487
-
488
- # Verify shared arguments work for Codex STDIO servers
489
- self.assertEqual(server_config.cwd, '/workspace')
490
- self.assertEqual(server_config.enabled_tools, ['tool1', 'tool2'])
491
- self.assertEqual(server_config.disabled_tools, ['tool3'])
492
-
493
-
494
- if __name__ == '__main__':
495
- unittest.main()
496
-