mcp-proxy-adapter 6.4.12__py3-none-any.whl → 6.4.15__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 (24) hide show
  1. mcp_proxy_adapter/core/app_factory.py +105 -32
  2. mcp_proxy_adapter/core/mtls_server.py +314 -0
  3. mcp_proxy_adapter/core/server_engine.py +1 -0
  4. mcp_proxy_adapter/examples/run_full_test_suite.py +1 -5
  5. mcp_proxy_adapter/examples/setup_test_environment.py +150 -19
  6. mcp_proxy_adapter/version.py +1 -1
  7. {mcp_proxy_adapter-6.4.12.dist-info → mcp_proxy_adapter-6.4.15.dist-info}/METADATA +2 -1
  8. {mcp_proxy_adapter-6.4.12.dist-info → mcp_proxy_adapter-6.4.15.dist-info}/RECORD +11 -23
  9. mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +0 -9
  10. mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +0 -4
  11. mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +0 -4
  12. mcp_proxy_adapter/examples/examples/basic_framework/main.py +0 -52
  13. mcp_proxy_adapter/examples/examples/full_application/__init__.py +0 -13
  14. mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +0 -7
  15. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +0 -92
  16. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +0 -97
  17. mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +0 -7
  18. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +0 -88
  19. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +0 -81
  20. mcp_proxy_adapter/examples/examples/full_application/main.py +0 -61
  21. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +0 -188
  22. {mcp_proxy_adapter-6.4.12.dist-info → mcp_proxy_adapter-6.4.15.dist-info}/WHEEL +0 -0
  23. {mcp_proxy_adapter-6.4.12.dist-info → mcp_proxy_adapter-6.4.15.dist-info}/entry_points.txt +0 -0
  24. {mcp_proxy_adapter-6.4.12.dist-info → mcp_proxy_adapter-6.4.15.dist-info}/top_level.txt +0 -0
