mcpower-proxy 0.0.61__tar.gz → 0.0.72__tar.gz

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 (58) hide show
  1. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/PKG-INFO +3 -3
  2. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/pyproject.toml +4 -4
  3. mcpower_proxy-0.0.72/src/ide_tools/__init__.py +12 -0
  4. mcpower_proxy-0.0.72/src/ide_tools/cursor/__init__.py +11 -0
  5. mcpower_proxy-0.0.72/src/ide_tools/cursor/constants.py +58 -0
  6. mcpower_proxy-0.0.72/src/ide_tools/cursor/format.py +35 -0
  7. mcpower_proxy-0.0.72/src/ide_tools/cursor/router.py +100 -0
  8. mcpower_proxy-0.0.72/src/ide_tools/router.py +45 -0
  9. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/main.py +11 -4
  10. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/mcpower_proxy.egg-info/PKG-INFO +3 -3
  11. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/mcpower_proxy.egg-info/SOURCES.txt +7 -0
  12. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/mcpower_proxy.egg-info/requires.txt +2 -2
  13. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/mcpower_proxy.egg-info/top_level.txt +1 -0
  14. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/apis/security_policy.py +11 -6
  15. mcpower_proxy-0.0.72/src/modules/decision_handler.py +219 -0
  16. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/logs/audit_trail.py +22 -17
  17. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/logs/logger.py +14 -18
  18. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/redaction/redactor.py +112 -107
  19. mcpower_proxy-0.0.72/src/modules/ui/__init__.py +1 -0
  20. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/ui/confirmation.py +0 -1
  21. mcpower_proxy-0.0.72/src/modules/utils/cli.py +76 -0
  22. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/utils/ids.py +55 -10
  23. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/utils/json.py +3 -3
  24. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/wrapper/__version__.py +1 -1
  25. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/wrapper/middleware.py +121 -210
  26. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/wrapper/server.py +19 -11
  27. mcpower_proxy-0.0.61/src/modules/ui/__init__.py +0 -1
  28. mcpower_proxy-0.0.61/src/modules/utils/cli.py +0 -46
  29. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/LICENSE +0 -0
  30. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/README.md +0 -0
  31. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/setup.cfg +0 -0
  32. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/LICENSE +0 -0
  33. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/mcpower_proxy.egg-info/dependency_links.txt +0 -0
  34. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/mcpower_proxy.egg-info/entry_points.txt +0 -0
  35. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/__init__.py +0 -0
  36. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/apis/__init__.py +0 -0
  37. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/logs/__init__.py +0 -0
  38. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/redaction/__init__.py +0 -0
  39. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/redaction/constants.py +0 -0
  40. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/redaction/gitleaks_rules.py +0 -0
  41. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/redaction/pii_rules.py +0 -0
  42. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/ui/classes.py +0 -0
  43. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/ui/simple_dialog.py +0 -0
  44. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/ui/xdialog/__init__.py +0 -0
  45. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/ui/xdialog/constants.py +0 -0
  46. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/ui/xdialog/mac_dialogs.py +0 -0
  47. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/ui/xdialog/tk_dialogs.py +0 -0
  48. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/ui/xdialog/windows_custom_dialog.py +0 -0
  49. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/ui/xdialog/windows_dialogs.py +0 -0
  50. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/ui/xdialog/windows_structs.py +0 -0
  51. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/ui/xdialog/yad_dialogs.py +0 -0
  52. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/ui/xdialog/zenity_dialogs.py +0 -0
  53. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/utils/__init__.py +0 -0
  54. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/utils/config.py +0 -0
  55. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/utils/copy.py +0 -0
  56. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/modules/utils/mcp_configs.py +0 -0
  57. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/wrapper/__init__.py +0 -0
  58. {mcpower_proxy-0.0.61 → mcpower_proxy-0.0.72}/src/wrapper/schema.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcpower-proxy
3
- Version: 0.0.61
3
+ Version: 0.0.72
4
4
  Summary: MCPower Security proxy
5
5
  Author-email: MCPower Security <support@mcpower.tech>
6
6
  License: Apache License
@@ -209,14 +209,14 @@ Keywords: mcp,security,proxy,monitoring,audit,redaction,policy-enforcement
209
209
  Requires-Python: ~=3.11.0
