mcp-proxy-adapter 6.6.0__py3-none-any.whl → 6.6.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/examples/check_config.py +415 -0
- mcp_proxy_adapter/examples/generate_config.py +343 -0
- mcp_proxy_adapter/examples/test_chk_hostname_automated.py +214 -0
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.6.0.dist-info → mcp_proxy_adapter-6.6.1.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.6.0.dist-info → mcp_proxy_adapter-6.6.1.dist-info}/RECORD +9 -6
- {mcp_proxy_adapter-6.6.0.dist-info → mcp_proxy_adapter-6.6.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.6.0.dist-info → mcp_proxy_adapter-6.6.1.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.6.0.dist-info → mcp_proxy_adapter-6.6.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,415 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Configuration Checker CLI for MCP Proxy Adapter
|
4
|
+
Validates configuration files and checks for common issues.
|
5
|
+
|
6
|
+
Author: Vasiliy Zdanovskiy
|
7
|
+
email: vasilyvz@gmail.com
|
8
|
+
"""
|
9
|
+
|
10
|
+
import argparse
|
11
|
+
import json
|
12
|
+
import sys
|
13
|
+
import os
|
14
|
+
from pathlib import Path
|
15
|
+
from typing import Dict, Any, List, Optional
|
16
|
+
|
17
|
+
# Add the project root to the path
|
18
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
19
|
+
|
20
|
+
from mcp_proxy_adapter.config import Config
|
21
|
+
from mcp_proxy_adapter.examples.config_builder import ConfigBuilder, Protocol, AuthMethod
|
22
|
+
|
23
|
+
|
24
|
+
class ConfigChecker:
|
25
|
+
"""Configuration checker with validation and analysis."""
|
26
|
+
|
27
|
+
def __init__(self):
|
28
|
+
self.errors: List[str] = []
|
29
|
+
self.warnings: List[str] = []
|
30
|
+
self.info: List[str] = []
|
31
|
+
|
32
|
+
def check_config_file(self, config_path: str) -> bool:
|
33
|
+
"""
|
34
|
+
Check a configuration file for issues.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
config_path: Path to configuration file
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
True if config is valid, False otherwise
|
41
|
+
"""
|
42
|
+
self.errors.clear()
|
43
|
+
self.warnings.clear()
|
44
|
+
self.info.clear()
|
45
|
+
|
46
|
+
print(f"🔍 Checking configuration: {config_path}")
|
47
|
+
print("=" * 60)
|
48
|
+
|
49
|
+
# Check if file exists
|
50
|
+
if not os.path.exists(config_path):
|
51
|
+
self.errors.append(f"Configuration file not found: {config_path}")
|
52
|
+
return False
|
53
|
+
|
54
|
+
# Check if file is readable
|
55
|
+
if not os.access(config_path, os.R_OK):
|
56
|
+
self.errors.append(f"Configuration file is not readable: {config_path}")
|
57
|
+
return False
|
58
|
+
|
59
|
+
try:
|
60
|
+
# Load and parse JSON
|
61
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
62
|
+
config_data = json.load(f)
|
63
|
+
except json.JSONDecodeError as e:
|
64
|
+
self.errors.append(f"Invalid JSON format: {e}")
|
65
|
+
return False
|
66
|
+
except Exception as e:
|
67
|
+
self.errors.append(f"Error reading configuration file: {e}")
|
68
|
+
return False
|
69
|
+
|
70
|
+
# Load with Config class
|
71
|
+
try:
|
72
|
+
config = Config(config_path)
|
73
|
+
except Exception as e:
|
74
|
+
self.errors.append(f"Error loading configuration with Config class: {e}")
|
75
|
+
return False
|
76
|
+
|
77
|
+
# Perform checks
|
78
|
+
self._check_basic_structure(config_data)
|
79
|
+
self._check_server_config(config)
|
80
|
+
self._check_security_config(config)
|
81
|
+
self._check_ssl_config(config)
|
82
|
+
self._check_logging_config(config)
|
83
|
+
self._check_file_paths(config_path, config)
|
84
|
+
self._check_port_availability(config)
|
85
|
+
|
86
|
+
# Print results
|
87
|
+
self._print_results()
|
88
|
+
|
89
|
+
return len(self.errors) == 0
|
90
|
+
|
91
|
+
def _check_basic_structure(self, config_data: Dict[str, Any]) -> None:
|
92
|
+
"""Check basic configuration structure."""
|
93
|
+
required_sections = ["server", "security", "logging", "transport"]
|
94
|
+
|
95
|
+
for section in required_sections:
|
96
|
+
if section not in config_data:
|
97
|
+
self.errors.append(f"Missing required section: {section}")
|
98
|
+
else:
|
99
|
+
self.info.append(f"✅ Section '{section}' present")
|
100
|
+
|
101
|
+
def _check_server_config(self, config: Config) -> None:
|
102
|
+
"""Check server configuration."""
|
103
|
+
protocol = config.get("server.protocol")
|
104
|
+
host = config.get("server.host")
|
105
|
+
port = config.get("server.port")
|
106
|
+
|
107
|
+
if not protocol:
|
108
|
+
self.errors.append("Server protocol not specified")
|
109
|
+
elif protocol not in ["http", "https", "mtls"]:
|
110
|
+
self.errors.append(f"Invalid server protocol: {protocol}")
|
111
|
+
else:
|
112
|
+
self.info.append(f"✅ Server protocol: {protocol}")
|
113
|
+
|
114
|
+
if not host:
|
115
|
+
self.warnings.append("Server host not specified, using default")
|
116
|
+
else:
|
117
|
+
self.info.append(f"✅ Server host: {host}")
|
118
|
+
|
119
|
+
if not port:
|
120
|
+
self.errors.append("Server port not specified")
|
121
|
+
elif not isinstance(port, int) or port < 1 or port > 65535:
|
122
|
+
self.errors.append(f"Invalid server port: {port}")
|
123
|
+
else:
|
124
|
+
self.info.append(f"✅ Server port: {port}")
|
125
|
+
|
126
|
+
def _check_security_config(self, config: Config) -> None:
|
127
|
+
"""Check security configuration."""
|
128
|
+
security_enabled = config.get("security.enabled", False)
|
129
|
+
tokens = config.get("security.tokens", {})
|
130
|
+
roles = config.get("security.roles", {})
|
131
|
+
roles_file = config.get("security.roles_file")
|
132
|
+
|
133
|
+
if security_enabled:
|
134
|
+
self.info.append("✅ Security enabled")
|
135
|
+
|
136
|
+
if not tokens and not roles and not roles_file:
|
137
|
+
self.warnings.append("Security enabled but no authentication methods configured")
|
138
|
+
else:
|
139
|
+
if tokens:
|
140
|
+
self.info.append(f"✅ Tokens configured: {len(tokens)} tokens")
|
141
|
+
if roles:
|
142
|
+
self.info.append(f"✅ Roles configured: {len(roles)} roles")
|
143
|
+
if roles_file:
|
144
|
+
self.info.append(f"✅ Roles file: {roles_file}")
|
145
|
+
else:
|
146
|
+
self.info.append("ℹ️ Security disabled")
|
147
|
+
|
148
|
+
def _check_ssl_config(self, config: Config) -> None:
|
149
|
+
"""Check SSL configuration."""
|
150
|
+
protocol = config.get("server.protocol")
|
151
|
+
chk_hostname = config.get("transport.ssl.chk_hostname")
|
152
|
+
|
153
|
+
if protocol in ["https", "mtls"]:
|
154
|
+
self.info.append(f"✅ SSL protocol: {protocol}")
|
155
|
+
|
156
|
+
if chk_hostname is None:
|
157
|
+
self.warnings.append("chk_hostname not specified for SSL protocol")
|
158
|
+
elif chk_hostname:
|
159
|
+
self.info.append("✅ Hostname checking enabled")
|
160
|
+
else:
|
161
|
+
self.warnings.append("Hostname checking disabled for SSL protocol")
|
162
|
+
else:
|
163
|
+
if chk_hostname:
|
164
|
+
self.warnings.append("Hostname checking enabled for non-SSL protocol")
|
165
|
+
else:
|
166
|
+
self.info.append("✅ Hostname checking disabled for HTTP")
|
167
|
+
|
168
|
+
def _check_logging_config(self, config: Config) -> None:
|
169
|
+
"""Check logging configuration."""
|
170
|
+
log_level = config.get("logging.level")
|
171
|
+
log_file = config.get("logging.file")
|
172
|
+
log_dir = config.get("logging.log_dir")
|
173
|
+
|
174
|
+
if log_level and log_level.upper() in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]:
|
175
|
+
self.info.append(f"✅ Log level: {log_level}")
|
176
|
+
else:
|
177
|
+
self.warnings.append(f"Invalid or missing log level: {log_level}")
|
178
|
+
|
179
|
+
if log_file:
|
180
|
+
self.info.append(f"✅ Log file: {log_file}")
|
181
|
+
|
182
|
+
if log_dir:
|
183
|
+
self.info.append(f"✅ Log directory: {log_dir}")
|
184
|
+
|
185
|
+
def _check_file_paths(self, config_path: str, config: Config) -> None:
|
186
|
+
"""Check file paths in configuration."""
|
187
|
+
config_dir = Path(config_path).parent
|
188
|
+
|
189
|
+
# Check roles file
|
190
|
+
roles_file = config.get("security.roles_file")
|
191
|
+
if roles_file:
|
192
|
+
roles_path = config_dir / roles_file
|
193
|
+
if not roles_path.exists():
|
194
|
+
self.warnings.append(f"Roles file not found: {roles_path}")
|
195
|
+
else:
|
196
|
+
self.info.append(f"✅ Roles file exists: {roles_path}")
|
197
|
+
|
198
|
+
# Check log directory
|
199
|
+
log_dir = config.get("logging.log_dir")
|
200
|
+
if log_dir:
|
201
|
+
log_path = config_dir / log_dir
|
202
|
+
if not log_path.exists():
|
203
|
+
self.warnings.append(f"Log directory not found: {log_path}")
|
204
|
+
else:
|
205
|
+
self.info.append(f"✅ Log directory exists: {log_path}")
|
206
|
+
|
207
|
+
def _check_port_availability(self, config: Config) -> None:
|
208
|
+
"""Check if the configured port is available."""
|
209
|
+
import socket
|
210
|
+
|
211
|
+
port = config.get("server.port")
|
212
|
+
if not port:
|
213
|
+
return
|
214
|
+
|
215
|
+
try:
|
216
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
217
|
+
s.bind(('localhost', port))
|
218
|
+
self.info.append(f"✅ Port {port} is available")
|
219
|
+
except OSError:
|
220
|
+
self.warnings.append(f"Port {port} is already in use")
|
221
|
+
|
222
|
+
def _print_results(self) -> None:
|
223
|
+
"""Print check results."""
|
224
|
+
if self.info:
|
225
|
+
print("\n📋 Information:")
|
226
|
+
for info in self.info:
|
227
|
+
print(f" {info}")
|
228
|
+
|
229
|
+
if self.warnings:
|
230
|
+
print("\n⚠️ Warnings:")
|
231
|
+
for warning in self.warnings:
|
232
|
+
print(f" {warning}")
|
233
|
+
|
234
|
+
if self.errors:
|
235
|
+
print("\n❌ Errors:")
|
236
|
+
for error in self.errors:
|
237
|
+
print(f" {error}")
|
238
|
+
|
239
|
+
print(f"\n📊 Summary:")
|
240
|
+
print(f" Information: {len(self.info)}")
|
241
|
+
print(f" Warnings: {len(self.warnings)}")
|
242
|
+
print(f" Errors: {len(self.errors)}")
|
243
|
+
|
244
|
+
if self.errors:
|
245
|
+
print(f" Status: ❌ FAILED")
|
246
|
+
elif self.warnings:
|
247
|
+
print(f" Status: ⚠️ WARNINGS")
|
248
|
+
else:
|
249
|
+
print(f" Status: ✅ PASSED")
|
250
|
+
|
251
|
+
|
252
|
+
def check_all_configs(config_dir: str = "./configs") -> None:
|
253
|
+
"""Check all configuration files in a directory."""
|
254
|
+
config_dir_path = Path(config_dir)
|
255
|
+
|
256
|
+
if not config_dir_path.exists():
|
257
|
+
print(f"❌ Configuration directory not found: {config_dir}")
|
258
|
+
return
|
259
|
+
|
260
|
+
config_files = list(config_dir_path.glob("*.json"))
|
261
|
+
|
262
|
+
if not config_files:
|
263
|
+
print(f"❌ No JSON configuration files found in: {config_dir}")
|
264
|
+
return
|
265
|
+
|
266
|
+
print(f"🔍 Checking {len(config_files)} configuration files in {config_dir}")
|
267
|
+
print("=" * 80)
|
268
|
+
|
269
|
+
checker = ConfigChecker()
|
270
|
+
total_checked = 0
|
271
|
+
total_passed = 0
|
272
|
+
|
273
|
+
for config_file in sorted(config_files):
|
274
|
+
if checker.check_config_file(str(config_file)):
|
275
|
+
total_passed += 1
|
276
|
+
total_checked += 1
|
277
|
+
print("\n" + "-" * 80 + "\n")
|
278
|
+
|
279
|
+
print(f"📊 Final Summary:")
|
280
|
+
print(f" Total files checked: {total_checked}")
|
281
|
+
print(f" Passed: {total_passed}")
|
282
|
+
print(f" Failed: {total_checked - total_passed}")
|
283
|
+
print(f" Success rate: {(total_passed / total_checked * 100):.1f}%")
|
284
|
+
|
285
|
+
|
286
|
+
def validate_config_syntax(config_path: str) -> bool:
|
287
|
+
"""Validate configuration syntax only."""
|
288
|
+
try:
|
289
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
290
|
+
json.load(f)
|
291
|
+
print(f"✅ Configuration syntax is valid: {config_path}")
|
292
|
+
return True
|
293
|
+
except json.JSONDecodeError as e:
|
294
|
+
print(f"❌ Invalid JSON syntax: {e}")
|
295
|
+
return False
|
296
|
+
except Exception as e:
|
297
|
+
print(f"❌ Error reading file: {e}")
|
298
|
+
return False
|
299
|
+
|
300
|
+
|
301
|
+
def compare_configs(config1_path: str, config2_path: str) -> None:
|
302
|
+
"""Compare two configuration files."""
|
303
|
+
print(f"🔍 Comparing configurations:")
|
304
|
+
print(f" Config 1: {config1_path}")
|
305
|
+
print(f" Config 2: {config2_path}")
|
306
|
+
print("=" * 60)
|
307
|
+
|
308
|
+
try:
|
309
|
+
with open(config1_path, 'r') as f:
|
310
|
+
config1 = json.load(f)
|
311
|
+
with open(config2_path, 'r') as f:
|
312
|
+
config2 = json.load(f)
|
313
|
+
except Exception as e:
|
314
|
+
print(f"❌ Error loading configurations: {e}")
|
315
|
+
return
|
316
|
+
|
317
|
+
# Compare key sections
|
318
|
+
sections_to_compare = ["server", "security", "logging", "transport"]
|
319
|
+
|
320
|
+
for section in sections_to_compare:
|
321
|
+
print(f"\n📋 Section: {section}")
|
322
|
+
|
323
|
+
if section not in config1 and section not in config2:
|
324
|
+
print(" Both configs missing this section")
|
325
|
+
continue
|
326
|
+
elif section not in config1:
|
327
|
+
print(f" ❌ Missing in {config1_path}")
|
328
|
+
continue
|
329
|
+
elif section not in config2:
|
330
|
+
print(f" ❌ Missing in {config2_path}")
|
331
|
+
continue
|
332
|
+
|
333
|
+
if config1[section] == config2[section]:
|
334
|
+
print(" ✅ Identical")
|
335
|
+
else:
|
336
|
+
print(" ⚠️ Different")
|
337
|
+
# Show differences for key fields
|
338
|
+
if section == "server":
|
339
|
+
for key in ["protocol", "host", "port"]:
|
340
|
+
val1 = config1[section].get(key)
|
341
|
+
val2 = config2[section].get(key)
|
342
|
+
if val1 != val2:
|
343
|
+
print(f" {key}: {val1} vs {val2}")
|
344
|
+
|
345
|
+
|
346
|
+
def main():
|
347
|
+
"""Main CLI function."""
|
348
|
+
parser = argparse.ArgumentParser(
|
349
|
+
description="MCP Proxy Adapter Configuration Checker",
|
350
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
351
|
+
epilog="""
|
352
|
+
Examples:
|
353
|
+
# Check a single configuration file
|
354
|
+
python check_config.py configs/http.json
|
355
|
+
|
356
|
+
# Check all configurations in a directory
|
357
|
+
python check_config.py --all
|
358
|
+
|
359
|
+
# Validate JSON syntax only
|
360
|
+
python check_config.py --syntax configs/http.json
|
361
|
+
|
362
|
+
# Compare two configurations
|
363
|
+
python check_config.py --compare configs/http.json configs/https.json
|
364
|
+
"""
|
365
|
+
)
|
366
|
+
|
367
|
+
parser.add_argument(
|
368
|
+
"config_file",
|
369
|
+
nargs="?",
|
370
|
+
help="Configuration file to check"
|
371
|
+
)
|
372
|
+
|
373
|
+
parser.add_argument(
|
374
|
+
"--all",
|
375
|
+
action="store_true",
|
376
|
+
help="Check all configuration files in ./configs directory"
|
377
|
+
)
|
378
|
+
|
379
|
+
parser.add_argument(
|
380
|
+
"--syntax",
|
381
|
+
action="store_true",
|
382
|
+
help="Validate JSON syntax only"
|
383
|
+
)
|
384
|
+
|
385
|
+
parser.add_argument(
|
386
|
+
"--compare",
|
387
|
+
nargs=2,
|
388
|
+
metavar=("CONFIG1", "CONFIG2"),
|
389
|
+
help="Compare two configuration files"
|
390
|
+
)
|
391
|
+
|
392
|
+
parser.add_argument(
|
393
|
+
"--config-dir",
|
394
|
+
default="./configs",
|
395
|
+
help="Configuration directory (default: ./configs)"
|
396
|
+
)
|
397
|
+
|
398
|
+
args = parser.parse_args()
|
399
|
+
|
400
|
+
if args.compare:
|
401
|
+
compare_configs(args.compare[0], args.compare[1])
|
402
|
+
elif args.all:
|
403
|
+
check_all_configs(args.config_dir)
|
404
|
+
elif args.config_file:
|
405
|
+
if args.syntax:
|
406
|
+
validate_config_syntax(args.config_file)
|
407
|
+
else:
|
408
|
+
checker = ConfigChecker()
|
409
|
+
checker.check_config_file(args.config_file)
|
410
|
+
else:
|
411
|
+
parser.print_help()
|
412
|
+
|
413
|
+
|
414
|
+
if __name__ == "__main__":
|
415
|
+
main()
|
@@ -0,0 +1,343 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Configuration Generator CLI for MCP Proxy Adapter
|
4
|
+
Generates configurations based on command line flags.
|
5
|
+
|
6
|
+
Author: Vasiliy Zdanovskiy
|
7
|
+
email: vasilyvz@gmail.com
|
8
|
+
"""
|
9
|
+
import argparse
|
10
|
+
import json
|
11
|
+
import sys
|
12
|
+
from pathlib import Path
|
13
|
+
from typing import Dict, Any, Optional
|
14
|
+
|
15
|
+
# Add the examples directory to the path to import config_builder
|
16
|
+
sys.path.insert(0, str(Path(__file__).parent / "mcp_proxy_adapter" / "examples"))
|
17
|
+
|
18
|
+
from config_builder import ConfigBuilder, ConfigFactory, Protocol, AuthMethod
|
19
|
+
|
20
|
+
|
21
|
+
def create_config_from_flags(
|
22
|
+
protocol: str,
|
23
|
+
token: bool = False,
|
24
|
+
roles: bool = False,
|
25
|
+
host: str = "127.0.0.1",
|
26
|
+
port: int = 8000,
|
27
|
+
cert_dir: str = "./certs",
|
28
|
+
key_dir: str = "./keys",
|
29
|
+
output_dir: str = "./configs"
|
30
|
+
) -> Dict[str, Any]:
|
31
|
+
"""
|
32
|
+
Create configuration based on command line flags.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
protocol: Protocol type (http, https, mtls)
|
36
|
+
token: Enable token authentication
|
37
|
+
roles: Enable role-based access control
|
38
|
+
host: Server host
|
39
|
+
port: Server port
|
40
|
+
cert_dir: Certificate directory
|
41
|
+
key_dir: Key directory
|
42
|
+
output_dir: Output directory for configs
|
43
|
+
|
44
|
+
Returns:
|
45
|
+
Configuration dictionary
|
46
|
+
"""
|
47
|
+
# Use the simplified config builder
|
48
|
+
from config_builder import create_config_from_flags as simple_create_config
|
49
|
+
|
50
|
+
return simple_create_config(protocol, token, roles, port)
|
51
|
+
|
52
|
+
|
53
|
+
def save_config(config: Dict[str, Any], filename: str, output_dir: str) -> Path:
|
54
|
+
"""Save configuration to file."""
|
55
|
+
output_path = Path(output_dir)
|
56
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
57
|
+
|
58
|
+
config_file = output_path / f"{filename}.json"
|
59
|
+
with open(config_file, 'w', encoding='utf-8') as f:
|
60
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
61
|
+
|
62
|
+
return config_file
|
63
|
+
|
64
|
+
|
65
|
+
def create_full_config_with_all_options(host: str = "127.0.0.1", port: int = 20000) -> Dict[str, Any]:
|
66
|
+
"""
|
67
|
+
Create a full configuration with all options enabled but set to HTTP base.
|
68
|
+
This allows testing all features by enabling different sections.
|
69
|
+
|
70
|
+
Args:
|
71
|
+
host: Server host
|
72
|
+
port: Server port
|
73
|
+
|
74
|
+
Returns:
|
75
|
+
Full configuration dictionary with all options
|
76
|
+
"""
|
77
|
+
builder = ConfigBuilder()
|
78
|
+
|
79
|
+
# Set server configuration
|
80
|
+
builder.set_server(host=host, port=port)
|
81
|
+
|
82
|
+
# Set to HTTP base (no SSL by default)
|
83
|
+
builder.set_protocol(Protocol.HTTP)
|
84
|
+
|
85
|
+
# Enable all authentication options but keep them disabled by default
|
86
|
+
api_keys = {
|
87
|
+
"admin": "admin-secret-key",
|
88
|
+
"user": "user-secret-key",
|
89
|
+
"readonly": "readonly-secret-key"
|
90
|
+
}
|
91
|
+
roles = {
|
92
|
+
"admin": ["read", "write", "delete", "admin"],
|
93
|
+
"user": ["read", "write"],
|
94
|
+
"readonly": ["read"]
|
95
|
+
}
|
96
|
+
|
97
|
+
# Set up all authentication methods but keep security disabled
|
98
|
+
builder.set_auth(AuthMethod.TOKEN_ROLES, api_keys=api_keys, roles=roles)
|
99
|
+
|
100
|
+
# Disable security by default (will be enabled in tests)
|
101
|
+
config = builder.build()
|
102
|
+
config["security"]["enabled"] = False
|
103
|
+
|
104
|
+
# Add protocol variants for easy switching
|
105
|
+
config["protocol_variants"] = {
|
106
|
+
"http": {"server": {"protocol": "http"}},
|
107
|
+
"https": {"server": {"protocol": "https"}},
|
108
|
+
"mtls": {"server": {"protocol": "mtls"}}
|
109
|
+
}
|
110
|
+
|
111
|
+
# Add authentication configurations for easy switching
|
112
|
+
config["auth_variants"] = {
|
113
|
+
"none": {"security": {"enabled": False}},
|
114
|
+
"token": {
|
115
|
+
"security": {
|
116
|
+
"enabled": True,
|
117
|
+
"tokens": api_keys,
|
118
|
+
"roles": roles,
|
119
|
+
"roles_file": None
|
120
|
+
}
|
121
|
+
},
|
122
|
+
"token_roles": {
|
123
|
+
"security": {
|
124
|
+
"enabled": True,
|
125
|
+
"tokens": api_keys,
|
126
|
+
"roles": roles,
|
127
|
+
"roles_file": "configs/roles.json"
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
return config
|
133
|
+
|
134
|
+
|
135
|
+
def generate_all_configs(output_dir: str = "./configs", host: str = "127.0.0.1") -> None:
|
136
|
+
"""Generate all standard configurations."""
|
137
|
+
configs = [
|
138
|
+
# HTTP configurations
|
139
|
+
("http", False, False, 20000),
|
140
|
+
("http", True, True, 20001), # token=True always includes roles
|
141
|
+
("http", True, True, 20002), # token_roles is same as token now
|
142
|
+
|
143
|
+
# HTTPS configurations
|
144
|
+
("https", False, False, 20003),
|
145
|
+
("https", True, True, 20004), # token=True always includes roles
|
146
|
+
("https", True, True, 20005), # token_roles is same as token now
|
147
|
+
|
148
|
+
# mTLS configurations
|
149
|
+
("mtls", False, False, 20006),
|
150
|
+
("mtls", True, True, 20007), # token=True always includes roles
|
151
|
+
("mtls", True, True, 20008), # token_roles is same as token now
|
152
|
+
]
|
153
|
+
|
154
|
+
print("🔧 Generating MCP Proxy Adapter configurations...")
|
155
|
+
print("=" * 60)
|
156
|
+
|
157
|
+
generated_files = []
|
158
|
+
|
159
|
+
for protocol, token, roles, port in configs:
|
160
|
+
# Create configuration name
|
161
|
+
name_parts = [protocol]
|
162
|
+
if token:
|
163
|
+
name_parts.append("token")
|
164
|
+
if roles:
|
165
|
+
name_parts.append("roles")
|
166
|
+
|
167
|
+
config_name = "_".join(name_parts)
|
168
|
+
|
169
|
+
# Generate configuration
|
170
|
+
config = create_config_from_flags(
|
171
|
+
protocol=protocol,
|
172
|
+
token=token,
|
173
|
+
roles=roles,
|
174
|
+
host=host,
|
175
|
+
port=port,
|
176
|
+
output_dir=output_dir
|
177
|
+
)
|
178
|
+
|
179
|
+
# Save configuration
|
180
|
+
config_file = save_config(config, config_name, output_dir)
|
181
|
+
generated_files.append(config_file)
|
182
|
+
|
183
|
+
print(f"✅ Created {config_name}.json (port {port})")
|
184
|
+
|
185
|
+
# Create roles.json file if any role-based configs were generated
|
186
|
+
roles_config = {
|
187
|
+
"enabled": True,
|
188
|
+
"default_policy": {
|
189
|
+
"deny_by_default": False,
|
190
|
+
"require_role_match": False,
|
191
|
+
"case_sensitive": False,
|
192
|
+
"allow_wildcard": False
|
193
|
+
},
|
194
|
+
"roles": {
|
195
|
+
"admin": ["read", "write", "delete", "admin"],
|
196
|
+
"user": ["read", "write"],
|
197
|
+
"readonly": ["read"],
|
198
|
+
"guest": ["read"],
|
199
|
+
"proxy": ["read", "write"]
|
200
|
+
},
|
201
|
+
"permissions": {
|
202
|
+
"read": ["GET"],
|
203
|
+
"write": ["POST", "PUT", "PATCH"],
|
204
|
+
"delete": ["DELETE"],
|
205
|
+
"admin": ["*"]
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
roles_file = Path(output_dir) / "roles.json"
|
210
|
+
with open(roles_file, 'w', encoding='utf-8') as f:
|
211
|
+
json.dump(roles_config, f, indent=2, ensure_ascii=False)
|
212
|
+
print(f"✅ Created roles.json")
|
213
|
+
|
214
|
+
print(f"\n🎉 Generated {len(generated_files)} configurations in {output_dir}/")
|
215
|
+
print("\n📋 Generated configurations:")
|
216
|
+
for config_file in generated_files:
|
217
|
+
print(f" - {config_file.name}")
|
218
|
+
|
219
|
+
|
220
|
+
def main():
|
221
|
+
"""Main CLI function."""
|
222
|
+
parser = argparse.ArgumentParser(
|
223
|
+
description="MCP Proxy Adapter Configuration Generator",
|
224
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
225
|
+
epilog="""
|
226
|
+
Examples:
|
227
|
+
# Generate all standard configurations
|
228
|
+
python generate_config.py --all
|
229
|
+
|
230
|
+
# Generate full config with all options (HTTP base)
|
231
|
+
python generate_config.py --full-config
|
232
|
+
|
233
|
+
# Generate specific configuration
|
234
|
+
python generate_config.py --protocol https --token --roles --port 8080
|
235
|
+
|
236
|
+
# Generate HTTP configuration with token auth
|
237
|
+
python generate_config.py --protocol http --token
|
238
|
+
|
239
|
+
# Generate mTLS configuration with roles
|
240
|
+
python generate_config.py --protocol mtls --roles
|
241
|
+
"""
|
242
|
+
)
|
243
|
+
|
244
|
+
# Configuration options
|
245
|
+
parser.add_argument("--protocol", choices=["http", "https", "mtls"],
|
246
|
+
help="Protocol type (http, https, mtls)")
|
247
|
+
parser.add_argument("--token", action="store_true",
|
248
|
+
help="Enable token authentication")
|
249
|
+
parser.add_argument("--roles", action="store_true",
|
250
|
+
help="Enable role-based access control")
|
251
|
+
parser.add_argument("--all", action="store_true",
|
252
|
+
help="Generate all standard configurations")
|
253
|
+
parser.add_argument("--full-config", action="store_true",
|
254
|
+
help="Generate full config with all options (HTTP base)")
|
255
|
+
|
256
|
+
# Server configuration
|
257
|
+
parser.add_argument("--host", default="127.0.0.1",
|
258
|
+
help="Server host (default: 127.0.0.1)")
|
259
|
+
parser.add_argument("--port", type=int, default=8000,
|
260
|
+
help="Server port (default: 8000)")
|
261
|
+
|
262
|
+
# Paths
|
263
|
+
parser.add_argument("--cert-dir", default="./certs",
|
264
|
+
help="Certificate directory (default: ./certs)")
|
265
|
+
parser.add_argument("--key-dir", default="./keys",
|
266
|
+
help="Key directory (default: ./keys)")
|
267
|
+
parser.add_argument("--output-dir", default="./configs",
|
268
|
+
help="Output directory (default: ./configs)")
|
269
|
+
|
270
|
+
# Output options
|
271
|
+
parser.add_argument("--output", "-o",
|
272
|
+
help="Output filename (without extension)")
|
273
|
+
parser.add_argument("--stdout", action="store_true",
|
274
|
+
help="Output to stdout instead of file")
|
275
|
+
|
276
|
+
args = parser.parse_args()
|
277
|
+
|
278
|
+
try:
|
279
|
+
if args.all:
|
280
|
+
# Generate all configurations
|
281
|
+
generate_all_configs(
|
282
|
+
output_dir=args.output_dir,
|
283
|
+
host=args.host
|
284
|
+
)
|
285
|
+
elif args.full_config:
|
286
|
+
# Generate full config with all options
|
287
|
+
config = create_full_config_with_all_options(
|
288
|
+
host=args.host,
|
289
|
+
port=args.port
|
290
|
+
)
|
291
|
+
|
292
|
+
if args.stdout:
|
293
|
+
# Output to stdout
|
294
|
+
print(json.dumps(config, indent=2, ensure_ascii=False))
|
295
|
+
else:
|
296
|
+
# Save to file
|
297
|
+
filename = args.output or "full_config"
|
298
|
+
config_file = save_config(config, filename, args.output_dir)
|
299
|
+
print(f"✅ Full configuration saved to: {config_file}")
|
300
|
+
elif args.protocol:
|
301
|
+
# Generate specific configuration
|
302
|
+
config = create_config_from_flags(
|
303
|
+
protocol=args.protocol,
|
304
|
+
token=args.token,
|
305
|
+
roles=args.roles,
|
306
|
+
host=args.host,
|
307
|
+
port=args.port,
|
308
|
+
cert_dir=args.cert_dir,
|
309
|
+
key_dir=args.key_dir,
|
310
|
+
output_dir=args.output_dir
|
311
|
+
)
|
312
|
+
|
313
|
+
if args.stdout:
|
314
|
+
# Output to stdout
|
315
|
+
print(json.dumps(config, indent=2, ensure_ascii=False))
|
316
|
+
else:
|
317
|
+
# Save to file
|
318
|
+
if args.output:
|
319
|
+
filename = args.output
|
320
|
+
else:
|
321
|
+
# Generate filename from flags
|
322
|
+
name_parts = [args.protocol]
|
323
|
+
if args.token:
|
324
|
+
name_parts.append("token")
|
325
|
+
if args.roles:
|
326
|
+
name_parts.append("roles")
|
327
|
+
filename = "_".join(name_parts)
|
328
|
+
|
329
|
+
config_file = save_config(config, filename, args.output_dir)
|
330
|
+
print(f"✅ Configuration saved to: {config_file}")
|
331
|
+
else:
|
332
|
+
parser.print_help()
|
333
|
+
return 1
|
334
|
+
|
335
|
+
return 0
|
336
|
+
|
337
|
+
except Exception as e:
|
338
|
+
print(f"❌ Error: {e}", file=sys.stderr)
|
339
|
+
return 1
|
340
|
+
|
341
|
+
|
342
|
+
if __name__ == "__main__":
|
343
|
+
sys.exit(main())
|
@@ -0,0 +1,214 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Automated tests for chk_hostname functionality in all SSL modes.
|
4
|
+
|
5
|
+
Author: Vasiliy Zdanovskiy
|
6
|
+
email: vasilyvz@gmail.com
|
7
|
+
"""
|
8
|
+
|
9
|
+
import json
|
10
|
+
import tempfile
|
11
|
+
import os
|
12
|
+
from pathlib import Path
|
13
|
+
import sys
|
14
|
+
|
15
|
+
# Add the project root to the path
|
16
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
17
|
+
|
18
|
+
from mcp_proxy_adapter.config import Config
|
19
|
+
from mcp_proxy_adapter.examples.config_builder import ConfigBuilder, Protocol, AuthMethod
|
20
|
+
|
21
|
+
|
22
|
+
def test_chk_hostname_default_config():
|
23
|
+
"""Test that default config has chk_hostname=False for HTTP."""
|
24
|
+
print("🧪 Testing default config chk_hostname...")
|
25
|
+
|
26
|
+
config = Config()
|
27
|
+
|
28
|
+
# Default should be HTTP with chk_hostname=False
|
29
|
+
assert config.get("server.protocol") == "http"
|
30
|
+
assert config.get("transport.ssl.chk_hostname") is False
|
31
|
+
|
32
|
+
print("✅ Default config: chk_hostname=False for HTTP")
|
33
|
+
|
34
|
+
|
35
|
+
def test_chk_hostname_http_config():
|
36
|
+
"""Test that HTTP config has chk_hostname=False."""
|
37
|
+
print("🧪 Testing HTTP config chk_hostname...")
|
38
|
+
|
39
|
+
# Create HTTP config
|
40
|
+
http_config = ConfigBuilder().set_protocol(Protocol.HTTP).build()
|
41
|
+
|
42
|
+
# Save to temporary file and load with Config
|
43
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
44
|
+
json.dump(http_config, f)
|
45
|
+
temp_config_path = f.name
|
46
|
+
|
47
|
+
try:
|
48
|
+
config = Config(temp_config_path)
|
49
|
+
|
50
|
+
# HTTP should have chk_hostname=False
|
51
|
+
assert config.get("server.protocol") == "http"
|
52
|
+
assert config.get("transport.ssl.chk_hostname") is False
|
53
|
+
|
54
|
+
print("✅ HTTP config: chk_hostname=False")
|
55
|
+
finally:
|
56
|
+
os.unlink(temp_config_path)
|
57
|
+
|
58
|
+
|
59
|
+
def test_chk_hostname_https_config():
|
60
|
+
"""Test that HTTPS config has chk_hostname=True."""
|
61
|
+
print("🧪 Testing HTTPS config chk_hostname...")
|
62
|
+
|
63
|
+
# Create HTTPS config
|
64
|
+
https_config = ConfigBuilder().set_protocol(Protocol.HTTPS).build()
|
65
|
+
|
66
|
+
# Save to temporary file and load with Config
|
67
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
68
|
+
json.dump(https_config, f)
|
69
|
+
temp_config_path = f.name
|
70
|
+
|
71
|
+
try:
|
72
|
+
config = Config(temp_config_path)
|
73
|
+
|
74
|
+
|
75
|
+
# HTTPS should have chk_hostname=True
|
76
|
+
assert config.get("server.protocol") == "https"
|
77
|
+
assert config.get("transport.ssl.chk_hostname") is True
|
78
|
+
|
79
|
+
print("✅ HTTPS config: chk_hostname=True")
|
80
|
+
finally:
|
81
|
+
os.unlink(temp_config_path)
|
82
|
+
|
83
|
+
|
84
|
+
def test_chk_hostname_mtls_config():
|
85
|
+
"""Test that mTLS config has chk_hostname=True."""
|
86
|
+
print("🧪 Testing mTLS config chk_hostname...")
|
87
|
+
|
88
|
+
# Create mTLS config
|
89
|
+
mtls_config = ConfigBuilder().set_protocol(Protocol.MTLS).build()
|
90
|
+
|
91
|
+
# Save to temporary file and load with Config
|
92
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
93
|
+
json.dump(mtls_config, f)
|
94
|
+
temp_config_path = f.name
|
95
|
+
|
96
|
+
try:
|
97
|
+
config = Config(temp_config_path)
|
98
|
+
|
99
|
+
# mTLS should have chk_hostname=True
|
100
|
+
assert config.get("server.protocol") == "mtls"
|
101
|
+
assert config.get("transport.ssl.chk_hostname") is True
|
102
|
+
|
103
|
+
print("✅ mTLS config: chk_hostname=True")
|
104
|
+
finally:
|
105
|
+
os.unlink(temp_config_path)
|
106
|
+
|
107
|
+
|
108
|
+
def test_chk_hostname_override():
|
109
|
+
"""Test that chk_hostname can be overridden in config."""
|
110
|
+
print("🧪 Testing chk_hostname override...")
|
111
|
+
|
112
|
+
# Create HTTPS config with chk_hostname=False override
|
113
|
+
https_config = ConfigBuilder().set_protocol(Protocol.HTTPS).build()
|
114
|
+
# Add transport section if it doesn't exist
|
115
|
+
if "transport" not in https_config:
|
116
|
+
https_config["transport"] = {}
|
117
|
+
if "ssl" not in https_config["transport"]:
|
118
|
+
https_config["transport"]["ssl"] = {}
|
119
|
+
https_config["transport"]["ssl"]["chk_hostname"] = False
|
120
|
+
https_config["transport"]["ssl"]["_chk_hostname_user_set"] = True
|
121
|
+
|
122
|
+
# Save to temporary file and load with Config
|
123
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
124
|
+
json.dump(https_config, f)
|
125
|
+
temp_config_path = f.name
|
126
|
+
|
127
|
+
try:
|
128
|
+
config = Config(temp_config_path)
|
129
|
+
|
130
|
+
|
131
|
+
# Should respect the override
|
132
|
+
assert config.get("server.protocol") == "https"
|
133
|
+
assert config.get("transport.ssl.chk_hostname") is False
|
134
|
+
|
135
|
+
print("✅ HTTPS config with chk_hostname=False override works")
|
136
|
+
finally:
|
137
|
+
os.unlink(temp_config_path)
|
138
|
+
|
139
|
+
|
140
|
+
def test_chk_hostname_all_combinations():
|
141
|
+
"""Test chk_hostname for all protocol and auth combinations."""
|
142
|
+
print("🧪 Testing chk_hostname for all combinations...")
|
143
|
+
|
144
|
+
protocols = [Protocol.HTTP, Protocol.HTTPS, Protocol.MTLS]
|
145
|
+
auth_methods = [AuthMethod.NONE, AuthMethod.TOKEN, AuthMethod.TOKEN_ROLES]
|
146
|
+
|
147
|
+
for protocol in protocols:
|
148
|
+
for auth_method in auth_methods:
|
149
|
+
# Create config
|
150
|
+
config_data = (ConfigBuilder()
|
151
|
+
.set_protocol(protocol)
|
152
|
+
.set_auth(auth_method)
|
153
|
+
.build())
|
154
|
+
|
155
|
+
# Save to temporary file and load with Config
|
156
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
157
|
+
json.dump(config_data, f)
|
158
|
+
temp_config_path = f.name
|
159
|
+
|
160
|
+
try:
|
161
|
+
config = Config(temp_config_path)
|
162
|
+
|
163
|
+
protocol_name = protocol.value
|
164
|
+
auth_name = auth_method.value
|
165
|
+
|
166
|
+
# Check chk_hostname based on protocol
|
167
|
+
if protocol_name == "http":
|
168
|
+
expected_chk_hostname = False
|
169
|
+
else: # https or mtls
|
170
|
+
expected_chk_hostname = True
|
171
|
+
|
172
|
+
actual_chk_hostname = config.get("transport.ssl.chk_hostname")
|
173
|
+
|
174
|
+
assert actual_chk_hostname == expected_chk_hostname, (
|
175
|
+
f"Protocol {protocol_name} with auth {auth_name}: "
|
176
|
+
f"expected chk_hostname={expected_chk_hostname}, "
|
177
|
+
f"got {actual_chk_hostname}"
|
178
|
+
)
|
179
|
+
|
180
|
+
print(f"✅ {protocol_name}+{auth_name}: chk_hostname={actual_chk_hostname}")
|
181
|
+
|
182
|
+
finally:
|
183
|
+
os.unlink(temp_config_path)
|
184
|
+
|
185
|
+
print("✅ All protocol+auth combinations have correct chk_hostname values")
|
186
|
+
|
187
|
+
|
188
|
+
def main():
|
189
|
+
"""Run all chk_hostname tests."""
|
190
|
+
print("🧪 Running Automated chk_hostname Tests")
|
191
|
+
print("=" * 50)
|
192
|
+
|
193
|
+
try:
|
194
|
+
test_chk_hostname_default_config()
|
195
|
+
test_chk_hostname_http_config()
|
196
|
+
test_chk_hostname_https_config()
|
197
|
+
test_chk_hostname_mtls_config()
|
198
|
+
test_chk_hostname_override()
|
199
|
+
test_chk_hostname_all_combinations()
|
200
|
+
|
201
|
+
print("=" * 50)
|
202
|
+
print("🎉 All chk_hostname tests passed!")
|
203
|
+
return True
|
204
|
+
|
205
|
+
except Exception as e:
|
206
|
+
print(f"❌ Test failed: {e}")
|
207
|
+
import traceback
|
208
|
+
traceback.print_exc()
|
209
|
+
return False
|
210
|
+
|
211
|
+
|
212
|
+
if __name__ == "__main__":
|
213
|
+
success = main()
|
214
|
+
sys.exit(0 if success else 1)
|
mcp_proxy_adapter/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mcp-proxy-adapter
|
3
|
-
Version: 6.6.
|
3
|
+
Version: 6.6.1
|
4
4
|
Summary: Powerful JSON-RPC microservices framework with built-in security, authentication, and proxy registration
|
5
5
|
Home-page: https://github.com/maverikod/mcp-proxy-adapter
|
6
6
|
Author: Vasiliy Zdanovskiy
|
@@ -4,7 +4,7 @@ mcp_proxy_adapter/config.py,sha256=NmwYJPfnAFWmGsOlyYkMDfuqA-SeduuvTSjZKJbzbKc,2
|
|
4
4
|
mcp_proxy_adapter/custom_openapi.py,sha256=XRviX-C-ZkSKdBhORhDTdeN_1FWyEfXZADiASft3t9I,28149
|
5
5
|
mcp_proxy_adapter/main.py,sha256=I9fYgMD8_RNYuSHn-paeTSr9coAuVlEkfLaWeYbZors,5724
|
6
6
|
mcp_proxy_adapter/openapi.py,sha256=2UZOI09ZDRJuBYBjKbMyb2U4uASszoCMD5o_4ktRpvg,13480
|
7
|
-
mcp_proxy_adapter/version.py,sha256=
|
7
|
+
mcp_proxy_adapter/version.py,sha256=Mh_TTmFK-EgbYu8AwRu8Ax1MTsScWluXVsIKkST5imU,74
|
8
8
|
mcp_proxy_adapter/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
9
|
mcp_proxy_adapter/api/app.py,sha256=cxjavhNTtaYg2ea-UeHSDnKh8edKVNQ2NbXUDYbufFU,34183
|
10
10
|
mcp_proxy_adapter/api/handlers.py,sha256=X-hcMNVeTAu4yVkKJEChEsj2bFptUS6sLNN-Wysjkow,10011
|
@@ -85,6 +85,7 @@ mcp_proxy_adapter/core/utils.py,sha256=wBdDYBDWQ6zbwrnl9tykHjo0FjJVsLT_x8Bjk1lZX
|
|
85
85
|
mcp_proxy_adapter/examples/__init__.py,sha256=k1F-EotAFbJ3JvK_rNgiH4bUztmxIWtYn0AfbAZ1ZGs,450
|
86
86
|
mcp_proxy_adapter/examples/bugfix_certificate_config.py,sha256=YGBE_SI6wYZUJLWl7-fP1OWXiSH4mHJAZHApgQWvG7s,10529
|
87
87
|
mcp_proxy_adapter/examples/cert_manager_bugfix.py,sha256=UWUwItjqHqSnOMHocsz40_3deoZE8-vdROLW9y2fEns,7259
|
88
|
+
mcp_proxy_adapter/examples/check_config.py,sha256=W6gS6stP-WfE71YmIemvbmolf04FiR7mvJYSJqDFdzQ,14090
|
88
89
|
mcp_proxy_adapter/examples/config_builder.py,sha256=kd3-donlGuDgZwJYsy_rjzrydBg_A1GaaJuJ8jj4VDc,9696
|
89
90
|
mcp_proxy_adapter/examples/config_builder_simple.py,sha256=qTG3tIIu8eUW8gb06snaWHOTp0BFRRim38VqKrP2Ezo,9109
|
90
91
|
mcp_proxy_adapter/examples/config_cli.py,sha256=ZhVG6XEpTFe5-MzELByVsUh0AD4bHPBZeoXnGWbqifs,11059
|
@@ -98,6 +99,7 @@ mcp_proxy_adapter/examples/generate_certificates_cli.py,sha256=jb5bdNhoODL6qTWBd
|
|
98
99
|
mcp_proxy_adapter/examples/generate_certificates_fixed.py,sha256=6zJj9xdEuwj-ZO2clMoB7pNj5hW4IgwK-qsLPzuq8VQ,12566
|
99
100
|
mcp_proxy_adapter/examples/generate_certificates_framework.py,sha256=TGLyy4AJNI0w-Dd2FUQyX2sOXt05o_q4zkjoapzG1Wc,15017
|
100
101
|
mcp_proxy_adapter/examples/generate_certificates_openssl.py,sha256=5V4B8Ys_-FZsDh-CH7BNf1XXkMtpidkm4iecY0XCFuE,16891
|
102
|
+
mcp_proxy_adapter/examples/generate_config.py,sha256=4WcyCMuQKqr5qtGekoqy-jrQ-71F-ZYdDYftwLrjJNg,11461
|
101
103
|
mcp_proxy_adapter/examples/proxy_registration_example.py,sha256=vemRhftnjbiOBCJkmtDGqlWQ8syTG0a8755GCOnaQsg,12503
|
102
104
|
mcp_proxy_adapter/examples/required_certificates.py,sha256=YW9-V78oFiZ-FmHlGP-8FQFS569VdDVyq9hfvCv31pk,7133
|
103
105
|
mcp_proxy_adapter/examples/run_example.py,sha256=yp-a6HIrSk3ddQmbn0KkuKwErId0aNfj028TE6U-zmY,2626
|
@@ -107,6 +109,7 @@ mcp_proxy_adapter/examples/run_security_tests_fixed.py,sha256=aIf57LcAlNUEoroRue
|
|
107
109
|
mcp_proxy_adapter/examples/security_test_client.py,sha256=HL0AhmmHZ9SHlUx6aJ32jTHJonjKmGil74ifLbsGkZA,48660
|
108
110
|
mcp_proxy_adapter/examples/setup_test_environment.py,sha256=JkMqLpH5ZmkNKE7-WT52_kYMxEKLFOyQWbtip29TeiU,51629
|
109
111
|
mcp_proxy_adapter/examples/simple_protocol_test.py,sha256=BzFUZvK9Fih3aG4IFLQTZPyPe_s6YjpZfB6uZmQ76rw,3969
|
112
|
+
mcp_proxy_adapter/examples/test_chk_hostname_automated.py,sha256=9n3V2rk3WdC9yPj40o_3MyRlVRM6jGBBgpyJ5PL2Fd8,7178
|
110
113
|
mcp_proxy_adapter/examples/test_config.py,sha256=ekEoUZe9q484vU_0IxOVhQdNMVJXG3IpmQpP--VmuDI,6491
|
111
114
|
mcp_proxy_adapter/examples/test_config_builder.py,sha256=SAcWIC54vzO0mrb1L9xZKK2IhNkZuSx7_cMEkC1Lm60,21607
|
112
115
|
mcp_proxy_adapter/examples/test_examples.py,sha256=CYlVatdHUVC_rwv4NsvxFG3GXiKIyxPDUH43BOJHjrU,12330
|
@@ -131,8 +134,8 @@ mcp_proxy_adapter/schemas/base_schema.json,sha256=v9G9cGMd4dRhCZsOQ_FMqOi5VFyVbI
|
|
131
134
|
mcp_proxy_adapter/schemas/openapi_schema.json,sha256=C3yLkwmDsvnLW9B5gnKKdBGl4zxkeU-rEmjTrNVsQU0,8405
|
132
135
|
mcp_proxy_adapter/schemas/roles.json,sha256=pgf_ZyqKyXbfGUxvobpiLiSJz9zzxrMuoVWEkEpz3N8,764
|
133
136
|
mcp_proxy_adapter/schemas/roles_schema.json,sha256=deHgI7L6GwfBXacOlNtDgDJelDThppClC3Ti4Eh8rJY,5659
|
134
|
-
mcp_proxy_adapter-6.6.
|
135
|
-
mcp_proxy_adapter-6.6.
|
136
|
-
mcp_proxy_adapter-6.6.
|
137
|
-
mcp_proxy_adapter-6.6.
|
138
|
-
mcp_proxy_adapter-6.6.
|
137
|
+
mcp_proxy_adapter-6.6.1.dist-info/METADATA,sha256=b7eTkQKOdUxqilzA8AMTgJRr3f6SrnBwrhgRmAzML04,8510
|
138
|
+
mcp_proxy_adapter-6.6.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
139
|
+
mcp_proxy_adapter-6.6.1.dist-info/entry_points.txt,sha256=Bf-O5Aq80n22Ayu9fI9BgidzWqwzIVaqextAddTuHZw,563
|
140
|
+
mcp_proxy_adapter-6.6.1.dist-info/top_level.txt,sha256=JZT7vPLBYrtroX-ij68JBhJYbjDdghcV-DFySRy-Nnw,18
|
141
|
+
mcp_proxy_adapter-6.6.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|