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
mcp_proxy_adapter/api/app.py
CHANGED
@@ -18,7 +18,7 @@ from mcp_proxy_adapter.config import config
|
|
18
18
|
from mcp_proxy_adapter.core.errors import MicroserviceError, NotFoundError
|
19
19
|
from mcp_proxy_adapter.core.logging import logger, RequestLogger
|
20
20
|
from mcp_proxy_adapter.commands.command_registry import registry
|
21
|
-
from mcp_proxy_adapter.custom_openapi import
|
21
|
+
from mcp_proxy_adapter.custom_openapi import custom_openapi_with_fallback
|
22
22
|
|
23
23
|
|
24
24
|
@asynccontextmanager
|
@@ -29,12 +29,16 @@ async def lifespan(app: FastAPI):
|
|
29
29
|
# Startup events
|
30
30
|
from mcp_proxy_adapter.commands.command_registry import registry
|
31
31
|
from mcp_proxy_adapter.commands.help_command import HelpCommand
|
32
|
+
from mcp_proxy_adapter.commands.health_command import HealthCommand
|
32
33
|
|
33
|
-
# Register built-in commands (
|
34
|
+
# Register built-in commands if they don't exist (user can override them)
|
34
35
|
if not registry.command_exists("help"):
|
35
36
|
registry.register(HelpCommand)
|
36
37
|
|
37
|
-
|
38
|
+
if not registry.command_exists("health"):
|
39
|
+
registry.register(HealthCommand)
|
40
|
+
|
41
|
+
# Discover and register additional commands automatically
|
38
42
|
registry.discover_commands()
|
39
43
|
|
40
44
|
logger.info(f"Application started with {len(registry.get_all_commands())} commands registered")
|
@@ -45,18 +49,28 @@ async def lifespan(app: FastAPI):
|
|
45
49
|
logger.info("Application shutting down")
|
46
50
|
|
47
51
|
|
48
|
-
def create_app() -> FastAPI:
|
52
|
+
def create_app(title: Optional[str] = None, description: Optional[str] = None, version: Optional[str] = None) -> FastAPI:
|
49
53
|
"""
|
50
54
|
Creates and configures FastAPI application.
|
51
55
|
|
56
|
+
Args:
|
57
|
+
title: Application title (default: "MCP Proxy Adapter")
|
58
|
+
description: Application description (default: "JSON-RPC API for interacting with MCP Proxy")
|
59
|
+
version: Application version (default: "1.0.0")
|
60
|
+
|
52
61
|
Returns:
|
53
62
|
Configured FastAPI application.
|
54
63
|
"""
|
64
|
+
# Use provided parameters or defaults
|
65
|
+
app_title = title or "MCP Proxy Adapter"
|
66
|
+
app_description = description or "JSON-RPC API for interacting with MCP Proxy"
|
67
|
+
app_version = version or "1.0.0"
|
68
|
+
|
55
69
|
# Create application
|
56
70
|
app = FastAPI(
|
57
|
-
title=
|
58
|
-
description=
|
59
|
-
version=
|
71
|
+
title=app_title,
|
72
|
+
description=app_description,
|
73
|
+
version=app_version,
|
60
74
|
docs_url="/docs",
|
61
75
|
redoc_url="/redoc",
|
62
76
|
lifespan=lifespan,
|
@@ -75,7 +89,7 @@ def create_app() -> FastAPI:
|
|
75
89
|
setup_middleware(app)
|
76
90
|
|
77
91
|
# Use custom OpenAPI schema
|
78
|
-
app.openapi = lambda:
|
92
|
+
app.openapi = lambda: custom_openapi_with_fallback(app)
|
79
93
|
|
80
94
|
# Explicit endpoint for OpenAPI schema
|
81
95
|
@app.get("/openapi.json")
|
@@ -83,8 +97,8 @@ def create_app() -> FastAPI:
|
|
83
97
|
"""
|
84
98
|
Returns optimized OpenAPI schema compatible with MCP-Proxy.
|
85
99
|
"""
|
86
|
-
return
|
87
|
-
|
100
|
+
return custom_openapi_with_fallback(app)
|
101
|
+
|
88
102
|
# JSON-RPC handler
|
89
103
|
@app.post("/api/jsonrpc", response_model=Union[JsonRpcSuccessResponse, JsonRpcErrorResponse, List[Union[JsonRpcSuccessResponse, JsonRpcErrorResponse]]])
|
90
104
|
async def jsonrpc_endpoint(request: Request, request_data: Union[Dict[str, Any], List[Dict[str, Any]]] = Body(...)):
|
@@ -119,7 +133,7 @@ def create_app() -> FastAPI:
|
|
119
133
|
else:
|
120
134
|
# Process single request
|
121
135
|
return await handle_json_rpc(request_data, request_id)
|
122
|
-
|
136
|
+
|
123
137
|
# Command execution endpoint (/cmd)
|
124
138
|
@app.post("/cmd")
|
125
139
|
async def cmd_endpoint(request: Request, command_data: Dict[str, Any] = Body(...)):
|
@@ -245,7 +259,7 @@ def create_app() -> FastAPI:
|
|
245
259
|
}
|
246
260
|
}
|
247
261
|
)
|
248
|
-
|
262
|
+
|
249
263
|
# Direct command call
|
250
264
|
@app.post("/api/command/{command_name}")
|
251
265
|
async def command_endpoint(request: Request, command_name: str, params: Dict[str, Any] = Body(default={})):
|
@@ -259,29 +273,50 @@ def create_app() -> FastAPI:
|
|
259
273
|
result = await execute_command(command_name, params, request_id)
|
260
274
|
return result
|
261
275
|
except MicroserviceError as e:
|
276
|
+
# Convert to proper HTTP status code
|
277
|
+
status_code = 400 if e.code < 0 else e.code
|
262
278
|
return JSONResponse(
|
263
|
-
status_code=
|
279
|
+
status_code=status_code,
|
264
280
|
content=e.to_dict()
|
265
281
|
)
|
266
|
-
|
282
|
+
|
267
283
|
# Server health check
|
268
284
|
@app.get("/health", operation_id="health_check")
|
269
285
|
async def health_endpoint():
|
270
286
|
"""
|
271
|
-
|
272
|
-
|
273
|
-
Возвращает информацию о состоянии сервиса.
|
287
|
+
Health check endpoint.
|
288
|
+
Returns server status and basic information.
|
274
289
|
"""
|
275
|
-
|
276
|
-
health_data = await get_server_health()
|
277
|
-
|
278
|
-
# Возвращаем информацию в формате, соответствующем схеме
|
279
|
-
return JSONResponse(content={
|
290
|
+
return {
|
280
291
|
"status": "ok",
|
281
292
|
"model": "mcp-proxy-adapter",
|
282
|
-
"version":
|
283
|
-
}
|
293
|
+
"version": "1.0.0"
|
294
|
+
}
|
284
295
|
|
296
|
+
# Graceful shutdown endpoint
|
297
|
+
@app.post("/shutdown")
|
298
|
+
async def shutdown_endpoint():
|
299
|
+
"""
|
300
|
+
Graceful shutdown endpoint.
|
301
|
+
Triggers server shutdown after completing current requests.
|
302
|
+
"""
|
303
|
+
import asyncio
|
304
|
+
|
305
|
+
# Schedule shutdown after a short delay to allow response
|
306
|
+
async def delayed_shutdown():
|
307
|
+
await asyncio.sleep(1)
|
308
|
+
# This will trigger the lifespan shutdown event
|
309
|
+
import os
|
310
|
+
os._exit(0)
|
311
|
+
|
312
|
+
# Start shutdown task
|
313
|
+
asyncio.create_task(delayed_shutdown())
|
314
|
+
|
315
|
+
return {
|
316
|
+
"status": "shutting_down",
|
317
|
+
"message": "Server shutdown initiated. New requests will be rejected."
|
318
|
+
}
|
319
|
+
|
285
320
|
# List of available commands
|
286
321
|
@app.get("/api/commands", response_model=CommandListResponse)
|
287
322
|
async def commands_list_endpoint():
|
@@ -290,7 +325,7 @@ def create_app() -> FastAPI:
|
|
290
325
|
"""
|
291
326
|
commands = await get_commands_list()
|
292
327
|
return {"commands": commands}
|
293
|
-
|
328
|
+
|
294
329
|
# Get command information by name
|
295
330
|
@app.get("/api/commands/{command_name}")
|
296
331
|
async def command_info_endpoint(request: Request, command_name: str):
|
@@ -317,7 +352,7 @@ def create_app() -> FastAPI:
|
|
317
352
|
}
|
318
353
|
}
|
319
354
|
)
|
320
|
-
|
355
|
+
|
321
356
|
# Get API tool description
|
322
357
|
@app.get("/api/tools/{tool_name}")
|
323
358
|
async def tool_description_endpoint(tool_name: str, format: Optional[str] = "json"):
|
@@ -368,7 +403,7 @@ def create_app() -> FastAPI:
|
|
368
403
|
}
|
369
404
|
}
|
370
405
|
)
|
371
|
-
|
406
|
+
|
372
407
|
# Execute API tool
|
373
408
|
@app.post("/api/tools/{tool_name}")
|
374
409
|
async def execute_tool_endpoint(tool_name: str, params: Dict[str, Any] = Body(...)):
|
@@ -408,5 +443,8 @@ def create_app() -> FastAPI:
|
|
408
443
|
return app
|
409
444
|
|
410
445
|
|
446
|
+
|
447
|
+
|
448
|
+
|
411
449
|
# Create global application instance
|
412
450
|
app = create_app()
|
@@ -43,7 +43,7 @@ async def execute_command(command_name: str, params: Dict[str, Any], request_id:
|
|
43
43
|
start_time = time.time()
|
44
44
|
|
45
45
|
# Use Command.run that handles instances with dependencies properly
|
46
|
-
command_class = registry.
|
46
|
+
command_class = registry.get_command_with_priority(command_name)
|
47
47
|
result = await command_class.run(**params)
|
48
48
|
|
49
49
|
execution_time = time.time() - start_time
|
@@ -35,7 +35,7 @@ class ErrorHandlingMiddleware(BaseMiddleware):
|
|
35
35
|
except CommandError as e:
|
36
36
|
# Command error
|
37
37
|
request_id = getattr(request.state, "request_id", "unknown")
|
38
|
-
logger.
|
38
|
+
logger.debug(f"[{request_id}] Command error: {str(e)}")
|
39
39
|
|
40
40
|
# Проверяем, является ли запрос JSON-RPC
|
41
41
|
is_jsonrpc = self._is_json_rpc_request(request)
|
@@ -52,7 +52,7 @@ class ErrorHandlingMiddleware(BaseMiddleware):
|
|
52
52
|
"error": {
|
53
53
|
"code": e.code,
|
54
54
|
"message": str(e),
|
55
|
-
"data": e.
|
55
|
+
"data": e.data if hasattr(e, "data") and e.data else None
|
56
56
|
},
|
57
57
|
"id": request_id_jsonrpc
|
58
58
|
}
|
@@ -67,7 +67,7 @@ class ErrorHandlingMiddleware(BaseMiddleware):
|
|
67
67
|
except ValidationError as e:
|
68
68
|
# Validation error
|
69
69
|
request_id = getattr(request.state, "request_id", "unknown")
|
70
|
-
logger.
|
70
|
+
logger.debug(f"[{request_id}] Validation error: {str(e)}")
|
71
71
|
|
72
72
|
# Get JSON-RPC request ID if available
|
73
73
|
request_id_jsonrpc = await self._get_json_rpc_id(request)
|
@@ -81,7 +81,7 @@ class ErrorHandlingMiddleware(BaseMiddleware):
|
|
81
81
|
"error": {
|
82
82
|
"code": -32602,
|
83
83
|
"message": "Invalid params",
|
84
|
-
"data": e.
|
84
|
+
"data": e.data if hasattr(e, "data") and e.data else None
|
85
85
|
},
|
86
86
|
"id": request_id_jsonrpc
|
87
87
|
}
|
@@ -96,7 +96,7 @@ class ErrorHandlingMiddleware(BaseMiddleware):
|
|
96
96
|
except MicroserviceError as e:
|
97
97
|
# Other microservice error
|
98
98
|
request_id = getattr(request.state, "request_id", "unknown")
|
99
|
-
logger.
|
99
|
+
logger.debug(f"[{request_id}] Microservice error: {str(e)}")
|
100
100
|
|
101
101
|
# Get JSON-RPC request ID if available
|
102
102
|
request_id_jsonrpc = await self._get_json_rpc_id(request)
|
@@ -104,13 +104,13 @@ class ErrorHandlingMiddleware(BaseMiddleware):
|
|
104
104
|
# If request was JSON-RPC
|
105
105
|
if self._is_json_rpc_request(request):
|
106
106
|
return JSONResponse(
|
107
|
-
status_code=
|
107
|
+
status_code=400,
|
108
108
|
content={
|
109
109
|
"jsonrpc": "2.0",
|
110
110
|
"error": {
|
111
111
|
"code": -32000,
|
112
112
|
"message": str(e),
|
113
|
-
"data": e.
|
113
|
+
"data": e.data if hasattr(e, "data") and e.data else None
|
114
114
|
},
|
115
115
|
"id": request_id_jsonrpc
|
116
116
|
}
|
@@ -118,14 +118,14 @@ class ErrorHandlingMiddleware(BaseMiddleware):
|
|
118
118
|
|
119
119
|
# Regular API error
|
120
120
|
return JSONResponse(
|
121
|
-
status_code=
|
121
|
+
status_code=400,
|
122
122
|
content=e.to_dict()
|
123
123
|
)
|
124
124
|
|
125
125
|
except Exception as e:
|
126
126
|
# Unexpected error
|
127
127
|
request_id = getattr(request.state, "request_id", "unknown")
|
128
|
-
logger.
|
128
|
+
logger.debug(f"[{request_id}] Unexpected error: {str(e)}")
|
129
129
|
|
130
130
|
# Get JSON-RPC request ID if available
|
131
131
|
request_id_jsonrpc = await self._get_json_rpc_id(request)
|
@@ -165,7 +165,8 @@ class ErrorHandlingMiddleware(BaseMiddleware):
|
|
165
165
|
Returns:
|
166
166
|
True if request is JSON-RPC, False otherwise.
|
167
167
|
"""
|
168
|
-
|
168
|
+
# Only requests to /api/jsonrpc are JSON-RPC requests
|
169
|
+
return request.url.path == "/api/jsonrpc"
|
169
170
|
|
170
171
|
async def _get_json_rpc_id(self, request: Request) -> Optional[Any]:
|
171
172
|
"""
|
@@ -126,7 +126,7 @@ class ToolIntegration:
|
|
126
126
|
|
127
127
|
logger.info(f"Successfully registered tool: {tool_name}")
|
128
128
|
except Exception as e:
|
129
|
-
logger.
|
129
|
+
logger.debug(f"Error registering tool {tool_name}: {e}")
|
130
130
|
results[tool_name] = {
|
131
131
|
"status": "error",
|
132
132
|
"error": str(e)
|
@@ -149,7 +149,10 @@ class ToolIntegration:
|
|
149
149
|
|
150
150
|
# Формируем словарь типов для всех параметров всех команд
|
151
151
|
for cmd_name, cmd_info in commands.items():
|
152
|
-
|
152
|
+
params = cmd_info.get("params", {})
|
153
|
+
if params is None:
|
154
|
+
continue
|
155
|
+
for param_name, param_info in params.items():
|
153
156
|
param_type = param_info.get("type", "значение")
|
154
157
|
|
155
158
|
# Преобразуем русские типы в типы JSON Schema
|
mcp_proxy_adapter/api/tools.py
CHANGED
@@ -50,14 +50,14 @@ class TSTCommandExecutor:
|
|
50
50
|
|
51
51
|
try:
|
52
52
|
# Проверяем существование команды
|
53
|
-
if not registry.
|
53
|
+
if not registry.command_exists_with_priority(command):
|
54
54
|
raise NotFoundError(f"Команда '{command}' не найдена")
|
55
55
|
|
56
56
|
# Получаем класс команды
|
57
|
-
command_class = registry.
|
57
|
+
command_class = registry.get_command_with_priority(command)
|
58
58
|
|
59
59
|
# Выполняем команду
|
60
|
-
result = await command_class.
|
60
|
+
result = await command_class.execute(**params)
|
61
61
|
|
62
62
|
# Возвращаем результат
|
63
63
|
return result.to_dict()
|
@@ -13,6 +13,7 @@ from mcp_proxy_adapter.core.errors import (
|
|
13
13
|
CommandError, InternalError, InvalidParamsError, NotFoundError, ValidationError
|
14
14
|
)
|
15
15
|
from mcp_proxy_adapter.core.logging import logger
|
16
|
+
from .hooks import hooks, HookContext
|
16
17
|
|
17
18
|
|
18
19
|
T = TypeVar("T", bound=CommandResult)
|
@@ -136,6 +137,20 @@ class Command(ABC):
|
|
136
137
|
# Parameters validation
|
137
138
|
validated_params = cls.validate_params(kwargs)
|
138
139
|
|
140
|
+
# Execute before hooks
|
141
|
+
hook_context = hooks.execute_before_hooks(command_name, validated_params)
|
142
|
+
|
143
|
+
# Check if standard processing should be skipped
|
144
|
+
if not hook_context.standard_processing:
|
145
|
+
logger.debug(f"Standard processing skipped for command {command_name} due to hook")
|
146
|
+
# Return the params as result if standard processing is disabled
|
147
|
+
return SuccessResult(data=validated_params)
|
148
|
+
|
149
|
+
# Get command with priority (custom commands first, then built-in)
|
150
|
+
priority_command_class = registry.get_priority_command(command_name)
|
151
|
+
if priority_command_class is None:
|
152
|
+
raise NotFoundError(f"Command '{command_name}' not found")
|
153
|
+
|
139
154
|
# Check if we have a registered instance for this command
|
140
155
|
if registry.has_instance(command_name):
|
141
156
|
# Use existing instance with dependencies
|
@@ -143,9 +158,12 @@ class Command(ABC):
|
|
143
158
|
result = await command.execute(**validated_params)
|
144
159
|
else:
|
145
160
|
# Create new instance for commands without dependencies
|
146
|
-
command =
|
161
|
+
command = priority_command_class()
|
147
162
|
result = await command.execute(**validated_params)
|
148
163
|
|
164
|
+
# Execute after hooks
|
165
|
+
hooks.execute_after_hooks(command_name, validated_params, result)
|
166
|
+
|
149
167
|
logger.debug(f"Command {cls.__name__} executed successfully")
|
150
168
|
return result
|
151
169
|
except ValidationError as e:
|