210
210
  Description-Content-Type: text/markdown
211
211
  License-File: LICENSE
212
- Requires-Dist: fastmcp>=2.10.5
212
+ Requires-Dist: fastmcp==2.13.0.2
213
213
  Requires-Dist: httpx>=0.25.0
214
214
  Requires-Dist: mcp>=1.0.0
215
215
  Requires-Dist: watchdog>=3.0.0
216
216
  Requires-Dist: jsonc-parser>=1.1.5
217
217
  Requires-Dist: jsonpath-ng>=1.7.0
218
218
  Requires-Dist: pydantic>=2.8.0
219
- Requires-Dist: mcpower-shared==0.1.1
219
+ Requires-Dist: mcpower-shared==0.1.3
220
220
  Dynamic: license-file
221
221
 
222
222
  # MCPower Proxy
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mcpower-proxy"
3
- version = "0.0.61"
3
+ version = "0.0.72"
4
4
  description = "MCPower Security proxy"
5
5
  readme = "README.md"
6
6
  requires-python = "~=3.11.0"
@@ -9,14 +9,14 @@ authors = [
9
9
  { name = "MCPower Security", email = "support@mcpower.tech" }
10
10
  ]
11
11
  dependencies = [
12
- "fastmcp>=2.10.5",
12
+ "fastmcp==2.13.0.2",
13
13
  "httpx>=0.25.0",
14
14
  "mcp>=1.0.0",
15
15
  "watchdog>=3.0.0",
16
16
  "jsonc-parser>=1.1.5",
17
17
  "jsonpath-ng>=1.7.0",
18
18
  "pydantic>=2.8.0",
19
- "mcpower-shared==0.1.1",
19
+ "mcpower-shared==0.1.3",
20
20
  ]
21
21
 
22
22
  [project.license]
@@ -31,7 +31,7 @@ build-backend = "setuptools.build_meta"
31
31
 
32
32
  [tool.setuptools]
33
33
  package-dir = {"" = "src"}
34
- packages = ["modules", "modules.apis", "modules.logs", "modules.redaction", "modules.ui", "modules.ui.xdialog", "modules.utils", "wrapper"]
34
+ packages = ["modules", "modules.apis", "modules.logs", "modules.redaction", "modules.ui", "modules.ui.xdialog", "modules.utils", "wrapper", "ide_tools", "ide_tools.cursor"]
35
35
  py-modules = ["main"]
36
36
 
37
37
  [tool.uv]
