mcp-proxy-adapter 6.2.24__py3-none-any.whl → 6.2.25__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/app.py +0 -3
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +10 -10
- mcp_proxy_adapter/commands/health_command.py +1 -1
- mcp_proxy_adapter/core/protocol_manager.py +9 -9
- mcp_proxy_adapter/examples/create_certificates_simple.py +7 -17
- mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/generate_test_configs.py +70 -33
- mcp_proxy_adapter/examples/run_full_test_suite.py +302 -109
- mcp_proxy_adapter/examples/run_security_tests.py +14 -5
- mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/main.py +0 -2
- {mcp_proxy_adapter-6.2.24.dist-info → mcp_proxy_adapter-6.2.25.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.2.24.dist-info → mcp_proxy_adapter-6.2.25.dist-info}/RECORD +31 -15
- {mcp_proxy_adapter-6.2.24.dist-info → mcp_proxy_adapter-6.2.25.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.2.24.dist-info → mcp_proxy_adapter-6.2.25.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.2.24.dist-info → mcp_proxy_adapter-6.2.25.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.2.24.dist-info → mcp_proxy_adapter-6.2.25.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,173 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Full Application Example
|
4
|
+
This is a complete application that demonstrates all features of MCP Proxy Adapter framework:
|
5
|
+
- Built-in commands
|
6
|
+
- Custom commands
|
7
|
+
- Dynamically loaded commands
|
8
|
+
- Built-in command hooks
|
9
|
+
- Application hooks
|
10
|
+
Author: Vasiliy Zdanovskiy
|
11
|
+
email: vasilyvz@gmail.com
|
12
|
+
"""
|
13
|
+
import sys
|
14
|
+
import argparse
|
15
|
+
import logging
|
16
|
+
from pathlib import Path
|
17
|
+
# Add the framework to the path
|
18
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
19
|
+
from mcp_proxy_adapter.core.app_factory import create_and_run_server
|
20
|
+
from mcp_proxy_adapter.api.app import create_app
|
21
|
+
from mcp_proxy_adapter.config import Config
|
22
|
+
from mcp_proxy_adapter.commands.command_registry import CommandRegistry
|
23
|
+
class FullApplication:
|
24
|
+
"""Full application example with all framework features."""
|
25
|
+
def __init__(self, config_path: str):
|
26
|
+
self.config_path = config_path
|
27
|
+
self.config = Config(config_path)
|
28
|
+
self.app = None
|
29
|
+
self.command_registry = None
|
30
|
+
# Setup logging
|
31
|
+
logging.basicConfig(level=logging.INFO)
|
32
|
+
self.logger = logging.getLogger(__name__)
|
33
|
+
def setup_hooks(self):
|
34
|
+
"""Setup application hooks."""
|
35
|
+
try:
|
36
|
+
# Import hooks
|
37
|
+
from hooks.application_hooks import ApplicationHooks
|
38
|
+
from hooks.builtin_command_hooks import BuiltinCommandHooks
|
39
|
+
# Register application hooks
|
40
|
+
self.logger.info("🔧 Setting up application hooks...")
|
41
|
+
# Register built-in command hooks
|
42
|
+
self.logger.info("🔧 Setting up built-in command hooks...")
|
43
|
+
# Note: In a real implementation, these hooks would be registered
|
44
|
+
# with the framework's hook system
|
45
|
+
self.logger.info("✅ Hooks setup completed")
|
46
|
+
except ImportError as e:
|
47
|
+
self.logger.warning(f"⚠️ Could not import hooks: {e}")
|
48
|
+
def setup_custom_commands(self):
|
49
|
+
"""Setup custom commands."""
|
50
|
+
try:
|
51
|
+
self.logger.info("🔧 Setting up custom commands...")
|
52
|
+
# Import custom commands
|
53
|
+
from commands.custom_echo_command import CustomEchoCommand
|
54
|
+
from commands.dynamic_calculator_command import DynamicCalculatorCommand
|
55
|
+
# Register custom commands
|
56
|
+
# Note: In a real implementation, these would be registered
|
57
|
+
# with the framework's command registry
|
58
|
+
self.logger.info("✅ Custom commands setup completed")
|
59
|
+
except ImportError as e:
|
60
|
+
self.logger.warning(f"⚠️ Could not import custom commands: {e}")
|
61
|
+
def setup_proxy_endpoints(self):
|
62
|
+
"""Setup proxy registration endpoints."""
|
63
|
+
try:
|
64
|
+
self.logger.info("🔧 Setting up proxy endpoints...")
|
65
|
+
# Import proxy endpoints
|
66
|
+
from proxy_endpoints import router as proxy_router
|
67
|
+
# Add proxy router to the application
|
68
|
+
self.app.include_router(proxy_router)
|
69
|
+
self.logger.info("✅ Proxy endpoints setup completed")
|
70
|
+
except ImportError as e:
|
71
|
+
self.logger.warning(f"⚠️ Could not import proxy endpoints: {e}")
|
72
|
+
def create_application(self):
|
73
|
+
"""Create the FastAPI application."""
|
74
|
+
self.logger.info("🔧 Creating application...")
|
75
|
+
# Setup hooks and commands before creating app
|
76
|
+
self.setup_hooks()
|
77
|
+
self.setup_custom_commands()
|
78
|
+
# Create application with configuration
|
79
|
+
self.app = create_app(app_config=self.config)
|
80
|
+
# Setup proxy endpoints after app creation
|
81
|
+
self.setup_proxy_endpoints()
|
82
|
+
self.logger.info("✅ Application created successfully")
|
83
|
+
def run(self, host: str = None, port: int = None, debug: bool = False):
|
84
|
+
"""Run the application using the factory method."""
|
85
|
+
# Override configuration if specified
|
86
|
+
config_overrides = {}
|
87
|
+
if host:
|
88
|
+
config_overrides["host"] = host
|
89
|
+
if port:
|
90
|
+
config_overrides["port"] = port
|
91
|
+
if debug:
|
92
|
+
config_overrides["debug"] = True
|
93
|
+
print(f"🚀 Starting Full Application Example")
|
94
|
+
print(f"📋 Configuration: {self.config_path}")
|
95
|
+
print(f"🔧 Features: Built-in commands, Custom commands, Dynamic commands, Hooks, Proxy endpoints")
|
96
|
+
print("=" * 60)
|
97
|
+
# Create application with configuration
|
98
|
+
self.create_application()
|
99
|
+
# Get server configuration
|
100
|
+
server_host = self.config.get("server.host", "0.0.0.0")
|
101
|
+
server_port = self.config.get("server.port", 8000)
|
102
|
+
server_debug = self.config.get("server.debug", False)
|
103
|
+
# Get SSL configuration
|
104
|
+
ssl_enabled = self.config.get("ssl.enabled", False)
|
105
|
+
ssl_cert_file = self.config.get("ssl.cert_file")
|
106
|
+
ssl_key_file = self.config.get("ssl.key_file")
|
107
|
+
ssl_ca_cert = self.config.get("ssl.ca_cert")
|
108
|
+
verify_client = self.config.get("ssl.verify_client", False)
|
109
|
+
print(f"🌐 Server: {server_host}:{server_port}")
|
110
|
+
print(f"🔧 Debug: {server_debug}")
|
111
|
+
if ssl_enabled:
|
112
|
+
print(f"🔐 SSL: Enabled")
|
113
|
+
print(f" Certificate: {ssl_cert_file}")
|
114
|
+
print(f" Key: {ssl_key_file}")
|
115
|
+
if ssl_ca_cert:
|
116
|
+
print(f" CA: {ssl_ca_cert}")
|
117
|
+
print(f" Client verification: {verify_client}")
|
118
|
+
print("=" * 60)
|
119
|
+
# Use hypercorn directly to run the application with proxy endpoints
|
120
|
+
try:
|
121
|
+
import hypercorn.asyncio
|
122
|
+
import hypercorn.config
|
123
|
+
import asyncio
|
124
|
+
# Configure hypercorn
|
125
|
+
config_hypercorn = hypercorn.config.Config()
|
126
|
+
config_hypercorn.bind = [f"{server_host}:{server_port}"]
|
127
|
+
config_hypercorn.loglevel = "debug" if server_debug else "info"
|
128
|
+
if ssl_enabled and ssl_cert_file and ssl_key_file:
|
129
|
+
config_hypercorn.certfile = ssl_cert_file
|
130
|
+
config_hypercorn.keyfile = ssl_key_file
|
131
|
+
if ssl_ca_cert:
|
132
|
+
config_hypercorn.ca_certs = ssl_ca_cert
|
133
|
+
if verify_client:
|
134
|
+
import ssl
|
135
|
+
config_hypercorn.verify_mode = ssl.CERT_REQUIRED
|
136
|
+
print(f"🔐 Starting HTTPS server with hypercorn...")
|
137
|
+
else:
|
138
|
+
print(f"🌐 Starting HTTP server with hypercorn...")
|
139
|
+
# Run the server
|
140
|
+
asyncio.run(hypercorn.asyncio.serve(self.app, config_hypercorn))
|
141
|
+
except ImportError:
|
142
|
+
print("❌ hypercorn not installed. Installing...")
|
143
|
+
import subprocess
|
144
|
+
subprocess.run([sys.executable, "-m", "pip", "install", "hypercorn"])
|
145
|
+
print("✅ hypercorn installed. Please restart the application.")
|
146
|
+
return
|
147
|
+
def main():
|
148
|
+
"""Main entry point for the full application example."""
|
149
|
+
parser = argparse.ArgumentParser(description="Full Application Example")
|
150
|
+
parser.add_argument("--config", "-c", required=True, help="Path to configuration file")
|
151
|
+
parser.add_argument("--host", help="Server host")
|
152
|
+
parser.add_argument("--port", type=int, help="Server port")
|
153
|
+
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
|
154
|
+
args = parser.parse_args()
|
155
|
+
# Create and run application
|
156
|
+
app = FullApplication(args.config)
|
157
|
+
app.run(host=args.host, port=args.port, debug=args.debug)
|
158
|
+
# Create global app instance for import
|
159
|
+
app = None
|
160
|
+
|
161
|
+
def get_app():
|
162
|
+
"""Get the FastAPI application instance."""
|
163
|
+
global app
|
164
|
+
if app is None:
|
165
|
+
# Create a default configuration for import
|
166
|
+
config = Config("configs/mtls_with_roles.json") # Default config
|
167
|
+
app_instance = FullApplication("configs/mtls_with_roles.json")
|
168
|
+
app_instance.create_application()
|
169
|
+
app = app_instance.app
|
170
|
+
return app
|
171
|
+
|
172
|
+
if __name__ == "__main__":
|
173
|
+
main()
|
@@ -0,0 +1,154 @@
|
|
1
|
+
"""
|
2
|
+
Proxy Registration Endpoints
|
3
|
+
This module provides proxy registration endpoints for testing.
|
4
|
+
Author: Vasiliy Zdanovskiy
|
5
|
+
email: vasilyvz@gmail.com
|
6
|
+
"""
|
7
|
+
from fastapi import APIRouter, HTTPException
|
8
|
+
from pydantic import BaseModel
|
9
|
+
from typing import Dict, List, Optional
|
10
|
+
import time
|
11
|
+
import uuid
|
12
|
+
# In-memory registry for testing
|
13
|
+
_registry: Dict[str, Dict] = {}
|
14
|
+
router = APIRouter(prefix="/proxy", tags=["proxy"])
|
15
|
+
class ServerRegistration(BaseModel):
|
16
|
+
"""Server registration request model."""
|
17
|
+
server_id: str
|
18
|
+
server_url: str
|
19
|
+
server_name: str
|
20
|
+
description: Optional[str] = None
|
21
|
+
version: Optional[str] = "1.0.0"
|
22
|
+
capabilities: Optional[List[str]] = None
|
23
|
+
endpoints: Optional[Dict[str, str]] = None
|
24
|
+
auth_method: Optional[str] = "none"
|
25
|
+
security_enabled: Optional[bool] = False
|
26
|
+
class ServerUnregistration(BaseModel):
|
27
|
+
"""Server unregistration request model."""
|
28
|
+
server_key: str # Use server_key directly
|
29
|
+
class HeartbeatData(BaseModel):
|
30
|
+
"""Heartbeat data model."""
|
31
|
+
server_id: str
|
32
|
+
server_key: str
|
33
|
+
timestamp: Optional[int] = None
|
34
|
+
status: Optional[str] = "healthy"
|
35
|
+
class RegistrationResponse(BaseModel):
|
36
|
+
"""Registration response model."""
|
37
|
+
success: bool
|
38
|
+
server_key: str
|
39
|
+
message: str
|
40
|
+
copy_number: int
|
41
|
+
class DiscoveryResponse(BaseModel):
|
42
|
+
"""Discovery response model."""
|
43
|
+
success: bool
|
44
|
+
servers: List[Dict]
|
45
|
+
total: int
|
46
|
+
active: int
|
47
|
+
@router.post("/register", response_model=RegistrationResponse)
|
48
|
+
async def register_server(registration: ServerRegistration):
|
49
|
+
"""Register a server with the proxy."""
|
50
|
+
try:
|
51
|
+
# Generate unique server key
|
52
|
+
server_key = f"{registration.server_id}_{uuid.uuid4().hex[:8]}"
|
53
|
+
copy_number = 1
|
54
|
+
# Store server information
|
55
|
+
_registry[server_key] = {
|
56
|
+
"server_id": registration.server_id,
|
57
|
+
"server_url": registration.server_url,
|
58
|
+
"server_name": registration.server_name,
|
59
|
+
"description": registration.description,
|
60
|
+
"version": registration.version,
|
61
|
+
"capabilities": registration.capabilities or [],
|
62
|
+
"endpoints": registration.endpoints or {},
|
63
|
+
"auth_method": registration.auth_method,
|
64
|
+
"security_enabled": registration.security_enabled,
|
65
|
+
"registered_at": int(time.time()),
|
66
|
+
"last_heartbeat": int(time.time()),
|
67
|
+
"status": "active"
|
68
|
+
}
|
69
|
+
return RegistrationResponse(
|
70
|
+
success=True,
|
71
|
+
server_key=server_key,
|
72
|
+
message=f"Server {registration.server_name} registered successfully",
|
73
|
+
copy_number=copy_number
|
74
|
+
)
|
75
|
+
except Exception as e:
|
76
|
+
raise HTTPException(status_code=500, detail=f"Registration failed: {str(e)}")
|
77
|
+
@router.post("/unregister")
|
78
|
+
async def unregister_server(unregistration: ServerUnregistration):
|
79
|
+
"""Unregister a server from the proxy."""
|
80
|
+
try:
|
81
|
+
# Check if server exists in registry
|
82
|
+
if unregistration.server_key not in _registry:
|
83
|
+
raise HTTPException(status_code=404, detail="Server not found")
|
84
|
+
# Remove from registry
|
85
|
+
del _registry[unregistration.server_key]
|
86
|
+
return {"success": True, "message": "Server unregistered successfully"}
|
87
|
+
except HTTPException:
|
88
|
+
raise
|
89
|
+
except Exception as e:
|
90
|
+
raise HTTPException(status_code=500, detail=f"Unregistration failed: {str(e)}")
|
91
|
+
@router.post("/heartbeat")
|
92
|
+
async def send_heartbeat(heartbeat: HeartbeatData):
|
93
|
+
"""Send heartbeat for a registered server."""
|
94
|
+
try:
|
95
|
+
if heartbeat.server_key not in _registry:
|
96
|
+
raise HTTPException(status_code=404, detail="Server not found")
|
97
|
+
# Update heartbeat information
|
98
|
+
_registry[heartbeat.server_key]["last_heartbeat"] = heartbeat.timestamp or int(time.time())
|
99
|
+
_registry[heartbeat.server_key]["status"] = heartbeat.status
|
100
|
+
return {"success": True, "message": "Heartbeat received"}
|
101
|
+
except HTTPException:
|
102
|
+
raise
|
103
|
+
except Exception as e:
|
104
|
+
raise HTTPException(status_code=500, detail=f"Heartbeat failed: {str(e)}")
|
105
|
+
@router.get("/discover", response_model=DiscoveryResponse)
|
106
|
+
async def discover_servers():
|
107
|
+
"""Discover active servers."""
|
108
|
+
try:
|
109
|
+
current_time = int(time.time())
|
110
|
+
active_servers = []
|
111
|
+
for server_key, server in _registry.items():
|
112
|
+
# Consider server active if heartbeat was within last 5 minutes
|
113
|
+
if current_time - server["last_heartbeat"] < 300:
|
114
|
+
active_servers.append({
|
115
|
+
"server_key": server_key,
|
116
|
+
"server_id": server["server_id"],
|
117
|
+
"server_name": server["server_name"],
|
118
|
+
"server_url": server["server_url"],
|
119
|
+
"status": server["status"],
|
120
|
+
"last_heartbeat": server["last_heartbeat"]
|
121
|
+
})
|
122
|
+
return DiscoveryResponse(
|
123
|
+
success=True,
|
124
|
+
servers=active_servers,
|
125
|
+
total=len(_registry),
|
126
|
+
active=len(active_servers)
|
127
|
+
)
|
128
|
+
except Exception as e:
|
129
|
+
raise HTTPException(status_code=500, detail=f"Discovery failed: {str(e)}")
|
130
|
+
@router.get("/status")
|
131
|
+
async def get_proxy_status():
|
132
|
+
"""Get proxy status."""
|
133
|
+
try:
|
134
|
+
current_time = int(time.time())
|
135
|
+
active_count = sum(
|
136
|
+
1 for server in _registry.values()
|
137
|
+
if current_time - server["last_heartbeat"] < 300
|
138
|
+
)
|
139
|
+
return {
|
140
|
+
"success": True,
|
141
|
+
"total_registered": len(_registry),
|
142
|
+
"active_servers": active_count,
|
143
|
+
"inactive_servers": len(_registry) - active_count
|
144
|
+
}
|
145
|
+
except Exception as e:
|
146
|
+
raise HTTPException(status_code=500, detail=f"Status check failed: {str(e)}")
|
147
|
+
@router.delete("/clear")
|
148
|
+
async def clear_registry():
|
149
|
+
"""Clear the registry (for testing)."""
|
150
|
+
try:
|
151
|
+
_registry.clear()
|
152
|
+
return {"success": True, "message": "Registry cleared"}
|
153
|
+
except Exception as e:
|
154
|
+
raise HTTPException(status_code=500, detail=f"Clear failed: {str(e)}")
|
@@ -9,7 +9,7 @@ import json
|
|
9
9
|
import os
|
10
10
|
import argparse
|
11
11
|
from typing import Dict, Any
|
12
|
-
def generate_http_simple_config(port: int = 20000, certs_dir: str = "
|
12
|
+
def generate_http_simple_config(port: int = 20000, certs_dir: str = "certs", keys_dir: str = "keys") -> Dict[str, Any]:
|
13
13
|
"""Generate HTTP configuration without authorization."""
|
14
14
|
return {
|
15
15
|
"server": {"host": "127.0.0.1", "port": port},
|
@@ -29,7 +29,7 @@ def generate_http_simple_config(port: int = 20000, certs_dir: str = "./certs", k
|
|
29
29
|
},
|
30
30
|
"protocols": {"enabled": True, "allowed_protocols": ["http"]}
|
31
31
|
}
|
32
|
-
def generate_http_token_config(port: int = 20001, certs_dir: str = "
|
32
|
+
def generate_http_token_config(port: int = 20001, certs_dir: str = "certs", keys_dir: str = "keys", roles_file: str = "configs/roles.json") -> Dict[str, Any]:
|
33
33
|
"""Generate HTTP configuration with token authorization."""
|
34
34
|
return {
|
35
35
|
"server": {"host": "127.0.0.1", "port": port},
|
@@ -48,7 +48,7 @@ def generate_http_token_config(port: int = 20001, certs_dir: str = "./certs", ke
|
|
48
48
|
"proxy-token-123": "proxy"
|
49
49
|
}
|
50
50
|
},
|
51
|
-
"permissions": {"enabled": True, "roles_file":
|
51
|
+
"permissions": {"enabled": True, "roles_file": roles_file}
|
52
52
|
},
|
53
53
|
"registration": {
|
54
54
|
"enabled": True,
|
@@ -61,14 +61,14 @@ def generate_http_token_config(port: int = 20001, certs_dir: str = "./certs", ke
|
|
61
61
|
},
|
62
62
|
"protocols": {"enabled": True, "allowed_protocols": ["http"]}
|
63
63
|
}
|
64
|
-
def generate_https_simple_config(port: int = 20002, certs_dir: str = "
|
64
|
+
def generate_https_simple_config(port: int = 20002, certs_dir: str = "certs", keys_dir: str = "keys") -> Dict[str, Any]:
|
65
65
|
"""Generate HTTPS configuration without client certificate verification and authorization."""
|
66
66
|
return {
|
67
67
|
"server": {"host": "127.0.0.1", "port": port},
|
68
68
|
"ssl": {
|
69
69
|
"enabled": True,
|
70
|
-
"cert_file": "
|
71
|
-
"key_file": "
|
70
|
+
"cert_file": f"{certs_dir}/localhost_server.crt",
|
71
|
+
"key_file": f"{keys_dir}/localhost_server.key"
|
72
72
|
},
|
73
73
|
"security": {"enabled": False},
|
74
74
|
"registration": {
|
@@ -82,14 +82,14 @@ def generate_https_simple_config(port: int = 20002, certs_dir: str = "./certs",
|
|
82
82
|
},
|
83
83
|
"protocols": {"enabled": True, "allowed_protocols": ["http", "https"]}
|
84
84
|
}
|
85
|
-
def generate_https_token_config(port: int = 20003, certs_dir: str = "
|
85
|
+
def generate_https_token_config(port: int = 20003, certs_dir: str = "certs", keys_dir: str = "keys") -> Dict[str, Any]:
|
86
86
|
"""Generate HTTPS configuration without client certificate verification with token authorization."""
|
87
87
|
return {
|
88
88
|
"server": {"host": "127.0.0.1", "port": port},
|
89
89
|
"ssl": {
|
90
90
|
"enabled": True,
|
91
|
-
"cert_file": "
|
92
|
-
"key_file": "
|
91
|
+
"cert_file": f"{certs_dir}/localhost_server.crt",
|
92
|
+
"key_file": f"{keys_dir}/localhost_server.key"
|
93
93
|
},
|
94
94
|
"security": {
|
95
95
|
"enabled": True,
|
@@ -104,7 +104,7 @@ def generate_https_token_config(port: int = 20003, certs_dir: str = "./certs", k
|
|
104
104
|
"proxy-token-123": "proxy"
|
105
105
|
}
|
106
106
|
},
|
107
|
-
"permissions": {"enabled": True, "roles_file": "./roles.json"}
|
107
|
+
"permissions": {"enabled": True, "roles_file": "./configs/roles.json"}
|
108
108
|
},
|
109
109
|
"registration": {
|
110
110
|
"enabled": True,
|
@@ -117,33 +117,33 @@ def generate_https_token_config(port: int = 20003, certs_dir: str = "./certs", k
|
|
117
117
|
},
|
118
118
|
"protocols": {"enabled": True, "allowed_protocols": ["http", "https"]}
|
119
119
|
}
|
120
|
-
def generate_mtls_no_roles_config(port: int = 20004, certs_dir: str = "
|
120
|
+
def generate_mtls_no_roles_config(port: int = 20004, certs_dir: str = "certs", keys_dir: str = "keys") -> Dict[str, Any]:
|
121
121
|
"""Generate mTLS configuration without roles."""
|
122
122
|
return {
|
123
123
|
"server": {"host": "127.0.0.1", "port": port},
|
124
124
|
"ssl": {
|
125
125
|
"enabled": True,
|
126
|
-
"cert_file": "
|
127
|
-
"key_file": "
|
128
|
-
"ca_cert": "
|
126
|
+
"cert_file": f"{certs_dir}/localhost_server.crt",
|
127
|
+
"key_file": f"{keys_dir}/localhost_server.key",
|
128
|
+
"ca_cert": f"{certs_dir}/mcp_proxy_adapter_ca_ca.crt",
|
129
129
|
"verify_client": True
|
130
130
|
},
|
131
131
|
"security": {
|
132
132
|
"enabled": True,
|
133
133
|
"auth": {"enabled": True, "methods": ["certificate"]},
|
134
|
-
"permissions": {"enabled":
|
134
|
+
"permissions": {"enabled": False}
|
135
135
|
},
|
136
136
|
"protocols": {"enabled": True, "allowed_protocols": ["https", "mtls"]}
|
137
137
|
}
|
138
|
-
def generate_mtls_with_roles_config(port: int = 20005, certs_dir: str = "
|
138
|
+
def generate_mtls_with_roles_config(port: int = 20005, certs_dir: str = "certs", keys_dir: str = "keys", roles_file: str = "configs/roles.json") -> Dict[str, Any]:
|
139
139
|
"""Generate mTLS configuration with roles."""
|
140
140
|
return {
|
141
141
|
"server": {"host": "127.0.0.1", "port": port},
|
142
142
|
"ssl": {
|
143
143
|
"enabled": True,
|
144
|
-
"cert_file": "
|
145
|
-
"key_file": "
|
146
|
-
"ca_cert": "
|
144
|
+
"cert_file": f"{certs_dir}/localhost_server.crt",
|
145
|
+
"key_file": f"{keys_dir}/localhost_server.key",
|
146
|
+
"ca_cert": f"{certs_dir}/mcp_proxy_adapter_ca_ca.crt",
|
147
147
|
"verify_client": True
|
148
148
|
},
|
149
149
|
"registration": {
|
@@ -161,7 +161,7 @@ def generate_mtls_with_roles_config(port: int = 20005, certs_dir: str = "./certs
|
|
161
161
|
"security": {
|
162
162
|
"enabled": True,
|
163
163
|
"auth": {"enabled": True, "methods": ["certificate"]},
|
164
|
-
"permissions": {"enabled": True, "roles_file":
|
164
|
+
"permissions": {"enabled": True, "roles_file": roles_file}
|
165
165
|
},
|
166
166
|
"protocols": {"enabled": True, "allowed_protocols": ["https", "mtls"]}
|
167
167
|
}
|
@@ -222,15 +222,18 @@ def generate_roles_config() -> Dict[str, Any]:
|
|
222
222
|
"tokens": ["proxy-token-123"]
|
223
223
|
}
|
224
224
|
}
|
225
|
-
def generate_all_configs(output_dir: str, certs_dir: str = "
|
225
|
+
def generate_all_configs(output_dir: str, certs_dir: str = "certs", keys_dir: str = "keys", roles_file: str = "configs/roles.json") -> None:
|
226
226
|
"""Generate all 6 configuration types and save them to files."""
|
227
|
+
# Ensure output directory exists first
|
228
|
+
os.makedirs(output_dir, exist_ok=True)
|
229
|
+
|
227
230
|
configs = {
|
228
231
|
"http_simple": generate_http_simple_config(20000, certs_dir, keys_dir),
|
229
|
-
"http_token": generate_http_token_config(20001, certs_dir, keys_dir),
|
232
|
+
"http_token": generate_http_token_config(20001, certs_dir, keys_dir, roles_file),
|
230
233
|
"https_simple": generate_https_simple_config(20002, certs_dir, keys_dir),
|
231
234
|
"https_token": generate_https_token_config(20003, certs_dir, keys_dir),
|
232
235
|
"mtls_no_roles": generate_mtls_no_roles_config(20004, certs_dir, keys_dir),
|
233
|
-
"mtls_with_roles": generate_mtls_with_roles_config(20005, certs_dir, keys_dir)
|
236
|
+
"mtls_with_roles": generate_mtls_with_roles_config(20005, certs_dir, keys_dir, roles_file)
|
234
237
|
}
|
235
238
|
# Ensure output directory exists
|
236
239
|
os.makedirs(output_dir, exist_ok=True)
|
@@ -242,17 +245,36 @@ def generate_all_configs(output_dir: str, certs_dir: str = "./certs", keys_dir:
|
|
242
245
|
print(f"Generated: {filename}")
|
243
246
|
# Generate roles configuration
|
244
247
|
roles_config = generate_roles_config()
|
248
|
+
|
249
|
+
# Create roles.json in the root directory (test environment root) for compatibility
|
250
|
+
# When running as module, we need to create roles.json in the current working directory
|
251
|
+
# This is the directory where the user is running the command from
|
252
|
+
try:
|
253
|
+
# Get the current working directory where the user is running the command
|
254
|
+
current_dir = os.getcwd()
|
255
|
+
root_roles_filename = os.path.join(current_dir, "roles.json")
|
256
|
+
|
257
|
+
# Create roles.json in the current working directory
|
258
|
+
with open(root_roles_filename, 'w', encoding='utf-8') as f:
|
259
|
+
json.dump(roles_config, f, indent=2, ensure_ascii=False)
|
260
|
+
print(f"Generated: {root_roles_filename}")
|
261
|
+
|
262
|
+
# Also create a copy in the output directory for reference
|
263
|
+
backup_roles_filename = os.path.join(output_dir, "roles_backup.json")
|
264
|
+
with open(backup_roles_filename, 'w', encoding='utf-8') as f:
|
265
|
+
json.dump(roles_config, f, indent=2, ensure_ascii=False)
|
266
|
+
print(f"Generated backup: {backup_roles_filename}")
|
267
|
+
|
268
|
+
except Exception as e:
|
269
|
+
print(f"Warning: Could not create roles.json in current directory: {e}")
|
270
|
+
print(f"Current working directory: {os.getcwd()}")
|
271
|
+
print(f"Script directory: {os.path.dirname(os.path.abspath(__file__))}")
|
272
|
+
|
273
|
+
# Also create roles.json in configs directory for reference
|
245
274
|
roles_filename = os.path.join(output_dir, "roles.json")
|
246
275
|
with open(roles_filename, 'w', encoding='utf-8') as f:
|
247
276
|
json.dump(roles_config, f, indent=2, ensure_ascii=False)
|
248
277
|
print(f"Generated: {roles_filename}")
|
249
|
-
# Also create roles.json in certs directory for compatibility
|
250
|
-
certs_dir = os.path.join(os.path.dirname(output_dir), "certs")
|
251
|
-
if os.path.exists(certs_dir):
|
252
|
-
certs_roles_filename = os.path.join(certs_dir, "roles.json")
|
253
|
-
with open(certs_roles_filename, 'w', encoding='utf-8') as f:
|
254
|
-
json.dump(roles_config, f, indent=2, ensure_ascii=False)
|
255
|
-
print(f"Generated: {certs_roles_filename}")
|
256
278
|
print(f"\nGenerated {len(configs)} configuration files and roles.json in {output_dir}")
|
257
279
|
|
258
280
|
print("\n" + "=" * 60)
|
@@ -273,12 +295,27 @@ def main():
|
|
273
295
|
)
|
274
296
|
parser.add_argument(
|
275
297
|
"--output-dir",
|
276
|
-
default="
|
277
|
-
help="Output directory for configuration files (default:
|
298
|
+
default="configs",
|
299
|
+
help="Output directory for configuration files (default: configs)"
|
300
|
+
)
|
301
|
+
parser.add_argument(
|
302
|
+
"--certs-dir",
|
303
|
+
default="certs",
|
304
|
+
help="Certificates directory (default: certs)"
|
305
|
+
)
|
306
|
+
parser.add_argument(
|
307
|
+
"--keys-dir",
|
308
|
+
default="keys",
|
309
|
+
help="Keys directory (default: keys)"
|
310
|
+
)
|
311
|
+
parser.add_argument(
|
312
|
+
"--roles-file",
|
313
|
+
default="configs/roles.json",
|
314
|
+
help="Roles file path (default: configs/roles.json)"
|
278
315
|
)
|
279
316
|
args = parser.parse_args()
|
280
317
|
try:
|
281
|
-
generate_all_configs(args.output_dir)
|
318
|
+
generate_all_configs(args.output_dir, args.certs_dir, args.keys_dir, args.roles_file)
|
282
319
|
print("Configuration generation completed successfully!")
|
283
320
|
except Exception as e:
|
284
321
|
print(f"\n❌ CONFIGURATION GENERATION FAILED: {e}")
|