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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cnhkmcp
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: A comprehensive Model Context Protocol (MCP) server for quantitative trading platform integration
5
5
  Home-page: https://github.com/cnhk/cnhkmcp
6
6
  Author: CNHK
@@ -50,7 +50,7 @@ from .untracked.forum_functions import (
50
50
  read_full_forum_post
51
51
  )
52
52
 
53
- __version__ = "1.2.2"
53
+ __version__ = "1.2.3"
54
54
  __author__ = "CNHK"
55
55
  __email__ = "cnhk@example.com"
56
56
 
@@ -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
- """Resolve the absolute path to the config file.
1217
-
1218
- Resolution order:
1219
- 1) BRAIN_CONFIG_PATH env var (explicit file path)
1220
- 2) File in the same directory as this Python module
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 is True, prefers module directory (or env path) and will
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
- # 1) Explicit override
1228
- env_path = os.environ.get("BRAIN_CONFIG_PATH")
1229
- if env_path:
1230
- return os.path.abspath(env_path)
1231
-
1232
- # 2) Same directory as this .py module
1233
- module_dir = os.path.dirname(os.path.abspath(__file__))
1234
- module_path = os.path.join(module_dir, CONFIG_FILE)
1235
- if not for_write and os.path.exists(module_path):
1236
- return module_path
1237
-
1238
- # 3) Fallback to CWD for backward compatibility (read-only preference)
1239
- cwd_path = os.path.abspath(CONFIG_FILE)
1240
- if not for_write and os.path.exists(cwd_path):
1241
- return cwd_path
1242
-
1243
- # For writes (or when nothing exists), prefer the module directory
1244
- return module_path
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
- # First try to load from .brain_credentials file
1311
- brain_creds = load_brain_credentials()
1312
- if brain_creds and not email and not password:
1313
- email, password = brain_creds
1314
- print(f"📧 Using credentials from .brain_credentials file: {email}")
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\"]"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cnhkmcp
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: A comprehensive Model Context Protocol (MCP) server for quantitative trading platform integration
5
5
  Home-page: https://github.com/cnhk/cnhkmcp
6
6
  Author: CNHK
@@ -13,7 +13,7 @@ def read_requirements():
13
13
 
14
14
  setup(
15
15
  name="cnhkmcp",
16
- version="1.2.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