@@ -0,0 +1,12 @@
1
+ """
2
+ IDE Tools - IDE Safety Validation Module
3
+
4
+ Provides validation and user confirmation for AI-generated operations in IDEs.
5
+ Supports multiple IDEs with per-IDE handlers.
6
+ """
7
+
8
+ from .router import main as router_main
9
+
10
+ __all__ = [
11
+ "router_main",
12
+ ]
@@ -0,0 +1,11 @@
1
+ """
2
+ Cursor IDE Handler
3
+
4
+ Handles Cursor-specific hooks and operations.
5
+ """
6
+
7
+ from .router import route_cursor_hook
8
+
9
+ __all__ = [
10
+ "route_cursor_hook",
11
+ ]
@@ -0,0 +1,58 @@
1
+ """
2
+ Cursor Hook Constants
3
+
4
+ Configuration values specific to Cursor hook handlers.
5
+ """
6
+
7
+ from enum import Enum
8
+
9
+ from ide_tools.common.hooks.types import HookConfig, OutputFormat
10
+ from ide_tools.cursor.format import cursor_output_formatter
11
+
12
+
13
+ class HookPermission(str, Enum):
14
+ """Cursor hook response permission values"""
15
+ ALLOW = "allow"
16
+ DENY = "deny"
17
+
18
+
19
+ # Cursor-specific configuration
20
+ CURSOR_CONFIG = HookConfig(
21
+ output_format=OutputFormat(
22
+ allow_exit_code=0,
23
+ deny_exit_code=1,
24
+ error_exit_code=1,
25
+ formatter=cursor_output_formatter
26
+ ),
27
+ server_name="mcpower_cursor",
28
+ client_name="cursor",
29
+ max_content_length=100000
30
+ )
31
+
32
+ # Hook descriptions from https://cursor.com/docs/agent/hooks#hook-events
33
+ CURSOR_HOOKS = {
34
+ "beforeShellExecution": {
35
+ "name": "beforeShellExecution",
36
+ "description": "Triggered before a shell command is executed by the agent. "
37
+ "Allows inspection and potential blocking of shell commands.",
38
+ "version": "1.0.0"
39
+ },
40
+ "afterShellExecution": {
41
+ "name": "afterShellExecution",
42
+ "description": "Triggered after a shell command completes execution. "
43
+ "Provides access to command output and exit status.",
44
+ "version": "1.0.0"
45
+ },
46
+ "beforeReadFile": {
47
+ "name": "beforeReadFile",
48
+ "description": "Triggered before the agent reads a file. "
49
+ "Allows inspection and potential blocking of file read operations.",
50
+ "version": "1.0.0"
51
+ },
52
+ "beforeSubmitPrompt": {
53
+ "name": "beforeSubmitPrompt",
54
+ "description": "Triggered before a prompt is submitted to the AI model. "
55
+ "Allows inspection and modification of prompts.",
56
+ "version": "1.0.0"
57
+ }
58
+ }
@@ -0,0 +1,35 @@
1
+ """
2
+ Cursor-specific output formatting
3
+ """
4
+
5
+ import json
6
+ from typing import Optional
7
+
8
+
9
+ def cursor_output_formatter(hook_type: str, allowed: bool, user_msg: Optional[str], agent_msg: Optional[str]) -> str:
10
+ """
11
+ Format output for Cursor IDE
12
+
13
+ Args:
14
+ hook_type: "permission" or "continue"
15
+ allowed: True for allow/continue, False for deny/block
16
+ user_msg: Message for user
17
+ agent_msg: Message for agent/logs
18
+
19
+ Returns:
20
+ JSON string in Cursor format
21
+ """
22
+ if hook_type == "permission":
23
+ result = {"permission": "allow" if allowed else "deny"}
24
+ if user_msg:
25
+ result["user_message"] = user_msg
26
+ if agent_msg:
27
+ result["agent_message"] = agent_msg
28
+ else: # continue
29
+ result = {"continue": allowed}
30
+ if user_msg:
31
+ result["user_message"] = user_msg
32
+ if agent_msg:
33
+ result["agent_message"] = agent_msg
34
+
35
+ return json.dumps(result)
@@ -0,0 +1,100 @@
1
+ """
2
+ Cursor Router
3
+
4
+ Routes Cursor hook calls to appropriate handlers.
5
+ """
6
+
7
+ import asyncio
8
+ import json
9
+ import sys
10
+ import uuid
11
+
12
+ from ide_tools.common.hooks.init import handle_init
13
+ from ide_tools.common.hooks.prompt_submit import handle_prompt_submit
14
+ from ide_tools.common.hooks.read_file import handle_read_file
15
+ from ide_tools.common.hooks.shell_execution import handle_shell_execution
16
+ from modules.logs.audit_trail import AuditTrailLogger
17
+ from modules.logs.logger import MCPLogger
18
+ from .constants import CURSOR_HOOKS, CURSOR_CONFIG
19
+
20
+
21
+ def route_cursor_hook(logger: MCPLogger, audit_logger: AuditTrailLogger, stdin_input: str):
22
+ """
23
+ Route Cursor hook to appropriate shared handler
24
+
25
+ Args:
26
+ logger: MCPLogger instance
27
+ audit_logger: AuditTrailLogger instance
28
+ stdin_input: Raw input string from stdin
29
+ """
30
+ try:
31
+ input_data = json.loads(stdin_input)
32
+
33
+ hook_event_name = input_data.get("hook_event_name")
34
+ if not hook_event_name:
35
+ logger.error("Missing required field 'hook_event_name' in input")
36
+ sys.exit(1)
37
+
38
+ conversation_id = input_data.get("conversation_id")
39
+ if not conversation_id:
40
+ logger.error("Missing required field 'conversation_id' in input")
41
+ sys.exit(1)
42
+
43
+ generation_id = input_data.get("generation_id")
44
+ if not generation_id:
45
+ logger.error("Missing required field 'generation_id' in input")
46
+ sys.exit(1)
47
+
48
+ workspace_roots = input_data.get("workspace_roots")
49
+ if workspace_roots is None:
50
+ logger.error("Missing required field 'workspace_roots' in input")
51
+ sys.exit(1)
52
+
53
+ if not isinstance(workspace_roots, list):
54
+ logger.error("Invalid 'workspace_roots': must be a list")
55
+ sys.exit(1)
56
+
57
+ prompt_id = conversation_id[:8]
58
+ event_id = uuid.uuid4().hex[:8]
59
+ cwd = workspace_roots[0] if workspace_roots else None
60
+
61
+ logger.info(
62
+ f"Cursor router: routing to {hook_event_name} handler "
63
+ f"(prompt_id={prompt_id}, event_id={event_id}, cwd={cwd})")
64
+
65
+ # Route to appropriate handler
66
+ if hook_event_name == "init":
67
+ asyncio.run(handle_init(
68
+ logger=logger,
69
+ audit_logger=audit_logger,
70
+ event_id=event_id,
71
+ cwd=cwd,
72
+ server_name=CURSOR_CONFIG.server_name,
73
+ client_name="cursor",
74
+ hooks=CURSOR_HOOKS
75
+ ))
76
+ elif hook_event_name == "beforeShellExecution":
77
+ asyncio.run(
78
+ handle_shell_execution(logger, audit_logger, stdin_input, prompt_id, event_id, cwd, CURSOR_CONFIG,
79
+ hook_event_name, is_request=True))
80
+ elif hook_event_name == "afterShellExecution":
81
+ asyncio.run(
82
+ handle_shell_execution(logger, audit_logger, stdin_input, prompt_id, event_id, cwd, CURSOR_CONFIG,
83
+ hook_event_name, is_request=False))
84
+ elif hook_event_name == "beforeReadFile":
85
+ asyncio.run(handle_read_file(logger, audit_logger, stdin_input, prompt_id, event_id, cwd, CURSOR_CONFIG,
86
+ hook_event_name))
87
+ elif hook_event_name == "beforeSubmitPrompt":
88
+ asyncio.run(
89
+ handle_prompt_submit(logger, audit_logger, stdin_input, prompt_id, event_id, cwd, CURSOR_CONFIG,
90
+ hook_event_name))
91
+ else:
92
+ logger.error(f"Unknown hook_event_name: {hook_event_name}")
93
+ sys.exit(1)
94
+
95
+ except json.JSONDecodeError as e:
96
+ logger.error(f"Failed to parse input JSON: {e}")
97
+ sys.exit(1)
98
+ except Exception as e:
99
+ logger.error(f"Routing error: {e}", exc_info=True)
100
+ sys.exit(1)
@@ -0,0 +1,45 @@
1
+ """
2
+ IDE Tools Router
3
+
4
+ Routes hook calls to appropriate IDE-specific routers based on --ide flag.
5
+ """
6
+
7
+ import sys
8
+ from typing import Optional
9
+
10
+ from modules.logs.audit_trail import AuditTrailLogger
11
+ from modules.logs.logger import MCPLogger
12
+
13
+
14
+ def main(logger: MCPLogger, audit_logger: AuditTrailLogger, ide: str, context: Optional[str]):
15
+ """
16
+ Main entry point for IDE tools - routes to appropriate IDE router
17
+
18
+ Args:
19
+ logger: MCPLogger instance
20
+ audit_logger: AuditTrailLogger instance
21
+ ide: IDE name (e.g., "cursor")
22
+ context: Additional context (to be verified as optional by the associated --ide handler)
23
+ """
24
+ # Monkey patch sys.exit to log exit code
25
+ original_exit = sys.exit
26
+
27
+ def logged_exit(code=0):
28
+ logger.info(f"sys.exit called with code: {code}")
29
+ original_exit(code)
30
+
31
+ sys.exit = logged_exit
32
+
33
+ logger.info(f"IDE Tools router: ide={ide}, context=${context}")
34
+
35
+ # Read stdin input once at the top level (raw string)
36
+ # Each handler will parse it according to its own schema
37
+ stdin_input = sys.stdin.read()
38
+
39
+ # Route to appropriate IDE handler with the raw input string
40
+ if ide == "cursor":
41
+ from ide_tools.cursor import route_cursor_hook
42
+ route_cursor_hook(logger, audit_logger, stdin_input)
43
+ else:
44
+ logger.error(f"Unknown IDE: {ide}")
45
+ sys.exit(1)
@@ -45,13 +45,20 @@ def main():
45
45
  logger.info('=' * 66)
