cnhkmcp 1.4.1__tar.gz → 1.4.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.4.1/cnhkmcp.egg-info → cnhkmcp-1.4.3}/PKG-INFO +1 -1
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/__init__.py +1 -1
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/platform_functions.py +143 -77
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3/cnhkmcp.egg-info}/PKG-INFO +1 -1
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/setup.py +1 -1
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/LICENSE +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/MANIFEST.in +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/README.md +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/.gitignore +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/MODULAR_STRUCTURE.md +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/README.md +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/__pycache__/app.cpython-313.pyc +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/__init__.py +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/__pycache__/__init__.cpython-313.pyc +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/__pycache__/feature_engineering.cpython-313.pyc +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/__pycache__/idea_house.cpython-313.pyc +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/__pycache__/inspiration_house.cpython-313.pyc +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/__pycache__/paper_analysis.cpython-313.pyc +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/__pycache__/simulator.cpython-313.pyc +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/__pycache__/unified_tools.cpython-313.pyc +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/__pycache__/wqb_simulator.cpython-313.pyc +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/feature_engineering.py +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/idea_house.py +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/inspiration_house.py +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/blueprints/paper_analysis.py +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/custom_templates/templates.json +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/hkSimulator/ace_lib.py +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/hkSimulator/autosimulator.py +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/hkSimulator/helpful_functions.py +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/mirror_config.txt +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/operaters.csv +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/requirements.txt +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/run_app.bat +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/run_app.sh +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/setup_tsinghua.bat +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/setup_tsinghua.sh +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/simulator/__pycache__/simulator_wqb.cpython-313.pyc +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/simulator/alpha_submitter.py +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/simulator/simulator_wqb.py +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/ssrn-3332513.pdf +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/static/brain.js +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/static/decoder.js +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/static/feature_engineering.js +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/static/idea_house.js +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/static/inspiration_house.js +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/static/paper_analysis.js +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/static/script.js +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/static/simulator.js +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/static/styles.css +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/templates/feature_engineering.html +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/templates/idea_house.html +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/templates/index.html +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/templates/inspiration_house.html +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/templates/paper_analysis.html +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP/templates/simulator.html +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/APP//350/277/220/350/241/214/346/211/223/345/274/200/346/210/221.py" +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/__pycache__/forum_functions.cpython-313.pyc +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/arXiv_API_Tool_Manual.md +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/arxiv_api.py +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/forum_functions.py +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/sample_mcp_config.json +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/user_config.json +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked//347/244/272/344/276/213/345/217/202/350/200/203/346/226/207/346/241/243_BRAIN_Alpha_Test_Requirements_and_Tips.md" +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_Alpha_explaination_workflow.md" +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_BRAIN_6_Tips_Datafield_Exploration_Guide.md" +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_BRAIN_Alpha_Improvement_Workflow.md" +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_Dataset_Exploration_Expert_Manual.md" +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_daily_report_workflow.md" +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp.egg-info/SOURCES.txt +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp.egg-info/dependency_links.txt +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp.egg-info/entry_points.txt +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp.egg-info/not-zip-safe +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp.egg-info/requires.txt +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp.egg-info/top_level.txt +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/requirements.txt +0 -0
- {cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/setup.cfg +0 -0
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
2
3
|
"""
|
|
3
4
|
WorldQuant BRAIN MCP Server - Python Version
|
|
4
5
|
A comprehensive Model Context Protocol (MCP) server for WorldQuant BRAIN platform integration.
|
|
5
6
|
"""
|
|
6
7
|
|
|
8
|
+
# Ensure proper encoding handling for Windows
|
|
9
|
+
import sys
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
# Note: We'll handle encoding issues in individual functions rather than
|
|
13
|
+
# overriding system streams to avoid conflicts with MCP server
|
|
14
|
+
|
|
7
15
|
import json
|
|
8
16
|
import time
|
|
9
17
|
import asyncio
|
|
@@ -11,8 +19,6 @@ import logging
|
|
|
11
19
|
from typing import Dict, List, Optional, Any, Union
|
|
12
20
|
from dataclasses import dataclass, asdict
|
|
13
21
|
from datetime import datetime, timedelta
|
|
14
|
-
import os
|
|
15
|
-
import sys
|
|
16
22
|
import math
|
|
17
23
|
from time import sleep
|
|
18
24
|
|
|
@@ -82,7 +88,20 @@ class BrainApiClient:
|
|
|
82
88
|
|
|
83
89
|
def log(self, message: str, level: str = "INFO"):
|
|
84
90
|
"""Log messages to stderr to avoid MCP protocol interference."""
|
|
85
|
-
|
|
91
|
+
try:
|
|
92
|
+
# Try to print with original message first
|
|
93
|
+
print(f"[{level}] {message}", file=sys.stderr)
|
|
94
|
+
except UnicodeEncodeError:
|
|
95
|
+
# Fallback: remove problematic characters and try again
|
|
96
|
+
try:
|
|
97
|
+
safe_message = message.encode('ascii', 'ignore').decode('ascii')
|
|
98
|
+
print(f"[{level}] {safe_message}", file=sys.stderr)
|
|
99
|
+
except Exception:
|
|
100
|
+
# Final fallback: just print the level and a safe message
|
|
101
|
+
print(f"[{level}] Log message", file=sys.stderr)
|
|
102
|
+
except Exception:
|
|
103
|
+
# Final fallback: just print the level and a safe message
|
|
104
|
+
print(f"[{level}] Log message", file=sys.stderr)
|
|
86
105
|
|
|
87
106
|
async def authenticate(self, email: str, password: str) -> Dict[str, Any]:
|
|
88
107
|
"""Authenticate with WorldQuant BRAIN platform with biometric support."""
|
|
@@ -716,68 +735,75 @@ class BrainApiClient:
|
|
|
716
735
|
|
|
717
736
|
from typing import Tuple
|
|
718
737
|
def process_description(desc: str, message_id: str) -> Tuple[str, List[str]]:
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
save_dir.mkdir(parents=True, exist_ok=True)
|
|
739
|
-
except Exception as e:
|
|
740
|
-
self.log(f"Could not create image save directory: {e}", "WARNING")
|
|
741
|
-
|
|
742
|
-
new_desc = desc
|
|
743
|
-
for idx, match in enumerate(matches, start=1):
|
|
744
|
-
data_uri = match.group(1) # data:image/...;base64,XXXX
|
|
745
|
-
if not data_uri.lower().startswith("data:image"):
|
|
746
|
-
continue
|
|
747
|
-
# Split header and base64 payload
|
|
748
|
-
if "," not in data_uri:
|
|
749
|
-
continue
|
|
750
|
-
header, b64_data = data_uri.split(",", 1)
|
|
751
|
-
mime_part = header.split(";")[0] # data:image/png
|
|
752
|
-
ext = "png"
|
|
753
|
-
if "/" in mime_part:
|
|
754
|
-
ext = mime_part.split("/")[1]
|
|
755
|
-
safe_ext = (ext or "img").split("?")[0]
|
|
756
|
-
placeholder_text = "[Embedded image]"
|
|
757
|
-
if image_handling == "ignore":
|
|
758
|
-
replacement = f"[Image removed: {safe_ext}]"
|
|
759
|
-
elif image_handling == "placeholder":
|
|
760
|
-
# Try decode & save
|
|
761
|
-
file_name = f"{message_id}_{idx}.{safe_ext}"
|
|
762
|
-
file_path = save_dir / file_name
|
|
738
|
+
try:
|
|
739
|
+
if not desc or image_handling == "keep":
|
|
740
|
+
return desc, []
|
|
741
|
+
attachments: List[str] = []
|
|
742
|
+
# Regex to capture full <img ...> tag with data URI
|
|
743
|
+
img_tag_pattern = re.compile(r"<img[^>]+src=\"(data:image/[^\"]+)\"[^>]*>", re.IGNORECASE)
|
|
744
|
+
# Iterate over unique matches to avoid double work
|
|
745
|
+
matches = list(img_tag_pattern.finditer(desc))
|
|
746
|
+
if not matches:
|
|
747
|
+
# Additional heuristic: very long base64-looking token inside quotes followed by </img>
|
|
748
|
+
# (legacy format noted by user sample). Replace with placeholder.
|
|
749
|
+
heuristic_pattern = re.compile(r"([A-Za-z0-9+/]{500,}={0,2})\"\s*</img>")
|
|
750
|
+
if image_handling != "keep" and heuristic_pattern.search(desc):
|
|
751
|
+
placeholder = "[Embedded image removed - large base64 sequence truncated]"
|
|
752
|
+
return heuristic_pattern.sub(placeholder + "</img>", desc), []
|
|
753
|
+
return desc, []
|
|
754
|
+
|
|
755
|
+
# Ensure save directory exists only if we will store something
|
|
756
|
+
if image_handling == "placeholder" and not save_dir.exists():
|
|
763
757
|
try:
|
|
764
|
-
|
|
765
|
-
if len(b64_data) > 7_000_000:
|
|
766
|
-
raise ValueError("Image too large to decode safely")
|
|
767
|
-
with open(file_path, "wb") as f:
|
|
768
|
-
f.write(base64.b64decode(b64_data))
|
|
769
|
-
attachments.append(str(file_path))
|
|
770
|
-
replacement = f"[Image extracted -> {file_path}]"
|
|
758
|
+
save_dir.mkdir(parents=True, exist_ok=True)
|
|
771
759
|
except Exception as e:
|
|
772
|
-
self.log(f"
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
760
|
+
self.log(f"Could not create image save directory: {e}", "WARNING")
|
|
761
|
+
|
|
762
|
+
new_desc = desc
|
|
763
|
+
for idx, match in enumerate(matches, start=1):
|
|
764
|
+
data_uri = match.group(1) # data:image/...;base64,XXXX
|
|
765
|
+
if not data_uri.lower().startswith("data:image"):
|
|
766
|
+
continue
|
|
767
|
+
# Split header and base64 payload
|
|
768
|
+
if "," not in data_uri:
|
|
769
|
+
continue
|
|
770
|
+
header, b64_data = data_uri.split(",", 1)
|
|
771
|
+
mime_part = header.split(";")[0] # data:image/png
|
|
772
|
+
ext = "png"
|
|
773
|
+
if "/" in mime_part:
|
|
774
|
+
ext = mime_part.split("/")[1]
|
|
775
|
+
safe_ext = (ext or "img").split("?")[0]
|
|
776
|
+
placeholder_text = "[Embedded image]"
|
|
777
|
+
if image_handling == "ignore":
|
|
778
|
+
replacement = f"[Image removed: {safe_ext}]"
|
|
779
|
+
elif image_handling == "placeholder":
|
|
780
|
+
# Try decode & save
|
|
781
|
+
file_name = f"{message_id}_{idx}.{safe_ext}"
|
|
782
|
+
file_path = save_dir / file_name
|
|
783
|
+
try:
|
|
784
|
+
# Guard extremely large strings (>5MB ~ 6.7M base64 chars) to avoid memory blow
|
|
785
|
+
if len(b64_data) > 7_000_000:
|
|
786
|
+
raise ValueError("Image too large to decode safely")
|
|
787
|
+
with open(file_path, "wb") as f:
|
|
788
|
+
f.write(base64.b64decode(b64_data))
|
|
789
|
+
attachments.append(str(file_path))
|
|
790
|
+
replacement = f"[Image extracted -> {file_path}]"
|
|
791
|
+
except Exception as e:
|
|
792
|
+
self.log(f"Failed to decode embedded image in message {message_id}: {e}", "WARNING")
|
|
793
|
+
replacement = "[Image extraction failed - content omitted]"
|
|
794
|
+
else: # keep
|
|
795
|
+
replacement = placeholder_text # shouldn't be used since early return, but safe
|
|
796
|
+
# Replace only the matched tag (not global) – use re.sub with count=1 on substring slice
|
|
797
|
+
# Safer to operate on new_desc using the exact matched string
|
|
798
|
+
original_tag = match.group(0)
|
|
799
|
+
new_desc = new_desc.replace(original_tag, replacement, 1)
|
|
800
|
+
return new_desc, attachments
|
|
801
|
+
except UnicodeEncodeError as ue:
|
|
802
|
+
self.log(f"Unicode encoding error in process_description: {ue}", "WARNING")
|
|
803
|
+
return desc, []
|
|
804
|
+
except Exception as e:
|
|
805
|
+
self.log(f"Error in process_description: {e}", "WARNING")
|
|
806
|
+
return desc, []
|
|
781
807
|
|
|
782
808
|
try:
|
|
783
809
|
params = {}
|
|
@@ -803,11 +829,18 @@ class BrainApiClient:
|
|
|
803
829
|
else:
|
|
804
830
|
# If changed but no attachments (ignore mode) mark sanitized
|
|
805
831
|
msg['sanitized'] = True
|
|
832
|
+
except UnicodeEncodeError as ue:
|
|
833
|
+
self.log(f"Unicode encoding error sanitizing message {msg.get('id')}: {ue}", "WARNING")
|
|
834
|
+
# Keep original description if encoding fails
|
|
835
|
+
continue
|
|
806
836
|
except Exception as inner_e:
|
|
807
837
|
self.log(f"Failed to sanitize message {msg.get('id')}: {inner_e}", "WARNING")
|
|
808
838
|
data['results'] = results
|
|
809
839
|
data['image_handling'] = image_handling
|
|
810
840
|
return data
|
|
841
|
+
except UnicodeEncodeError as ue:
|
|
842
|
+
self.log(f"Failed to get messages due to encoding error: {str(ue)}", "ERROR")
|
|
843
|
+
raise
|
|
811
844
|
except Exception as e:
|
|
812
845
|
self.log(f"Failed to get messages: {str(e)}", "ERROR")
|
|
813
846
|
raise
|
|
@@ -2110,9 +2143,9 @@ async def get_documentations() -> Dict[str, Any]:
|
|
|
2110
2143
|
# get_messages_summary MCP tool removed as requested
|
|
2111
2144
|
|
|
2112
2145
|
@mcp.tool()
|
|
2113
|
-
async def get_messages(limit: Optional[int] =
|
|
2146
|
+
async def get_messages(limit: Optional[int] = 0, offset: int = 0) -> Dict[str, Any]:
|
|
2114
2147
|
"""
|
|
2115
|
-
|
|
2148
|
+
Get messages for the current user with optional pagination.
|
|
2116
2149
|
|
|
2117
2150
|
Args:
|
|
2118
2151
|
limit: Maximum number of messages to return (e.g., 10 for top 10 messages)
|
|
@@ -2123,6 +2156,7 @@ async def get_messages(limit: Optional[int] = None, offset: int = 0) -> Dict[str
|
|
|
2123
2156
|
Returns:
|
|
2124
2157
|
Messages for the current user, optionally limited by count
|
|
2125
2158
|
"""
|
|
2159
|
+
# Wrap the entire function in a try-catch to handle any encoding issues
|
|
2126
2160
|
try:
|
|
2127
2161
|
# Enhanced parameter validation and conversion
|
|
2128
2162
|
validated_limit = None
|
|
@@ -2162,8 +2196,11 @@ async def get_messages(limit: Optional[int] = None, offset: int = 0) -> Dict[str
|
|
|
2162
2196
|
if validated_offset < 0:
|
|
2163
2197
|
return {"error": f"Offset must be non-negative, got: {offset}"}
|
|
2164
2198
|
|
|
2165
|
-
# Log the validated parameters for debugging
|
|
2166
|
-
|
|
2199
|
+
# Log the validated parameters for debugging (without emojis to avoid encoding issues)
|
|
2200
|
+
try:
|
|
2201
|
+
print(f"get_messages called with validated parameters: limit={validated_limit}, offset={validated_offset}")
|
|
2202
|
+
except Exception:
|
|
2203
|
+
print(f"get_messages called with parameters: limit={validated_limit}, offset={validated_offset}")
|
|
2167
2204
|
|
|
2168
2205
|
# Call the brain client with validated parameters
|
|
2169
2206
|
result = await brain_client.get_messages(validated_limit, validated_offset)
|
|
@@ -2183,11 +2220,32 @@ async def get_messages(limit: Optional[int] = None, offset: int = 0) -> Dict[str
|
|
|
2183
2220
|
|
|
2184
2221
|
return result
|
|
2185
2222
|
|
|
2223
|
+
except UnicodeEncodeError as ue:
|
|
2224
|
+
# Handle encoding errors specifically
|
|
2225
|
+
error_msg = f"get_messages failed due to encoding error: {str(ue)}"
|
|
2226
|
+
try:
|
|
2227
|
+
print(f"ENCODING ERROR: {error_msg}")
|
|
2228
|
+
except Exception:
|
|
2229
|
+
print(f"get_messages encoding error: {str(ue)}")
|
|
2230
|
+
return {
|
|
2231
|
+
"error": error_msg,
|
|
2232
|
+
"error_type": "UnicodeEncodeError",
|
|
2233
|
+
"original_params": {
|
|
2234
|
+
"limit": limit,
|
|
2235
|
+
"offset": offset,
|
|
2236
|
+
"limit_type": str(type(limit)),
|
|
2237
|
+
"offset_type": str(type(offset))
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2186
2240
|
except Exception as e:
|
|
2187
2241
|
error_msg = f"get_messages failed: {str(e)}"
|
|
2188
|
-
|
|
2242
|
+
try:
|
|
2243
|
+
print(f"ERROR: {error_msg}")
|
|
2244
|
+
except Exception:
|
|
2245
|
+
print(f"get_messages failed: {str(e)}")
|
|
2189
2246
|
return {
|
|
2190
2247
|
"error": error_msg,
|
|
2248
|
+
"error_type": type(e).__name__,
|
|
2191
2249
|
"original_params": {
|
|
2192
2250
|
"limit": limit,
|
|
2193
2251
|
"offset": offset,
|
|
@@ -2689,24 +2747,28 @@ async def get_daily_and_quarterly_payment(email: str = "", password: str = "") -
|
|
|
2689
2747
|
if not config.get('is_authenticated'):
|
|
2690
2748
|
return {"error": "Not authenticated. Please provide email and password or authenticate first."}
|
|
2691
2749
|
|
|
2750
|
+
|
|
2751
|
+
# Set required Accept header for API v3.0
|
|
2752
|
+
header = {"Accept": "application/json;version=3.0"}
|
|
2753
|
+
|
|
2692
2754
|
# Get base payment data
|
|
2693
2755
|
base_payment_response = brain_client.session.get(
|
|
2694
|
-
'https://api.worldquantbrain.com/users/self/activities/base-payment'
|
|
2756
|
+
'https://api.worldquantbrain.com/users/self/activities/base-payment', headers=header
|
|
2695
2757
|
)
|
|
2696
|
-
|
|
2758
|
+
|
|
2697
2759
|
if base_payment_response.status_code != 200:
|
|
2698
2760
|
return {"error": f"Failed to get base payment data: {base_payment_response.status_code}"}
|
|
2699
|
-
|
|
2761
|
+
|
|
2700
2762
|
base_payment_data = base_payment_response.json()
|
|
2701
|
-
|
|
2763
|
+
|
|
2702
2764
|
# Get other payment data
|
|
2703
2765
|
other_payment_response = brain_client.session.get(
|
|
2704
|
-
'https://api.worldquantbrain.com/users/self/activities/other-payment'
|
|
2766
|
+
'https://api.worldquantbrain.com/users/self/activities/other-payment', headers=header
|
|
2705
2767
|
)
|
|
2706
|
-
|
|
2768
|
+
|
|
2707
2769
|
if other_payment_response.status_code != 200:
|
|
2708
2770
|
return {"error": f"Failed to get other payment data: {other_payment_response.status_code}"}
|
|
2709
|
-
|
|
2771
|
+
|
|
2710
2772
|
other_payment_data = other_payment_response.json()
|
|
2711
2773
|
|
|
2712
2774
|
# Return comprehensive payment information
|
|
@@ -2780,6 +2842,10 @@ async def get_SimError_detail(locations: Sequence[str]) -> dict:
|
|
|
2780
2842
|
return {"results": results}
|
|
2781
2843
|
|
|
2782
2844
|
if __name__ == "__main__":
|
|
2783
|
-
|
|
2784
|
-
|
|
2845
|
+
try:
|
|
2846
|
+
print("WorldQuant BRAIN MCP Server Starting...", file=sys.stderr)
|
|
2847
|
+
mcp.run()
|
|
2848
|
+
except Exception as e:
|
|
2849
|
+
print(f"Failed to start MCP server: {e}", file=sys.stderr)
|
|
2850
|
+
sys.exit(1)
|
|
2785
2851
|
|
|
@@ -13,7 +13,7 @@ def read_requirements():
|
|
|
13
13
|
|
|
14
14
|
setup(
|
|
15
15
|
name="cnhkmcp",
|
|
16
|
-
version="1.4.
|
|
16
|
+
version="1.4.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
|
|
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
|
|
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
|
|
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
|
{cnhkmcp-1.4.1 → cnhkmcp-1.4.3}/cnhkmcp/untracked/__pycache__/forum_functions.cpython-313.pyc
RENAMED
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|