mcp-proxy-adapter 3.1.6__py3-none-any.whl → 4.1.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.
- mcp_proxy_adapter/api/app.py +65 -27
- mcp_proxy_adapter/api/handlers.py +1 -1
- mcp_proxy_adapter/api/middleware/error_handling.py +11 -10
- mcp_proxy_adapter/api/tool_integration.py +5 -2
- mcp_proxy_adapter/api/tools.py +3 -3
- mcp_proxy_adapter/commands/base.py +19 -1
- mcp_proxy_adapter/commands/command_registry.py +254 -8
- mcp_proxy_adapter/commands/hooks.py +260 -0
- mcp_proxy_adapter/commands/reload_command.py +211 -0
- mcp_proxy_adapter/commands/reload_settings_command.py +125 -0
- mcp_proxy_adapter/commands/settings_command.py +189 -0
- mcp_proxy_adapter/config.py +16 -1
- mcp_proxy_adapter/core/__init__.py +44 -0
- mcp_proxy_adapter/core/logging.py +87 -34
- mcp_proxy_adapter/core/settings.py +376 -0
- mcp_proxy_adapter/core/utils.py +2 -2
- mcp_proxy_adapter/custom_openapi.py +81 -2
- mcp_proxy_adapter/examples/README.md +124 -0
- mcp_proxy_adapter/examples/__init__.py +7 -0
- mcp_proxy_adapter/examples/basic_server/README.md +60 -0
- mcp_proxy_adapter/examples/basic_server/__init__.py +7 -0
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +39 -0
- mcp_proxy_adapter/examples/basic_server/config.json +35 -0
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +238 -0
- mcp_proxy_adapter/examples/basic_server/server.py +98 -0
- mcp_proxy_adapter/examples/custom_commands/README.md +127 -0
- mcp_proxy_adapter/examples/custom_commands/__init__.py +27 -0
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +250 -0
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +6 -0
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +103 -0
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +111 -0
- mcp_proxy_adapter/examples/custom_commands/config.json +62 -0
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +169 -0
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +215 -0
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +76 -0
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +96 -0
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +241 -0
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +135 -0
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +122 -0
- mcp_proxy_adapter/examples/custom_commands/hooks.py +230 -0
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +123 -0
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +103 -0
- mcp_proxy_adapter/examples/custom_commands/server.py +223 -0
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +176 -0
- mcp_proxy_adapter/examples/deployment/README.md +49 -0
- mcp_proxy_adapter/examples/deployment/__init__.py +7 -0
- mcp_proxy_adapter/examples/deployment/config.development.json +8 -0
- {examples/basic_example → mcp_proxy_adapter/examples/deployment}/config.json +11 -7
- mcp_proxy_adapter/examples/deployment/config.production.json +12 -0
- mcp_proxy_adapter/examples/deployment/config.staging.json +11 -0
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +31 -0
- mcp_proxy_adapter/examples/deployment/run.sh +43 -0
- mcp_proxy_adapter/examples/deployment/run_docker.sh +84 -0
- mcp_proxy_adapter/openapi.py +3 -2
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +617 -0
- mcp_proxy_adapter/tests/api/test_handlers.py +522 -0
- mcp_proxy_adapter/tests/api/test_schemas.py +546 -0
- mcp_proxy_adapter/tests/api/test_tool_integration.py +531 -0
- mcp_proxy_adapter/tests/unit/test_base_command.py +391 -85
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.1.0.dist-info}/METADATA +3 -3
- mcp_proxy_adapter-4.1.0.dist-info/RECORD +110 -0
- {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.1.0.dist-info}/WHEEL +1 -1
- {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.1.0.dist-info}/top_level.txt +0 -1
- examples/__init__.py +0 -19
- examples/anti_patterns/README.md +0 -51
- examples/anti_patterns/__init__.py +0 -9
- examples/anti_patterns/bad_design/README.md +0 -72
- examples/anti_patterns/bad_design/global_state.py +0 -170
- examples/anti_patterns/bad_design/monolithic_command.py +0 -272
- examples/basic_example/README.md +0 -245
- examples/basic_example/__init__.py +0 -8
- examples/basic_example/commands/__init__.py +0 -5
- examples/basic_example/commands/echo_command.py +0 -95
- examples/basic_example/commands/math_command.py +0 -151
- examples/basic_example/commands/time_command.py +0 -152
- examples/basic_example/docs/EN/README.md +0 -177
- examples/basic_example/docs/RU/README.md +0 -177
- examples/basic_example/server.py +0 -151
- examples/basic_example/tests/conftest.py +0 -243
- examples/check_vstl_schema.py +0 -106
- examples/commands/echo_command.py +0 -52
- examples/commands/echo_command_di.py +0 -152
- examples/commands/echo_result.py +0 -65
- examples/commands/get_date_command.py +0 -98
- examples/commands/new_uuid4_command.py +0 -91
- examples/complete_example/Dockerfile +0 -24
- examples/complete_example/README.md +0 -92
- examples/complete_example/__init__.py +0 -8
- examples/complete_example/commands/__init__.py +0 -5
- examples/complete_example/commands/system_command.py +0 -328
- examples/complete_example/config.json +0 -41
- examples/complete_example/configs/config.dev.yaml +0 -40
- examples/complete_example/configs/config.docker.yaml +0 -40
- examples/complete_example/docker-compose.yml +0 -35
- examples/complete_example/requirements.txt +0 -20
- examples/complete_example/server.py +0 -113
- examples/di_example/.pytest_cache/README.md +0 -8
- examples/di_example/server.py +0 -249
- examples/fix_vstl_help.py +0 -123
- examples/minimal_example/README.md +0 -65
- examples/minimal_example/__init__.py +0 -8
- examples/minimal_example/config.json +0 -14
- examples/minimal_example/main.py +0 -136
- examples/minimal_example/simple_server.py +0 -163
- examples/minimal_example/tests/conftest.py +0 -171
- examples/minimal_example/tests/test_hello_command.py +0 -111
- examples/minimal_example/tests/test_integration.py +0 -181
- examples/patch_vstl_service.py +0 -105
- examples/patch_vstl_service_mcp.py +0 -108
- examples/server.py +0 -69
- examples/simple_server.py +0 -128
- examples/test_package_3.1.4.py +0 -177
- examples/test_server.py +0 -134
- examples/tool_description_example.py +0 -82
- mcp_proxy_adapter/py.typed +0 -0
- mcp_proxy_adapter-3.1.6.dist-info/RECORD +0 -118
- {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -122,15 +122,28 @@ def setup_logging(
|
|
122
122
|
# Get parameters from configuration if not explicitly specified
|
123
123
|
level = level or config.get("logging.level", "INFO")
|
124
124
|
log_file = log_file or config.get("logging.file")
|
125
|
-
rotation_type = rotation_type or
|
125
|
+
rotation_type = rotation_type or "size" # Default to size-based rotation
|
126
126
|
|
127
|
-
#
|
128
|
-
|
129
|
-
|
127
|
+
# Get log directory and file settings from config
|
128
|
+
log_dir = config.get("logging.log_dir", "./logs")
|
129
|
+
log_file_name = config.get("logging.log_file", "mcp_proxy_adapter.log")
|
130
|
+
error_log_file = config.get("logging.error_log_file", "mcp_proxy_adapter_error.log")
|
131
|
+
access_log_file = config.get("logging.access_log_file", "mcp_proxy_adapter_access.log")
|
130
132
|
|
131
|
-
#
|
132
|
-
|
133
|
-
|
133
|
+
# Get rotation settings from config
|
134
|
+
max_file_size_str = config.get("logging.max_file_size", "10MB")
|
135
|
+
backup_count = backup_count or config.get("logging.backup_count", 5)
|
136
|
+
|
137
|
+
# Parse max file size (e.g., "10MB" -> 10 * 1024 * 1024)
|
138
|
+
max_bytes = max_bytes or _parse_file_size(max_file_size_str)
|
139
|
+
|
140
|
+
# Get format settings
|
141
|
+
log_format = config.get("logging.format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
142
|
+
date_format = config.get("logging.date_format", "%Y-%m-%d %H:%M:%S")
|
143
|
+
|
144
|
+
# Get output settings
|
145
|
+
console_output = config.get("logging.console_output", True)
|
146
|
+
file_output = config.get("logging.file_output", True)
|
134
147
|
|
135
148
|
# Convert string logging level to constant
|
136
149
|
numeric_level = getattr(logging, level.upper(), None)
|
@@ -142,42 +155,60 @@ def setup_logging(
|
|
142
155
|
logger.setLevel(numeric_level)
|
143
156
|
logger.handlers = [] # Clear handlers in case of repeated call
|
144
157
|
|
145
|
-
# Create
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
158
|
+
# Create formatter
|
159
|
+
formatter = logging.Formatter(log_format, date_format)
|
160
|
+
|
161
|
+
# Create console handler if enabled
|
162
|
+
if console_output:
|
163
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
164
|
+
console_handler.setLevel(numeric_level)
|
165
|
+
console_handler.setFormatter(CustomFormatter())
|
166
|
+
logger.addHandler(console_handler)
|
167
|
+
|
168
|
+
# Create file handlers if file output is enabled
|
169
|
+
if file_output and log_dir:
|
170
|
+
# Create directory for log files if it doesn't exist
|
171
|
+
if not os.path.exists(log_dir):
|
156
172
|
os.makedirs(log_dir, exist_ok=True)
|
157
173
|
|
158
|
-
#
|
159
|
-
if
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
174
|
+
# Main log file
|
175
|
+
if log_file_name:
|
176
|
+
main_log_path = os.path.join(log_dir, log_file_name)
|
177
|
+
main_handler = RotatingFileHandler(
|
178
|
+
main_log_path,
|
179
|
+
maxBytes=max_bytes,
|
180
|
+
backupCount=backup_count,
|
181
|
+
encoding="utf-8"
|
182
|
+
)
|
183
|
+
main_handler.setLevel(numeric_level)
|
184
|
+
main_handler.setFormatter(formatter)
|
185
|
+
logger.addHandler(main_handler)
|
186
|
+
|
187
|
+
# Error log file
|
188
|
+
if error_log_file:
|
189
|
+
error_log_path = os.path.join(log_dir, error_log_file)
|
190
|
+
error_handler = RotatingFileHandler(
|
191
|
+
error_log_path,
|
192
|
+
maxBytes=max_bytes,
|
164
193
|
backupCount=backup_count,
|
165
194
|
encoding="utf-8"
|
166
195
|
)
|
167
|
-
|
168
|
-
|
169
|
-
|
196
|
+
error_handler.setLevel(logging.ERROR)
|
197
|
+
error_handler.setFormatter(formatter)
|
198
|
+
logger.addHandler(error_handler)
|
199
|
+
|
200
|
+
# Access log file (for HTTP requests)
|
201
|
+
if access_log_file:
|
202
|
+
access_log_path = os.path.join(log_dir, access_log_file)
|
203
|
+
access_handler = RotatingFileHandler(
|
204
|
+
access_log_path,
|
170
205
|
maxBytes=max_bytes,
|
171
206
|
backupCount=backup_count,
|
172
207
|
encoding="utf-8"
|
173
208
|
)
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
178
|
-
)
|
179
|
-
file_handler.setFormatter(file_formatter)
|
180
|
-
logger.addHandler(file_handler)
|
209
|
+
access_handler.setLevel(logging.INFO)
|
210
|
+
access_handler.setFormatter(formatter)
|
211
|
+
logger.addHandler(access_handler)
|
181
212
|
|
182
213
|
# Configure loggers for external libraries
|
183
214
|
log_levels = config.get("logging.levels", {})
|
@@ -188,6 +219,28 @@ def setup_logging(
|
|
188
219
|
return logger
|
189
220
|
|
190
221
|
|
222
|
+
def _parse_file_size(size_str: str) -> int:
|
223
|
+
"""
|
224
|
+
Parse file size string to bytes.
|
225
|
+
|
226
|
+
Args:
|
227
|
+
size_str: Size string (e.g., "10MB", "1GB", "100KB")
|
228
|
+
|
229
|
+
Returns:
|
230
|
+
Size in bytes
|
231
|
+
"""
|
232
|
+
size_str = size_str.upper()
|
233
|
+
if size_str.endswith("KB"):
|
234
|
+
return int(size_str[:-2]) * 1024
|
235
|
+
elif size_str.endswith("MB"):
|
236
|
+
return int(size_str[:-2]) * 1024 * 1024
|
237
|
+
elif size_str.endswith("GB"):
|
238
|
+
return int(size_str[:-2]) * 1024 * 1024 * 1024
|
239
|
+
else:
|
240
|
+
# Assume bytes if no unit specified
|
241
|
+
return int(size_str)
|
242
|
+
|
243
|
+
|
191
244
|
def get_logger(name: str) -> logging.Logger:
|
192
245
|
"""
|
193
246
|
Get a logger with the specified name.
|
@@ -0,0 +1,376 @@
|
|
1
|
+
"""
|
2
|
+
Settings management for the MCP Proxy Adapter framework.
|
3
|
+
Provides utilities for reading and managing framework settings from configuration.
|
4
|
+
"""
|
5
|
+
|
6
|
+
from typing import Any, Dict, Optional, Union
|
7
|
+
from mcp_proxy_adapter.config import config
|
8
|
+
|
9
|
+
|
10
|
+
class Settings:
|
11
|
+
"""
|
12
|
+
Settings management class for the framework.
|
13
|
+
Provides easy access to configuration values with type conversion and validation.
|
14
|
+
"""
|
15
|
+
|
16
|
+
# Store custom settings as a class variable
|
17
|
+
_custom_settings: Dict[str, Any] = {}
|
18
|
+
|
19
|
+
@classmethod
|
20
|
+
def add_custom_settings(cls, settings: Dict[str, Any]) -> None:
|
21
|
+
"""
|
22
|
+
Add custom settings to the settings manager.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
settings: Dictionary with custom settings
|
26
|
+
"""
|
27
|
+
cls._custom_settings.update(settings)
|
28
|
+
|
29
|
+
@classmethod
|
30
|
+
def get_custom_settings(cls) -> Dict[str, Any]:
|
31
|
+
"""
|
32
|
+
Get all custom settings.
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
Dictionary with all custom settings
|
36
|
+
"""
|
37
|
+
return cls._custom_settings.copy()
|
38
|
+
|
39
|
+
@classmethod
|
40
|
+
def get_custom_setting_value(cls, key: str, default: Any = None) -> Any:
|
41
|
+
"""
|
42
|
+
Get custom setting value.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
key: Setting key
|
46
|
+
default: Default value if key not found
|
47
|
+
|
48
|
+
Returns:
|
49
|
+
Setting value
|
50
|
+
"""
|
51
|
+
return cls._custom_settings.get(key, default)
|
52
|
+
|
53
|
+
@classmethod
|
54
|
+
def set_custom_setting_value(cls, key: str, value: Any) -> None:
|
55
|
+
"""
|
56
|
+
Set custom setting value.
|
57
|
+
|
58
|
+
Args:
|
59
|
+
key: Setting key
|
60
|
+
value: Value to set
|
61
|
+
"""
|
62
|
+
cls._custom_settings[key] = value
|
63
|
+
|
64
|
+
@classmethod
|
65
|
+
def clear_custom_settings(cls) -> None:
|
66
|
+
"""
|
67
|
+
Clear all custom settings.
|
68
|
+
"""
|
69
|
+
cls._custom_settings.clear()
|
70
|
+
|
71
|
+
@staticmethod
|
72
|
+
def get_server_settings() -> Dict[str, Any]:
|
73
|
+
"""
|
74
|
+
Get server configuration settings.
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
Dictionary with server settings
|
78
|
+
"""
|
79
|
+
return {
|
80
|
+
"host": config.get("server.host", "0.0.0.0"),
|
81
|
+
"port": config.get("server.port", 8000),
|
82
|
+
"debug": config.get("server.debug", False),
|
83
|
+
"log_level": config.get("server.log_level", "INFO")
|
84
|
+
}
|
85
|
+
|
86
|
+
@staticmethod
|
87
|
+
def get_logging_settings() -> Dict[str, Any]:
|
88
|
+
"""
|
89
|
+
Get logging configuration settings.
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
Dictionary with logging settings
|
93
|
+
"""
|
94
|
+
return {
|
95
|
+
"level": config.get("logging.level", "INFO"),
|
96
|
+
"file": config.get("logging.file"),
|
97
|
+
"log_dir": config.get("logging.log_dir", "./logs"),
|
98
|
+
"log_file": config.get("logging.log_file", "mcp_proxy_adapter.log"),
|
99
|
+
"error_log_file": config.get("logging.error_log_file", "mcp_proxy_adapter_error.log"),
|
100
|
+
"access_log_file": config.get("logging.access_log_file", "mcp_proxy_adapter_access.log"),
|
101
|
+
"max_file_size": config.get("logging.max_file_size", "10MB"),
|
102
|
+
"backup_count": config.get("logging.backup_count", 5),
|
103
|
+
"format": config.get("logging.format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s"),
|
104
|
+
"date_format": config.get("logging.date_format", "%Y-%m-%d %H:%M:%S"),
|
105
|
+
"console_output": config.get("logging.console_output", True),
|
106
|
+
"file_output": config.get("logging.file_output", True)
|
107
|
+
}
|
108
|
+
|
109
|
+
@staticmethod
|
110
|
+
def get_commands_settings() -> Dict[str, Any]:
|
111
|
+
"""
|
112
|
+
Get commands configuration settings.
|
113
|
+
|
114
|
+
Returns:
|
115
|
+
Dictionary with commands settings
|
116
|
+
"""
|
117
|
+
return {
|
118
|
+
"auto_discovery": config.get("commands.auto_discovery", True),
|
119
|
+
"discovery_path": config.get("commands.discovery_path", "mcp_proxy_adapter.commands"),
|
120
|
+
"custom_commands_path": config.get("commands.custom_commands_path")
|
121
|
+
}
|
122
|
+
|
123
|
+
@staticmethod
|
124
|
+
def get_custom_setting(key: str, default: Any = None) -> Any:
|
125
|
+
"""
|
126
|
+
Get custom setting from configuration.
|
127
|
+
|
128
|
+
Args:
|
129
|
+
key: Configuration key in dot notation (e.g., "custom.feature_enabled")
|
130
|
+
default: Default value if key not found
|
131
|
+
|
132
|
+
Returns:
|
133
|
+
Configuration value
|
134
|
+
"""
|
135
|
+
return config.get(key, default)
|
136
|
+
|
137
|
+
@staticmethod
|
138
|
+
def get_all_settings() -> Dict[str, Any]:
|
139
|
+
"""
|
140
|
+
Get all configuration settings including custom settings.
|
141
|
+
|
142
|
+
Returns:
|
143
|
+
Dictionary with all configuration settings
|
144
|
+
"""
|
145
|
+
all_settings = config.get_all()
|
146
|
+
all_settings['custom_settings'] = Settings._custom_settings
|
147
|
+
return all_settings
|
148
|
+
|
149
|
+
@staticmethod
|
150
|
+
def set_custom_setting(key: str, value: Any) -> None:
|
151
|
+
"""
|
152
|
+
Set custom setting in configuration.
|
153
|
+
|
154
|
+
Args:
|
155
|
+
key: Configuration key in dot notation
|
156
|
+
value: Value to set
|
157
|
+
"""
|
158
|
+
config.set(key, value)
|
159
|
+
|
160
|
+
@staticmethod
|
161
|
+
def reload_config() -> None:
|
162
|
+
"""
|
163
|
+
Reload configuration from file and environment variables.
|
164
|
+
"""
|
165
|
+
config.load_config()
|
166
|
+
|
167
|
+
|
168
|
+
class ServerSettings:
|
169
|
+
"""
|
170
|
+
Server-specific settings helper.
|
171
|
+
"""
|
172
|
+
|
173
|
+
@staticmethod
|
174
|
+
def get_host() -> str:
|
175
|
+
"""Get server host."""
|
176
|
+
return config.get("server.host", "0.0.0.0")
|
177
|
+
|
178
|
+
@staticmethod
|
179
|
+
def get_port() -> int:
|
180
|
+
"""Get server port."""
|
181
|
+
return config.get("server.port", 8000)
|
182
|
+
|
183
|
+
@staticmethod
|
184
|
+
def get_debug() -> bool:
|
185
|
+
"""Get debug mode."""
|
186
|
+
return config.get("server.debug", False)
|
187
|
+
|
188
|
+
@staticmethod
|
189
|
+
def get_log_level() -> str:
|
190
|
+
"""Get log level."""
|
191
|
+
return config.get("server.log_level", "INFO")
|
192
|
+
|
193
|
+
|
194
|
+
class LoggingSettings:
|
195
|
+
"""
|
196
|
+
Logging-specific settings helper.
|
197
|
+
"""
|
198
|
+
|
199
|
+
@staticmethod
|
200
|
+
def get_level() -> str:
|
201
|
+
"""Get logging level."""
|
202
|
+
return config.get("logging.level", "INFO")
|
203
|
+
|
204
|
+
@staticmethod
|
205
|
+
def get_log_dir() -> str:
|
206
|
+
"""Get log directory."""
|
207
|
+
return config.get("logging.log_dir", "./logs")
|
208
|
+
|
209
|
+
@staticmethod
|
210
|
+
def get_log_file() -> Optional[str]:
|
211
|
+
"""Get main log file name."""
|
212
|
+
return config.get("logging.log_file", "mcp_proxy_adapter.log")
|
213
|
+
|
214
|
+
@staticmethod
|
215
|
+
def get_error_log_file() -> Optional[str]:
|
216
|
+
"""Get error log file name."""
|
217
|
+
return config.get("logging.error_log_file", "mcp_proxy_adapter_error.log")
|
218
|
+
|
219
|
+
@staticmethod
|
220
|
+
def get_access_log_file() -> Optional[str]:
|
221
|
+
"""Get access log file name."""
|
222
|
+
return config.get("logging.access_log_file", "mcp_proxy_adapter_access.log")
|
223
|
+
|
224
|
+
@staticmethod
|
225
|
+
def get_max_file_size() -> str:
|
226
|
+
"""Get max file size."""
|
227
|
+
return config.get("logging.max_file_size", "10MB")
|
228
|
+
|
229
|
+
@staticmethod
|
230
|
+
def get_backup_count() -> int:
|
231
|
+
"""Get backup count."""
|
232
|
+
return config.get("logging.backup_count", 5)
|
233
|
+
|
234
|
+
@staticmethod
|
235
|
+
def get_format() -> str:
|
236
|
+
"""Get log format."""
|
237
|
+
return config.get("logging.format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
238
|
+
|
239
|
+
@staticmethod
|
240
|
+
def get_date_format() -> str:
|
241
|
+
"""Get date format."""
|
242
|
+
return config.get("logging.date_format", "%Y-%m-%d %H:%M:%S")
|
243
|
+
|
244
|
+
@staticmethod
|
245
|
+
def get_console_output() -> bool:
|
246
|
+
"""Get console output setting."""
|
247
|
+
return config.get("logging.console_output", True)
|
248
|
+
|
249
|
+
@staticmethod
|
250
|
+
def get_file_output() -> bool:
|
251
|
+
"""Get file output setting."""
|
252
|
+
return config.get("logging.file_output", True)
|
253
|
+
|
254
|
+
|
255
|
+
class CommandsSettings:
|
256
|
+
"""
|
257
|
+
Commands-specific settings helper.
|
258
|
+
"""
|
259
|
+
|
260
|
+
@staticmethod
|
261
|
+
def get_auto_discovery() -> bool:
|
262
|
+
"""Get auto discovery setting."""
|
263
|
+
return config.get("commands.auto_discovery", True)
|
264
|
+
|
265
|
+
@staticmethod
|
266
|
+
def get_discovery_path() -> str:
|
267
|
+
"""Get discovery path."""
|
268
|
+
return config.get("commands.discovery_path", "mcp_proxy_adapter.commands")
|
269
|
+
|
270
|
+
@staticmethod
|
271
|
+
def get_custom_commands_path() -> Optional[str]:
|
272
|
+
"""Get custom commands path."""
|
273
|
+
return config.get("commands.custom_commands_path")
|
274
|
+
|
275
|
+
|
276
|
+
# Convenience functions for easy access
|
277
|
+
def get_server_host() -> str:
|
278
|
+
"""Get server host."""
|
279
|
+
return ServerSettings.get_host()
|
280
|
+
|
281
|
+
|
282
|
+
def get_server_port() -> int:
|
283
|
+
"""Get server port."""
|
284
|
+
return ServerSettings.get_port()
|
285
|
+
|
286
|
+
|
287
|
+
def get_server_debug() -> bool:
|
288
|
+
"""Get server debug mode."""
|
289
|
+
return ServerSettings.get_debug()
|
290
|
+
|
291
|
+
|
292
|
+
def get_logging_level() -> str:
|
293
|
+
"""Get logging level."""
|
294
|
+
return LoggingSettings.get_level()
|
295
|
+
|
296
|
+
|
297
|
+
def get_logging_dir() -> str:
|
298
|
+
"""Get logging directory."""
|
299
|
+
return LoggingSettings.get_log_dir()
|
300
|
+
|
301
|
+
|
302
|
+
def get_auto_discovery() -> bool:
|
303
|
+
"""Get auto discovery setting."""
|
304
|
+
return CommandsSettings.get_auto_discovery()
|
305
|
+
|
306
|
+
|
307
|
+
def get_discovery_path() -> str:
|
308
|
+
"""Get discovery path."""
|
309
|
+
return CommandsSettings.get_discovery_path()
|
310
|
+
|
311
|
+
|
312
|
+
def get_setting(key: str, default: Any = None) -> Any:
|
313
|
+
"""Get any setting by key."""
|
314
|
+
return Settings.get_custom_setting(key, default)
|
315
|
+
|
316
|
+
|
317
|
+
def set_setting(key: str, value: Any) -> None:
|
318
|
+
"""Set any setting by key."""
|
319
|
+
Settings.set_custom_setting(key, value)
|
320
|
+
|
321
|
+
|
322
|
+
def reload_settings() -> None:
|
323
|
+
"""Reload all settings from configuration."""
|
324
|
+
Settings.reload_config()
|
325
|
+
|
326
|
+
|
327
|
+
def add_custom_settings(settings: Dict[str, Any]) -> None:
|
328
|
+
"""
|
329
|
+
Add custom settings to the settings manager.
|
330
|
+
|
331
|
+
Args:
|
332
|
+
settings: Dictionary with custom settings
|
333
|
+
"""
|
334
|
+
Settings.add_custom_settings(settings)
|
335
|
+
|
336
|
+
|
337
|
+
def get_custom_settings() -> Dict[str, Any]:
|
338
|
+
"""
|
339
|
+
Get all custom settings.
|
340
|
+
|
341
|
+
Returns:
|
342
|
+
Dictionary with all custom settings
|
343
|
+
"""
|
344
|
+
return Settings.get_custom_settings()
|
345
|
+
|
346
|
+
|
347
|
+
def get_custom_setting_value(key: str, default: Any = None) -> Any:
|
348
|
+
"""
|
349
|
+
Get custom setting value.
|
350
|
+
|
351
|
+
Args:
|
352
|
+
key: Setting key
|
353
|
+
default: Default value if key not found
|
354
|
+
|
355
|
+
Returns:
|
356
|
+
Setting value
|
357
|
+
"""
|
358
|
+
return Settings.get_custom_setting_value(key, default)
|
359
|
+
|
360
|
+
|
361
|
+
def set_custom_setting_value(key: str, value: Any) -> None:
|
362
|
+
"""
|
363
|
+
Set custom setting value.
|
364
|
+
|
365
|
+
Args:
|
366
|
+
key: Setting key
|
367
|
+
value: Value to set
|
368
|
+
"""
|
369
|
+
Settings.set_custom_setting_value(key, value)
|
370
|
+
|
371
|
+
|
372
|
+
def clear_custom_settings() -> None:
|
373
|
+
"""
|
374
|
+
Clear all custom settings.
|
375
|
+
"""
|
376
|
+
Settings.clear_custom_settings()
|
mcp_proxy_adapter/core/utils.py
CHANGED
@@ -8,7 +8,7 @@ import os
|
|
8
8
|
import sys
|
9
9
|
import time
|
10
10
|
import uuid
|
11
|
-
from datetime import datetime
|
11
|
+
from datetime import datetime, timezone
|
12
12
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
13
13
|
|
14
14
|
from mcp_proxy_adapter.core.logging import logger
|
@@ -45,7 +45,7 @@ def format_datetime(dt: Optional[datetime] = None, format_str: str = "%Y-%m-%dT%
|
|
45
45
|
Returns:
|
46
46
|
Formatted date/time string.
|
47
47
|
"""
|
48
|
-
dt = dt or datetime.
|
48
|
+
dt = dt or datetime.now(timezone.utc)
|
49
49
|
return dt.strftime(format_str)
|
50
50
|
|
51
51
|
|
@@ -4,7 +4,7 @@ Custom OpenAPI schema generator for MCP Microservice compatible with MCP-Proxy.
|
|
4
4
|
import json
|
5
5
|
from copy import deepcopy
|
6
6
|
from pathlib import Path
|
7
|
-
from typing import Any, Dict, List, Optional, Set, Type
|
7
|
+
from typing import Any, Dict, List, Optional, Set, Type, Callable
|
8
8
|
|
9
9
|
from fastapi import FastAPI
|
10
10
|
from fastapi.openapi.utils import get_openapi
|
@@ -55,6 +55,15 @@ class CustomOpenAPIGenerator:
|
|
55
55
|
# Get all commands from the registry
|
56
56
|
commands = registry.get_all_commands()
|
57
57
|
|
58
|
+
# Ensure CommandRequest exists in schemas
|
59
|
+
if "CommandRequest" not in schema["components"]["schemas"]:
|
60
|
+
schema["components"]["schemas"]["CommandRequest"] = {
|
61
|
+
"properties": {
|
62
|
+
"command": {"type": "string", "enum": []},
|
63
|
+
"params": {"type": "object", "oneOf": []}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
58
67
|
# Add command names to the CommandRequest enum
|
59
68
|
schema["components"]["schemas"]["CommandRequest"]["properties"]["command"]["enum"] = [
|
60
69
|
cmd for cmd in commands.keys()
|
@@ -284,4 +293,74 @@ def custom_openapi(app: FastAPI) -> Dict[str, Any]:
|
|
284
293
|
# Cache the schema
|
285
294
|
app.openapi_schema = openapi_schema
|
286
295
|
|
287
|
-
return openapi_schema
|
296
|
+
return openapi_schema
|
297
|
+
|
298
|
+
|
299
|
+
# Registry for custom OpenAPI generators
|
300
|
+
_openapi_generators: Dict[str, Callable] = {}
|
301
|
+
|
302
|
+
|
303
|
+
def register_openapi_generator(name: str, generator_func: Callable[[FastAPI], Dict[str, Any]]) -> None:
|
304
|
+
"""
|
305
|
+
Register a custom OpenAPI generator.
|
306
|
+
|
307
|
+
Args:
|
308
|
+
name: Generator name.
|
309
|
+
generator_func: Function that generates OpenAPI schema.
|
310
|
+
"""
|
311
|
+
_openapi_generators[name] = generator_func
|
312
|
+
logger.info(f"Registered custom OpenAPI generator: {name}")
|
313
|
+
|
314
|
+
|
315
|
+
def get_openapi_generator(name: str) -> Optional[Callable[[FastAPI], Dict[str, Any]]]:
|
316
|
+
"""
|
317
|
+
Get a custom OpenAPI generator by name.
|
318
|
+
|
319
|
+
Args:
|
320
|
+
name: Generator name.
|
321
|
+
|
322
|
+
Returns:
|
323
|
+
Generator function or None if not found.
|
324
|
+
"""
|
325
|
+
return _openapi_generators.get(name)
|
326
|
+
|
327
|
+
|
328
|
+
def list_openapi_generators() -> List[str]:
|
329
|
+
"""
|
330
|
+
Get list of registered OpenAPI generators.
|
331
|
+
|
332
|
+
Returns:
|
333
|
+
List of generator names.
|
334
|
+
"""
|
335
|
+
return list(_openapi_generators.keys())
|
336
|
+
|
337
|
+
|
338
|
+
def custom_openapi_with_fallback(app: FastAPI) -> Dict[str, Any]:
|
339
|
+
"""
|
340
|
+
EN:
|
341
|
+
Create a custom OpenAPI schema for the FastAPI application.
|
342
|
+
Checks for custom generators first, then falls back to default generator.
|
343
|
+
Passes app's title, description, and version to the generator.
|
344
|
+
|
345
|
+
RU:
|
346
|
+
Создаёт кастомную OpenAPI-схему для FastAPI-приложения.
|
347
|
+
Сначала проверяет наличие кастомных генераторов, затем использует встроенный генератор.
|
348
|
+
Передаёт параметры title, description и version из приложения в генератор схемы.
|
349
|
+
|
350
|
+
Args:
|
351
|
+
app: The FastAPI application / FastAPI-приложение
|
352
|
+
|
353
|
+
Returns:
|
354
|
+
Dict containing the custom OpenAPI schema / Словарь с кастомной OpenAPI-схемой
|
355
|
+
"""
|
356
|
+
# Check if there are any custom generators
|
357
|
+
if _openapi_generators:
|
358
|
+
# Use the first registered generator
|
359
|
+
generator_name = list(_openapi_generators.keys())[0]
|
360
|
+
generator_func = _openapi_generators[generator_name]
|
361
|
+
logger.info(f"Using custom OpenAPI generator: {generator_name}")
|
362
|
+
return generator_func(app)
|
363
|
+
|
364
|
+
# Fall back to default generator
|
365
|
+
logger.info("Using default OpenAPI generator")
|
366
|
+
return custom_openapi(app)
|