46
46
  logger.info('')
47
47
 
48
- # Start config monitoring
49
- config.start_monitoring(logger)
50
-
51
48
  # Setup audit trail logging
52
49
  audit_logger = setup_audit_trail_logger(logger)
53
50
 
54
- logger.info(f"Starting MCPower Proxy:\n{{'args': {args}, 'log_file': {log_file}, 'log_level': {log_level}, 'debug_mode': {debug_mode}}}")
51
+ if args.ide_tool:
52
+ from ide_tools.router import main as ide_tools_main
53
+ ide_tools_main(logger, audit_logger, args.ide, args.context)
54
+ return
55
+
56
+ # Continue with MCP wrapper mode
57
+ # Start config monitoring
58
+ config.start_monitoring(logger)
59
+
60
+ logger.info(
61
+ f"Starting MCPower Proxy:\n{{'args': {args}, 'log_file': {log_file}, 'log_level': {log_level}, 'debug_mode': {debug_mode}}}")
55
62
 
56
63
  try:
57
64
  # Parse JSON/JSONC config
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcpower-proxy
3
- Version: 0.0.61
3
+ Version: 0.0.72
4
4
  Summary: MCPower Security proxy
5
5
  Author-email: MCPower Security <support@mcpower.tech>
6
6
  License: Apache License
