cnhkmcp 1.2.2__tar.gz → 1.2.4__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.4
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.4"
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__)
@@ -601,8 +604,8 @@ class BrainApiClient:
601
604
  self.log(f"Failed to get user profile: {str(e)}", "ERROR")
602
605
  raise
603
606
 
604
- async def get_tutorials(self) -> Dict[str, Any]:
605
- """Get available tutorials and learning materials."""
607
+ async def get_documentations(self) -> Dict[str, Any]:
608
+ """Get available documentations and learning materials."""
606
609
  await self.ensure_authenticated()
607
610
 
608
611
  try:
@@ -610,7 +613,7 @@ class BrainApiClient:
610
613
  response.raise_for_status()
611
614
  return response.json()
612
615
  except Exception as e:
613
- self.log(f"Failed to get tutorials: {str(e)}", "ERROR")
616
+ self.log(f"Failed to get documentations: {str(e)}", "ERROR")
614
617
  raise
615
618
 
616
619
  # get_messages_summary function removed as requested
@@ -1189,8 +1192,8 @@ class BrainApiClient:
1189
1192
 
1190
1193
  # generate_alpha_links function removed as requested
1191
1194
 
1192
- async def get_tutorial_page(self, page_id: str) -> Dict[str, Any]:
1193
- """Retrieve detailed content of a specific tutorial page/article."""
1195
+ async def get_documentation_page(self, page_id: str) -> Dict[str, Any]:
1196
+ """Retrieve detailed content of a specific documentation page/article."""
1194
1197
  await self.ensure_authenticated()
1195
1198
 
1196
1199
  try:
@@ -1198,7 +1201,7 @@ class BrainApiClient:
1198
1201
  response.raise_for_status()
1199
1202
  return response.json()
1200
1203
  except Exception as e:
1201
- self.log(f"Failed to get tutorial page: {str(e)}", "ERROR")
1204
+ self.log(f"Failed to get documentation page: {str(e)}", "ERROR")
1202
1205
  raise
1203
1206
 
1204
1207
  # Badge status function removed as requested
@@ -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\"]"}
@@ -1756,15 +1743,15 @@ async def get_user_profile(user_id: str = "self") -> Dict[str, Any]:
1756
1743
  return {"error": str(e)}
1757
1744
 
1758
1745
  @mcp.tool()
1759
- async def get_tutorials() -> Dict[str, Any]:
1746
+ async def get_documentations() -> Dict[str, Any]:
1760
1747
  """
1761
- 📚 Get available tutorials and learning materials.
1748
+ 📚 Get available documentations and learning materials.
1762
1749
 
1763
1750
  Returns:
1764
- List of tutorials
1751
+ List of documentations
1765
1752
  """
1766
1753
  try:
1767
- return await brain_client.get_tutorials()
1754
+ return await brain_client.get_documentations()
1768
1755
  except Exception as e:
1769
1756
  return {"error": str(e)}
1770
1757
 
@@ -2029,10 +2016,10 @@ async def expand_nested_data(data: List[Dict[str, Any]], preserve_original: bool
2029
2016
  # generate_alpha_links MCP tool removed as requested
2030
2017
 
2031
2018
  @mcp.tool()
2032
- async def get_tutorial_page(page_id: str) -> Dict[str, Any]:
2033
- """Retrieve detailed content of a specific tutorial page/article."""
2019
+ async def get_documentation_page(page_id: str) -> Dict[str, Any]:
2020
+ """Retrieve detailed content of a specific documentation page/article."""
2034
2021
  try:
2035
- return await brain_client.get_tutorial_page(page_id)
2022
+ return await brain_client.get_documentation_page(page_id)
2036
2023
  except Exception as e:
2037
2024
  return {"error": str(e)}
2038
2025
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cnhkmcp
3
- Version: 1.2.2
3
+ Version: 1.2.4
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.4",
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