mcpower-proxy 0.0.65__py3-none-any.whl → 0.0.79__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.

Potentially problematic release.


This version of mcpower-proxy might be problematic. Click here for more details.

Files changed (41) hide show
  1. ide_tools/__init__.py +12 -0
  2. ide_tools/common/__init__.py +5 -0
  3. ide_tools/common/hooks/__init__.py +5 -0
  4. ide_tools/common/hooks/init.py +130 -0
  5. ide_tools/common/hooks/output.py +63 -0
  6. ide_tools/common/hooks/prompt_submit.py +136 -0
  7. ide_tools/common/hooks/read_file.py +170 -0
  8. ide_tools/common/hooks/shell_execution.py +257 -0
  9. ide_tools/common/hooks/shell_parser_bashlex.py +394 -0
  10. ide_tools/common/hooks/types.py +34 -0
  11. ide_tools/common/hooks/utils.py +286 -0
  12. ide_tools/cursor/__init__.py +11 -0
  13. ide_tools/cursor/constants.py +77 -0
  14. ide_tools/cursor/format.py +35 -0
  15. ide_tools/cursor/router.py +107 -0
  16. ide_tools/router.py +48 -0
  17. main.py +11 -4
  18. {mcpower_proxy-0.0.65.dist-info → mcpower_proxy-0.0.79.dist-info}/METADATA +4 -3
  19. mcpower_proxy-0.0.79.dist-info/RECORD +62 -0
  20. {mcpower_proxy-0.0.65.dist-info → mcpower_proxy-0.0.79.dist-info}/top_level.txt +1 -0
  21. modules/apis/security_policy.py +11 -6
  22. modules/decision_handler.py +219 -0
  23. modules/logs/audit_trail.py +20 -18
  24. modules/logs/logger.py +14 -18
  25. modules/redaction/gitleaks_rules.py +1 -1
  26. modules/redaction/pii_rules.py +0 -48
  27. modules/redaction/redactor.py +112 -107
  28. modules/ui/__init__.py +1 -1
  29. modules/ui/confirmation.py +0 -1
  30. modules/utils/cli.py +36 -6
  31. modules/utils/ids.py +55 -10
  32. modules/utils/json.py +3 -3
  33. modules/utils/platform.py +23 -0
  34. modules/utils/string.py +17 -0
  35. wrapper/__version__.py +1 -1
  36. wrapper/middleware.py +144 -221
  37. wrapper/server.py +19 -11
  38. mcpower_proxy-0.0.65.dist-info/RECORD +0 -43
  39. {mcpower_proxy-0.0.65.dist-info → mcpower_proxy-0.0.79.dist-info}/WHEEL +0 -0
  40. {mcpower_proxy-0.0.65.dist-info → mcpower_proxy-0.0.79.dist-info}/entry_points.txt +0 -0
  41. {mcpower_proxy-0.0.65.dist-info → mcpower_proxy-0.0.79.dist-info}/licenses/LICENSE +0 -0
modules/utils/ids.py CHANGED
@@ -2,6 +2,7 @@
2
2
  Utilities for generating event IDs, session IDs, app UIDs, and timing helpers
3
3
  """
4
4
  import os
5
+ import sys
5
6
  import time
6
7
  import uuid
7
8
  from pathlib import Path
@@ -23,6 +24,16 @@ def generate_event_id() -> str:
23
24
  return f"{timestamp}-{unique_part}"
24
25
 
25
26
 
27
+ def generate_prompt_id() -> str:
28
+ """
29
+ Generate truly-random 8-character prompt ID for user request correlation
30
+
31
+ Returns:
32
+ 8-character random ID string
33
+ """
34
+ return str(uuid.uuid4())[:8]
35
+
36
+
26
37
  def get_session_id() -> str:
27
38
  """
28
39
  Get session ID for the current process. Returns the same value for all calls
@@ -67,7 +78,8 @@ def _atomic_write_uuid(file_path: Path, new_uuid: str) -> bool:
67
78
  True if write succeeded, False if file exists
68
79
  """
69
80
  try:
70
- fd = os.open(str(file_path), os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)
81
+ mode = 0o666 if sys.platform == 'win32' else 0o600
82
+ fd = os.open(str(file_path), os.O_CREAT | os.O_EXCL | os.O_WRONLY, mode)
71
83
  try:
72
84
  os.write(fd, new_uuid.encode('utf-8'))
73
85
  finally:
@@ -91,7 +103,7 @@ def _get_or_create_uuid(uid_path: Path, logger, id_type: str) -> str:
91
103
  UUID string
92
104
  """
93
105
  uid_path.parent.mkdir(parents=True, exist_ok=True)
94
-
106
+
95
107
  max_attempts = 3
96
108
  for attempt in range(max_attempts):
97
109
  if uid_path.exists():
@@ -107,20 +119,53 @@ def _get_or_create_uuid(uid_path: Path, logger, id_type: str) -> str:
107
119
  time.sleep(0.1 * (2 ** attempt))
108
120
  continue
109
121
  raise
110
-
122
+
111
123
  new_uid = str(uuid.uuid4())
112
-
124
+
113
125
  if _atomic_write_uuid(uid_path, new_uid):
114
- logger.info(f"Generated {id_type}: {new_uid}")
126
+ logger.info(f"Generated {id_type}: {new_uid} at {uid_path}")
115
127
  return new_uid