@@ -1,88 +0,0 @@
1
- """
2
- Application Hooks
3
- This module demonstrates application-level hooks in the full application example.
4
- Author: Vasiliy Zdanovskiy
5
- email: vasilyvz@gmail.com
6
- """
7
-
8
- import logging
9
- from typing import Dict, Any, Optional
10
- from datetime import datetime
11
-
12
- logger = logging.getLogger(__name__)
13
-
14
-
15
- class ApplicationHooks:
16
- """Application-level hooks."""
17
-
18
- @staticmethod
19
- def on_startup():
20
- """Hook executed on application startup."""
21
- logger.info("🚀 Application startup hook executed")
22
- # Initialize application-specific resources
23
- logger.info("📊 Initializing application metrics")
24
- logger.info("🔐 Loading security configurations")
25
- logger.info("📝 Setting up logging")
26
-
27
- @staticmethod
28
- def on_shutdown():
29
- """Hook executed on application shutdown."""
30
- logger.info("🛑 Application shutdown hook executed")
31
- # Cleanup application resources
32
- logger.info("🧹 Cleaning up resources")
33
- logger.info("💾 Saving application state")
34
- logger.info("📊 Finalizing metrics")
35
-
36
- @staticmethod
37
- def before_request(request_data: Dict[str, Any]) -> Dict[str, Any]:
38
- """Hook executed before processing any request."""
39
- logger.info(f"🔧 Application hook: before_request with data: {request_data}")
40
- # Add request metadata
41
- request_data["app_metadata"] = {
42
- "request_id": f"req_{datetime.now().timestamp()}",
43
- "timestamp": datetime.now().isoformat(),
44
- "application": "full_application_example",
45
- }
46
- return request_data
47
-
48
- @staticmethod
49
- def after_request(result: Dict[str, Any]) -> Dict[str, Any]:
50
- """Hook executed after processing any request."""
51
- logger.info(f"🔧 Application hook: after_request with result: {result}")
52
- # Add response metadata
53
- result["app_response_metadata"] = {
54
- "processed_at": datetime.now().isoformat(),
55
- "application": "full_application_example",
56
- "version": "1.0.0",
57
- }
58
- return result
59
-
60
- @staticmethod
61
- def on_error(error: Exception, context: Dict[str, Any]):
62
- """Hook executed when an error occurs."""
63
- logger.error(f"🔧 Application hook: on_error - {error} in context: {context}")
64
- # Log error details
65
- logger.error(f"Error type: {type(error).__name__}")
66
- logger.error(f"Error message: {str(error)}")
67
- logger.error(f"Context: {context}")
68
-
69
- @staticmethod
70
- def on_command_registered(command_name: str, command_info: Dict[str, Any]):
71
- """Hook executed when a command is registered."""
72
- logger.info(f"🔧 Application hook: on_command_registered - {command_name}")
73
- logger.info(f"Command info: {command_info}")
74
- # Track registered commands
75
- logger.info(f"📝 Command '{command_name}' registered successfully")
76
-
77
- @staticmethod
78
- def on_command_executed(command_name: str, execution_time: float, success: bool):
79
- """Hook executed when a command is executed."""
80
- logger.info(f"🔧 Application hook: on_command_executed - {command_name}")
81
- logger.info(f"Execution time: {execution_time}s, Success: {success}")
82
- # Track command execution metrics
83
- if success:
84
- logger.info(
85
- f"✅ Command '{command_name}' executed successfully in {execution_time}s"
86
- )
87
- else:
88
- logger.warning(f"⚠️ Command '{command_name}' failed after {execution_time}s")
@@ -1,81 +0,0 @@
1
- """
2
- Built-in Command Hooks
3
- This module demonstrates hooks for built-in commands in the full application example.
4
- Author: Vasiliy Zdanovskiy
5
- email: vasilyvz@gmail.com
6
- """
7
-
8
- import logging
9
- from typing import Dict, Any, Optional
10
- from datetime import datetime
11
-
12
- logger = logging.getLogger(__name__)
13
-
14
-
15
- class BuiltinCommandHooks:
16
- """Hooks for built-in commands."""
17
-
18
- @staticmethod
19
- def before_echo_command(params: Dict[str, Any]) -> Dict[str, Any]:
20
- """Hook executed before echo command."""
21
- logger.info(f"🔧 Built-in hook: before_echo_command with params: {params}")
22
- # Add timestamp to message
23
- if "message" in params:
24
- timestamp = datetime.now().isoformat()
25
- params["message"] = f"[{timestamp}] {params['message']}"
26
- return params
27
-
28
- @staticmethod
29
- def after_echo_command(result: Dict[str, Any]) -> Dict[str, Any]:
30
- """Hook executed after echo command."""
31
- logger.info(f"🔧 Built-in hook: after_echo_command with result: {result}")
32
- # Add hook metadata
33
- result["hook_metadata"] = {
34
- "hook_type": "builtin_after_echo",
35
- "timestamp": datetime.now().isoformat(),
36
- "processed": True,
37
- }
38
- return result
39
-
40
- @staticmethod
41
- def before_health_command(params: Dict[str, Any]) -> Dict[str, Any]:
42
- """Hook executed before health command."""
43
- logger.info(f"🔧 Built-in hook: before_health_command with params: {params}")
44
- # Add custom health check parameters
45
- params["include_detailed_info"] = True
46
- params["check_dependencies"] = True
47
- return params
48
-
49
- @staticmethod
50
- def after_health_command(result: Dict[str, Any]) -> Dict[str, Any]:
51
- """Hook executed after health command."""
52
- logger.info(f"🔧 Built-in hook: after_health_command with result: {result}")
53
- # Add custom health metrics
54
- if "status" in result and result["status"] == "healthy":
55
- result["custom_metrics"] = {
56
- "uptime": "24h",
57
- "memory_usage": "45%",
58
- "cpu_usage": "12%",
59
- }
60
- return result
61
-
62
- @staticmethod
63
- def before_config_command(params: Dict[str, Any]) -> Dict[str, Any]:
64
- """Hook executed before config command."""
65
- logger.info(f"🔧 Built-in hook: before_config_command with params: {params}")
66
- # Add configuration validation
67
- params["validate_config"] = True
68
- params["include_secrets"] = False
69
- return params
70
-
71
- @staticmethod
72
- def after_config_command(result: Dict[str, Any]) -> Dict[str, Any]:
73
- """Hook executed after config command."""
74
- logger.info(f"🔧 Built-in hook: after_config_command with result: {result}")
75
- # Add configuration metadata
76
- result["config_metadata"] = {
77
- "last_modified": datetime.now().isoformat(),
78
- "version": "1.0.0",
79
- "environment": "development",
80
- }
81
- return result
@@ -1,61 +0,0 @@
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 asyncio
16
- import logging
17
- from pathlib import Path
18
-
19
- # Add the framework to the path
20
- sys.path.insert(0, str(Path(__file__).parent.parent.parent))
21
- from mcp_proxy_adapter.core.app_factory import create_and_run_server
22
-
23
-
24
- def main():
25
- """Main entry point for the full application example."""
26
- parser = argparse.ArgumentParser(description="Full Application Example")
27
- parser.add_argument(
28
- "--config", "-c", required=True, help="Path to configuration file"
29
- )
30
- parser.add_argument("--host", help="Server host")
31
- parser.add_argument("--port", type=int, help="Server port")
32
- parser.add_argument("--debug", action="store_true", help="Enable debug mode")
33
- args = parser.parse_args()
34
-
35
- # Override configuration if specified
36
- config_overrides = {}
37
- if args.host:
38
- config_overrides["host"] = args.host
39
- if args.port:
40
- config_overrides["port"] = args.port
41
- if args.debug:
42
- config_overrides["debug"] = True
43
-
44
- print(f"🚀 Starting Full Application Example")
45
- print(f"📋 Configuration: {args.config}")
46
- print(f"🔧 Features: Built-in commands, Custom commands, Dynamic commands, Hooks, Proxy endpoints")
47
- print("=" * 60)
48
-
49
- # Use the factory method to create and run the server
50
- asyncio.run(create_and_run_server(
51
- config_path=args.config,
52
- title="Full Application Example",
53
- description="Complete MCP Proxy Adapter with all features",
54
- version="1.0.0",
55
- host=config_overrides.get("host", "0.0.0.0"),
56
- log_level="debug" if config_overrides.get("debug", False) else "info",
57
- ))
58
-
59
-
60
- if __name__ == "__main__":
61
- main()
@@ -1,188 +0,0 @@
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
-
8
- from fastapi import APIRouter, HTTPException
9
- from pydantic import BaseModel
10
- from typing import Dict, List, Optional
11
- import time
12
- import uuid
13
-
14
- # In-memory registry for testing
15
- _registry: Dict[str, Dict] = {}
16
- router = APIRouter(prefix="/proxy", tags=["proxy"])
17
-
18
-
19
- class ServerRegistration(BaseModel):
20
- """Server registration request model."""
21
-
22
- server_id: str
23
- server_url: str
24
- server_name: str
25
- description: Optional[str] = None
26
- version: Optional[str] = "1.0.0"
27
- capabilities: Optional[List[str]] = None
28
- endpoints: Optional[Dict[str, str]] = None
29
- auth_method: Optional[str] = "none"
30
- security_enabled: Optional[bool] = False
31
-
32
-
33
- class ServerUnregistration(BaseModel):
34
- """Server unregistration request model."""
35
-
36
- server_key: str # Use server_key directly
37
-
38
-
39
- class HeartbeatData(BaseModel):
40
- """Heartbeat data model."""
41
-
42
- server_id: str
43
- server_key: str
44
- timestamp: Optional[int] = None
45
- status: Optional[str] = "healthy"
46
-
47
-
48
- class RegistrationResponse(BaseModel):
49
- """Registration response model."""
50
-
51
- success: bool
52
- server_key: str
53
- message: str
54
- copy_number: int
55
-
56
-
57
- class DiscoveryResponse(BaseModel):
58
- """Discovery response model."""
59
-
60
- success: bool
61
- servers: List[Dict]
62
- total: int
63
- active: int
64
-
65
-
66
- @router.post("/register", response_model=RegistrationResponse)
67
- async def register_server(registration: ServerRegistration):
68
- """Register a server with the proxy."""
69
- try:
70
- # Generate unique server key
71
- server_key = f"{registration.server_id}_{uuid.uuid4().hex[:8]}"
72
- copy_number = 1
73
- # Store server information
74
- _registry[server_key] = {
75
- "server_id": registration.server_id,
76
- "server_url": registration.server_url,
77
- "server_name": registration.server_name,
78
- "description": registration.description,
79
- "version": registration.version,
80
- "capabilities": registration.capabilities or [],
81
- "endpoints": registration.endpoints or {},
82
- "auth_method": registration.auth_method,
83
- "security_enabled": registration.security_enabled,
84
- "registered_at": int(time.time()),
85
- "last_heartbeat": int(time.time()),
86
- "status": "active",
87
- }
88
- return RegistrationResponse(
89
- success=True,
90
- server_key=server_key,
91
- message=f"Server {registration.server_name} registered successfully",
92
- copy_number=copy_number,
93
- )
94
- except Exception as e:
95
- raise HTTPException(status_code=500, detail=f"Registration failed: {str(e)}")
96
-
97
-
98
- @router.post("/unregister")
99
- async def unregister_server(unregistration: ServerUnregistration):
100
- """Unregister a server from the proxy."""
101
- try:
102
- # Check if server exists in registry
103
- if unregistration.server_key not in _registry:
104
- raise HTTPException(status_code=404, detail="Server not found")
105
- # Remove from registry
106
- del _registry[unregistration.server_key]
107
- return {"success": True, "message": "Server unregistered successfully"}
108
- except HTTPException:
109
- raise
110
- except Exception as e:
111
- raise HTTPException(status_code=500, detail=f"Unregistration failed: {str(e)}")
112
-
113
-
114
- @router.post("/heartbeat")
115
- async def send_heartbeat(heartbeat: HeartbeatData):
116
- """Send heartbeat for a registered server."""
117
- try:
118
- if heartbeat.server_key not in _registry:
119
- raise HTTPException(status_code=404, detail="Server not found")
120
- # Update heartbeat information
121
- _registry[heartbeat.server_key]["last_heartbeat"] = heartbeat.timestamp or int(
122
- time.time()
123
- )
124
- _registry[heartbeat.server_key]["status"] = heartbeat.status
125
- return {"success": True, "message": "Heartbeat received"}
126
- except HTTPException:
127
- raise
128
- except Exception as e:
129
- raise HTTPException(status_code=500, detail=f"Heartbeat failed: {str(e)}")
130
-
131
-
132
- @router.get("/discover", response_model=DiscoveryResponse)
133
- async def discover_servers():
134
- """Discover active servers."""
135
- try:
136
- current_time = int(time.time())
137
- active_servers = []
138
- for server_key, server in _registry.items():
139
- # Consider server active if heartbeat was within last 5 minutes
140
- if current_time - server["last_heartbeat"] < 300:
141
- active_servers.append(
142
- {
143
- "server_key": server_key,
144
- "server_id": server["server_id"],
145
- "server_name": server["server_name"],
146
- "server_url": server["server_url"],
147
- "status": server["status"],
148
- "last_heartbeat": server["last_heartbeat"],
149
- }
150
- )
151
- return DiscoveryResponse(
152
- success=True,
153
- servers=active_servers,
154
- total=len(_registry),
155
- active=len(active_servers),
156
- )
157
- except Exception as e:
158
- raise HTTPException(status_code=500, detail=f"Discovery failed: {str(e)}")
159
-
160
-
161
- @router.get("/status")
162
- async def get_proxy_status():
163
- """Get proxy status."""
164
- try:
165
- current_time = int(time.time())
166
- active_count = sum(
167
- 1
168
- for server in _registry.values()
169
- if current_time - server["last_heartbeat"] < 300
170
- )
171
- return {
172
- "success": True,
173
- "total_registered": len(_registry),
174
- "active_servers": active_count,
175
- "inactive_servers": len(_registry) - active_count,
176
- }
177
- except Exception as e:
178
- raise HTTPException(status_code=500, detail=f"Status check failed: {str(e)}")
179
-
180
-
181
- @router.delete("/clear")
182
- async def clear_registry():
183
- """Clear the registry (for testing)."""
184
- try:
185
- _registry.clear()
186
- return {"success": True, "message": "Registry cleared"}
187
- except Exception as e:
188
- raise HTTPException(status_code=500, detail=f"Clear failed: {str(e)}")