cnhkmcp 1.4.1__tar.gz → 1.4.2__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.
Files changed (76) hide show
  1. {cnhkmcp-1.4.1/cnhkmcp.egg-info → cnhkmcp-1.4.2}/PKG-INFO +1 -1
  2. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/__init__.py +1 -1
  3. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/platform_functions.py +132 -70
  4. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2/cnhkmcp.egg-info}/PKG-INFO +1 -1
  5. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/setup.py +1 -1
  6. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/LICENSE +0 -0
  7. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/MANIFEST.in +0 -0
  8. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/README.md +0 -0
  9. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/.gitignore +0 -0
  10. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/MODULAR_STRUCTURE.md +0 -0
  11. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/README.md +0 -0
  12. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/__pycache__/app.cpython-313.pyc +0 -0
  13. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/__init__.py +0 -0
  14. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/__pycache__/__init__.cpython-313.pyc +0 -0
  15. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/__pycache__/feature_engineering.cpython-313.pyc +0 -0
  16. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/__pycache__/idea_house.cpython-313.pyc +0 -0
  17. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/__pycache__/inspiration_house.cpython-313.pyc +0 -0
  18. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/__pycache__/paper_analysis.cpython-313.pyc +0 -0
  19. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/__pycache__/simulator.cpython-313.pyc +0 -0
  20. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/__pycache__/unified_tools.cpython-313.pyc +0 -0
  21. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/__pycache__/wqb_simulator.cpython-313.pyc +0 -0
  22. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/feature_engineering.py +0 -0
  23. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/idea_house.py +0 -0
  24. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/inspiration_house.py +0 -0
  25. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/blueprints/paper_analysis.py +0 -0
  26. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/custom_templates/templates.json +0 -0
  27. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/hkSimulator/ace_lib.py +0 -0
  28. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/hkSimulator/autosimulator.py +0 -0
  29. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/hkSimulator/helpful_functions.py +0 -0
  30. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/mirror_config.txt +0 -0
  31. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/operaters.csv +0 -0
  32. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/requirements.txt +0 -0
  33. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/run_app.bat +0 -0
  34. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/run_app.sh +0 -0
  35. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/setup_tsinghua.bat +0 -0
  36. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/setup_tsinghua.sh +0 -0
  37. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/simulator/__pycache__/simulator_wqb.cpython-313.pyc +0 -0
  38. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/simulator/alpha_submitter.py +0 -0
  39. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/simulator/simulator_wqb.py +0 -0
  40. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/ssrn-3332513.pdf +0 -0
  41. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/static/brain.js +0 -0
  42. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/static/decoder.js +0 -0
  43. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/static/feature_engineering.js +0 -0
  44. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/static/idea_house.js +0 -0
  45. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/static/inspiration_house.js +0 -0
  46. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/static/paper_analysis.js +0 -0
  47. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/static/script.js +0 -0
  48. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/static/simulator.js +0 -0
  49. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/static/styles.css +0 -0
  50. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/templates/feature_engineering.html +0 -0
  51. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/templates/idea_house.html +0 -0
  52. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/templates/index.html +0 -0
  53. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/templates/inspiration_house.html +0 -0
  54. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/templates/paper_analysis.html +0 -0
  55. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP/templates/simulator.html +0 -0
  56. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/APP//350/277/220/350/241/214/346/211/223/345/274/200/346/210/221.py" +0 -0
  57. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/__pycache__/forum_functions.cpython-313.pyc +0 -0
  58. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/arXiv_API_Tool_Manual.md +0 -0
  59. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/arxiv_api.py +0 -0
  60. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/forum_functions.py +0 -0
  61. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/sample_mcp_config.json +0 -0
  62. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked/user_config.json +0 -0
  63. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/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
  64. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_Alpha_explaination_workflow.md" +0 -0
  65. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/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
  66. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_BRAIN_Alpha_Improvement_Workflow.md" +0 -0
  67. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_Dataset_Exploration_Expert_Manual.md" +0 -0
  68. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_daily_report_workflow.md" +0 -0
  69. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp.egg-info/SOURCES.txt +0 -0
  70. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp.egg-info/dependency_links.txt +0 -0
  71. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp.egg-info/entry_points.txt +0 -0
  72. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp.egg-info/not-zip-safe +0 -0
  73. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp.egg-info/requires.txt +0 -0
  74. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/cnhkmcp.egg-info/top_level.txt +0 -0
  75. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/requirements.txt +0 -0
  76. {cnhkmcp-1.4.1 → cnhkmcp-1.4.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cnhkmcp
3
- Version: 1.4.1
3
+ Version: 1.4.2
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.4.1"
53
+ __version__ = "1.4.2"
54
54
  __author__ = "CNHK"
55
55
  __email__ = "cnhk@example.com"
56
56
 
@@ -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
- print(f"[{level}] {message}", file=sys.stderr)
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
- if not desc or image_handling == "keep":
720
- return desc, []
721
- attachments: List[str] = []
722
- # Regex to capture full <img ...> tag with data URI
723
- img_tag_pattern = re.compile(r"<img[^>]+src=\"(data:image/[^\"]+)\"[^>]*>", re.IGNORECASE)
724
- # Iterate over unique matches to avoid double work
725
- matches = list(img_tag_pattern.finditer(desc))
726
- if not matches:
727
- # Additional heuristic: very long base64-looking token inside quotes followed by </img>
728
- # (legacy format noted by user sample). Replace with placeholder.
729
- heuristic_pattern = re.compile(r"([A-Za-z0-9+/]{500,}={0,2})\"\s*</img>")
730
- if image_handling != "keep" and heuristic_pattern.search(desc):
731
- placeholder = "[Embedded image removed - large base64 sequence truncated]"
732
- return heuristic_pattern.sub(placeholder + "</img>", desc), []
733
- return desc, []
734
-
735
- # Ensure save directory exists only if we will store something
736
- if image_handling == "placeholder" and not save_dir.exists():
737
- try:
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
- # Guard extremely large strings (>5MB ~ 6.7M base64 chars) to avoid memory blow
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"Failed to decode embedded image in message {message_id}: {e}", "WARNING")
773
- replacement = "[Image extraction failed - content omitted]"
774
- else: # keep
775
- replacement = placeholder_text # shouldn't be used since early return, but safe
776
- # Replace only the matched tag (not global) – use re.sub with count=1 on substring slice
777
- # Safer to operate on new_desc using the exact matched string
778
- original_tag = match.group(0)
779
- new_desc = new_desc.replace(original_tag, replacement, 1)
780
- return new_desc, attachments
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] = None, offset: int = 0) -> Dict[str, Any]:
2146
+ async def get_messages(limit: Optional[int] = 0, offset: int = 0) -> Dict[str, Any]:
2114
2147
  """
2115
- 💬 Get messages for the current user with optional pagination.
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
- print(f"🔍 get_messages called with validated parameters: limit={validated_limit}, offset={validated_offset}")
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
- print(f"❌ {error_msg}")
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,
@@ -2780,6 +2838,10 @@ async def get_SimError_detail(locations: Sequence[str]) -> dict:
2780
2838
  return {"results": results}
2781
2839
 
2782
2840
  if __name__ == "__main__":
2783
- print("WorldQuant BRAIN MCP Server Starting...", file=sys.stderr)
2784
- mcp.run()
2841
+ try:
2842
+ print("WorldQuant BRAIN MCP Server Starting...", file=sys.stderr)
2843
+ mcp.run()
2844
+ except Exception as e:
2845
+ print(f"Failed to start MCP server: {e}", file=sys.stderr)
2846
+ sys.exit(1)
2785
2847
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cnhkmcp
3
- Version: 1.4.1
3
+ Version: 1.4.2
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.4.1",
16
+ version="1.4.2",
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