hatch-xclam 0.7.0__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 (93) hide show
  1. hatch/__init__.py +21 -0
  2. hatch/cli_hatch.py +2748 -0
  3. hatch/environment_manager.py +1375 -0
  4. hatch/installers/__init__.py +25 -0
  5. hatch/installers/dependency_installation_orchestrator.py +636 -0
  6. hatch/installers/docker_installer.py +545 -0
  7. hatch/installers/hatch_installer.py +198 -0
  8. hatch/installers/installation_context.py +109 -0
  9. hatch/installers/installer_base.py +195 -0
  10. hatch/installers/python_installer.py +342 -0
  11. hatch/installers/registry.py +179 -0
  12. hatch/installers/system_installer.py +588 -0
  13. hatch/mcp_host_config/__init__.py +38 -0
  14. hatch/mcp_host_config/backup.py +458 -0
  15. hatch/mcp_host_config/host_management.py +572 -0
  16. hatch/mcp_host_config/models.py +602 -0
  17. hatch/mcp_host_config/reporting.py +181 -0
  18. hatch/mcp_host_config/strategies.py +513 -0
  19. hatch/package_loader.py +263 -0
  20. hatch/python_environment_manager.py +734 -0
  21. hatch/registry_explorer.py +171 -0
  22. hatch/registry_retriever.py +335 -0
  23. hatch/template_generator.py +179 -0
  24. hatch_xclam-0.7.0.dist-info/METADATA +150 -0
  25. hatch_xclam-0.7.0.dist-info/RECORD +93 -0
  26. hatch_xclam-0.7.0.dist-info/WHEEL +5 -0
  27. hatch_xclam-0.7.0.dist-info/entry_points.txt +2 -0
  28. hatch_xclam-0.7.0.dist-info/licenses/LICENSE +661 -0
  29. hatch_xclam-0.7.0.dist-info/top_level.txt +2 -0
  30. tests/__init__.py +1 -0
  31. tests/run_environment_tests.py +124 -0
  32. tests/test_cli_version.py +122 -0
  33. tests/test_data/packages/basic/base_pkg/hatch_mcp_server.py +18 -0
  34. tests/test_data/packages/basic/base_pkg/mcp_server.py +21 -0
  35. tests/test_data/packages/basic/base_pkg_v2/hatch_mcp_server.py +18 -0
  36. tests/test_data/packages/basic/base_pkg_v2/mcp_server.py +21 -0
  37. tests/test_data/packages/basic/utility_pkg/hatch_mcp_server.py +18 -0
  38. tests/test_data/packages/basic/utility_pkg/mcp_server.py +21 -0
  39. tests/test_data/packages/dependencies/complex_dep_pkg/hatch_mcp_server.py +18 -0
  40. tests/test_data/packages/dependencies/complex_dep_pkg/mcp_server.py +21 -0
  41. tests/test_data/packages/dependencies/docker_dep_pkg/hatch_mcp_server.py +18 -0
  42. tests/test_data/packages/dependencies/docker_dep_pkg/mcp_server.py +21 -0
  43. tests/test_data/packages/dependencies/mixed_dep_pkg/hatch_mcp_server.py +18 -0
  44. tests/test_data/packages/dependencies/mixed_dep_pkg/mcp_server.py +21 -0
  45. tests/test_data/packages/dependencies/python_dep_pkg/hatch_mcp_server.py +18 -0
  46. tests/test_data/packages/dependencies/python_dep_pkg/mcp_server.py +21 -0
  47. tests/test_data/packages/dependencies/simple_dep_pkg/hatch_mcp_server.py +18 -0
  48. tests/test_data/packages/dependencies/simple_dep_pkg/mcp_server.py +21 -0
  49. tests/test_data/packages/dependencies/system_dep_pkg/hatch_mcp_server.py +18 -0
  50. tests/test_data/packages/dependencies/system_dep_pkg/mcp_server.py +21 -0
  51. tests/test_data/packages/error_scenarios/circular_dep_pkg/hatch_mcp_server.py +18 -0
  52. tests/test_data/packages/error_scenarios/circular_dep_pkg/mcp_server.py +21 -0
  53. tests/test_data/packages/error_scenarios/circular_dep_pkg_b/hatch_mcp_server.py +18 -0
  54. tests/test_data/packages/error_scenarios/circular_dep_pkg_b/mcp_server.py +21 -0
  55. tests/test_data/packages/error_scenarios/invalid_dep_pkg/hatch_mcp_server.py +18 -0
  56. tests/test_data/packages/error_scenarios/invalid_dep_pkg/mcp_server.py +21 -0
  57. tests/test_data/packages/error_scenarios/version_conflict_pkg/hatch_mcp_server.py +18 -0
  58. tests/test_data/packages/error_scenarios/version_conflict_pkg/mcp_server.py +21 -0
  59. tests/test_data/packages/schema_versions/schema_v1_1_0_pkg/main.py +11 -0
  60. tests/test_data/packages/schema_versions/schema_v1_2_0_pkg/main.py +11 -0
  61. tests/test_data/packages/schema_versions/schema_v1_2_1_pkg/hatch_mcp_server.py +18 -0
  62. tests/test_data/packages/schema_versions/schema_v1_2_1_pkg/mcp_server.py +21 -0
  63. tests/test_data_utils.py +472 -0
  64. tests/test_dependency_orchestrator_consent.py +266 -0
  65. tests/test_docker_installer.py +524 -0
  66. tests/test_env_manip.py +991 -0
  67. tests/test_hatch_installer.py +179 -0
  68. tests/test_installer_base.py +221 -0
  69. tests/test_mcp_atomic_operations.py +276 -0
  70. tests/test_mcp_backup_integration.py +308 -0
  71. tests/test_mcp_cli_all_host_specific_args.py +303 -0
  72. tests/test_mcp_cli_backup_management.py +295 -0
  73. tests/test_mcp_cli_direct_management.py +453 -0
  74. tests/test_mcp_cli_discovery_listing.py +582 -0
  75. tests/test_mcp_cli_host_config_integration.py +823 -0
  76. tests/test_mcp_cli_package_management.py +360 -0
  77. tests/test_mcp_cli_partial_updates.py +859 -0
  78. tests/test_mcp_environment_integration.py +520 -0
  79. tests/test_mcp_host_config_backup.py +257 -0
  80. tests/test_mcp_host_configuration_manager.py +331 -0
  81. tests/test_mcp_host_registry_decorator.py +348 -0
  82. tests/test_mcp_pydantic_architecture_v4.py +603 -0
  83. tests/test_mcp_server_config_models.py +242 -0
  84. tests/test_mcp_server_config_type_field.py +221 -0
  85. tests/test_mcp_sync_functionality.py +316 -0
  86. tests/test_mcp_user_feedback_reporting.py +359 -0
  87. tests/test_non_tty_integration.py +281 -0
  88. tests/test_online_package_loader.py +202 -0
  89. tests/test_python_environment_manager.py +882 -0
  90. tests/test_python_installer.py +327 -0
  91. tests/test_registry.py +51 -0
  92. tests/test_registry_retriever.py +250 -0
  93. tests/test_system_installer.py +733 -0