@@ -209,14 +209,14 @@ Keywords: mcp,security,proxy,monitoring,audit,redaction,policy-enforcement
209
209
  Requires-Python: ~=3.11.0
210
210
  Description-Content-Type: text/markdown
211
211
  License-File: LICENSE
212
- Requires-Dist: fastmcp>=2.10.5
212
+ Requires-Dist: fastmcp==2.13.0.2
213
213
  Requires-Dist: httpx>=0.25.0
214
214
  Requires-Dist: mcp>=1.0.0
215
215
  Requires-Dist: watchdog>=3.0.0
216
216
  Requires-Dist: jsonc-parser>=1.1.5
217
217
  Requires-Dist: jsonpath-ng>=1.7.0
218
218
  Requires-Dist: pydantic>=2.8.0
219
- Requires-Dist: mcpower-shared==0.1.1
219
+ Requires-Dist: mcpower-shared==0.1.3
220
220
  Dynamic: license-file
221
221
 
222
222
  # MCPower Proxy
@@ -3,6 +3,12 @@ README.md
3
3
  pyproject.toml
4
4
  src/LICENSE
5
5
  src/main.py
6
+ src/ide_tools/__init__.py
7
+ src/ide_tools/router.py
8
+ src/ide_tools/cursor/__init__.py
9
+ src/ide_tools/cursor/constants.py
10
+ src/ide_tools/cursor/format.py
11
+ src/ide_tools/cursor/router.py
6
12
  src/mcpower_proxy.egg-info/PKG-INFO
7
13
  src/mcpower_proxy.egg-info/SOURCES.txt
8
14
  src/mcpower_proxy.egg-info/dependency_links.txt
@@ -10,6 +16,7 @@ src/mcpower_proxy.egg-info/entry_points.txt
10
16
  src/mcpower_proxy.egg-info/requires.txt
11
17
  src/mcpower_proxy.egg-info/top_level.txt
12
18
  src/modules/__init__.py
19
+ src/modules/decision_handler.py
13
20
  src/modules/apis/__init__.py
14
21
  src/modules/apis/security_policy.py
15
22
  src/modules/logs/__init__.py
@@ -1,8 +1,8 @@
1
- fastmcp>=2.10.5
1
+ fastmcp==2.13.0.2
2
2
  httpx>=0.25.0
3
3
  mcp>=1.0.0
4
4
  watchdog>=3.0.0
5
5
  jsonc-parser>=1.1.5
