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.
- hatch/__init__.py +1 -1
- hatch/cli/__init__.py +71 -0
- hatch/cli/__main__.py +1035 -0
- hatch/cli/cli_env.py +865 -0
- hatch/cli/cli_mcp.py +1965 -0
- hatch/cli/cli_package.py +566 -0
- hatch/cli/cli_system.py +136 -0
- hatch/cli/cli_utils.py +1289 -0
- hatch/cli_hatch.py +160 -2838
- hatch/mcp_host_config/__init__.py +10 -10
- hatch/mcp_host_config/adapters/__init__.py +34 -0
- hatch/mcp_host_config/adapters/base.py +170 -0
- hatch/mcp_host_config/adapters/claude.py +105 -0
- hatch/mcp_host_config/adapters/codex.py +104 -0
- hatch/mcp_host_config/adapters/cursor.py +83 -0
- hatch/mcp_host_config/adapters/gemini.py +75 -0
- hatch/mcp_host_config/adapters/kiro.py +78 -0
- hatch/mcp_host_config/adapters/lmstudio.py +79 -0
- hatch/mcp_host_config/adapters/registry.py +149 -0
- hatch/mcp_host_config/adapters/vscode.py +83 -0
- hatch/mcp_host_config/backup.py +5 -3
- hatch/mcp_host_config/fields.py +126 -0
- hatch/mcp_host_config/models.py +161 -456
- hatch/mcp_host_config/reporting.py +57 -16
- hatch/mcp_host_config/strategies.py +155 -87
- hatch/template_generator.py +1 -1
- {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/METADATA +3 -2
- {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/RECORD +52 -43
- {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/WHEEL +1 -1
- hatch_xclam-0.8.0.dev1.dist-info/entry_points.txt +2 -0
- tests/cli_test_utils.py +280 -0
- tests/integration/cli/__init__.py +14 -0
- tests/integration/cli/test_cli_reporter_integration.py +2439 -0
- tests/integration/mcp/__init__.py +0 -0
- tests/integration/mcp/test_adapter_serialization.py +173 -0
- tests/regression/cli/__init__.py +16 -0
- tests/regression/cli/test_color_logic.py +268 -0
- tests/regression/cli/test_consequence_type.py +298 -0
- tests/regression/cli/test_error_formatting.py +328 -0
- tests/regression/cli/test_result_reporter.py +586 -0
- tests/regression/cli/test_table_formatter.py +211 -0
- tests/regression/mcp/__init__.py +0 -0
- tests/regression/mcp/test_field_filtering.py +162 -0
- tests/test_cli_version.py +7 -5
- tests/test_data/fixtures/cli_reporter_fixtures.py +184 -0
- tests/unit/__init__.py +0 -0
- tests/unit/mcp/__init__.py +0 -0
- tests/unit/mcp/test_adapter_protocol.py +138 -0
- tests/unit/mcp/test_adapter_registry.py +158 -0
- tests/unit/mcp/test_config_model.py +146 -0
- hatch_xclam-0.7.1.dev3.dist-info/entry_points.txt +0 -2
- tests/integration/test_mcp_kiro_integration.py +0 -153
- tests/regression/test_mcp_codex_backup_integration.py +0 -162
- tests/regression/test_mcp_codex_host_strategy.py +0 -163
- tests/regression/test_mcp_codex_model_validation.py +0 -117
- tests/regression/test_mcp_kiro_backup_integration.py +0 -241
- tests/regression/test_mcp_kiro_cli_integration.py +0 -141
- tests/regression/test_mcp_kiro_decorator_registration.py +0 -71
- tests/regression/test_mcp_kiro_host_strategy.py +0 -214
- tests/regression/test_mcp_kiro_model_validation.py +0 -116
- tests/regression/test_mcp_kiro_omni_conversion.py +0 -104
- tests/test_mcp_atomic_operations.py +0 -276
- tests/test_mcp_backup_integration.py +0 -308
- tests/test_mcp_cli_all_host_specific_args.py +0 -496
- tests/test_mcp_cli_backup_management.py +0 -295
- tests/test_mcp_cli_direct_management.py +0 -456
- tests/test_mcp_cli_discovery_listing.py +0 -582
- tests/test_mcp_cli_host_config_integration.py +0 -823
- tests/test_mcp_cli_package_management.py +0 -360
- tests/test_mcp_cli_partial_updates.py +0 -859
- tests/test_mcp_environment_integration.py +0 -520
- tests/test_mcp_host_config_backup.py +0 -257
- tests/test_mcp_host_configuration_manager.py +0 -331
- tests/test_mcp_host_registry_decorator.py +0 -348
- tests/test_mcp_pydantic_architecture_v4.py +0 -603
- tests/test_mcp_server_config_models.py +0 -242
- tests/test_mcp_server_config_type_field.py +0 -221
- tests/test_mcp_sync_functionality.py +0 -316
- tests/test_mcp_user_feedback_reporting.py +0 -359
- {hatch_xclam-0.7.1.dev3.dist-info → hatch_xclam-0.8.0.dev1.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
|