@@ -0,0 +1,18 @@
1
+ """
2
+ HatchMCP wrapper for circular_dep_pkg_b.
3
+ """
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ # Add package directory to path
8
+ sys.path.insert(0, str(Path(__file__).parent))
9
+
10
+ from mcp_server import mcp
11
+
12
+ def main():
13
+ """Main entry point for HatchMCP wrapper."""
14
+ print("Starting circular_dep_pkg_b via HatchMCP wrapper")
15
+ mcp.run()
16
+
17
+ if __name__ == "__main__":
18
+ main()
@@ -0,0 +1,21 @@
1
+ """
2
+ FastMCP server implementation for circular_dep_pkg_b.
3
+ """
4
+ from mcp.server.fastmcp import FastMCP
5
+
6
+ mcp = FastMCP("circular_dep_pkg_b", log_level="WARNING")
7
+
8
+ @mcp.tool()
9
+ def circular_dep_pkg_b_tool(param: str) -> str:
10
+ """Example tool function for circular_dep_pkg_b.
11
+
12
+ Args:
13
+ param (str): Example parameter.
14
+
15
+ Returns:
16
+ str: Example result.
17
+ """
18
+ return f"Processed by circular_dep_pkg_b: {param}"
19
+
20
+ if __name__ == "__main__":
21
+ mcp.run()
@@ -0,0 +1,18 @@
1
+ """
2
+ HatchMCP wrapper for invalid_dep_pkg.
3
+ """
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ # Add package directory to path
8
+ sys.path.insert(0, str(Path(__file__).parent))
9
+
10
+ from mcp_server import mcp
11
+
12
+ def main():
13
+ """Main entry point for HatchMCP wrapper."""
14
+ print("Starting invalid_dep_pkg via HatchMCP wrapper")
15
+ mcp.run()
16
+
17
+ if __name__ == "__main__":
18
+ main()
@@ -0,0 +1,21 @@
1
+ """
2
+ FastMCP server implementation for invalid_dep_pkg.
3
+ """
4
+ from mcp.server.fastmcp import FastMCP
5
+
6
+ mcp = FastMCP("invalid_dep_pkg", log_level="WARNING")
7
+
8
+ @mcp.tool()
9
+ def invalid_dep_pkg_tool(param: str) -> str:
10
+ """Example tool function for invalid_dep_pkg.
11
+
12
+ Args:
13
+ param (str): Example parameter.
14
+
15
+ Returns:
16
+ str: Example result.
17
+ """
18
+ return f"Processed by invalid_dep_pkg: {param}"
19
+
20
+ if __name__ == "__main__":
21
+ mcp.run()
@@ -0,0 +1,18 @@
1
+ """
2
+ HatchMCP wrapper for version_conflict_pkg.
3
+ """
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ # Add package directory to path
8
+ sys.path.insert(0, str(Path(__file__).parent))
9
+
10
+ from mcp_server import mcp
11
+
12
+ def main():
13
+ """Main entry point for HatchMCP wrapper."""
14
+ print("Starting version_conflict_pkg via HatchMCP wrapper")
15
+ mcp.run()
16
+
17
+ if __name__ == "__main__":
18
+ main()
@@ -0,0 +1,21 @@
1
+ """
2
+ FastMCP server implementation for version_conflict_pkg.
3
+ """
4
+ from mcp.server.fastmcp import FastMCP
5
+
6
+ mcp = FastMCP("version_conflict_pkg", log_level="WARNING")
7
+
8
+ @mcp.tool()
9
+ def version_conflict_pkg_tool(param: str) -> str:
10
+ """Example tool function for version_conflict_pkg.
11
+
12
+ Args:
13
+ param (str): Example parameter.
14
+
15
+ Returns:
16
+ str: Example result.
17
+ """
18
+ return f"Processed by version_conflict_pkg: {param}"
19
+
20
+ if __name__ == "__main__":
21
+ mcp.run()
@@ -0,0 +1,11 @@
1
+ """
2
+ Test package: schema_v1_1_0_pkg
3
+ """
4
+
5
+ def main():
6
+ """Main entry point for schema_v1_1_0_pkg."""
7
+ print("Hello from schema_v1_1_0_pkg!")
8
+ return "schema_v1_1_0_pkg executed successfully"
9
+
10
+ if __name__ == "__main__":
11
+ main()
@@ -0,0 +1,11 @@
1
+ """
2
+ Test package: schema_v1_2_0_pkg
3
+ """
4
+
5
+ def main():
6
+ """Main entry point for schema_v1_2_0_pkg."""
7
+ print("Hello from schema_v1_2_0_pkg!")
8
+ return "schema_v1_2_0_pkg executed successfully"
9
+
10
+ if __name__ == "__main__":
11
+ main()
@@ -0,0 +1,18 @@
1
+ """
2
+ HatchMCP wrapper for schema_v1_2_1_pkg.
3
+ """
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ # Add package directory to path
8
+ sys.path.insert(0, str(Path(__file__).parent))
9
+
10
+ from mcp_server import mcp
11
+
12
+ def main():
13
+ """Main entry point for HatchMCP wrapper."""
14
+ print("Starting schema_v1_2_1_pkg via HatchMCP wrapper")
15
+ mcp.run()
16
+
17
+ if __name__ == "__main__":
18
+ main()
@@ -0,0 +1,21 @@
1
+ """
2
+ FastMCP server implementation for schema_v1_2_1_pkg.
3
+ """
4
+ from mcp.server.fastmcp import FastMCP
5
+
6
+ mcp = FastMCP("schema_v1_2_1_pkg", log_level="WARNING")
7
+
8
+ @mcp.tool()
9
+ def schema_v1_2_1_pkg_tool(param: str) -> str:
10
+ """Example tool function for schema_v1_2_1_pkg.
11
+
12
+ Args:
13
+ param (str): Example parameter.
14
+
15
+ Returns:
16
+ str: Example result.
17
+ """
18
+ return f"Processed by schema_v1_2_1_pkg: {param}"
19
+
20
+ if __name__ == "__main__":
21
+ mcp.run()
@@ -0,0 +1,472 @@
1
+ """Test data utilities for Hatch test suite.
2
+
3
+ This module provides utilities for loading test data from static test packages.
4
+ All dynamic package generation has been removed in favor of static packages.
5
+ """
6
+
7
+ import json
8
+ from pathlib import Path
9
+ from typing import Any, Dict, List
10
+
11
+
12
+ class TestDataLoader:
13
+ """Utility class for loading test data from standardized locations."""
14
+
15
+ def __init__(self):
16
+ """Initialize the test data loader."""
17
+ self.test_data_dir = Path(__file__).parent / "test_data"
18
+ self.configs_dir = self.test_data_dir / "configs"
19
+ self.responses_dir = self.test_data_dir / "responses"
20
+ self.packages_dir = self.test_data_dir / "packages"
21
+
22
+ # Ensure directories exist
23
+ self.configs_dir.mkdir(parents=True, exist_ok=True)
24
+ self.responses_dir.mkdir(parents=True, exist_ok=True)
25
+ self.packages_dir.mkdir(parents=True, exist_ok=True)
26
+
27
+ def load_config(self, config_name: str) -> Dict[str, Any]:
28
+ """Load a test configuration file.
29
+
30
+ Args:
31
+ config_name: Name of the config file (without .json extension)
32
+
33
+ Returns:
34
+ Loaded configuration as a dictionary
35
+ """
36
+ config_path = self.configs_dir / f"{config_name}.json"
37
+ if not config_path.exists():
38
+ # Create default config if it doesn't exist
39
+ self._create_default_config(config_name)
40
+
41
+ with open(config_path, 'r') as f:
42
+ return json.load(f)
43
+
44
+ def load_response(self, response_name: str) -> Dict[str, Any]:
45
+ """Load a mock response file.
46
+
47
+ Args:
48
+ response_name: Name of the response file (without .json extension)
49
+
50
+ Returns:
51
+ Loaded response as a dictionary
52
+ """
53
+ response_path = self.responses_dir / f"{response_name}.json"
54
+ if not response_path.exists():
55
+ # Create default response if it doesn't exist
56
+ self._create_default_response(response_name)
57
+
58
+ with open(response_path, 'r') as f:
59
+ return json.load(f)
60
+
61
+ def setup(self):
62
+ """Set up test data (placeholder for future setup logic)."""
63
+ # Currently no setup needed as test packages are static
64
+ pass
65
+
66
+ def cleanup(self):
67
+ """Clean up test data (placeholder for future cleanup logic)."""
68
+ # Currently no cleanup needed as test packages are persistent
69
+ pass
70
+
71
+ def get_test_packages_dir(self) -> Path:
72
+ """Get the test packages directory path.
73
+
74
+ Returns:
75
+ Path to the test packages directory
76
+ """
77
+ return self.packages_dir
78
+
79
+ def _create_default_config(self, config_name: str):
80
+ """Create a default configuration file."""
81
+ default_configs = {
82
+ "test_settings": {
83
+ "test_timeout": 30,
84
+ "temp_dir_prefix": "hatch_test_",
85
+ "cleanup_temp_dirs": True,
86
+ "mock_external_services": True
87
+ },
88
+ "installer_configs": {
89
+ "python_installer": {
90
+ "pip_timeout": 60,
91
+ "use_cache": False
92
+ },
93
+ "docker_installer": {
94
+ "timeout": 120,
95
+ "cleanup_containers": True
96
+ }
97
+ }
98
+ }
99
+
100
+ config = default_configs.get(config_name, {})
101
+ config_path = self.configs_dir / f"{config_name}.json"
102
+ with open(config_path, 'w') as f:
103
+ json.dump(config, f, indent=2)
104
+
105
+ def _create_default_response(self, response_name: str):
106
+ """Create a default response file."""
107
+ default_responses = {
108
+ "registry_responses": {
109
+ "success": {
110
+ "status": "success",
111
+ "data": {"packages": []}
112
+ },
113
+ "error": {
114
+ "status": "error",
115
+ "message": "Registry not available"
116
+ }
117
+ }
118
+ }
119
+
120
+ response = default_responses.get(response_name, {})
121
+ response_path = self.responses_dir / f"{response_name}.json"
122
+ with open(response_path, 'w') as f:
123
+ json.dump(response, f, indent=2)
124
+
125
+ def load_fixture(self, fixture_name: str) -> Dict[str, Any]:
126
+ """Load a test fixture file.
127
+
128
+ Args:
129
+ fixture_name: Name of the fixture file (without .json extension)
130
+
131
+ Returns:
132
+ Loaded fixture as a dictionary
133
+ """
134
+ fixtures_dir = self.test_data_dir / "fixtures"
135
+ fixture_path = fixtures_dir / f"{fixture_name}.json"
136
+ with open(fixture_path, 'r') as f:
137
+ return json.load(f)
138
+
139
+
140
+ class NonTTYTestDataLoader(TestDataLoader):
141
+ """Specialized test data loader for non-TTY handling tests."""
142
+
143
+ def get_installation_plan(self, plan_name: str) -> Dict[str, Any]:
144
+ """Load standardized installation plan data.
145
+
146
+ Args:
147
+ plan_name: Name of the installation plan to load
148
+
149
+ Returns:
150
+ Installation plan dictionary
151
+ """
152
+ plans = self.load_fixture("installation_plans")
153
+ return plans.get(plan_name, plans["basic_python_plan"])
154
+
155
+ def get_non_tty_config(self) -> Dict[str, Any]:
156
+ """Load non-TTY test configuration.
157
+
158
+ Returns:
159
+ Non-TTY test configuration dictionary
160
+ """
161
+ return self.load_config("non_tty_test_config")
162
+
163
+ def get_environment_variable_scenarios(self) -> List[Dict[str, Any]]:
164
+ """Get environment variable test scenarios.
165
+
166
+ Returns:
167
+ List of environment variable test scenarios
168
+ """
169
+ config = self.get_non_tty_config()
170
+ return config["environment_variables"]["test_scenarios"]
171
+
172
+ def get_user_input_scenarios(self) -> Dict[str, List[str]]:
173
+ """Get user input test scenarios.
174
+
175
+ Returns:
176
+ Dictionary of user input scenarios
177
+ """
178
+ config = self.get_non_tty_config()
179
+ return config["user_input_scenarios"]
180
+
181
+ def get_logging_messages(self) -> Dict[str, str]:
182
+ """Get expected logging messages.
183
+
184
+ Returns:
185
+ Dictionary of expected logging messages
186
+ """
187
+ config = self.get_non_tty_config()
188
+ return config["logging_messages"]
189
+
190
+ class MCPBackupTestDataLoader(TestDataLoader):
191
+ """Specialized test data loader for MCP backup system tests."""
192
+
193
+ def __init__(self):
194
+ super().__init__()
195
+ self.mcp_backup_configs_dir = self.configs_dir / "mcp_backup_test_configs"
196
+ self.mcp_backup_configs_dir.mkdir(exist_ok=True)
197
+
198
+ def load_host_agnostic_config(self, config_type: str) -> Dict[str, Any]:
199
+ """Load host-agnostic test configuration.
200
+
201
+ Args:
202
+ config_type: Type of configuration to load
203
+
204
+ Returns:
205
+ Host-agnostic configuration dictionary
206
+ """
207
+ config_path = self.mcp_backup_configs_dir / f"{config_type}.json"
208
+ if not config_path.exists():
209
+ self._create_default_mcp_config(config_type)
210
+
211
+ with open(config_path, 'r') as f:
212
+ return json.load(f)
213
+
214
+ def _create_default_mcp_config(self, config_type: str):
215
+ """Create default host-agnostic MCP configuration."""
216
+ default_configs = {
217
+ "simple_server": {
218
+ "servers": {
219
+ "test_server": {
220
+ "command": "python",
221
+ "args": ["server.py"]
222
+ }
223
+ }
224
+ },
225
+ "complex_server": {
226
+ "servers": {
227
+ "server1": {"command": "python", "args": ["server1.py"]},
228
+ "server2": {"command": "node", "args": ["server2.js"]},
229
+ "server3": {"command": "python", "args": ["server3.py"], "env": {"API_KEY": "test"}}
230
+ }
231
+ },
232
+ "empty_config": {"servers": {}}
233
+ }
234
+
235
+ config = default_configs.get(config_type, {"servers": {}})
236
+ config_path = self.mcp_backup_configs_dir / f"{config_type}.json"
237
+ with open(config_path, 'w') as f:
238
+ json.dump(config, f, indent=2)
239
+
240
+
241
+ # Global instance for easy access
242
+ test_data = TestDataLoader()
243
+
244
+ # Convenience functions
245
+ def load_test_config(config_name: str) -> Dict[str, Any]:
246
+ """Load test configuration."""
247
+ return test_data.load_config(config_name)
248
+
249
+
250
+ def load_mock_response(response_name: str) -> Dict[str, Any]:
251
+ """Load mock response."""
252
+ return test_data.load_response(response_name)
253
+
254
+
255
+ def get_test_packages_dir() -> Path:
256
+ """Get test packages directory."""
257
+ return test_data.get_test_packages_dir()
258
+
259
+
260
+ class MCPHostConfigTestDataLoader(TestDataLoader):
261
+ """Specialized test data loader for MCP host configuration tests v2."""
262
+
263
+ def __init__(self):
264
+ super().__init__()
265
+ self.mcp_host_configs_dir = self.configs_dir / "mcp_host_test_configs"
266
+ self.mcp_host_configs_dir.mkdir(exist_ok=True)
267
+
268
+ def load_host_config_template(self, host_type: str, config_type: str = "simple") -> Dict[str, Any]:
269
+ """Load host-specific configuration template."""
270
+ config_path = self.mcp_host_configs_dir / f"{host_type}_{config_type}.json"
271
+ if not config_path.exists():
272
+ self._create_host_config_template(host_type, config_type)
273
+
274
+ with open(config_path, 'r') as f:
275
+ return json.load(f)
276
+
277
+ def load_corrected_environment_data(self, data_type: str = "simple") -> Dict[str, Any]:
278
+ """Load corrected environment data structure (v2)."""
279
+ config_path = self.mcp_host_configs_dir / f"environment_v2_{data_type}.json"
280
+ if not config_path.exists():
281
+ self._create_corrected_environment_data(data_type)
282
+
283
+ with open(config_path, 'r') as f:
284
+ return json.load(f)
285
+
286
+ def load_mcp_server_config(self, server_type: str = "local") -> Dict[str, Any]:
287
+ """Load consolidated MCPServerConfig templates."""
288
+ config_path = self.mcp_host_configs_dir / f"mcp_server_{server_type}.json"
289
+ if not config_path.exists():
290
+ self._create_mcp_server_config(server_type)
291
+
292
+ with open(config_path, 'r') as f:
293
+ return json.load(f)
294
+
295
+ def _create_host_config_template(self, host_type: str, config_type: str):
296
+ """Create host-specific configuration templates with inheritance patterns."""
297
+ templates = {
298
+ # Claude family templates
299
+ "claude-desktop_simple": {
300
+ "mcpServers": {
301
+ "test_server": {
302
+ "command": "/usr/local/bin/python", # Absolute path required
303
+ "args": ["server.py"],
304
+ "env": {"API_KEY": "test"}
305
+ }
306
+ },
307
+ "theme": "dark", # Claude-specific settings
308
+ "auto_update": True
309
+ },
310
+ "claude-code_simple": {
311
+ "mcpServers": {
312
+ "test_server": {
313
+ "command": "/usr/local/bin/python", # Absolute path required
314
+ "args": ["server.py"],
315
+ "env": {}
316
+ }
317
+ },
318
+ "workspace_settings": {"mcp_enabled": True} # Claude Code specific
319
+ },
320
+
321
+ # Cursor family templates
322
+ "cursor_simple": {
323
+ "mcpServers": {
324
+ "test_server": {
325
+ "command": "python", # Flexible path handling
326
+ "args": ["server.py"],
327
+ "env": {"API_KEY": "test"}
328
+ }
329
+ }
330
+ },
331
+ "cursor_remote": {
332
+ "mcpServers": {
333
+ "remote_server": {
334
+ "url": "https://api.example.com/mcp",
335
+ "headers": {"Authorization": "Bearer token"}
336
+ }
337
+ }
338
+ },
339
+ "lmstudio_simple": {
340
+ "mcpServers": {
341
+ "test_server": {
342
+ "command": "python", # Inherits Cursor format
343
+ "args": ["server.py"],
344
+ "env": {}
345
+ }
346
+ }
347
+ },
348
+
349
+ # Independent strategy templates
350
+ "vscode_simple": {
351
+ "mcp": {
352
+ "servers": {
353
+ "test_server": {
354
+ "command": "python",
355
+ "args": ["server.py"]
356
+ }
357
+ }
358
+ }
359
+ },
360
+ "gemini_simple": {
361
+ "mcpServers": {
362
+ "test_server": {
363
+ "command": "python",
364
+ "args": ["server.py"]
365
+ }
366
+ }
367
+ }
368
+ }
369
+
370
+ template_key = f"{host_type}_{config_type}"
371
+ config = templates.get(template_key, {"mcpServers": {}})
372
+ config_path = self.mcp_host_configs_dir / f"{template_key}.json"
373
+ with open(config_path, 'w') as f:
374
+ json.dump(config, f, indent=2)
375
+
376
+ def _create_corrected_environment_data(self, data_type: str):
377
+ """Create corrected environment data templates (v2 structure)."""
378
+ templates = {
379
+ "simple": {
380
+ "name": "test_environment",
381
+ "description": "Test environment with corrected MCP structure",
382
+ "created_at": "2025-09-21T10:00:00.000000",
383
+ "packages": [
384
+ {
385
+ "name": "weather-toolkit",
386
+ "version": "1.0.0",
387
+ "type": "hatch",
388
+ "source": "github:user/weather-toolkit",
389
+ "installed_at": "2025-09-21T10:00:00.000000",
390
+ "configured_hosts": {
391
+ "claude-desktop": {
392
+ "config_path": "~/Library/Application Support/Claude/claude_desktop_config.json",
393
+ "configured_at": "2025-09-21T10:00:00.000000",
394
+ "last_synced": "2025-09-21T10:00:00.000000",
395
+ "server_config": {
396
+ "command": "/usr/local/bin/python",
397
+ "args": ["weather.py"],
398
+ "env": {"API_KEY": "weather_key"}
399
+ }
400
+ }
401
+ }
402
+ }
403
+ ]
404
+ },
405
+ "multi_host": {
406
+ "name": "multi_host_environment",
407
+ "description": "Environment with single server configured across multiple hosts",
408
+ "created_at": "2025-09-21T10:00:00.000000",
409
+ "packages": [
410
+ {
411
+ "name": "file-manager",
412
+ "version": "2.0.0",
413
+ "type": "hatch",
414
+ "source": "github:user/file-manager",
415
+ "installed_at": "2025-09-21T10:00:00.000000",
416
+ "configured_hosts": {
417
+ "claude-desktop": {
418
+ "config_path": "~/Library/Application Support/Claude/claude_desktop_config.json",
419
+ "configured_at": "2025-09-21T10:00:00.000000",
420
+ "last_synced": "2025-09-21T10:00:00.000000",
421
+ "server_config": {
422
+ "command": "/usr/local/bin/python",
423
+ "args": ["file_manager.py"],
424
+ "env": {"DEBUG": "true"}
425
+ }
426
+ },
427
+ "cursor": {
428
+ "config_path": "~/.cursor/mcp.json",
429
+ "configured_at": "2025-09-21T10:00:00.000000",
430
+ "last_synced": "2025-09-21T10:00:00.000000",
431
+ "server_config": {
432
+ "command": "python",
433
+ "args": ["file_manager.py"],
434
+ "env": {"DEBUG": "true"}
435
+ }
436
+ }
437
+ }
438
+ }
439
+ ]
440
+ }
441
+ }
442
+
443
+ config = templates.get(data_type, {"packages": []})
444
+ config_path = self.mcp_host_configs_dir / f"environment_v2_{data_type}.json"
445
+ with open(config_path, 'w') as f:
446
+ json.dump(config, f, indent=2)
447
+
448
+ def _create_mcp_server_config(self, server_type: str):
449
+ """Create consolidated MCPServerConfig templates."""
450
+ templates = {
451
+ "local": {
452
+ "command": "python",
453
+ "args": ["server.py", "--port", "8080"],
454
+ "env": {"API_KEY": "test", "DEBUG": "true"}
455
+ },
456
+ "remote": {
457
+ "url": "https://api.example.com/mcp",
458
+ "headers": {"Authorization": "Bearer token", "Content-Type": "application/json"}
459
+ },
460
+ "local_minimal": {
461
+ "command": "python",
462
+ "args": ["minimal_server.py"]
463
+ },
464
+ "remote_minimal": {
465
+ "url": "https://minimal.example.com/mcp"
466
+ }
467
+ }
468
+
469
+ config = templates.get(server_type, {})
470
+ config_path = self.mcp_host_configs_dir / f"mcp_server_{server_type}.json"
471
+ with open(config_path, 'w') as f:
472
+ json.dump(config, f, indent=2)