mcp-proxy-adapter 3.1.5__py3-none-any.whl → 4.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. mcp_proxy_adapter/api/app.py +86 -27
  2. mcp_proxy_adapter/api/handlers.py +1 -1
  3. mcp_proxy_adapter/api/middleware/error_handling.py +11 -10
  4. mcp_proxy_adapter/api/tool_integration.py +5 -2
  5. mcp_proxy_adapter/api/tools.py +3 -3
  6. mcp_proxy_adapter/commands/base.py +19 -1
  7. mcp_proxy_adapter/commands/command_registry.py +258 -6
  8. mcp_proxy_adapter/commands/help_command.py +54 -65
  9. mcp_proxy_adapter/commands/hooks.py +260 -0
  10. mcp_proxy_adapter/commands/reload_command.py +211 -0
  11. mcp_proxy_adapter/commands/reload_settings_command.py +125 -0
  12. mcp_proxy_adapter/commands/settings_command.py +189 -0
  13. mcp_proxy_adapter/config.py +16 -1
  14. mcp_proxy_adapter/core/__init__.py +44 -0
  15. mcp_proxy_adapter/core/logging.py +87 -34
  16. mcp_proxy_adapter/core/settings.py +376 -0
  17. mcp_proxy_adapter/core/utils.py +2 -2
  18. mcp_proxy_adapter/custom_openapi.py +81 -2
  19. mcp_proxy_adapter/examples/README.md +124 -0
  20. mcp_proxy_adapter/examples/__init__.py +7 -0
  21. mcp_proxy_adapter/examples/basic_server/README.md +60 -0
  22. mcp_proxy_adapter/examples/basic_server/__init__.py +7 -0
  23. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +39 -0
  24. mcp_proxy_adapter/examples/basic_server/config.json +35 -0
  25. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +238 -0
  26. mcp_proxy_adapter/examples/basic_server/server.py +98 -0
  27. mcp_proxy_adapter/examples/custom_commands/README.md +127 -0
  28. mcp_proxy_adapter/examples/custom_commands/__init__.py +27 -0
  29. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +250 -0
  30. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +6 -0
  31. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +103 -0
  32. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +111 -0
  33. mcp_proxy_adapter/examples/custom_commands/config.json +62 -0
  34. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +169 -0
  35. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +215 -0
  36. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +76 -0
  37. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +96 -0
  38. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +241 -0
  39. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +135 -0
  40. mcp_proxy_adapter/examples/custom_commands/echo_command.py +122 -0
  41. mcp_proxy_adapter/examples/custom_commands/hooks.py +230 -0
  42. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +123 -0
  43. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +103 -0
  44. mcp_proxy_adapter/examples/custom_commands/server.py +223 -0
  45. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +176 -0
  46. mcp_proxy_adapter/examples/deployment/README.md +49 -0
  47. mcp_proxy_adapter/examples/deployment/__init__.py +7 -0
  48. mcp_proxy_adapter/examples/deployment/config.development.json +8 -0
  49. {examples/basic_example → mcp_proxy_adapter/examples/deployment}/config.json +11 -7
  50. mcp_proxy_adapter/examples/deployment/config.production.json +12 -0
  51. mcp_proxy_adapter/examples/deployment/config.staging.json +11 -0
  52. mcp_proxy_adapter/examples/deployment/docker-compose.yml +31 -0
  53. mcp_proxy_adapter/examples/deployment/run.sh +43 -0
  54. mcp_proxy_adapter/examples/deployment/run_docker.sh +84 -0
  55. mcp_proxy_adapter/openapi.py +3 -2
  56. mcp_proxy_adapter/tests/api/test_custom_openapi.py +617 -0
  57. mcp_proxy_adapter/tests/api/test_handlers.py +522 -0
  58. mcp_proxy_adapter/tests/api/test_schemas.py +546 -0
  59. mcp_proxy_adapter/tests/api/test_tool_integration.py +531 -0
  60. mcp_proxy_adapter/tests/commands/test_help_command.py +8 -5
  61. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +4 -5
  62. mcp_proxy_adapter/tests/test_command_registry.py +37 -1
  63. mcp_proxy_adapter/tests/unit/test_base_command.py +391 -85
  64. mcp_proxy_adapter/version.py +1 -1
  65. {mcp_proxy_adapter-3.1.5.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/METADATA +1 -1
  66. mcp_proxy_adapter-4.0.0.dist-info/RECORD +110 -0
  67. {mcp_proxy_adapter-3.1.5.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/WHEEL +1 -1
  68. {mcp_proxy_adapter-3.1.5.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/top_level.txt +0 -1
  69. examples/__init__.py +0 -19
  70. examples/anti_patterns/README.md +0 -51
  71. examples/anti_patterns/__init__.py +0 -9
  72. examples/anti_patterns/bad_design/README.md +0 -72
  73. examples/anti_patterns/bad_design/global_state.py +0 -170
  74. examples/anti_patterns/bad_design/monolithic_command.py +0 -272
  75. examples/basic_example/README.md +0 -245
  76. examples/basic_example/__init__.py +0 -8
  77. examples/basic_example/commands/__init__.py +0 -5
  78. examples/basic_example/commands/echo_command.py +0 -95
  79. examples/basic_example/commands/math_command.py +0 -151
  80. examples/basic_example/commands/time_command.py +0 -152
  81. examples/basic_example/docs/EN/README.md +0 -177
  82. examples/basic_example/docs/RU/README.md +0 -177
  83. examples/basic_example/server.py +0 -151
  84. examples/basic_example/tests/conftest.py +0 -243
  85. examples/check_vstl_schema.py +0 -106
  86. examples/commands/echo_command.py +0 -52
  87. examples/commands/echo_command_di.py +0 -152
  88. examples/commands/echo_result.py +0 -65
  89. examples/commands/get_date_command.py +0 -98
  90. examples/commands/new_uuid4_command.py +0 -91
  91. examples/complete_example/Dockerfile +0 -24
  92. examples/complete_example/README.md +0 -92
  93. examples/complete_example/__init__.py +0 -8
  94. examples/complete_example/commands/__init__.py +0 -5
  95. examples/complete_example/commands/system_command.py +0 -328
  96. examples/complete_example/config.json +0 -41
  97. examples/complete_example/configs/config.dev.yaml +0 -40
  98. examples/complete_example/configs/config.docker.yaml +0 -40
  99. examples/complete_example/docker-compose.yml +0 -35
  100. examples/complete_example/requirements.txt +0 -20
  101. examples/complete_example/server.py +0 -113
  102. examples/di_example/.pytest_cache/README.md +0 -8
  103. examples/di_example/server.py +0 -249
  104. examples/fix_vstl_help.py +0 -123
  105. examples/minimal_example/README.md +0 -65
  106. examples/minimal_example/__init__.py +0 -8
  107. examples/minimal_example/config.json +0 -14
  108. examples/minimal_example/main.py +0 -136
  109. examples/minimal_example/simple_server.py +0 -163
  110. examples/minimal_example/tests/conftest.py +0 -171
  111. examples/minimal_example/tests/test_hello_command.py +0 -111
  112. examples/minimal_example/tests/test_integration.py +0 -181
  113. examples/patch_vstl_service.py +0 -105
  114. examples/patch_vstl_service_mcp.py +0 -108
  115. examples/server.py +0 -69
  116. examples/simple_server.py +0 -128
  117. examples/test_package_3.1.4.py +0 -177
  118. examples/test_server.py +0 -134
  119. examples/tool_description_example.py +0 -82
  120. mcp_proxy_adapter/py.typed +0 -0
  121. mcp_proxy_adapter-3.1.5.dist-info/RECORD +0 -118
  122. {mcp_proxy_adapter-3.1.5.dist-info → mcp_proxy_adapter-4.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,40 +0,0 @@
1
- # Development configuration for complete MCP Microservice example
2
-
3
- # Server settings
4
- server:
5
- host: "localhost" # Host to bind server
6
- port: 8000 # Port to bind server
7
- workers: 1 # Number of worker processes
8
- debug: true # Enable debug mode
9
-
10
- # Logging settings
11
- logging:
12
- level: "DEBUG" # Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
13
- file: "logs/complete_example.log" # Log file path
14
- rotation:
15
- type: "size" # Log rotation type (size, time)
16
- max_bytes: 10485760 # Maximum log file size (10 MB)
17
- backup_count: 5 # Number of backup files
18
-
19
- # Command discovery settings
20
- discovery:
21
- enabled: true
22
- package: "commands" # Package to discover commands
23
-
24
- # Cache settings
25
- cache:
26
- enabled: true
27
- type: "file" # Cache type (file, memory, redis)
28
- path: "cache" # Cache directory
29
- ttl: 300 # Default TTL in seconds
30
-
31
- # Database settings (for db_command)
32
- database:
33
- enabled: false # Database is disabled in development
34
- type: "sqlite" # Database type
35
- path: ":memory:" # Database path (in-memory SQLite)
36
-
37
- # File operations settings (for file_command)
38
- files:
39
- base_path: "." # Base path for file operations
40
- allowed_extensions: [".txt", ".log", ".json", ".yaml"] # Allowed file extensions
@@ -1,40 +0,0 @@
1
- # Docker configuration for complete MCP Microservice example
2
-
3
- # Server settings
4
- server:
5
- host: "0.0.0.0" # Bind to all interfaces in Docker
6
- port: 8000 # Port to bind server
7
- workers: 2 # Number of worker processes
8
- debug: false # Disable debug mode in production
9
-
10
- # Logging settings
11
- logging:
12
- level: "INFO" # Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
13
- file: "/app/logs/complete_example.log" # Log file path in Docker container
14
- rotation:
15
- type: "size" # Log rotation type (size, time)
16
- max_bytes: 10485760 # Maximum log file size (10 MB)
17
- backup_count: 10 # Number of backup files
18
-
19
- # Command discovery settings
20
- discovery:
21
- enabled: true
22
- package: "commands" # Package to discover commands
23
-
24
- # Cache settings
25
- cache:
26
- enabled: true
27
- type: "file" # Cache type (file, memory, redis)
28
- path: "/app/cache" # Cache directory in Docker container
29
- ttl: 3600 # Default TTL in seconds (1 hour)
30
-
31
- # Database settings (for db_command)
32
- database:
33
- enabled: true # Enable database in Docker environment
34
- type: "sqlite" # Database type
35
- path: "/app/data/db.sqlite" # Database path in Docker container
36
-
37
- # File operations settings (for file_command)
38
- files:
39
- base_path: "/app/data" # Base path for file operations in Docker
40
- allowed_extensions: [".txt", ".log", ".json", ".yaml"] # Allowed file extensions
@@ -1,35 +0,0 @@
1
- version: '3.8'
2
-
3
- services:
4
- mcp-proxy-adapter:
5
- build:
6
- context: .
7
- dockerfile: Dockerfile
8
- image: mcp-proxy-adapter-example:latest
9
- container_name: mcp-proxy-adapter-example
10
- restart: unless-stopped
11
- ports:
12
- - "8000:8000"
13
- volumes:
14
- # Mount configuration, logs, and cache from host
15
- - ./configs:/app/configs:ro
16
- - ./logs:/app/logs
17
- - ./cache:/app/cache
18
- - ./data:/app/data
19
- environment:
20
- - PYTHONUNBUFFERED=1
21
- - CONFIG_PATH=/app/configs/config.docker.yaml
22
- networks:
23
- - smart-assistant
24
- user: "${UID:-1000}:${GID:-1000}" # Use host user ID to avoid permission issues
25
- healthcheck:
26
- test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
27
- interval: 30s
28
- timeout: 10s
29
- retries: 3
30
- start_period: 10s
31
-
32
- networks:
33
- smart-assistant:
34
- # Use external network if it exists, otherwise create it
35
- external: true
@@ -1,20 +0,0 @@
1
- # Python dependencies for MCP Microservice complete example
2
-
3
- # Core dependencies
4
- fastapi>=0.95.0,<1.0.0
5
- pydantic>=2.0.0,<3.0.0
6
- uvicorn>=0.22.0,<1.0.0
7
- docstring-parser>=0.15,<1.0.0
8
- typing-extensions>=4.5.0,<5.0.0
9
- jsonrpc>=1.2.0,<2.0.0
10
-
11
- # For system_command
12
- psutil>=5.9.0,<6.0.0
13
- pytz>=2023.3
14
-
15
- # For file_command
16
- aiofiles>=23.1.0,<24.0.0
17
-
18
- # For db_command
19
- aiosqlite>=0.17.0,<1.0.0
20
- SQLAlchemy>=2.0.0,<3.0.0
@@ -1,113 +0,0 @@
1
- """
2
- Complete MCP Microservice example.
3
-
4
- This example demonstrates a complete microservice application with Docker support,
5
- environment-specific configuration, and multiple commands.
6
- """
7
-
8
- import os
9
- import sys
10
- import argparse
11
- from pathlib import Path
12
- import uvicorn
13
- from mcp_proxy_adapter.api.app import create_app
14
- from mcp_proxy_adapter.commands.command_registry import registry
15
- from mcp_proxy_adapter.config import config
16
- from mcp_proxy_adapter.core.logging import logger, setup_logging
17
-
18
- # Add commands directory to path for local imports
19
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
20
-
21
-
22
- def parse_args():
23
- """
24
- Parse command line arguments.
25
-
26
- Returns:
27
- Parsed arguments
28
- """
29
- parser = argparse.ArgumentParser(description="MCP Complete Example")
30
- parser.add_argument(
31
- "--config",
32
- default="configs/config.dev.yaml",
33
- help="Path to configuration file"
34
- )
35
- return parser.parse_args()
36
-
37
-
38
- def ensure_directories():
39
- """
40
- Create necessary directories based on configuration.
41
- """
42
- base_dir = os.path.dirname(os.path.abspath(__file__))
43
- os.makedirs(os.path.join(base_dir, "logs"), exist_ok=True)
44
- os.makedirs(os.path.join(base_dir, "cache"), exist_ok=True)
45
- os.makedirs(os.path.join(base_dir, "data"), exist_ok=True)
46
-
47
-
48
- def setup_application(config_file=None):
49
- """
50
- Setup and configure the microservice application.
51
-
52
- Args:
53
- config_file: Path to configuration file (optional)
54
-
55
- Returns:
56
- Configured FastAPI application
57
- """
58
- if config_file is None:
59
- args = parse_args()
60
- config_file = args.config
61
- current_dir = Path(__file__).parent.absolute()
62
- config_path = current_dir / config_file
63
- if not config_path.exists():
64
- config_path = current_dir / "config.json"
65
- ensure_directories()
66
- if config_path.exists():
67
- config.load_from_file(str(config_path))
68
- logger.info(f"Loaded configuration from {config_path}")
69
- else:
70
- logger.warning(f"Configuration file {config_path} not found, using defaults")
71
- app = create_app()
72
- app.title = "Complete MCP Microservice Example"
73
- app.description = "Full-featured microservice with Docker support"
74
- app.version = "1.0.0"
75
- # Discover and register commands from the commands directory
76
- package_path = "commands"
77
- try:
78
- registered_commands = registry.get_all_commands()
79
- for cmd_name in list(registered_commands.keys()):
80
- try:
81
- registry.unregister(cmd_name)
82
- except Exception as e:
83
- logger.debug(f"Error unregistering command {cmd_name}: {e}")
84
- registry.discover_commands(package_path)
85
- logger.info(f"Discovered commands from package: {package_path}")
86
- except Exception as e:
87
- logger.error(f"Error discovering commands: {e}")
88
- return app
89
-
90
-
91
- def main():
92
- """Run microservice with command discovery."""
93
- log_level = config.get("logging.level", "INFO")
94
- setup_logging(log_level)
95
- app = setup_application()
96
- host = config.get("server.host", "0.0.0.0")
97
- port = config.get("server.port", 8000)
98
- if "TEST_SERVER_PORT" in os.environ:
99
- port = int(os.environ["TEST_SERVER_PORT"])
100
- logger.info(f"Using test port from environment: {port}")
101
- logger.info(f"Starting server on {host}:{port}")
102
- debug = config.get("server.debug", False)
103
- uvicorn.run(
104
- app,
105
- host=host,
106
- port=port,
107
- reload=debug,
108
- log_level=log_level.lower()
109
- )
110
-
111
-
112
- if __name__ == "__main__":
113
- main()
@@ -1,8 +0,0 @@
1
- # pytest cache directory #
2
-
3
- This directory contains data from the pytest's cache plugin,
4
- which provides the `--lf` and `--ff` options, as well as the `cache` fixture.
5
-
6
- **Do not** commit this to version control.
7
-
8
- See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information.
@@ -1,249 +0,0 @@
1
- """
2
- Example of using dependency injection in a microservice.
3
-
4
- This example demonstrates how to:
5
- 1. Create service dependencies
6
- 2. Configure a dependency container
7
- 3. Register commands with dependencies
8
- 4. Run a microservice with dependency injection
9
- """
10
-
11
- import asyncio
12
- import logging
13
- import os
14
- from typing import Dict, List, Any
15
-
16
- import uvicorn
17
- from fastapi import FastAPI
18
-
19
- from mcp_proxy_adapter import create_app
20
- from mcp_proxy_adapter.commands import registry, container
21
- from examples.commands.echo_command_di import EchoCommand, TimeService
22
-
23
-
24
- class DatabaseService:
25
- """
26
- Mock database service as an example dependency.
27
- """
28
-
29
- def __init__(self, connection_string: str):
30
- """
31
- Initialize database service.
32
-
33
- Args:
34
- connection_string: Database connection string
35
- """
36
- self.connection_string = connection_string
37
- self.is_connected = False
38
- self.data = {}
39
-
40
- async def connect(self):
41
- """Connect to the database."""
42
- # Simulate connection delay
43
- await asyncio.sleep(0.1)
44
- self.is_connected = True
45
- print(f"Connected to database: {self.connection_string}")
46
-
47
- async def disconnect(self):
48
- """Disconnect from the database."""
49
- if self.is_connected:
50
- # Simulate disconnection delay
51
- await asyncio.sleep(0.1)
52
- self.is_connected = False
53
- print("Disconnected from database")
54
-
55
- def get_item(self, key: str) -> Any:
56
- """Get item from the database."""
57
- if not self.is_connected:
58
- raise RuntimeError("Not connected to database")
59
- return self.data.get(key)
60
-
61
- def set_item(self, key: str, value: Any):
62
- """Set item in the database."""
63
- if not self.is_connected:
64
- raise RuntimeError("Not connected to database")
65
- self.data[key] = value
66
-
67
-
68
- class ConfigService:
69
- """
70
- Configuration service as an example dependency.
71
- """
72
-
73
- def __init__(self, config_path: str = None):
74
- """
75
- Initialize configuration service.
76
-
77
- Args:
78
- config_path: Path to config file (optional)
79
- """
80
- self.config_path = config_path
81
- self.config = {
82
- "app_name": "DI Example",
83
- "version": "1.0.0",
84
- "debug": True
85
- }
86
-
87
- def get(self, key: str, default: Any = None) -> Any:
88
- """Get configuration value."""
89
- return self.config.get(key, default)
90
-
91
- def set(self, key: str, value: Any):
92
- """Set configuration value."""
93
- self.config[key] = value
94
-
95
-
96
- class DataCommand(EchoCommand):
97
- """
98
- Command that uses multiple dependencies.
99
-
100
- This command demonstrates using multiple injected dependencies.
101
- """
102
-
103
- name = "data"
104
-
105
- def __init__(self, time_service: TimeService, db_service: DatabaseService, config: ConfigService):
106
- """
107
- Initialize command with multiple dependencies.
108
-
109
- Args:
110
- time_service: Service for time operations
111
- db_service: Service for database operations
112
- config: Service for configuration
113
- """
114
- super().__init__(time_service)
115
- self.db_service = db_service
116
- self.config = config
117
-
118
- async def execute(self, action: str = "get", key: str = "data", value: str = None) -> Any:
119
- """
120
- Execute data command.
121
-
122
- Args:
123
- action: Operation to perform (get or set)
124
- key: Data key
125
- value: Data value (for set operation)
126
-
127
- Returns:
128
- Command result
129
- """
130
- timestamp = self.time_service.get_current_time()
131
-
132
- if action == "get":
133
- result = self.db_service.get_item(key)
134
- return EchoCommand.result_class(
135
- message=f"Got {key}: {result}",
136
- timestamp=timestamp,
137
- data=result
138
- )
139
- elif action == "set" and value is not None:
140
- self.db_service.set_item(key, value)
141
- return EchoCommand.result_class(
142
- message=f"Set {key} to {value}",
143
- timestamp=timestamp,
144
- data={key: value}
145
- )
146
- else:
147
- return EchoCommand.result_class(
148
- message=f"Invalid action: {action}",
149
- timestamp=timestamp,
150
- error="invalid_action"
151
- )
152
-
153
- @classmethod
154
- def get_schema(cls) -> Dict[str, Any]:
155
- """Returns JSON schema for command parameters."""
156
- return {
157
- "type": "object",
158
- "properties": {
159
- "action": {
160
- "type": "string",
161
- "enum": ["get", "set"],
162
- "description": "Action to perform on data"
163
- },
164
- "key": {
165
- "type": "string",
166
- "description": "Data key"
167
- },
168
- "value": {
169
- "type": ["string", "null"],
170
- "description": "Data value (for set action)"
171
- }
172
- },
173
- "required": ["action", "key"]
174
- }
175
-
176
-
177
- async def setup_services():
178
- """Set up and register all services in the container."""
179
- # Create services
180
- time_service = TimeService()
181
- db_service = DatabaseService("sqlite://:memory:")
182
- config_service = ConfigService()
183
-
184
- # Connect to database
185
- await db_service.connect()
186
-
187
- # Register in container
188
- container.register("time_service", time_service)
189
- container.register("db_service", db_service)
190
- container.register("config", config_service)
191
-
192
- return {
193
- "time_service": time_service,
194
- "db_service": db_service,
195
- "config": config_service
196
- }
197
-
198
-
199
- def register_commands(services):
200
- """Register commands with dependencies."""
201
- # Create command instances with dependencies
202
- echo_command = EchoCommand(services["time_service"])
203
- data_command = DataCommand(
204
- services["time_service"],
205
- services["db_service"],
206
- services["config"]
207
- )
208
-
209
- # Register commands
210
- registry.register(echo_command)
211
- registry.register(data_command)
212
-
213
-
214
- async def main():
215
- """Run the example server with dependency injection."""
216
- # Configure logging
217
- logging.basicConfig(
218
- level=logging.INFO,
219
- format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
220
- )
221
-
222
- # Setup services
223
- services = await setup_services()
224
-
225
- # Register commands with their dependencies
226
- register_commands(services)
227
-
228
- # Create FastAPI app with MCP adapter
229
- app = create_app()
230
-
231
- # Add startup and shutdown events
232
- @app.on_event("shutdown")
233
- async def shutdown_event():
234
- # Disconnect database on shutdown
235
- await services["db_service"].disconnect()
236
-
237
- # Run the server
238
- config = uvicorn.Config(
239
- app=app,
240
- host="0.0.0.0",
241
- port=8000,
242
- log_level="info"
243
- )
244
- server = uvicorn.Server(config)
245
- await server.serve()
246
-
247
-
248
- if __name__ == "__main__":
249
- asyncio.run(main())
examples/fix_vstl_help.py DELETED
@@ -1,123 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Скрипт для исправления проблемы с обработкой JavaScript null в сервисе VSTL.
4
-
5
- Этот скрипт демонстрирует проблему с обработкой null значений в VSTL
6
- и способ её решения с помощью обновленной реализации метода validate_params.
7
- """
8
-
9
- import sys
10
- import json
11
- import requests
12
- from typing import Dict, Any, Optional
13
-
14
- # URL и заголовки для VSTL сервиса
15
- VSTL_URL = "http://localhost:8000/cmd"
16
- HEADERS = {"Content-Type": "application/json"}
17
-
18
- def call_vstl_help(params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
19
- """
20
- Вызывает команду help в сервисе VSTL.
21
-
22
- Args:
23
- params: Параметры для команды help
24
-
25
- Returns:
26
- Dict[str, Any]: Ответ от сервиса
27
- """
28
- payload = {
29
- "jsonrpc": "2.0",
30
- "method": "help",
31
- "params": params or {},
32
- "id": 1
33
- }
34
-
35
- response = requests.post(VSTL_URL, json=payload, headers=HEADERS)
36
- return response.json()
37
-
38
- def test_validate_params_fix():
39
- """
40
- Демонстрирует проблему с обработкой null и решение с помощью
41
- улучшенной реализации метода validate_params.
42
- """
43
- # Оригинальная реализация метода validate_params
44
- def original_validate_params(params: Dict[str, Any]) -> Dict[str, Any]:
45
- if params is None:
46
- params = {}
47
-
48
- validated_params = params.copy()
49
-
50
- for key, value in list(validated_params.items()):
51
- if value is None or (isinstance(value, str) and value == ""):
52
- if key in ["cmdname"]:
53
- pass
54
- else:
55
- del validated_params[key]
56
-
57
- return validated_params
58
-
59
- # Улучшенная реализация метода validate_params
60
- def improved_validate_params(params: Dict[str, Any]) -> Dict[str, Any]:
61
- if params is None:
62
- params = {}
63
-
64
- validated_params = params.copy()
65
-
66
- for key, value in list(validated_params.items()):
67
- if value is None or (isinstance(value, str) and value.lower() in ["null", "none", ""]):
68
- if key in ["cmdname"]:
69
- validated_params[key] = None
70
- else:
71
- del validated_params[key]
72
-
73
- return validated_params
74
-
75
- # Тестирование оригинальной реализации
76
- original_params = {"unknown_param": "null"}
77
-
78
- print("\n=== Оригинальные параметры ===")
79
- print(f"Параметры: {original_params}")
80
- print("Результат validate_params (оригинальный):")
81
- try:
82
- print(original_validate_params(original_params))
83
- except Exception as e:
84
- print(f"ОШИБКА: {e}")
85
-
86
- # Тестирование улучшенной реализации
87
- print("\n=== Улучшенная обработка 'null' ===")
88
- print(f"Параметры: {original_params}")
89
- print("Результат validate_params (улучшенный):")
90
- try:
91
- print(improved_validate_params(original_params))
92
- except Exception as e:
93
- print(f"ОШИБКА: {e}")
94
-
95
- # Проверка запроса к VSTL с null в параметрах
96
- print("\n=== Тестирование запроса к VSTL ===")
97
-
98
- # Тест с null в параметрах
99
- print("\nТест 1: Запрос с null в параметрах")
100
- response = call_vstl_help({"unknown_param": None})
101
- print(f"Ответ: {json.dumps(response, indent=2)}")
102
-
103
- # Тест с строковым "null" в параметрах
104
- print("\nТест 2: Запрос со строковым 'null' в параметрах")
105
- response = call_vstl_help({"unknown_param": "null"})
106
- print(f"Ответ: {json.dumps(response, indent=2)}")
107
-
108
- # Рекомендации по исправлению
109
- print("\n=== Рекомендации по исправлению ===")
110
- print("""
111
- 1. Обновите метод validate_params в файле commands/base.py на улучшенную версию:
112
- - Добавьте проверку на строки "null" и "none" (в любом регистре)
113
- - Для параметров, которые могут быть None, преобразуйте их в Python None
114
-
115
- 2. Обновите метод execute команды help, чтобы игнорировать неизвестные параметры:
116
- - Добавьте аргумент **kwargs в сигнатуру метода
117
-
118
- Эти изменения улучшат совместимость с клиентами, отправляющими JavaScript null
119
- и сделают API более устойчивым к различным форматам входных данных.
120
- """)
121
-
122
- if __name__ == "__main__":
123
- test_validate_params_fix()