cnhkmcp 1.2.2__tar.gz → 1.2.3__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.
- {cnhkmcp-1.2.2/cnhkmcp.egg-info → cnhkmcp-1.2.3}/PKG-INFO +1 -1
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/cnhkmcp/__init__.py +1 -1
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/cnhkmcp/untracked/platform_functions.py +45 -58
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3/cnhkmcp.egg-info}/PKG-INFO +1 -1
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/setup.py +1 -1
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/LICENSE +0 -0
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/MANIFEST.in +0 -0
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/README.md +0 -0
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/cnhkmcp/untracked/forum_functions.py +0 -0
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/cnhkmcp/untracked/user_config.json +0 -0
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/cnhkmcp.egg-info/SOURCES.txt +0 -0
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/cnhkmcp.egg-info/dependency_links.txt +0 -0
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/cnhkmcp.egg-info/entry_points.txt +0 -0
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/cnhkmcp.egg-info/not-zip-safe +0 -0
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/cnhkmcp.egg-info/requires.txt +0 -0
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/cnhkmcp.egg-info/top_level.txt +0 -0
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/requirements.txt +0 -0
- {cnhkmcp-1.2.2 → cnhkmcp-1.2.3}/setup.cfg +0 -0
|
@@ -26,6 +26,9 @@ from bs4 import BeautifulSoup
|
|
|
26
26
|
from mcp.server.fastmcp import FastMCP
|
|
27
27
|
from pydantic import BaseModel, Field, EmailStr
|
|
28
28
|
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
|
|
31
|
+
|
|
29
32
|
# Configure logging
|
|
30
33
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
31
34
|
logger = logging.getLogger(__name__)
|
|
@@ -1213,39 +1216,44 @@ brain_client = BrainApiClient()
|
|
|
1213
1216
|
CONFIG_FILE = "user_config.json"
|
|
1214
1217
|
|
|
1215
1218
|
def _resolve_config_path(for_write: bool = False) -> str:
|
|
1216
|
-
"""
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
3) Current working directory (read fallback only)
|
|
1219
|
+
"""
|
|
1220
|
+
Resolve the config file path with this priority:
|
|
1221
|
+
1) BRAIN_CONFIG_PATH (file or directory)
|
|
1222
|
+
2) Directory of running script when available, else current working directory
|
|
1223
|
+
3) Current working directory
|
|
1222
1224
|
|
|
1223
|
-
When for_write
|
|
1224
|
-
return that even if it doesn't exist yet.
|
|
1225
|
+
When for_write=True, returns the preferred path even if it doesn't exist yet.
|
|
1225
1226
|
"""
|
|
1227
|
+
# 1) Explicit override via env var
|
|
1228
|
+
env_path = os.environ.get("BRAIN_CONFIG_PATH")
|
|
1229
|
+
if env_path:
|
|
1230
|
+
p = Path(env_path).expanduser()
|
|
1231
|
+
target = p / CONFIG_FILE if p.is_dir() else p
|
|
1232
|
+
# For read, only if it exists; for write, allow regardless
|
|
1233
|
+
if for_write or target.exists():
|
|
1234
|
+
return str(target.resolve())
|
|
1235
|
+
|
|
1236
|
+
# 2) Script/module directory when available, else CWD (works in notebooks)
|
|
1237
|
+
base_dir = Path.cwd()
|
|
1226
1238
|
try:
|
|
1227
|
-
#
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
except Exception as e:
|
|
1246
|
-
# As a last resort, use the bare filename in CWD
|
|
1247
|
-
logger.error(f"Failed to resolve config path, defaulting to CWD: {e}")
|
|
1248
|
-
return os.path.abspath(CONFIG_FILE)
|
|
1239
|
+
# __file__ is not defined in notebooks; this will fail there and keep CWD
|
|
1240
|
+
script_dir = Path(__file__).resolve().parent # type: ignore[name-defined]
|
|
1241
|
+
base_dir = script_dir
|
|
1242
|
+
except Exception:
|
|
1243
|
+
# Fall back to current working directory for notebooks/REPL
|
|
1244
|
+
pass
|
|
1245
|
+
|
|
1246
|
+
module_path = base_dir / CONFIG_FILE
|
|
1247
|
+
if not for_write and module_path.exists():
|
|
1248
|
+
return str(module_path.resolve())
|
|
1249
|
+
|
|
1250
|
+
# 3) Fallback to CWD for backward compatibility
|
|
1251
|
+
cwd_path = Path.cwd() / CONFIG_FILE
|
|
1252
|
+
if not for_write and cwd_path.exists():
|
|
1253
|
+
return str(cwd_path.resolve())
|
|
1254
|
+
|
|
1255
|
+
# For writes (or when nothing exists), prefer the module/base directory
|
|
1256
|
+
return str(module_path.resolve())
|
|
1249
1257
|
|
|
1250
1258
|
def load_config() -> Dict[str, Any]:
|
|
1251
1259
|
"""Load configuration from file with robust path resolution.
|
|
@@ -1262,19 +1270,6 @@ def load_config() -> Dict[str, Any]:
|
|
|
1262
1270
|
logger.error(f"Failed to load config from '{path}': {e}")
|
|
1263
1271
|
return {}
|
|
1264
1272
|
|
|
1265
|
-
def load_brain_credentials() -> Optional[tuple]:
|
|
1266
|
-
"""Load credentials from .brain_credentials file in home directory."""
|
|
1267
|
-
try:
|
|
1268
|
-
from os.path import expanduser
|
|
1269
|
-
credentials_file = expanduser('~/.brain_credentials')
|
|
1270
|
-
if os.path.exists(credentials_file):
|
|
1271
|
-
with open(credentials_file, 'r') as f:
|
|
1272
|
-
credentials = json.load(f)
|
|
1273
|
-
if isinstance(credentials, list) and len(credentials) == 2:
|
|
1274
|
-
return tuple(credentials)
|
|
1275
|
-
except Exception as e:
|
|
1276
|
-
logger.error(f"Failed to load .brain_credentials: {e}")
|
|
1277
|
-
return None
|
|
1278
1273
|
|
|
1279
1274
|
def save_config(config: Dict[str, Any]):
|
|
1280
1275
|
"""Save configuration to file using the resolved config path.
|
|
@@ -1293,7 +1288,7 @@ def save_config(config: Dict[str, Any]):
|
|
|
1293
1288
|
# MCP Tools
|
|
1294
1289
|
|
|
1295
1290
|
@mcp.tool()
|
|
1296
|
-
async def authenticate(email: str = "", password: str = "") -> Dict[str, Any]:
|
|
1291
|
+
async def authenticate(email: Optional[str] = "", password: Optional[str] = "") -> Dict[str, Any]:
|
|
1297
1292
|
"""
|
|
1298
1293
|
🔐 Authenticate with WorldQuant BRAIN platform.
|
|
1299
1294
|
|
|
@@ -1307,20 +1302,12 @@ async def authenticate(email: str = "", password: str = "") -> Dict[str, Any]:
|
|
|
1307
1302
|
Authentication result with user info and permissions
|
|
1308
1303
|
"""
|
|
1309
1304
|
try:
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
# If still no credentials, try config file
|
|
1317
|
-
if not email or not password:
|
|
1318
|
-
config = load_config()
|
|
1319
|
-
if 'credentials' in config:
|
|
1320
|
-
if not email:
|
|
1321
|
-
email = config['credentials'].get('email', '')
|
|
1322
|
-
if not password:
|
|
1323
|
-
password = config['credentials'].get('password', '')
|
|
1305
|
+
config = load_config()
|
|
1306
|
+
if 'credentials' in config:
|
|
1307
|
+
if not email:
|
|
1308
|
+
email = config['credentials'].get('email', '')
|
|
1309
|
+
if not password:
|
|
1310
|
+
password = config['credentials'].get('password', '')
|
|
1324
1311
|
|
|
1325
1312
|
if not email or not password:
|
|
1326
1313
|
return {"error": "Email and password required. Either provide them as arguments, configure them in user_config.json, or create a .brain_credentials file in your home directory with format: [\"email\", \"password\"]"}
|
|
@@ -13,7 +13,7 @@ def read_requirements():
|
|
|
13
13
|
|
|
14
14
|
setup(
|
|
15
15
|
name="cnhkmcp",
|
|
16
|
-
version="1.2.
|
|
16
|
+
version="1.2.3",
|
|
17
17
|
author="CNHK",
|
|
18
18
|
author_email="cnhk@example.com",
|
|
19
19
|
description="A comprehensive Model Context Protocol (MCP) server for quantitative trading platform integration",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|