futu-stock-mcp-server 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl
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.
Potentially problematic release.
This version of futu-stock-mcp-server might be problematic. Click here for more details.
- futu_stock_mcp_server/server.py +167 -40
- {futu_stock_mcp_server-0.1.1.dist-info → futu_stock_mcp_server-0.1.3.dist-info}/METADATA +42 -26
- futu_stock_mcp_server-0.1.3.dist-info/RECORD +7 -0
- futu_stock_mcp_server-0.1.1.dist-info/RECORD +0 -7
- {futu_stock_mcp_server-0.1.1.dist-info → futu_stock_mcp_server-0.1.3.dist-info}/WHEEL +0 -0
- {futu_stock_mcp_server-0.1.1.dist-info → futu_stock_mcp_server-0.1.3.dist-info}/entry_points.txt +0 -0
- {futu_stock_mcp_server-0.1.1.dist-info → futu_stock_mcp_server-0.1.3.dist-info}/licenses/LICENSE +0 -0
futu_stock_mcp_server/server.py
CHANGED
|
@@ -8,15 +8,18 @@ from loguru import logger
|
|
|
8
8
|
import os
|
|
9
9
|
import sys
|
|
10
10
|
from dotenv import load_dotenv
|
|
11
|
-
from mcp.server.fastmcp import FastMCP
|
|
11
|
+
from mcp.server.fastmcp import FastMCP, Context
|
|
12
12
|
from mcp.types import TextContent, PromptMessage
|
|
13
13
|
from mcp.server import Server
|
|
14
|
+
from mcp.server.session import ServerSession
|
|
14
15
|
import atexit
|
|
15
16
|
import signal
|
|
16
17
|
import fcntl
|
|
17
18
|
import psutil
|
|
18
19
|
import time
|
|
19
20
|
from datetime import datetime
|
|
21
|
+
import logging
|
|
22
|
+
import warnings
|
|
20
23
|
|
|
21
24
|
# Get the project root directory and add it to Python path
|
|
22
25
|
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
@@ -26,30 +29,104 @@ sys.path.insert(0, project_root)
|
|
|
26
29
|
env_path = os.path.join(project_root, '.env')
|
|
27
30
|
load_dotenv(env_path)
|
|
28
31
|
|
|
29
|
-
# Configure logging
|
|
30
|
-
|
|
32
|
+
# CRITICAL: Configure logging to be MCP-compatible
|
|
33
|
+
# According to MCP best practices, we should:
|
|
34
|
+
# 1. Never write to stdout (reserved for MCP JSON communication)
|
|
35
|
+
# 2. Use file logging for debugging
|
|
36
|
+
# 3. Use MCP Context for operational logging when available
|
|
37
|
+
# 4. Suppress third-party library logs that might pollute output
|
|
38
|
+
|
|
39
|
+
# Completely silence warnings and third-party logs
|
|
40
|
+
warnings.filterwarnings("ignore")
|
|
41
|
+
|
|
42
|
+
# Configure loguru for file-only logging
|
|
43
|
+
logger.remove() # Remove all default handlers
|
|
31
44
|
|
|
32
45
|
# Get the project root directory
|
|
33
46
|
log_dir = os.path.join(project_root, "logs")
|
|
34
47
|
os.makedirs(log_dir, exist_ok=True)
|
|
35
48
|
|
|
36
|
-
# Add file handler
|
|
49
|
+
# Add file handler only - NO console output to avoid MCP communication interference
|
|
37
50
|
logger.add(
|
|
38
51
|
os.path.join(log_dir, "futu_server.log"),
|
|
39
52
|
rotation="500 MB",
|
|
40
53
|
retention="10 days",
|
|
41
54
|
level="DEBUG",
|
|
42
|
-
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}"
|
|
55
|
+
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {name} | {message}",
|
|
56
|
+
enqueue=True, # Thread-safe logging
|
|
57
|
+
backtrace=True,
|
|
58
|
+
diagnose=True
|
|
43
59
|
)
|
|
44
60
|
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
# Only add stderr logging if explicitly in debug mode and not in MCP mode
|
|
62
|
+
if os.getenv('FUTU_DEBUG_MODE') == '1' and not os.getenv('MCP_MODE'):
|
|
63
|
+
logger.add(
|
|
64
|
+
sys.stderr,
|
|
65
|
+
level="INFO",
|
|
66
|
+
format="{time:HH:mm:ss} | {level} | {message}",
|
|
67
|
+
colorize=False,
|
|
68
|
+
filter=lambda record: record["level"].name in ["INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Suppress all third-party library logging to prevent stdout pollution
|
|
72
|
+
logging.disable(logging.CRITICAL)
|
|
73
|
+
|
|
74
|
+
# Set up null handlers for problematic loggers
|
|
75
|
+
class NullHandler(logging.Handler):
|
|
76
|
+
def emit(self, record):
|
|
77
|
+
pass
|
|
51
78
|
|
|
52
|
-
|
|
79
|
+
null_handler = NullHandler()
|
|
80
|
+
root_logger = logging.getLogger()
|
|
81
|
+
root_logger.addHandler(null_handler)
|
|
82
|
+
root_logger.setLevel(logging.CRITICAL + 1)
|
|
83
|
+
|
|
84
|
+
# Specifically silence known problematic loggers
|
|
85
|
+
for logger_name in [
|
|
86
|
+
'mcp', 'fastmcp', 'futu', 'uvicorn', 'asyncio',
|
|
87
|
+
'websockets', 'aiohttp', 'urllib3', 'requests'
|
|
88
|
+
]:
|
|
89
|
+
lib_logger = logging.getLogger(logger_name)
|
|
90
|
+
lib_logger.disabled = True
|
|
91
|
+
lib_logger.addHandler(null_handler)
|
|
92
|
+
lib_logger.setLevel(logging.CRITICAL + 1)
|
|
93
|
+
lib_logger.propagate = False
|
|
94
|
+
|
|
95
|
+
# MCP-compatible logging helper functions
|
|
96
|
+
async def log_to_mcp(ctx: Context, level: str, message: str):
|
|
97
|
+
"""Send log message through MCP Context when available"""
|
|
98
|
+
try:
|
|
99
|
+
if level.upper() == "DEBUG":
|
|
100
|
+
await ctx.debug(message)
|
|
101
|
+
elif level.upper() == "INFO":
|
|
102
|
+
await ctx.info(message)
|
|
103
|
+
elif level.upper() == "WARNING":
|
|
104
|
+
await ctx.warning(message)
|
|
105
|
+
elif level.upper() == "ERROR":
|
|
106
|
+
await ctx.error(message)
|
|
107
|
+
else:
|
|
108
|
+
await ctx.info(f"[{level}] {message}")
|
|
109
|
+
except Exception:
|
|
110
|
+
# Fallback to file logging if MCP context fails
|
|
111
|
+
logger.log(level.upper(), message)
|
|
112
|
+
|
|
113
|
+
def safe_log(level: str, message: str, ctx: Context = None):
|
|
114
|
+
"""Safe logging that uses MCP context when available, file logging otherwise"""
|
|
115
|
+
# Always log to file
|
|
116
|
+
logger.log(level.upper(), message)
|
|
117
|
+
|
|
118
|
+
# Also send to MCP if context is available
|
|
119
|
+
if ctx:
|
|
120
|
+
try:
|
|
121
|
+
import asyncio
|
|
122
|
+
loop = asyncio.get_event_loop()
|
|
123
|
+
if loop.is_running():
|
|
124
|
+
asyncio.create_task(log_to_mcp(ctx, level, message))
|
|
125
|
+
except Exception:
|
|
126
|
+
pass # Ignore MCP logging errors
|
|
127
|
+
|
|
128
|
+
logger.info(f"Starting Futu MCP Server with log directory: {log_dir}")
|
|
129
|
+
logger.info("Logging configured for MCP compatibility - stdout reserved for JSON communication")
|
|
53
130
|
|
|
54
131
|
# PID file path
|
|
55
132
|
PID_FILE = os.path.join(project_root, '.futu_mcp.pid')
|
|
@@ -404,7 +481,7 @@ def handle_return_data(ret: int, data: Any) -> Dict[str, Any]:
|
|
|
404
481
|
|
|
405
482
|
# Market Data Tools
|
|
406
483
|
@mcp.tool()
|
|
407
|
-
async def get_stock_quote(symbols: List[str]) -> Dict[str, Any]:
|
|
484
|
+
async def get_stock_quote(symbols: List[str], ctx: Context[ServerSession, None] = None) -> Dict[str, Any]:
|
|
408
485
|
"""Get stock quote data for given symbols
|
|
409
486
|
|
|
410
487
|
Args:
|
|
@@ -448,21 +525,32 @@ async def get_stock_quote(symbols: List[str]) -> Dict[str, Any]:
|
|
|
448
525
|
- Consider actual needs when selecting stocks
|
|
449
526
|
- Handle exceptions properly
|
|
450
527
|
"""
|
|
451
|
-
|
|
452
|
-
if ret != RET_OK:
|
|
453
|
-
return {'error': str(data)}
|
|
528
|
+
safe_log("info", f"Getting stock quotes for symbols: {symbols}", ctx)
|
|
454
529
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
result = {
|
|
462
|
-
'quote_list': data
|
|
463
|
-
}
|
|
530
|
+
try:
|
|
531
|
+
ret, data = quote_ctx.get_stock_quote(symbols)
|
|
532
|
+
if ret != RET_OK:
|
|
533
|
+
error_msg = f"Failed to get stock quote: {str(data)}"
|
|
534
|
+
safe_log("error", error_msg, ctx)
|
|
535
|
+
return {'error': error_msg}
|
|
464
536
|
|
|
465
|
-
|
|
537
|
+
# Convert DataFrame to dict if necessary
|
|
538
|
+
if hasattr(data, 'to_dict'):
|
|
539
|
+
result = {
|
|
540
|
+
'quote_list': data.to_dict('records')
|
|
541
|
+
}
|
|
542
|
+
else:
|
|
543
|
+
result = {
|
|
544
|
+
'quote_list': data
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
safe_log("info", f"Successfully retrieved quotes for {len(symbols)} symbols", ctx)
|
|
548
|
+
return result
|
|
549
|
+
|
|
550
|
+
except Exception as e:
|
|
551
|
+
error_msg = f"Exception in get_stock_quote: {str(e)}"
|
|
552
|
+
safe_log("error", error_msg, ctx)
|
|
553
|
+
return {'error': error_msg}
|
|
466
554
|
|
|
467
555
|
@mcp.tool()
|
|
468
556
|
async def get_market_snapshot(symbols: List[str]) -> Dict[str, Any]:
|
|
@@ -1119,12 +1207,29 @@ async def get_option_butterfly(symbol: str, expiry: str, strike_price: float) ->
|
|
|
1119
1207
|
|
|
1120
1208
|
# Account Query Tools
|
|
1121
1209
|
@mcp.tool()
|
|
1122
|
-
async def get_account_list() -> Dict[str, Any]:
|
|
1210
|
+
async def get_account_list(ctx: Context[ServerSession, None] = None) -> Dict[str, Any]:
|
|
1123
1211
|
"""Get account list"""
|
|
1212
|
+
safe_log("info", "Attempting to get account list", ctx)
|
|
1213
|
+
|
|
1124
1214
|
if not init_trade_connection():
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1215
|
+
error_msg = 'Failed to initialize trade connection'
|
|
1216
|
+
safe_log("error", error_msg, ctx)
|
|
1217
|
+
return {'error': error_msg}
|
|
1218
|
+
|
|
1219
|
+
try:
|
|
1220
|
+
ret, data = trade_ctx.get_acc_list()
|
|
1221
|
+
result = handle_return_data(ret, data)
|
|
1222
|
+
|
|
1223
|
+
if 'error' not in result:
|
|
1224
|
+
safe_log("info", "Successfully retrieved account list", ctx)
|
|
1225
|
+
else:
|
|
1226
|
+
safe_log("error", f"Failed to get account list: {result['error']}", ctx)
|
|
1227
|
+
|
|
1228
|
+
return result
|
|
1229
|
+
except Exception as e:
|
|
1230
|
+
error_msg = f"Exception in get_account_list: {str(e)}"
|
|
1231
|
+
safe_log("error", error_msg, ctx)
|
|
1232
|
+
return {'error': error_msg}
|
|
1128
1233
|
|
|
1129
1234
|
@mcp.tool()
|
|
1130
1235
|
async def get_funds() -> Dict[str, Any]:
|
|
@@ -1450,39 +1555,61 @@ async def get_current_time() -> Dict[str, Any]:
|
|
|
1450
1555
|
def main():
|
|
1451
1556
|
"""Main entry point for the futu-mcp-server command."""
|
|
1452
1557
|
try:
|
|
1453
|
-
#
|
|
1558
|
+
# CRITICAL: Set MCP mode BEFORE any logging to ensure clean stdout
|
|
1559
|
+
os.environ['MCP_MODE'] = '1'
|
|
1560
|
+
|
|
1561
|
+
# Ensure no color output or ANSI escape sequences in MCP mode
|
|
1562
|
+
os.environ['NO_COLOR'] = '1'
|
|
1563
|
+
os.environ['TERM'] = 'dumb'
|
|
1564
|
+
os.environ['FORCE_COLOR'] = '0'
|
|
1565
|
+
os.environ['COLORTERM'] = ''
|
|
1566
|
+
os.environ['ANSI_COLORS_DISABLED'] = '1'
|
|
1567
|
+
|
|
1568
|
+
# Disable Python buffering to ensure clean MCP JSON communication
|
|
1569
|
+
os.environ['PYTHONUNBUFFERED'] = '1'
|
|
1570
|
+
os.environ['PYTHONIOENCODING'] = 'utf-8'
|
|
1571
|
+
|
|
1572
|
+
# According to MCP best practices:
|
|
1573
|
+
# - stdout is RESERVED for MCP JSON communication only
|
|
1574
|
+
# - All logging should go to files
|
|
1575
|
+
# - No stderr output in production mode to avoid pollution
|
|
1576
|
+
|
|
1577
|
+
# Clean up stale processes and acquire lock
|
|
1454
1578
|
cleanup_stale_processes()
|
|
1455
1579
|
|
|
1456
|
-
# 获取锁
|
|
1457
1580
|
lock_fd = acquire_lock()
|
|
1458
1581
|
if lock_fd is None:
|
|
1582
|
+
# Use file logging only - no stderr output in MCP mode
|
|
1459
1583
|
logger.error("Failed to acquire lock. Another instance may be running.")
|
|
1460
1584
|
sys.exit(1)
|
|
1461
1585
|
|
|
1462
|
-
#
|
|
1586
|
+
# Set up signal handlers
|
|
1463
1587
|
signal.signal(signal.SIGINT, signal_handler)
|
|
1464
1588
|
signal.signal(signal.SIGTERM, signal_handler)
|
|
1465
1589
|
|
|
1466
|
-
|
|
1590
|
+
# Initialize Futu connection with file logging only
|
|
1591
|
+
logger.info("Initializing Futu connection for MCP server...")
|
|
1467
1592
|
if init_futu_connection():
|
|
1468
1593
|
logger.info("Successfully initialized Futu connection")
|
|
1469
|
-
logger.info("Starting MCP server in stdio mode
|
|
1470
|
-
|
|
1594
|
+
logger.info("Starting MCP server in stdio mode - stdout reserved for JSON communication")
|
|
1595
|
+
|
|
1471
1596
|
try:
|
|
1597
|
+
# Run MCP server - stdout will be used for JSON communication only
|
|
1472
1598
|
mcp.run(transport='stdio')
|
|
1473
1599
|
except KeyboardInterrupt:
|
|
1474
|
-
logger.info("Received keyboard interrupt, shutting down...")
|
|
1600
|
+
logger.info("Received keyboard interrupt, shutting down gracefully...")
|
|
1475
1601
|
cleanup_all()
|
|
1476
1602
|
os._exit(0)
|
|
1477
1603
|
except Exception as e:
|
|
1478
|
-
logger.error(f"Error running server: {str(e)}")
|
|
1604
|
+
logger.error(f"Error running MCP server: {str(e)}")
|
|
1479
1605
|
cleanup_all()
|
|
1480
1606
|
os._exit(1)
|
|
1481
1607
|
else:
|
|
1482
|
-
logger.error("Failed to initialize Futu connection.
|
|
1608
|
+
logger.error("Failed to initialize Futu connection. MCP server will not start.")
|
|
1483
1609
|
os._exit(1)
|
|
1610
|
+
|
|
1484
1611
|
except Exception as e:
|
|
1485
|
-
logger.error(f"Error starting server: {str(e)}")
|
|
1612
|
+
logger.error(f"Error starting MCP server: {str(e)}")
|
|
1486
1613
|
sys.exit(1)
|
|
1487
1614
|
finally:
|
|
1488
1615
|
cleanup_all()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: futu-stock-mcp-server
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: A Model Context Protocol (MCP) server for accessing Futu OpenAPI functionality
|
|
5
5
|
Project-URL: Homepage, https://github.com/shuizhengqi1/futu-stock-mcp-server
|
|
6
6
|
Project-URL: Documentation, https://github.com/shuizhengqi1/futu-stock-mcp-server#readme
|
|
@@ -40,6 +40,8 @@ Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
|
40
40
|
Requires-Dist: ruff; extra == 'dev'
|
|
41
41
|
Description-Content-Type: text/markdown
|
|
42
42
|
|
|
43
|
+
[](https://mseep.ai/app/shuizhengqi1-futu-stocp-mcp-server)
|
|
44
|
+
|
|
43
45
|
# Futu Stock MCP Server
|
|
44
46
|
|
|
45
47
|
[](https://www.python.org)
|
|
@@ -101,16 +103,25 @@ Description-Content-Type: text/markdown
|
|
|
101
103
|
|
|
102
104
|
## 🚀 快速开始
|
|
103
105
|
|
|
104
|
-
### 方式一:通过
|
|
106
|
+
### 方式一:通过 pipx 安装(推荐)
|
|
105
107
|
|
|
106
108
|
```bash
|
|
109
|
+
# 安装 pipx(如果还没有安装)
|
|
110
|
+
brew install pipx # macOS
|
|
111
|
+
# 或者 pip install --user pipx # 其他系统
|
|
112
|
+
|
|
107
113
|
# 安装包
|
|
108
|
-
|
|
114
|
+
pipx install futu-stock-mcp-server
|
|
109
115
|
|
|
110
116
|
# 运行服务器
|
|
111
117
|
futu-mcp-server
|
|
112
118
|
```
|
|
113
119
|
|
|
120
|
+
> **为什么使用 pipx?**
|
|
121
|
+
> - pipx 专门用于安装 Python 应用程序到全局环境
|
|
122
|
+
> - 自动管理独立的虚拟环境,避免依赖冲突
|
|
123
|
+
> - 命令直接可用,无需激活虚拟环境
|
|
124
|
+
|
|
114
125
|
### 方式二:通过 Docker 运行
|
|
115
126
|
|
|
116
127
|
```bash
|
|
@@ -239,12 +250,13 @@ ruff format .
|
|
|
239
250
|
}
|
|
240
251
|
```
|
|
241
252
|
|
|
242
|
-
3.
|
|
253
|
+
3. **故障排除配置**:
|
|
254
|
+
如果上述配置不工作,可以尝试使用完整路径:
|
|
243
255
|
```json
|
|
244
256
|
{
|
|
245
257
|
"mcpServers": {
|
|
246
258
|
"futu-stock": {
|
|
247
|
-
"command": "/
|
|
259
|
+
"command": "/Users/your-username/.local/bin/futu-mcp-server",
|
|
248
260
|
"env": {
|
|
249
261
|
"FUTU_HOST": "127.0.0.1",
|
|
250
262
|
"FUTU_PORT": "11111"
|
|
@@ -254,22 +266,7 @@ ruff format .
|
|
|
254
266
|
}
|
|
255
267
|
```
|
|
256
268
|
|
|
257
|
-
|
|
258
|
-
```json
|
|
259
|
-
{
|
|
260
|
-
"mcpServers": {
|
|
261
|
-
"futu-stock": {
|
|
262
|
-
"command": "python",
|
|
263
|
-
"args": ["-m", "futu_stock_mcp_server.server"],
|
|
264
|
-
"cwd": "/path/to/futu-stock-mcp-server",
|
|
265
|
-
"env": {
|
|
266
|
-
"FUTU_HOST": "127.0.0.1",
|
|
267
|
-
"FUTU_PORT": "11111"
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
```
|
|
269
|
+
> **提示**:使用 `which futu-mcp-server` 命令查看完整路径
|
|
273
270
|
|
|
274
271
|
### 在其他 MCP 客户端中配置
|
|
275
272
|
|
|
@@ -363,10 +360,13 @@ LOG_LEVEL=INFO
|
|
|
363
360
|
#### 1. 命令 `futu-mcp-server` 找不到
|
|
364
361
|
```bash
|
|
365
362
|
# 确保已正确安装
|
|
366
|
-
|
|
363
|
+
pipx install futu-stock-mcp-server
|
|
367
364
|
|
|
368
|
-
#
|
|
365
|
+
# 检查命令是否可用
|
|
369
366
|
which futu-mcp-server
|
|
367
|
+
|
|
368
|
+
# 如果还是找不到,检查 PATH
|
|
369
|
+
echo $PATH | grep -o '[^:]*\.local/bin[^:]*'
|
|
370
370
|
```
|
|
371
371
|
|
|
372
372
|
#### 2. Ctrl+C 无法退出服务器
|
|
@@ -399,13 +399,29 @@ python -m futu_stock_mcp_server.server
|
|
|
399
399
|
```
|
|
400
400
|
|
|
401
401
|
### 日志调试
|
|
402
|
-
|
|
402
|
+
|
|
403
|
+
本项目已根据 [MCP 官方文档](https://github.com/modelcontextprotocol/python-sdk) 的最佳实践配置了日志系统:
|
|
404
|
+
|
|
405
|
+
#### MCP 兼容的日志配置
|
|
406
|
+
- **文件日志**: 所有日志写入 `logs/futu_server.log`,自动轮转和清理
|
|
407
|
+
- **MCP Context 日志**: 工具执行期间通过 MCP Context 发送日志给客户端
|
|
408
|
+
- **stdout 保护**: 确保 stdout 仅用于 MCP JSON 通信,避免污染
|
|
409
|
+
|
|
410
|
+
#### 调试模式(仅开发时使用)
|
|
403
411
|
```bash
|
|
404
|
-
|
|
412
|
+
# 启用调试模式(会向 stderr 输出日志)
|
|
413
|
+
export FUTU_DEBUG_MODE=1
|
|
405
414
|
futu-mcp-server
|
|
406
415
|
```
|
|
407
416
|
|
|
408
|
-
|
|
417
|
+
**注意**: 在 MCP 客户端中不要启用调试模式,因为它会向 stderr 输出日志。
|
|
418
|
+
|
|
419
|
+
#### 日志文件位置
|
|
420
|
+
- 主日志文件:`./logs/futu_server.log`
|
|
421
|
+
- 自动轮转:500 MB 后轮转
|
|
422
|
+
- 自动清理:保留 10 天
|
|
423
|
+
|
|
424
|
+
详细的日志配置说明请参考 [docs/LOGGING.md](docs/LOGGING.md)。
|
|
409
425
|
tools = await session.list_tools()
|
|
410
426
|
|
|
411
427
|
# Call a tool
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
futu_stock_mcp_server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
futu_stock_mcp_server/server.py,sha256=d03R4mc1eKNNX1JhYDnbj59NRN12O8XG_oc_T8mNVJU,57336
|
|
3
|
+
futu_stock_mcp_server-0.1.3.dist-info/METADATA,sha256=PeSU1LZfoEXaThL6W-ZUhQMr7VR9JcWNQp6M6i56Or8,21205
|
|
4
|
+
futu_stock_mcp_server-0.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
5
|
+
futu_stock_mcp_server-0.1.3.dist-info/entry_points.txt,sha256=GAdKqPJD9dJ_fRA3e3m0NRia0elN5OcjEeAI30vOcIM,70
|
|
6
|
+
futu_stock_mcp_server-0.1.3.dist-info/licenses/LICENSE,sha256=XQBSQkjjpkymu_uLdyis4oNynV60VH1X7nS16uwM6g0,1069
|
|
7
|
+
futu_stock_mcp_server-0.1.3.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
futu_stock_mcp_server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
futu_stock_mcp_server/server.py,sha256=41rXd58tq0RQDfM34BRgDt9cMLY7YZHKxP-6CtecnaM,52235
|
|
3
|
-
futu_stock_mcp_server-0.1.1.dist-info/METADATA,sha256=J0mHYGus5Mm4Np4QgTzQsfNEqBHjnVqItlJ2H_i2Mac,20087
|
|
4
|
-
futu_stock_mcp_server-0.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
5
|
-
futu_stock_mcp_server-0.1.1.dist-info/entry_points.txt,sha256=GAdKqPJD9dJ_fRA3e3m0NRia0elN5OcjEeAI30vOcIM,70
|
|
6
|
-
futu_stock_mcp_server-0.1.1.dist-info/licenses/LICENSE,sha256=XQBSQkjjpkymu_uLdyis4oNynV60VH1X7nS16uwM6g0,1069
|
|
7
|
-
futu_stock_mcp_server-0.1.1.dist-info/RECORD,,
|
|
File without changes
|
{futu_stock_mcp_server-0.1.1.dist-info → futu_stock_mcp_server-0.1.3.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{futu_stock_mcp_server-0.1.1.dist-info → futu_stock_mcp_server-0.1.3.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|