mcp-query-table 0.3.4__py3-none-any.whl → 0.3.6__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.
- mcp_query_table/__main__.py +2 -2
- mcp_query_table/_version.py +1 -1
- mcp_query_table/server.py +10 -11
- mcp_query_table/sites/iwencai.py +1 -0
- mcp_query_table/sites/tdx.py +1 -1
- mcp_query_table/tool.py +26 -11
- {mcp_query_table-0.3.4.dist-info → mcp_query_table-0.3.6.dist-info}/METADATA +16 -3
- mcp_query_table-0.3.6.dist-info/RECORD +19 -0
- mcp_query_table-0.3.4.dist-info/RECORD +0 -19
- {mcp_query_table-0.3.4.dist-info → mcp_query_table-0.3.6.dist-info}/WHEEL +0 -0
- {mcp_query_table-0.3.4.dist-info → mcp_query_table-0.3.6.dist-info}/licenses/LICENSE +0 -0
- {mcp_query_table-0.3.4.dist-info → mcp_query_table-0.3.6.dist-info}/top_level.txt +0 -0
mcp_query_table/__main__.py
CHANGED
|
@@ -10,7 +10,7 @@ def main():
|
|
|
10
10
|
|
|
11
11
|
parser.add_argument("--format", type=str, help="输出格式",
|
|
12
12
|
default='markdown', choices=['markdown', 'csv', 'json'])
|
|
13
|
-
parser.add_argument("--
|
|
13
|
+
parser.add_argument("--endpoint", type=str, help="浏览器CDP地址/WS地址",
|
|
14
14
|
default="http://127.0.0.1:9222")
|
|
15
15
|
parser.add_argument("--executable_path", type=str, help="浏览器类型",
|
|
16
16
|
default=r'C:\Program Files\Google\Chrome\Application\chrome.exe')
|
|
@@ -22,7 +22,7 @@ def main():
|
|
|
22
22
|
parser.add_argument("--port", type=int, help="MCP服务端绑定端口",
|
|
23
23
|
default='8000')
|
|
24
24
|
args = parser.parse_args()
|
|
25
|
-
serve(args.format, args.
|
|
25
|
+
serve(args.format, args.endpoint, args.executable_path,
|
|
26
26
|
args.transport, args.host, args.port)
|
|
27
27
|
|
|
28
28
|
|
mcp_query_table/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.3.
|
|
1
|
+
__version__ = "0.3.6"
|
mcp_query_table/server.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Annotated,
|
|
1
|
+
from typing import Annotated, List
|
|
2
2
|
|
|
3
3
|
from loguru import logger
|
|
4
4
|
from mcp.server.fastmcp import FastMCP
|
|
@@ -10,12 +10,13 @@ from mcp_query_table.tool import BrowserManager
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class QueryServer:
|
|
13
|
-
def __init__(self
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
def __init__(self) -> None:
|
|
14
|
+
self.format: str = "markdown"
|
|
15
|
+
self.browser = None
|
|
16
|
+
|
|
17
|
+
def start(self, format, endpoint, executable_path):
|
|
17
18
|
self.format: str = format
|
|
18
|
-
self.browser = BrowserManager(
|
|
19
|
+
self.browser = BrowserManager(endpoint=endpoint, executable_path=executable_path, debug=False)
|
|
19
20
|
|
|
20
21
|
async def query(self, query_input: str, query_type: QueryType, max_page: int, site: Site):
|
|
21
22
|
page = await self.browser.get_page()
|
|
@@ -64,12 +65,10 @@ async def chat(
|
|
|
64
65
|
return await qsv.chat(prompt, create, files, provider)
|
|
65
66
|
|
|
66
67
|
|
|
67
|
-
def serve(format,
|
|
68
|
-
qsv.format
|
|
69
|
-
qsv.cdp_endpoint = cdp_endpoint
|
|
70
|
-
qsv.executable_path = executable_path
|
|
68
|
+
def serve(format, endpoint, executable_path, transport, host, port):
|
|
69
|
+
qsv.start(format, endpoint, executable_path)
|
|
71
70
|
logger.info(f"{format=},{transport=}")
|
|
72
|
-
logger.info(f"{
|
|
71
|
+
logger.info(f"{endpoint=},{executable_path=}")
|
|
73
72
|
if transport == 'sse':
|
|
74
73
|
logger.info(f"{host=},{port=}", transport, host, port)
|
|
75
74
|
|
mcp_query_table/sites/iwencai.py
CHANGED
|
@@ -140,6 +140,7 @@ json_data['answer']['components'][0]['data']['meta']['extra']['row_count']
|
|
|
140
140
|
|
|
141
141
|
async def on_response(response):
|
|
142
142
|
if response.url == _PAGE1_:
|
|
143
|
+
# TODO 不支持headless模式,需要以后解决
|
|
143
144
|
P.update(*get_robot_data(await response.json()))
|
|
144
145
|
if response.url == _PAGE2_:
|
|
145
146
|
P.update(*getDataList(await response.json()))
|
mcp_query_table/sites/tdx.py
CHANGED
mcp_query_table/tool.py
CHANGED
|
@@ -3,6 +3,7 @@ import sys
|
|
|
3
3
|
import time
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Optional, List, Tuple
|
|
6
|
+
from urllib.parse import urlparse
|
|
6
7
|
|
|
7
8
|
import pandas as pd
|
|
8
9
|
from loguru import logger
|
|
@@ -36,6 +37,13 @@ def is_local_url(url: str) -> bool:
|
|
|
36
37
|
return False
|
|
37
38
|
|
|
38
39
|
|
|
40
|
+
def is_cdp_url(url: str) -> bool:
|
|
41
|
+
"""判断url是否是CDP地址"""
|
|
42
|
+
if url.startswith('ws://') or url.startswith('wss://'):
|
|
43
|
+
return False
|
|
44
|
+
return True
|
|
45
|
+
|
|
46
|
+
|
|
39
47
|
def get_executable_path(executable_path) -> Optional[str]:
|
|
40
48
|
"""获取浏览器可执行文件路径"""
|
|
41
49
|
browsers = {
|
|
@@ -59,22 +67,22 @@ class BrowserManager:
|
|
|
59
67
|
await self.cleanup()
|
|
60
68
|
|
|
61
69
|
def __init__(self,
|
|
62
|
-
|
|
70
|
+
endpoint: Optional[str] = None,
|
|
63
71
|
executable_path: Optional[str] = None,
|
|
64
72
|
debug: bool = False):
|
|
65
73
|
"""
|
|
66
74
|
|
|
67
75
|
Parameters
|
|
68
76
|
----------
|
|
69
|
-
|
|
70
|
-
浏览器CDP地址
|
|
77
|
+
endpoint:str
|
|
78
|
+
浏览器CDP地址/WS地址
|
|
71
79
|
executable_path:str
|
|
72
80
|
浏览器可执行文件路径。推荐使用chrome,因为Microsoft Edge必须在任务管理器中完全退出才能启动调试端口
|
|
73
81
|
debug:bool
|
|
74
82
|
是否显示开发者工具
|
|
75
83
|
|
|
76
84
|
"""
|
|
77
|
-
self.
|
|
85
|
+
self.endpoint = endpoint or 'http://127.0.0.1:9222'
|
|
78
86
|
self.executable_path = executable_path
|
|
79
87
|
self.debug = debug
|
|
80
88
|
|
|
@@ -92,7 +100,7 @@ class BrowserManager:
|
|
|
92
100
|
|
|
93
101
|
async def _connect_to_local(self) -> None:
|
|
94
102
|
"""连接本地浏览器"""
|
|
95
|
-
port = self.
|
|
103
|
+
port = urlparse(self.endpoint).port
|
|
96
104
|
executable_path = get_executable_path(self.executable_path)
|
|
97
105
|
command = [executable_path, f'--remote-debugging-port={port}', '--start-maximized']
|
|
98
106
|
if self.debug:
|
|
@@ -100,7 +108,7 @@ class BrowserManager:
|
|
|
100
108
|
|
|
101
109
|
for i in range(2):
|
|
102
110
|
try:
|
|
103
|
-
self.browser = await self.playwright.chromium.connect_over_cdp(self.
|
|
111
|
+
self.browser = await self.playwright.chromium.connect_over_cdp(self.endpoint,
|
|
104
112
|
timeout=10000, slow_mo=1000)
|
|
105
113
|
break
|
|
106
114
|
except:
|
|
@@ -116,10 +124,14 @@ class BrowserManager:
|
|
|
116
124
|
async def _connect_to_remote(self) -> None:
|
|
117
125
|
"""连接远程浏览器"""
|
|
118
126
|
try:
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
if is_cdp_url(self.endpoint):
|
|
128
|
+
self.browser = await self.playwright.chromium.connect_over_cdp(self.endpoint,
|
|
129
|
+
timeout=10000, slow_mo=1000)
|
|
130
|
+
else:
|
|
131
|
+
self.browser = await self.playwright.chromium.connect(self.endpoint,
|
|
132
|
+
timeout=10000, slow_mo=1000)
|
|
121
133
|
except:
|
|
122
|
-
raise ConnectionError(f"连接远程浏览器失败,请检查CDP地址和端口是否正确。{self.
|
|
134
|
+
raise ConnectionError(f"连接远程浏览器失败,请检查CDP/WS地址和端口是否正确。{self.endpoint}")
|
|
123
135
|
|
|
124
136
|
async def _launch(self) -> None:
|
|
125
137
|
"""启动浏览器,并连接CDP协议
|
|
@@ -131,12 +143,15 @@ class BrowserManager:
|
|
|
131
143
|
"""
|
|
132
144
|
self.playwright = await async_playwright().start()
|
|
133
145
|
|
|
134
|
-
if is_local_url(self.
|
|
146
|
+
if is_local_url(self.endpoint) and is_cdp_url(self.endpoint):
|
|
135
147
|
await self._connect_to_local()
|
|
136
148
|
else:
|
|
137
149
|
await self._connect_to_remote()
|
|
138
150
|
|
|
139
|
-
|
|
151
|
+
if len(self.browser.contexts) == 0:
|
|
152
|
+
self.context = await self.browser.new_context()
|
|
153
|
+
else:
|
|
154
|
+
self.context = self.browser.contexts[0]
|
|
140
155
|
# 复用打开的page
|
|
141
156
|
for page in self.context.pages:
|
|
142
157
|
# 防止开发者工具被使用
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp_query_table
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
4
4
|
Summary: query table from website, support MCP
|
|
5
5
|
Author-email: wukan <wu-kan@163.com>
|
|
6
6
|
License: MIT License
|
|
@@ -70,7 +70,7 @@ from mcp_query_table import *
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
async def main() -> None:
|
|
73
|
-
async with BrowserManager(
|
|
73
|
+
async with BrowserManager(endpoint="http://127.0.0.1:9222", executable_path=None, debug=True) as bm:
|
|
74
74
|
# 问财需要保证浏览器宽度>768,防止界面变成适应手机
|
|
75
75
|
page = await bm.get_page()
|
|
76
76
|
df = await query(page, '收益最好的200只ETF', query_type=QueryType.ETF, max_page=1, site=Site.THS)
|
|
@@ -135,6 +135,13 @@ if __name__ == '__main__':
|
|
|
135
135
|
在`Cline`中可以配置如下。其中`command`是`python`的绝对路径,`executable_path`是`Chrome`的绝对路径,`timeout`是超时时间,单位为秒。
|
|
136
136
|
在各`AI`平台中由于返回时间常需1分钟以上,所以需要设置大的超时时间。
|
|
137
137
|
|
|
138
|
+
`endpoint`支持两方式,一种是`cdp_endpoint`方式,一种是`ws_endpoint`方式。
|
|
139
|
+
|
|
140
|
+
- cdp方式:通过启动时加参数`--remote-debugging-port=9222`来启动浏览器
|
|
141
|
+
- ws方式:服务器上`docker run -p 3000:3000 --rm --init -it --workdir /home/pwuser --user pwuser mcr.microsoft.com/playwright:v1.51.0-noble /bin/sh -c "npx -y playwright@1.51.0 run-server --port 3000 --host 0.0.0.0"`
|
|
142
|
+
|
|
143
|
+
参考:https://playwright.dev/python/docs/docker#remote-connection
|
|
144
|
+
|
|
138
145
|
### STDIO方式
|
|
139
146
|
|
|
140
147
|
```json
|
|
@@ -148,7 +155,7 @@ if __name__ == '__main__':
|
|
|
148
155
|
"mcp_query_table",
|
|
149
156
|
"--format",
|
|
150
157
|
"markdown",
|
|
151
|
-
"--
|
|
158
|
+
"--endpoint",
|
|
152
159
|
"http://127.0.0.1:9222",
|
|
153
160
|
"--executable_path",
|
|
154
161
|
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
|
@@ -205,6 +212,12 @@ npx @modelcontextprotocol/inspector python -m mcp_query_table --format markdown
|
|
|
205
212
|
- 向东方财富板块查询 “去年涨的最差的行业板块”,再查询此板块中去年涨的最好的5只股票
|
|
206
213
|
> 分成两步查询,先查询板块,再查询股票。但最好不要全自动,因为第一步的结果它不理解“今日涨幅”和“区间涨幅”,需要交互修正
|
|
207
214
|
|
|
215
|
+
## 支持`Streamlit`
|
|
216
|
+
|
|
217
|
+
实现在同一页面中查询金融数据,并手工输入到`AI`中进行深度分析。参考`streamlit`目录下的`README.md`文件。
|
|
218
|
+
|
|
219
|
+

|
|
220
|
+
|
|
208
221
|
## 参考
|
|
209
222
|
|
|
210
223
|
- [Playwright](https://playwright.dev/python/docs/intro)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
mcp_query_table/__init__.py,sha256=NmnAOKcJjQoeTJMbIY79-JsNNdj-PJpnZ-x8MPjpE4o,272
|
|
2
|
+
mcp_query_table/__main__.py,sha256=a_CiwS8y0R_8XDDfOeUMyIBmDr1rdAGBgVG8pfzoNJw,1181
|
|
3
|
+
mcp_query_table/_version.py,sha256=W_9dCm49nLvZulVAvvsafxLJjVBSKDBHz9K7szFZllo,22
|
|
4
|
+
mcp_query_table/enums.py,sha256=Hen0X1f2of69f08epun6HvYIgbpw_rf8BvoRQ184kS4,679
|
|
5
|
+
mcp_query_table/server.py,sha256=bAqS3At2T8TlnEUtgScFc0q-UapHsVe12n1k9uw9mVQ,3303
|
|
6
|
+
mcp_query_table/tool.py,sha256=i1Wrwj038V5EGPcYC-qzfOlY31IIUvz_mugzKgi0XkU,9681
|
|
7
|
+
mcp_query_table/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
mcp_query_table/providers/baidu.py,sha256=q0vZxl_UTO8v4LbznwT2b1hM-00jswDR8CqB3tc-X9U,3389
|
|
9
|
+
mcp_query_table/providers/n.py,sha256=J0Xo-BlXveIQdDbwKkuEP4I1mfAr8i1rviD0UTztI9Y,3009
|
|
10
|
+
mcp_query_table/providers/yuanbao.py,sha256=4DzoLfgg7dSP1BhiS_tKyTQ0byc1zHRuZQjyq-2pZfQ,3148
|
|
11
|
+
mcp_query_table/sites/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
mcp_query_table/sites/eastmoney.py,sha256=LImjpYVuM5YnXwnNzB2hkKfHofocZZScetGqMOCHZpk,4477
|
|
13
|
+
mcp_query_table/sites/iwencai.py,sha256=YDGiV9wMvWxmLM3dvm4ccZxBhDOW8PdaPm9toQN0ue4,5119
|
|
14
|
+
mcp_query_table/sites/tdx.py,sha256=P-GNFUsS5_INy3sicaZbUBdudgQBZuhu_QyVvyw4yDg,4126
|
|
15
|
+
mcp_query_table-0.3.6.dist-info/licenses/LICENSE,sha256=rbvv_CTd7biGwT21tvhgQ2zkbPFXOoON7WFQWEdElBA,1063
|
|
16
|
+
mcp_query_table-0.3.6.dist-info/METADATA,sha256=nubh5fDdQXsGu7E0wP_EOKNeDZ7uaYzlFV5byH8T0Vo,9040
|
|
17
|
+
mcp_query_table-0.3.6.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
18
|
+
mcp_query_table-0.3.6.dist-info/top_level.txt,sha256=5M_8dkO1USOX7_EWbWS6O_TEsZ5yo-AodFNKeUEgvEQ,16
|
|
19
|
+
mcp_query_table-0.3.6.dist-info/RECORD,,
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
mcp_query_table/__init__.py,sha256=NmnAOKcJjQoeTJMbIY79-JsNNdj-PJpnZ-x8MPjpE4o,272
|
|
2
|
-
mcp_query_table/__main__.py,sha256=W3tMnZZTvkhjlLHmSYHng3CTvYX6-LjyaBDhf-ypH7w,1186
|
|
3
|
-
mcp_query_table/_version.py,sha256=oYLGMpySamd16KLiaBTfRyrAS7_oyp-TOEHmzmeumwg,22
|
|
4
|
-
mcp_query_table/enums.py,sha256=Hen0X1f2of69f08epun6HvYIgbpw_rf8BvoRQ184kS4,679
|
|
5
|
-
mcp_query_table/server.py,sha256=oV8CSi-EW5-xlPn38R1wLu7U-pCHgAJci1W8rJFsXSk,3430
|
|
6
|
-
mcp_query_table/tool.py,sha256=kCnfBNv1T88gNnFLsz-ZjXNuxbzhGvLPcPfNkkJ7Dyg,9096
|
|
7
|
-
mcp_query_table/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
mcp_query_table/providers/baidu.py,sha256=q0vZxl_UTO8v4LbznwT2b1hM-00jswDR8CqB3tc-X9U,3389
|
|
9
|
-
mcp_query_table/providers/n.py,sha256=J0Xo-BlXveIQdDbwKkuEP4I1mfAr8i1rviD0UTztI9Y,3009
|
|
10
|
-
mcp_query_table/providers/yuanbao.py,sha256=4DzoLfgg7dSP1BhiS_tKyTQ0byc1zHRuZQjyq-2pZfQ,3148
|
|
11
|
-
mcp_query_table/sites/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
mcp_query_table/sites/eastmoney.py,sha256=LImjpYVuM5YnXwnNzB2hkKfHofocZZScetGqMOCHZpk,4477
|
|
13
|
-
mcp_query_table/sites/iwencai.py,sha256=g56pj3pbxu4mXLNnaaS3Hdx-DvEy_9OBrQJe26z4z08,5059
|
|
14
|
-
mcp_query_table/sites/tdx.py,sha256=RIUQaB7Tn4AVyWaevk9SzTKIDwVO2f9erIlI-adXPLY,4126
|
|
15
|
-
mcp_query_table-0.3.4.dist-info/licenses/LICENSE,sha256=rbvv_CTd7biGwT21tvhgQ2zkbPFXOoON7WFQWEdElBA,1063
|
|
16
|
-
mcp_query_table-0.3.4.dist-info/METADATA,sha256=gc0VlBZxpcc_uPxkRA0WXdYNQ0cJeP3K5oTRKx5_4VU,8372
|
|
17
|
-
mcp_query_table-0.3.4.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
18
|
-
mcp_query_table-0.3.4.dist-info/top_level.txt,sha256=5M_8dkO1USOX7_EWbWS6O_TEsZ5yo-AodFNKeUEgvEQ,16
|
|
19
|
-
mcp_query_table-0.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|