116
-
117
- logger.debug(f"{id_type.title()} file created by another process, reading (attempt {attempt + 1}/{max_attempts})")
128
+
129
+ logger.debug(
130
+ f"{id_type.title()} file created by another process, reading (attempt {attempt + 1}/{max_attempts})")
118
131
  if attempt < max_attempts - 1:
119
132
  time.sleep(0.05)
120
-
133
+
121
134
  raise RuntimeError(f"Failed to get or create {id_type} after {max_attempts} attempts")
122
135
 
123
136
 
137
+ def get_home_mcpower_dir() -> Path:
138
+ """
139
+ Get the global MCPower directory path in user's home directory
140
+
141
+ Returns:
142
+ Path to ~/.mcpower directory
143
+ """
144
+ return Path.home() / ".mcpower"
145
+
146
+
147
+ def get_project_mcpower_dir(project_path: Optional[str] = None) -> str:
148
+ """
149
+ Get the MCPower directory path, with fallback to global ~/.mcpower
150
+
151
+ Args:
152
+ project_path: Optional project/workspace path. If None or invalid, falls back to ~/.mcpower
153
+
154
+ Returns:
155
+ Path to use for MCPower data (either project/.mcpower or ~/.mcpower)
156
+ """
157
+ if project_path:
158
+ try:
159
+ path = Path(project_path)
160
+ if path.exists() and path.is_dir():
161
+ return str(path)
162
+ except Exception:
163
+ pass
164
+
165
+ # Fallback to global ~/.mcpower
166
+ return str(get_home_mcpower_dir())
167
+
168
+
124
169
  def get_or_create_user_id(logger) -> str:
125
170
  """
126
171
  Get or create machine-wide user ID from ~/.mcpower/uid
@@ -132,7 +177,7 @@ def get_or_create_user_id(logger) -> str:
132
177
  Returns:
133
178
  User ID string
134
179
  """
135
- uid_path = Path.home() / ".mcpower" / "uid"
180
+ uid_path = get_home_mcpower_dir() / "uid"
136
181
  return _get_or_create_uuid(uid_path, logger, "user ID")
137
182
 
138
183
 
@@ -156,5 +201,5 @@ def read_app_uid(logger, project_folder_path: str) -> str:
156
201
  else:
157
202
  # Project-specific case
158
203
  uid_path = project_path / ".mcpower" / "app_uid"
159
-
204
+
160
205
  return _get_or_create_uuid(uid_path, logger, "app UID")
modules/utils/json.py CHANGED
@@ -52,7 +52,7 @@ def safe_json_dumps(obj: Any, **kwargs) -> str:
52
52
  # If it's a Pydantic BaseModel, use its built-in JSON serialization
53
53
  if isinstance(obj, BaseModel):
54
54
  return obj.model_dump_json(**kwargs)
55
-
55
+
56
56
  # If it's a dict or list that might contain Pydantic objects, use custom serializer
57
57
  def default_serializer(o):
58
58
  if isinstance(o, BaseModel):
@@ -72,7 +72,7 @@ def safe_json_dumps(obj: Any, **kwargs) -> str:
72
72
  return o.__dict__
73
73
  # Fallback to string representation
74
74
  return str(o)
75
-
75
+
76
76
  return json.dumps(obj, default=default_serializer, **kwargs)
77
77
 
78
78
 
@@ -117,4 +117,4 @@ def parse_jsonc(text: str) -> Any:
117
117
  return json.loads(text)
118
118
  except json.JSONDecodeError:
119
119
  # Re-raise the original JSONC error if JSON also fails
120
- raise json.JSONDecodeError(f"JSONC parsing failed: {str(e)}", text, 0)
120
+ raise json.JSONDecodeError(f"JSONC parsing failed: {str(e)}", text, 0)
@@ -0,0 +1,23 @@
1
+ """Platform detection utilities"""
2
+ import sys
3
+ from typing import Optional, Literal
4
+
5
+
6
+ def get_client_os() -> Optional[Literal["macos", "windows", "linux"]]:
7
+ """
8
+ Fetch Python's sys.platform and convert to standardized OS names.
9
+
10
+ Returns:
11
+ "macos", "windows", "linux", or None if platform is unknown
12
+ """
13
+ platform = sys.platform
14
+
15
+ if platform == "darwin":
16
+ return "macos"
17
+ elif platform == "win32":
18
+ return "windows"
19
+ elif platform == "linux":
20
+ return "linux"
21
+ else:
22
+ return None
23
+
@@ -0,0 +1,17 @@
1
+ """String utility functions"""
2
+
3
+
4
+ def truncate_at(text: str, max_length: int) -> str:
5
+ """
6
+ Truncate string at max_length, appending '...' only if truncated.
7
+
8
+ Args:
9
+ text: String to truncate
10
+ max_length: Maximum length before truncation
11
+
12
+ Returns:
13
+ Truncated string with '...' suffix if truncated, original if not
14
+ """
15
+ if len(text) <= max_length:
16
+ return text
17
+ return f"{text[:max_length]}..."
wrapper/__version__.py CHANGED
@@ -3,4 +3,4 @@
3
3
  Wrapper MCP Server Version
4
4
  """
5
5
 
6
- __version__ = "0.0.65"
6
+ __version__ = "0.0.79"