6
6
  jsonpath-ng>=1.7.0
7
7
  pydantic>=2.8.0
8
- mcpower-shared==0.1.1
8
+ mcpower-shared==0.1.3
@@ -1,3 +1,4 @@
1
+ ide_tools
1
2
  main
2
3
  modules
3
4
  wrapper
@@ -1,9 +1,9 @@
1
1
  """Security Policy API Client"""
2
2
 
3
3
  import json
4
+ import time
4
5
  import uuid
5
6
  from typing import Dict, Any, Optional, List
6
- import time
7
7
 
8
8
  import httpx
9
9
 
@@ -13,6 +13,7 @@ from modules.logs.logger import MCPLogger
13
13
  from modules.redaction import redact
14
14
  from modules.utils.config import get_api_url, get_user_id
15
15
  from modules.utils.json import safe_json_dumps, to_dict
16
+ from wrapper.__version__ import __version__
16
17
 
17
18
 
18
19
  class SecurityAPIError(Exception):
@@ -22,6 +23,7 @@ class SecurityAPIError(Exception):
22
23
 
23
24
  class RateLimitExhaustedError(SecurityAPIError):
24
25
  """Security API rate limit exhausted (429) error"""
26
+
25
27
  def __init__(self, message: str, retry_after: int = None):
26
28
  super().__init__(message)
27
29
  self.retry_after = retry_after
@@ -52,7 +54,7 @@ class SecurityPolicyClient:
52
54
  if self.client:
53
55
  await self.client.aclose()
54
56
 
55
- async def inspect_policy_request(self, policy_request: PolicyRequest,
57
+ async def inspect_policy_request(self, policy_request: PolicyRequest,
56
58
  prompt_id: str) -> InspectDecision:
57
59
  """Call inspect_policy_request API endpoint"""
58
60
  if not self.client:
@@ -155,7 +157,7 @@ class SecurityPolicyClient:
155
157
  audit_payload = {"payload": {"server": payload_dict["server"], "tools": payload_dict["tools"]}}
156
158
  else:
157
159
  audit_payload = {"payload": payload_dict}
158
-
160
+
159
161
  self.audit_logger.log_event(
160
162
  audit_event_type,
161
163
  audit_payload,
@@ -166,6 +168,7 @@ class SecurityPolicyClient:
166
168
 
167
169
  headers = {
168
170
  "Content-Type": "application/json",
171
+ "User-Agent": f"MCPower-{__version__}",
169
172
  "X-User-UID": self.user_id,
170
173
  "X-App-UID": self.app_id
171
174
  }
@@ -188,7 +191,8 @@ class SecurityPolicyClient:
188
191
  raise SecurityAPIError(f"Unsupported HTTP method: {method}. Supported methods: POST, PUT")
189
192
 
190
193
  on_make_request_duration = time.time() - on_make_request_start_time
191
- self.logger.info(f"PROFILE: {method} id: {id} make_request duration: {on_make_request_duration:.2f} seconds url: {url}")
194
+ self.logger.debug(
195
+ f"PROFILE: {method} id: {id} make_request duration: {on_make_request_duration:.2f} seconds url: {url}")
192
196
 
193
197
  match response.status_code:
194
198
  case 200:
@@ -215,7 +219,7 @@ class SecurityPolicyClient:
215
219
  else:
216
220
  # Other responses (e.g., /init) - log entire response
217
221
  audit_result = {"result": data_dict}
218
-
222
+
219
223
  self.audit_logger.log_event(
220
224
  f"{audit_event_type}_result",
221
225
  audit_result,
@@ -278,7 +282,8 @@ class SecurityPolicyClient:
278
282
  def _handle_quota_restoration(self, endpoint: str):
279
283
  """Handle quota restoration (when non-429 response received)"""
280
284
  if self.session_id in self._session_notification_times:
281
- self.logger.info(f"Quota restored - received successful response from {endpoint}. Session: {self.session_id}")
285
+ self.logger.info(
286
+ f"Quota restored - received successful response from {endpoint}. Session: {self.session_id}")
282
287
  del self._session_notification_times[self.session_id]
283
288
 
284
289
  def _send_throttled_quota_notification(self, retry_after: int, endpoint: str):