mcp-proxy-adapter 6.1.0__py3-none-any.whl → 6.1.1__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.
- mcp_proxy_adapter/api/middleware/__init__.py +2 -2
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +32 -13
- mcp_proxy_adapter/api/middleware/unified_security.py +12 -4
- mcp_proxy_adapter/core/protocol_manager.py +132 -10
- mcp_proxy_adapter/core/security_integration.py +19 -11
- mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +285 -0
- mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +285 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +4 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +36 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +4 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +34 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +35 -0
- mcp_proxy_adapter/examples/run_security_tests.py +0 -0
- mcp_proxy_adapter/examples/security_test_client.py +0 -0
- mcp_proxy_adapter/examples/test_config_generator.py +110 -0
- mcp_proxy_adapter/utils/config_generator.py +90 -2
- mcp_proxy_adapter/version.py +4 -2
- {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/RECORD +21 -17
- mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
- {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,285 @@
|
|
1
|
+
# Руководство по устранению неполадок
|
2
|
+
|
3
|
+
Это руководство посвящено решению распространенных проблем с фреймворком MCP Proxy Adapter, особенно связанных с ProtocolMiddleware и конфигурацией SSL/TLS.
|
4
|
+
|
5
|
+
## Распространенные проблемы
|
6
|
+
|
7
|
+
### Проблема 1: ProtocolMiddleware блокирует HTTPS запросы
|
8
|
+
|
9
|
+
**Проблема:** ProtocolMiddleware инициализируется с дефолтными настройками и не обновляется при изменении конфигурации SSL.
|
10
|
+
|
11
|
+
**Симптомы:**
|
12
|
+
```
|
13
|
+
Protocol 'https' not allowed for request to /health
|
14
|
+
INFO: 127.0.0.1:42038 - "GET /health HTTP/1.1" 403 Forbidden
|
15
|
+
```
|
16
|
+
|
17
|
+
**Причина:** ProtocolMiddleware создавался как глобальный экземпляр с дефолтными настройками и не обновлялся при включении SSL.
|
18
|
+
|
19
|
+
**Решение:**
|
20
|
+
1. **Использовать обновленный ProtocolManager** (Исправлено в v1.1.0):
|
21
|
+
- ProtocolManager теперь динамически обновляется на основе конфигурации SSL
|
22
|
+
- Автоматически разрешает HTTPS при включении SSL
|
23
|
+
|
24
|
+
2. **Отключить ProtocolMiddleware для HTTPS** (Временное решение):
|
25
|
+
```json
|
26
|
+
{
|
27
|
+
"server": {"host": "127.0.0.1", "port": 10004},
|
28
|
+
"ssl": {"enabled": true, "cert_file": "./certs/server.crt", "key_file": "./certs/server.key"},
|
29
|
+
"security": {"enabled": true, "auth": {"enabled": true, "methods": ["api_key"]}},
|
30
|
+
"protocols": {"enabled": false}
|
31
|
+
}
|
32
|
+
```
|
33
|
+
|
34
|
+
### Проблема 2: Конфликты конфигурации SSL
|
35
|
+
|
36
|
+
**Проблема:** Фреймворк читает конфигурацию SSL из двух мест: `ssl` (legacy) и `security.ssl`, что приводит к путанице.
|
37
|
+
|
38
|
+
**Симптомы:**
|
39
|
+
```
|
40
|
+
🔍 Debug: SSL config at start of validation: enabled=False
|
41
|
+
🔍 Debug: Root SSL section found: enabled=True
|
42
|
+
🔍 Debug: _get_ssl_config: security.ssl key_file=None
|
43
|
+
🔍 Debug: _get_ssl_config: legacy ssl key_file=./certs/server.key
|
44
|
+
```
|
45
|
+
|
46
|
+
**Решение:**
|
47
|
+
1. **Использовать унифицированную конфигурацию SSL** (Рекомендуется):
|
48
|
+
```json
|
49
|
+
{
|
50
|
+
"security": {
|
51
|
+
"ssl": {
|
52
|
+
"enabled": true,
|
53
|
+
"cert_file": "./certs/server.crt",
|
54
|
+
"key_file": "./certs/server.key"
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
```
|
59
|
+
|
60
|
+
2. **Использовать legacy конфигурацию SSL** (Обратная совместимость):
|
61
|
+
```json
|
62
|
+
{
|
63
|
+
"ssl": {
|
64
|
+
"enabled": true,
|
65
|
+
"cert_file": "./certs/server.crt",
|
66
|
+
"key_file": "./certs/server.key"
|
67
|
+
}
|
68
|
+
}
|
69
|
+
```
|
70
|
+
|
71
|
+
### Проблема 3: Ошибки инициализации security framework
|
72
|
+
|
73
|
+
**Проблема:** Security framework падает при инициализации из-за отсутствующих или null значений конфигурации.
|
74
|
+
|
75
|
+
**Симптомы:**
|
76
|
+
```
|
77
|
+
Failed to initialize security components: Failed to load roles configuration: argument should be a str or an os.PathLike object where __fspath__ returns a str, not 'NoneType'
|
78
|
+
```
|
79
|
+
|
80
|
+
**Решение:**
|
81
|
+
1. **Предоставить файл ролей** (Если используются роли):
|
82
|
+
```json
|
83
|
+
{
|
84
|
+
"security": {
|
85
|
+
"permissions": {
|
86
|
+
"enabled": true,
|
87
|
+
"roles_file": "./roles.json"
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
```
|
92
|
+
|
93
|
+
2. **Отключить permissions** (Если роли не используются):
|
94
|
+
```json
|
95
|
+
{
|
96
|
+
"security": {
|
97
|
+
"permissions": {
|
98
|
+
"enabled": false
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
```
|
103
|
+
|
104
|
+
3. **Использовать graceful fallback** (Исправлено в v1.1.0):
|
105
|
+
- Security framework теперь продолжает работу без ролей, если roles_file равен null
|
106
|
+
- Логирует предупреждение вместо падения
|
107
|
+
|
108
|
+
## Примеры конфигураций
|
109
|
+
|
110
|
+
### HTTP Simple
|
111
|
+
```json
|
112
|
+
{
|
113
|
+
"server": {"host": "127.0.0.1", "port": 10001},
|
114
|
+
"ssl": {"enabled": false},
|
115
|
+
"security": {"enabled": false},
|
116
|
+
"protocols": {"enabled": true, "allowed_protocols": ["http"]}
|
117
|
+
}
|
118
|
+
```
|
119
|
+
|
120
|
+
### HTTPS Simple
|
121
|
+
```json
|
122
|
+
{
|
123
|
+
"server": {"host": "127.0.0.1", "port": 10002},
|
124
|
+
"ssl": {"enabled": true, "cert_file": "./certs/server.crt", "key_file": "./certs/server.key"},
|
125
|
+
"security": {"enabled": false},
|
126
|
+
"protocols": {"enabled": true, "allowed_protocols": ["http", "https"]}
|
127
|
+
}
|
128
|
+
```
|
129
|
+
|
130
|
+
### HTTPS с токен-аутентификацией
|
131
|
+
```json
|
132
|
+
{
|
133
|
+
"server": {"host": "127.0.0.1", "port": 10003},
|
134
|
+
"ssl": {"enabled": true, "cert_file": "./certs/server.crt", "key_file": "./certs/server.key"},
|
135
|
+
"security": {
|
136
|
+
"enabled": true,
|
137
|
+
"auth": {"enabled": true, "methods": ["api_key"]}
|
138
|
+
},
|
139
|
+
"protocols": {"enabled": true, "allowed_protocols": ["http", "https"]}
|
140
|
+
}
|
141
|
+
```
|
142
|
+
|
143
|
+
### HTTPS без ProtocolMiddleware
|
144
|
+
```json
|
145
|
+
{
|
146
|
+
"server": {"host": "127.0.0.1", "port": 10004},
|
147
|
+
"ssl": {"enabled": true, "cert_file": "./certs/server.crt", "key_file": "./certs/server.key"},
|
148
|
+
"security": {
|
149
|
+
"enabled": true,
|
150
|
+
"auth": {"enabled": true, "methods": ["api_key"]}
|
151
|
+
},
|
152
|
+
"protocols": {"enabled": false}
|
153
|
+
}
|
154
|
+
```
|
155
|
+
|
156
|
+
### mTLS Simple
|
157
|
+
```json
|
158
|
+
{
|
159
|
+
"server": {"host": "127.0.0.1", "port": 10005},
|
160
|
+
"ssl": {
|
161
|
+
"enabled": true,
|
162
|
+
"cert_file": "./certs/server.crt",
|
163
|
+
"key_file": "./certs/server.key",
|
164
|
+
"ca_cert": "./certs/ca.crt",
|
165
|
+
"verify_client": true
|
166
|
+
},
|
167
|
+
"security": {
|
168
|
+
"enabled": true,
|
169
|
+
"auth": {"enabled": true, "methods": ["certificate"]}
|
170
|
+
},
|
171
|
+
"protocols": {"enabled": true, "allowed_protocols": ["https", "mtls"]}
|
172
|
+
}
|
173
|
+
```
|
174
|
+
|
175
|
+
## Тестирование конфигурации
|
176
|
+
|
177
|
+
### Тест HTTP
|
178
|
+
```bash
|
179
|
+
curl http://127.0.0.1:10001/health
|
180
|
+
```
|
181
|
+
|
182
|
+
### Тест HTTPS
|
183
|
+
```bash
|
184
|
+
curl -k https://127.0.0.1:10002/health
|
185
|
+
```
|
186
|
+
|
187
|
+
### Тест HTTPS с аутентификацией
|
188
|
+
```bash
|
189
|
+
curl -k -H "Authorization: Bearer your-api-key" https://127.0.0.1:10003/health
|
190
|
+
```
|
191
|
+
|
192
|
+
### Тест mTLS
|
193
|
+
```bash
|
194
|
+
curl -k --cert ./certs/client.crt --key ./certs/client.key https://127.0.0.1:10005/health
|
195
|
+
```
|
196
|
+
|
197
|
+
## Отладка
|
198
|
+
|
199
|
+
### Включить debug логирование
|
200
|
+
```json
|
201
|
+
{
|
202
|
+
"logging": {
|
203
|
+
"level": "DEBUG",
|
204
|
+
"console_output": true
|
205
|
+
}
|
206
|
+
}
|
207
|
+
```
|
208
|
+
|
209
|
+
### Проверить статус Protocol Manager
|
210
|
+
```python
|
211
|
+
from mcp_proxy_adapter.core.protocol_manager import get_protocol_manager
|
212
|
+
from mcp_proxy_adapter.config import config
|
213
|
+
|
214
|
+
pm = get_protocol_manager(config.get_all())
|
215
|
+
print(f"Allowed protocols: {pm.get_allowed_protocols()}")
|
216
|
+
print(f"Protocol info: {pm.get_protocol_info()}")
|
217
|
+
```
|
218
|
+
|
219
|
+
### Проверить конфигурацию SSL
|
220
|
+
```python
|
221
|
+
from mcp_proxy_adapter.config import config
|
222
|
+
|
223
|
+
ssl_config = config.get("ssl", {})
|
224
|
+
security_ssl = config.get("security", {}).get("ssl", {})
|
225
|
+
print(f"Legacy SSL: {ssl_config}")
|
226
|
+
print(f"Security SSL: {security_ssl}")
|
227
|
+
```
|
228
|
+
|
229
|
+
## Руководство по миграции
|
230
|
+
|
231
|
+
### От legacy к новой конфигурации
|
232
|
+
|
233
|
+
**Старая (Legacy):**
|
234
|
+
```json
|
235
|
+
{
|
236
|
+
"ssl": {"enabled": true, "cert_file": "./certs/server.crt", "key_file": "./certs/server.key"}
|
237
|
+
}
|
238
|
+
```
|
239
|
+
|
240
|
+
**Новая (Рекомендуется):**
|
241
|
+
```json
|
242
|
+
{
|
243
|
+
"security": {
|
244
|
+
"ssl": {"enabled": true, "cert_file": "./certs/server.crt", "key_file": "./certs/server.key"}
|
245
|
+
}
|
246
|
+
}
|
247
|
+
```
|
248
|
+
|
249
|
+
### Добавление управления протоколами
|
250
|
+
|
251
|
+
**Без управления протоколами:**
|
252
|
+
```json
|
253
|
+
{
|
254
|
+
"protocols": {"enabled": false}
|
255
|
+
}
|
256
|
+
```
|
257
|
+
|
258
|
+
**С управлением протоколами:**
|
259
|
+
```json
|
260
|
+
{
|
261
|
+
"protocols": {
|
262
|
+
"enabled": true,
|
263
|
+
"allowed_protocols": ["http", "https"]
|
264
|
+
}
|
265
|
+
}
|
266
|
+
```
|
267
|
+
|
268
|
+
## Лучшие практики
|
269
|
+
|
270
|
+
1. **Используйте security.ssl вместо legacy ssl** для новых конфигураций
|
271
|
+
2. **Отключайте ProtocolMiddleware** если не нужна валидация протоколов
|
272
|
+
3. **Предоставляйте roles_file** или отключайте permissions при использовании security framework
|
273
|
+
4. **Тестируйте конфигурации** перед развертыванием в продакшене
|
274
|
+
5. **Используйте debug логирование** для отладки
|
275
|
+
6. **Храните сертификаты и ключи в безопасности** и правильно настраивайте
|
276
|
+
|
277
|
+
## Поддержка
|
278
|
+
|
279
|
+
Если вы столкнулись с проблемами, не описанными в этом руководстве:
|
280
|
+
|
281
|
+
1. Проверьте логи для получения подробных сообщений об ошибках
|
282
|
+
2. Включите debug логирование для получения дополнительной информации
|
283
|
+
3. Убедитесь, что файлы сертификатов существуют и доступны для чтения
|
284
|
+
4. Тестируйте с простыми конфигурациями сначала
|
285
|
+
5. Сообщайте о проблемах с полной конфигурацией и логами ошибок
|
@@ -0,0 +1,36 @@
|
|
1
|
+
{
|
2
|
+
"server": {
|
3
|
+
"host": "0.0.0.0",
|
4
|
+
"port": 8445,
|
5
|
+
"debug": false,
|
6
|
+
"log_level": "INFO"
|
7
|
+
},
|
8
|
+
"ssl": {
|
9
|
+
"enabled": true,
|
10
|
+
"cert_file": "./certs/server.crt",
|
11
|
+
"key_file": "./certs/server.key"
|
12
|
+
},
|
13
|
+
"security": {
|
14
|
+
"enabled": true,
|
15
|
+
"auth": {
|
16
|
+
"enabled": true,
|
17
|
+
"methods": ["api_key"],
|
18
|
+
"api_keys": {
|
19
|
+
"admin": "admin-secret-key-123",
|
20
|
+
"user": "user-secret-key-456"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
},
|
24
|
+
"protocols": {
|
25
|
+
"enabled": false
|
26
|
+
},
|
27
|
+
"logging": {
|
28
|
+
"level": "INFO",
|
29
|
+
"console_output": true,
|
30
|
+
"file_output": false
|
31
|
+
},
|
32
|
+
"commands": {
|
33
|
+
"auto_discovery": true,
|
34
|
+
"commands_directory": "./commands"
|
35
|
+
}
|
36
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
{
|
2
|
+
"server": {
|
3
|
+
"host": "0.0.0.0",
|
4
|
+
"port": 8447,
|
5
|
+
"debug": false,
|
6
|
+
"log_level": "INFO"
|
7
|
+
},
|
8
|
+
"ssl": {
|
9
|
+
"enabled": true,
|
10
|
+
"cert_file": "./certs/server.crt",
|
11
|
+
"key_file": "./certs/server.key",
|
12
|
+
"ca_cert": "./certs/ca.crt",
|
13
|
+
"verify_client": true
|
14
|
+
},
|
15
|
+
"security": {
|
16
|
+
"enabled": true,
|
17
|
+
"auth": {
|
18
|
+
"enabled": true,
|
19
|
+
"methods": ["certificate"]
|
20
|
+
}
|
21
|
+
},
|
22
|
+
"protocols": {
|
23
|
+
"enabled": false
|
24
|
+
},
|
25
|
+
"logging": {
|
26
|
+
"level": "INFO",
|
27
|
+
"console_output": true,
|
28
|
+
"file_output": false
|
29
|
+
},
|
30
|
+
"commands": {
|
31
|
+
"auto_discovery": true,
|
32
|
+
"commands_directory": "./commands"
|
33
|
+
}
|
34
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
{
|
2
|
+
"server": {
|
3
|
+
"host": "0.0.0.0",
|
4
|
+
"port": 8446,
|
5
|
+
"debug": false,
|
6
|
+
"log_level": "INFO"
|
7
|
+
},
|
8
|
+
"ssl": {
|
9
|
+
"enabled": true,
|
10
|
+
"cert_file": "./certs/server.crt",
|
11
|
+
"key_file": "./certs/server.key",
|
12
|
+
"ca_cert": "./certs/ca.crt",
|
13
|
+
"verify_client": true
|
14
|
+
},
|
15
|
+
"security": {
|
16
|
+
"enabled": true,
|
17
|
+
"auth": {
|
18
|
+
"enabled": true,
|
19
|
+
"methods": ["certificate"]
|
20
|
+
}
|
21
|
+
},
|
22
|
+
"protocols": {
|
23
|
+
"enabled": true,
|
24
|
+
"allowed_protocols": ["https", "mtls"]
|
25
|
+
},
|
26
|
+
"logging": {
|
27
|
+
"level": "INFO",
|
28
|
+
"console_output": true,
|
29
|
+
"file_output": false
|
30
|
+
},
|
31
|
+
"commands": {
|
32
|
+
"auto_discovery": true,
|
33
|
+
"commands_directory": "./commands"
|
34
|
+
}
|
35
|
+
}
|
File without changes
|
File without changes
|
@@ -0,0 +1,110 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Test script for configuration generator utility.
|
4
|
+
|
5
|
+
This script tests the configuration generator to ensure it properly generates
|
6
|
+
configurations with the new protocols section and fixes for ProtocolMiddleware issues.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import sys
|
10
|
+
import json
|
11
|
+
from pathlib import Path
|
12
|
+
|
13
|
+
# Add the project root to the path
|
14
|
+
project_root = Path(__file__).parent.parent.parent
|
15
|
+
sys.path.insert(0, str(project_root))
|
16
|
+
|
17
|
+
from mcp_proxy_adapter.utils.config_generator import ConfigGenerator
|
18
|
+
|
19
|
+
|
20
|
+
def test_config_generator():
|
21
|
+
"""Test the configuration generator with different types."""
|
22
|
+
generator = ConfigGenerator()
|
23
|
+
|
24
|
+
# Test configuration types
|
25
|
+
config_types = [
|
26
|
+
"basic_http",
|
27
|
+
"http_token",
|
28
|
+
"https",
|
29
|
+
"https_token",
|
30
|
+
"https_no_protocol_middleware",
|
31
|
+
"mtls",
|
32
|
+
"mtls_no_protocol_middleware"
|
33
|
+
]
|
34
|
+
|
35
|
+
print("Testing Configuration Generator")
|
36
|
+
print("=" * 50)
|
37
|
+
|
38
|
+
for config_type in config_types:
|
39
|
+
print(f"\nTesting {config_type} configuration:")
|
40
|
+
print("-" * 30)
|
41
|
+
|
42
|
+
try:
|
43
|
+
# Generate configuration
|
44
|
+
config = generator._get_config_by_type(config_type)
|
45
|
+
|
46
|
+
# Check if protocols section exists
|
47
|
+
if "protocols" in config:
|
48
|
+
protocols = config["protocols"]
|
49
|
+
print(f"✅ Protocols section found:")
|
50
|
+
print(f" - enabled: {protocols.get('enabled', 'NOT SET')}")
|
51
|
+
print(f" - allowed_protocols: {protocols.get('allowed_protocols', 'NOT SET')}")
|
52
|
+
print(f" - default_protocol: {protocols.get('default_protocol', 'NOT SET')}")
|
53
|
+
else:
|
54
|
+
print("❌ Protocols section missing!")
|
55
|
+
|
56
|
+
# Check SSL configuration
|
57
|
+
ssl_enabled = config.get("ssl", {}).get("enabled", False)
|
58
|
+
security_ssl_enabled = config.get("security", {}).get("ssl", {}).get("enabled", False)
|
59
|
+
print(f" - legacy ssl.enabled: {ssl_enabled}")
|
60
|
+
print(f" - security.ssl.enabled: {security_ssl_enabled}")
|
61
|
+
|
62
|
+
# Check if configuration is valid for its type
|
63
|
+
if config_type == "https_no_protocol_middleware" or config_type == "mtls_no_protocol_middleware":
|
64
|
+
if protocols.get("enabled") == False:
|
65
|
+
print("✅ ProtocolMiddleware correctly disabled")
|
66
|
+
else:
|
67
|
+
print("❌ ProtocolMiddleware should be disabled but is enabled")
|
68
|
+
else:
|
69
|
+
if protocols.get("enabled") == True:
|
70
|
+
print("✅ ProtocolMiddleware correctly enabled")
|
71
|
+
else:
|
72
|
+
print("❌ ProtocolMiddleware should be enabled but is disabled")
|
73
|
+
|
74
|
+
# Save configuration to file for inspection
|
75
|
+
output_file = f"test_config_{config_type}.json"
|
76
|
+
with open(output_file, 'w') as f:
|
77
|
+
json.dump(config, f, indent=2)
|
78
|
+
print(f" - Configuration saved to {output_file}")
|
79
|
+
|
80
|
+
except Exception as e:
|
81
|
+
print(f"❌ Error generating {config_type} configuration: {e}")
|
82
|
+
|
83
|
+
print("\n" + "=" * 50)
|
84
|
+
print("Configuration generator test completed!")
|
85
|
+
|
86
|
+
|
87
|
+
def test_config_with_comments():
|
88
|
+
"""Test configuration generation with comments."""
|
89
|
+
generator = ConfigGenerator()
|
90
|
+
|
91
|
+
print("\nTesting configuration with comments:")
|
92
|
+
print("-" * 40)
|
93
|
+
|
94
|
+
try:
|
95
|
+
# Generate HTTPS configuration with comments
|
96
|
+
commented_config = generator.generate_config_with_comments("https")
|
97
|
+
print("✅ HTTPS configuration with comments generated successfully")
|
98
|
+
|
99
|
+
# Save to file
|
100
|
+
with open("test_https_with_comments.json", 'w') as f:
|
101
|
+
f.write(commented_config)
|
102
|
+
print(" - Configuration saved to test_https_with_comments.json")
|
103
|
+
|
104
|
+
except Exception as e:
|
105
|
+
print(f"❌ Error generating configuration with comments: {e}")
|
106
|
+
|
107
|
+
|
108
|
+
if __name__ == "__main__":
|
109
|
+
test_config_generator()
|
110
|
+
test_config_with_comments()
|