mcpower-proxy 0.0.58__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.
- main.py +112 -0
- mcpower_proxy-0.0.58.dist-info/METADATA +250 -0
- mcpower_proxy-0.0.58.dist-info/RECORD +43 -0
- mcpower_proxy-0.0.58.dist-info/WHEEL +5 -0
- mcpower_proxy-0.0.58.dist-info/entry_points.txt +2 -0
- mcpower_proxy-0.0.58.dist-info/licenses/LICENSE +201 -0
- mcpower_proxy-0.0.58.dist-info/top_level.txt +3 -0
- modules/__init__.py +1 -0
- modules/apis/__init__.py +1 -0
- modules/apis/security_policy.py +322 -0
- modules/logs/__init__.py +1 -0
- modules/logs/audit_trail.py +162 -0
- modules/logs/logger.py +128 -0
- modules/redaction/__init__.py +13 -0
- modules/redaction/constants.py +38 -0
- modules/redaction/gitleaks_rules.py +1268 -0
- modules/redaction/pii_rules.py +271 -0
- modules/redaction/redactor.py +599 -0
- modules/ui/__init__.py +1 -0
- modules/ui/classes.py +48 -0
- modules/ui/confirmation.py +200 -0
- modules/ui/simple_dialog.py +104 -0
- modules/ui/xdialog/__init__.py +249 -0
- modules/ui/xdialog/constants.py +13 -0
- modules/ui/xdialog/mac_dialogs.py +190 -0
- modules/ui/xdialog/tk_dialogs.py +78 -0
- modules/ui/xdialog/windows_custom_dialog.py +426 -0
- modules/ui/xdialog/windows_dialogs.py +250 -0
- modules/ui/xdialog/windows_structs.py +183 -0
- modules/ui/xdialog/yad_dialogs.py +236 -0
- modules/ui/xdialog/zenity_dialogs.py +156 -0
- modules/utils/__init__.py +1 -0
- modules/utils/cli.py +46 -0
- modules/utils/config.py +193 -0
- modules/utils/copy.py +36 -0
- modules/utils/ids.py +160 -0
- modules/utils/json.py +120 -0
- modules/utils/mcp_configs.py +48 -0
- wrapper/__init__.py +1 -0
- wrapper/__version__.py +6 -0
- wrapper/middleware.py +750 -0
- wrapper/schema.py +227 -0
- wrapper/server.py +78 -0
modules/utils/json.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JSON utilities for safe serialization and JSONC parsing
|
|
3
|
+
"""
|
|
4
|
+
import json
|
|
5
|
+
from dataclasses import is_dataclass, asdict
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from jsonc_parser.parser import JsoncParser
|
|
10
|
+
from pydantic import BaseModel, AnyUrl
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def to_dict(value: Any) -> Any:
|
|
14
|
+
"""
|
|
15
|
+
Convert any value to a dict/primitive structure suitable for JSON serialization.
|
|
16
|
+
Handles dataclasses, Pydantic models, dicts, lists, Enums, and nested structures.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
value: Any value to convert
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Dict, list, or primitive value ready for JSON serialization
|
|
23
|
+
"""
|
|
24
|
+
if value is None:
|
|
25
|
+
return None
|
|
26
|
+
elif isinstance(value, (str, int, float, bool)):
|
|
27
|
+
return value
|
|
28
|
+
elif isinstance(value, Enum):
|
|
29
|
+
# Convert Enum to its value for JSON serialization
|
|
30
|
+
return value.value
|
|
31
|
+
elif isinstance(value, dict):
|
|
32
|
+
return {k: to_dict(v) for k, v in value.items()}
|
|
33
|
+
elif isinstance(value, (list, tuple)):
|
|
34
|
+
return [to_dict(item) for item in value]
|
|
35
|
+
elif is_dataclass(value):
|
|
36
|
+
# Use asdict and recursively convert Enums to their values
|
|
37
|
+
return to_dict(asdict(value))
|
|
38
|
+
elif hasattr(value, 'model_dump'):
|
|
39
|
+
return value.model_dump()
|
|
40
|
+
elif hasattr(value, '__dict__'):
|
|
41
|
+
# Extract __dict__ for objects, excluding private attrs only
|
|
42
|
+
return {k: to_dict(v) for k, v in value.__dict__.items() if not k.startswith('_')}
|
|
43
|
+
else:
|
|
44
|
+
# Last resort - convert to string
|
|
45
|
+
return str(value)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def safe_json_dumps(obj: Any, **kwargs) -> str:
|
|
49
|
+
"""
|
|
50
|
+
Safely serialize objects to JSON, using Pydantic's built-in serialization when available
|
|
51
|
+
"""
|
|
52
|
+
# If it's a Pydantic BaseModel, use its built-in JSON serialization
|
|
53
|
+
if isinstance(obj, BaseModel):
|
|
54
|
+
return obj.model_dump_json(**kwargs)
|
|
55
|
+
|
|
56
|
+
# If it's a dict or list that might contain Pydantic objects, use custom serializer
|
|
57
|
+
def default_serializer(o):
|
|
58
|
+
if isinstance(o, BaseModel):
|
|
59
|
+
return o.model_dump()
|
|
60
|
+
if isinstance(o, AnyUrl):
|
|
61
|
+
return str(o)
|
|
62
|
+
# Handle Enums by converting to their value
|
|
63
|
+
if isinstance(o, Enum):
|
|
64
|
+
return o.value
|
|
65
|
+
# Handle dataclasses properly - recursively convert via to_dict to handle nested Enums
|
|
66
|
+
if is_dataclass(o):
|
|
67
|
+
return to_dict(o)
|
|
68
|
+
# Handle other objects with dict method
|
|
69
|
+
if hasattr(o, 'dict') and callable(o.dict):
|
|
70
|
+
return o.dict()
|
|
71
|
+
if hasattr(o, '__dict__'):
|
|
72
|
+
return o.__dict__
|
|
73
|
+
# Fallback to string representation
|
|
74
|
+
return str(o)
|
|
75
|
+
|
|
76
|
+
return json.dumps(obj, default=default_serializer, **kwargs)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def stringify_jsonc(obj: Any, **kwargs) -> str:
|
|
80
|
+
"""
|
|
81
|
+
Serialize object to JSONC format when possible, falling back to regular JSON
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
obj: The object to serialize
|
|
85
|
+
**kwargs: Additional arguments passed to the serializer
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
JSONC string representation
|
|
89
|
+
"""
|
|
90
|
+
try:
|
|
91
|
+
# Use regular JSON for serialization
|
|
92
|
+
return json.dumps(obj, **kwargs)
|
|
93
|
+
except Exception:
|
|
94
|
+
# Fallback to safe_json_dumps if regular json.dumps fails
|
|
95
|
+
return safe_json_dumps(obj, **kwargs)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def parse_jsonc(text: str) -> Any:
|
|
99
|
+
"""
|
|
100
|
+
Parse JSONC (JSON with Comments) text using jsonc-parser, falling back to regular JSON if parsing fails
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
text: The JSONC/JSON string to parse
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Parsed object
|
|
107
|
+
|
|
108
|
+
Raises:
|
|
109
|
+
json.JSONDecodeError: If parsing fails with both JSONC and JSON parsers
|
|
110
|
+
"""
|
|
111
|
+
try:
|
|
112
|
+
# Use jsonc-parser for JSONC handling (comments support)
|
|
113
|
+
return JsoncParser.parse_str(text)
|
|
114
|
+
except Exception as e:
|
|
115
|
+
# If JSONC parsing fails, try regular JSON as fallback
|
|
116
|
+
try:
|
|
117
|
+
return json.loads(text)
|
|
118
|
+
except json.JSONDecodeError:
|
|
119
|
+
# Re-raise the original JSONC error if JSON also fails
|
|
120
|
+
raise json.JSONDecodeError(f"JSONC parsing failed: {str(e)}", text, 0)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from fastmcp import mcp_config
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def extract_wrapped_server_info(wrapper_server_name, logger, configs: dict) -> tuple[str, str]:
|
|
5
|
+
"""
|
|
6
|
+
Extract wrapped server name and transport using FastMCP utilities
|
|
7
|
+
|
|
8
|
+
Returns:
|
|
9
|
+
tuple: (server_name, transport_name)
|
|
10
|
+
"""
|
|
11
|
+
try:
|
|
12
|
+
# Parse config using FastMCP's built-in utilities
|
|
13
|
+
parsed_config = mcp_config.MCPConfig.from_dict(configs)
|
|
14
|
+
|
|
15
|
+
# Get the first server (the most common case - single wrapped server)
|
|
16
|
+
first_server = list(parsed_config.mcpServers.values())[0]
|
|
17
|
+
first_server_key = list(parsed_config.mcpServers.keys())[0]
|
|
18
|
+
|
|
19
|
+
# Extract server name
|
|
20
|
+
# If the key is "default" (from raw config conversion) - use the wrapper name
|
|
21
|
+
if first_server_key == "default":
|
|
22
|
+
server_name = wrapper_server_name
|
|
23
|
+
else:
|
|
24
|
+
server_name = first_server_key
|
|
25
|
+
|
|
26
|
+
# Extract transport using FastMCP's transport resolution
|
|
27
|
+
transport_obj = first_server.to_transport()
|
|
28
|
+
transport_class = type(transport_obj).__name__
|
|
29
|
+
|
|
30
|
+
# Map FastMCP transport classes to simple string names
|
|
31
|
+
transport_mapping = {
|
|
32
|
+
'StdioTransport': 'stdio',
|
|
33
|
+
'StreamableHttpTransport': 'streamable-http',
|
|
34
|
+
'SSETransport': 'sse',
|
|
35
|
+
'WSTransport': 'ws',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
wrapped_server_transport_name = transport_mapping.get(transport_class,
|
|
39
|
+
transport_class.lower().replace('transport', ''))
|
|
40
|
+
|
|
41
|
+
logger.debug(f"Extracted wrapped server info: name={server_name}, "
|
|
42
|
+
f"transport={wrapped_server_transport_name}")
|
|
43
|
+
|
|
44
|
+
return server_name, wrapped_server_transport_name
|
|
45
|
+
|
|
46
|
+
except Exception as e:
|
|
47
|
+
logger.error(f"Failed to extract wrapped server info: {e}")
|
|
48
|
+
raise e
|
wrapper/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# MCP Wrapper package
|