kl-mcp-client 1.0.0__tar.gz → 1.0.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.
- kl_mcp_client-1.0.2/PKG-INFO +251 -0
- kl_mcp_client-1.0.2/README.md +240 -0
- kl_mcp_client-1.0.2/kl_mcp_client/__init__.py +7 -0
- kl_mcp_client-1.0.2/kl_mcp_client/__version__.py +1 -0
- kl_mcp_client-1.0.2/kl_mcp_client/async/__init__.py +7 -0
- kl_mcp_client-1.0.2/kl_mcp_client/async/client.py +141 -0
- kl_mcp_client-1.0.2/kl_mcp_client/async/tools.py +179 -0
- kl_mcp_client-1.0.2/kl_mcp_client.egg-info/PKG-INFO +251 -0
- kl_mcp_client-1.0.2/kl_mcp_client.egg-info/SOURCES.txt +14 -0
- kl_mcp_client-1.0.2/kl_mcp_client.egg-info/requires.txt +2 -0
- kl_mcp_client-1.0.2/kl_mcp_client.egg-info/top_level.txt +1 -0
- {kl_mcp_client-1.0.0 → kl_mcp_client-1.0.2}/pyproject.toml +6 -2
- kl_mcp_client-1.0.0/PKG-INFO +0 -8
- kl_mcp_client-1.0.0/README.md +0 -0
- kl_mcp_client-1.0.0/kl_mcp_client.egg-info/PKG-INFO +0 -8
- kl_mcp_client-1.0.0/kl_mcp_client.egg-info/SOURCES.txt +0 -9
- kl_mcp_client-1.0.0/kl_mcp_client.egg-info/top_level.txt +0 -1
- kl_mcp_client-1.0.0/mcp_client/__init__.py +0 -0
- {kl_mcp_client-1.0.0/mcp_client → kl_mcp_client-1.0.2/kl_mcp_client}/client.py +0 -0
- {kl_mcp_client-1.0.0/mcp_client → kl_mcp_client-1.0.2/kl_mcp_client}/tools.py +0 -0
- {kl_mcp_client-1.0.0 → kl_mcp_client-1.0.2}/kl_mcp_client.egg-info/dependency_links.txt +0 -0
- {kl_mcp_client-1.0.0 → kl_mcp_client-1.0.2}/setup.cfg +0 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kl-mcp-client
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: MCP Client
|
|
5
|
+
Author-email: Kyle <hngan.it@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: requests>=2.32.5
|
|
10
|
+
Requires-Dist: httpx>=0.28.1
|
|
11
|
+
|
|
12
|
+
# 📦 kl-mcp-client
|
|
13
|
+
|
|
14
|
+
**kl-mcp-client** là SDK Python giúp giao tiếp với **MCP (Model Context Protocol) Browser Server** — một server trung gian điều khiển Chrome/Chromium qua CDP.
|
|
15
|
+
|
|
16
|
+
Thư viện này được thiết kế để:
|
|
17
|
+
|
|
18
|
+
- Điều khiển Chrome tự động: click, nhập liệu, screenshot, đọc DOM…
|
|
19
|
+
- Kết nối trình duyệt Chrome Remote qua CDP (Chrome DevTools Protocol)
|
|
20
|
+
- Tích hợp làm Web Automation Agent trong **Google ADK**
|
|
21
|
+
- Hoạt động độc lập như một browser automation SDK
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# 🚀 Cài đặt
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install kl-mcp-client
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
# 🧩 Thành phần của thư viện
|
|
34
|
+
|
|
35
|
+
Package gồm 2 module chính:
|
|
36
|
+
|
|
37
|
+
| File | Vai trò |
|
|
38
|
+
|------|---------|
|
|
39
|
+
| `client.py` | JSON-RPC HTTP Client giao tiếp với MCP Server |
|
|
40
|
+
| `tools.py` | Wrapper cấp cao, cung cấp API tiện dụng cho các Agent & automation |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# ✨ Tính năng chính
|
|
45
|
+
|
|
46
|
+
### ✔ Điều khiển trình duyệt
|
|
47
|
+
- Mở tab, load URL
|
|
48
|
+
- Click CSS selector, click bằng text, click bằng nodeId
|
|
49
|
+
- Nhập text vào input
|
|
50
|
+
- Screenshot (chuẩn ADK hiển thị được)
|
|
51
|
+
|
|
52
|
+
### ✔ DOM Tools nâng cao
|
|
53
|
+
- Find element by selector / text / XPath
|
|
54
|
+
- Lấy bounding box
|
|
55
|
+
- Lấy toàn bộ DOM Tree
|
|
56
|
+
- Lấy danh sách clickable elements
|
|
57
|
+
- Highlight elements (nếu server hỗ trợ)
|
|
58
|
+
|
|
59
|
+
### ✔ Tương tác hệ thống
|
|
60
|
+
- Upload file bằng base64
|
|
61
|
+
- Import cookies
|
|
62
|
+
- Evaluate JavaScript
|
|
63
|
+
|
|
64
|
+
### ✔ Chrome Remote Debugging
|
|
65
|
+
- Tạo session từ địa chỉ:
|
|
66
|
+
`http://localhost:9222/json/version`
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
# 🧭 Cách sử dụng
|
|
71
|
+
|
|
72
|
+
## 1. Import client & tools
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from kl_mcp_client.client import MCPClient
|
|
76
|
+
from kl_mcp_client.tools import MCPTools
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 2. Tạo kết nối tới MCP Server
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
mcp = MCPClient(
|
|
85
|
+
base_url="http://localhost:3000/mcp",
|
|
86
|
+
timeout=30,
|
|
87
|
+
retries=2
|
|
88
|
+
)
|
|
89
|
+
tools = MCPTools(mcp)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 3. Tạo session (Chrome Remote)
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
session = tools.create_session("http://localhost:9222/json/version")
|
|
98
|
+
sid = session["sessionId"]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 4. Mở URL
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
tools.open_page(sid, "https://google.com")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 5. Screenshot (hiển thị được trong ADK Web)
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
img = tools.screenshot(sid)
|
|
115
|
+
print(img)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Trả về:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"type": "image",
|
|
123
|
+
"mimeType": "image/png",
|
|
124
|
+
"data": "<base64>"
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 6. Click & Type
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
tools.click(sid, "#login")
|
|
134
|
+
tools.type(sid, "input[name=q]", "Hello world")
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 7. Find Elements
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
tools.find_element(sid, "#content")
|
|
143
|
+
tools.find_element_by_text(sid, "Đăng nhập")
|
|
144
|
+
tools.find_element_xpath(sid, "//input[@type='email']")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 8. Upload File
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
import base64
|
|
153
|
+
data = base64.b64encode(open("test.pdf", "rb").read()).decode()
|
|
154
|
+
|
|
155
|
+
tools.upload_file(
|
|
156
|
+
sid,
|
|
157
|
+
selector="input[type=file]",
|
|
158
|
+
filename="test.pdf",
|
|
159
|
+
base64Data=data
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 9. Import Cookies
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
tools.import_cookies(sid, [
|
|
169
|
+
{"name": "token", "value": "abc", "domain": "example.com", "path": "/"}
|
|
170
|
+
])
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 10. Đóng session
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
tools.close_session(sid)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
# 🧪 Ví dụ đầy đủ
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from kl_mcp_client.client import MCPClient
|
|
187
|
+
from kl_mcp_client.tools import MCPTools
|
|
188
|
+
import base64
|
|
189
|
+
|
|
190
|
+
mcp = MCPClient("http://localhost:3000/mcp")
|
|
191
|
+
tools = MCPTools(mcp)
|
|
192
|
+
|
|
193
|
+
# Create session
|
|
194
|
+
sid = tools.create_session("http://localhost:9222/json/version")["sessionId"]
|
|
195
|
+
|
|
196
|
+
# Navigate
|
|
197
|
+
tools.open_page(sid, "https://google.com")
|
|
198
|
+
|
|
199
|
+
# Screenshot
|
|
200
|
+
img = tools.screenshot(sid)
|
|
201
|
+
print("Screenshot returned:", img["mimeType"])
|
|
202
|
+
|
|
203
|
+
# Search
|
|
204
|
+
tools.type(sid, "input[name=q]", "Hello MCP")
|
|
205
|
+
tools.click_to_text(sid, "Google Search")
|
|
206
|
+
|
|
207
|
+
# Close
|
|
208
|
+
tools.close_session(sid)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
# 🏗 Kiến trúc
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
Python App / ADK Agent
|
|
217
|
+
↓
|
|
218
|
+
kl-mcp-client
|
|
219
|
+
↓
|
|
220
|
+
MCP Browser Server
|
|
221
|
+
↓
|
|
222
|
+
Chrome / CDP
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
# 📘 Yêu cầu
|
|
228
|
+
|
|
229
|
+
- Python ≥ 3.8
|
|
230
|
+
- MCP Server chạy sẵn (Chromedp backend)
|
|
231
|
+
- Chrome/Chromium với cờ:
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
chrome.exe --remote-debugging-port=9222
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
# 📝 License
|
|
240
|
+
|
|
241
|
+
MIT License.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
# 📚 Liên hệ
|
|
246
|
+
|
|
247
|
+
Nếu bạn cần:
|
|
248
|
+
|
|
249
|
+
- MCP Server đầy đủ
|
|
250
|
+
- Hỗ trợ tích hợp ADK Web Agent
|
|
251
|
+
- Thêm tool: DOM Tree, highlight, selector map…
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# 📦 kl-mcp-client
|
|
2
|
+
|
|
3
|
+
**kl-mcp-client** là SDK Python giúp giao tiếp với **MCP (Model Context Protocol) Browser Server** — một server trung gian điều khiển Chrome/Chromium qua CDP.
|
|
4
|
+
|
|
5
|
+
Thư viện này được thiết kế để:
|
|
6
|
+
|
|
7
|
+
- Điều khiển Chrome tự động: click, nhập liệu, screenshot, đọc DOM…
|
|
8
|
+
- Kết nối trình duyệt Chrome Remote qua CDP (Chrome DevTools Protocol)
|
|
9
|
+
- Tích hợp làm Web Automation Agent trong **Google ADK**
|
|
10
|
+
- Hoạt động độc lập như một browser automation SDK
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# 🚀 Cài đặt
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install kl-mcp-client
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# 🧩 Thành phần của thư viện
|
|
23
|
+
|
|
24
|
+
Package gồm 2 module chính:
|
|
25
|
+
|
|
26
|
+
| File | Vai trò |
|
|
27
|
+
|------|---------|
|
|
28
|
+
| `client.py` | JSON-RPC HTTP Client giao tiếp với MCP Server |
|
|
29
|
+
| `tools.py` | Wrapper cấp cao, cung cấp API tiện dụng cho các Agent & automation |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
# ✨ Tính năng chính
|
|
34
|
+
|
|
35
|
+
### ✔ Điều khiển trình duyệt
|
|
36
|
+
- Mở tab, load URL
|
|
37
|
+
- Click CSS selector, click bằng text, click bằng nodeId
|
|
38
|
+
- Nhập text vào input
|
|
39
|
+
- Screenshot (chuẩn ADK hiển thị được)
|
|
40
|
+
|
|
41
|
+
### ✔ DOM Tools nâng cao
|
|
42
|
+
- Find element by selector / text / XPath
|
|
43
|
+
- Lấy bounding box
|
|
44
|
+
- Lấy toàn bộ DOM Tree
|
|
45
|
+
- Lấy danh sách clickable elements
|
|
46
|
+
- Highlight elements (nếu server hỗ trợ)
|
|
47
|
+
|
|
48
|
+
### ✔ Tương tác hệ thống
|
|
49
|
+
- Upload file bằng base64
|
|
50
|
+
- Import cookies
|
|
51
|
+
- Evaluate JavaScript
|
|
52
|
+
|
|
53
|
+
### ✔ Chrome Remote Debugging
|
|
54
|
+
- Tạo session từ địa chỉ:
|
|
55
|
+
`http://localhost:9222/json/version`
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
# 🧭 Cách sử dụng
|
|
60
|
+
|
|
61
|
+
## 1. Import client & tools
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from kl_mcp_client.client import MCPClient
|
|
65
|
+
from kl_mcp_client.tools import MCPTools
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 2. Tạo kết nối tới MCP Server
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
mcp = MCPClient(
|
|
74
|
+
base_url="http://localhost:3000/mcp",
|
|
75
|
+
timeout=30,
|
|
76
|
+
retries=2
|
|
77
|
+
)
|
|
78
|
+
tools = MCPTools(mcp)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 3. Tạo session (Chrome Remote)
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
session = tools.create_session("http://localhost:9222/json/version")
|
|
87
|
+
sid = session["sessionId"]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 4. Mở URL
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
tools.open_page(sid, "https://google.com")
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 5. Screenshot (hiển thị được trong ADK Web)
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
img = tools.screenshot(sid)
|
|
104
|
+
print(img)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Trả về:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"type": "image",
|
|
112
|
+
"mimeType": "image/png",
|
|
113
|
+
"data": "<base64>"
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 6. Click & Type
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
tools.click(sid, "#login")
|
|
123
|
+
tools.type(sid, "input[name=q]", "Hello world")
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 7. Find Elements
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
tools.find_element(sid, "#content")
|
|
132
|
+
tools.find_element_by_text(sid, "Đăng nhập")
|
|
133
|
+
tools.find_element_xpath(sid, "//input[@type='email']")
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 8. Upload File
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
import base64
|
|
142
|
+
data = base64.b64encode(open("test.pdf", "rb").read()).decode()
|
|
143
|
+
|
|
144
|
+
tools.upload_file(
|
|
145
|
+
sid,
|
|
146
|
+
selector="input[type=file]",
|
|
147
|
+
filename="test.pdf",
|
|
148
|
+
base64Data=data
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 9. Import Cookies
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
tools.import_cookies(sid, [
|
|
158
|
+
{"name": "token", "value": "abc", "domain": "example.com", "path": "/"}
|
|
159
|
+
])
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 10. Đóng session
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
tools.close_session(sid)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
# 🧪 Ví dụ đầy đủ
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from kl_mcp_client.client import MCPClient
|
|
176
|
+
from kl_mcp_client.tools import MCPTools
|
|
177
|
+
import base64
|
|
178
|
+
|
|
179
|
+
mcp = MCPClient("http://localhost:3000/mcp")
|
|
180
|
+
tools = MCPTools(mcp)
|
|
181
|
+
|
|
182
|
+
# Create session
|
|
183
|
+
sid = tools.create_session("http://localhost:9222/json/version")["sessionId"]
|
|
184
|
+
|
|
185
|
+
# Navigate
|
|
186
|
+
tools.open_page(sid, "https://google.com")
|
|
187
|
+
|
|
188
|
+
# Screenshot
|
|
189
|
+
img = tools.screenshot(sid)
|
|
190
|
+
print("Screenshot returned:", img["mimeType"])
|
|
191
|
+
|
|
192
|
+
# Search
|
|
193
|
+
tools.type(sid, "input[name=q]", "Hello MCP")
|
|
194
|
+
tools.click_to_text(sid, "Google Search")
|
|
195
|
+
|
|
196
|
+
# Close
|
|
197
|
+
tools.close_session(sid)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
# 🏗 Kiến trúc
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
Python App / ADK Agent
|
|
206
|
+
↓
|
|
207
|
+
kl-mcp-client
|
|
208
|
+
↓
|
|
209
|
+
MCP Browser Server
|
|
210
|
+
↓
|
|
211
|
+
Chrome / CDP
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
# 📘 Yêu cầu
|
|
217
|
+
|
|
218
|
+
- Python ≥ 3.8
|
|
219
|
+
- MCP Server chạy sẵn (Chromedp backend)
|
|
220
|
+
- Chrome/Chromium với cờ:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
chrome.exe --remote-debugging-port=9222
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
# 📝 License
|
|
229
|
+
|
|
230
|
+
MIT License.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
# 📚 Liên hệ
|
|
235
|
+
|
|
236
|
+
Nếu bạn cần:
|
|
237
|
+
|
|
238
|
+
- MCP Server đầy đủ
|
|
239
|
+
- Hỗ trợ tích hợp ADK Web Agent
|
|
240
|
+
- Thêm tool: DOM Tree, highlight, selector map…
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__VERSION__ = "1.0.0"
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# mcp_client_async.py
|
|
2
|
+
import uuid
|
|
3
|
+
import time
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
import asyncio
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
DEFAULT_HEADERS = {
|
|
11
|
+
"Content-Type": "application/json",
|
|
12
|
+
"Accept": "application/json, text/event-stream",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MCPError(Exception):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class MCPClient:
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
base_url: str,
|
|
24
|
+
headers: Optional[Dict[str, str]] = None,
|
|
25
|
+
timeout: int = 30,
|
|
26
|
+
retries: int = 1,
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
base_url: full MCP HTTP endpoint e.g. http://localhost:3000/mcp
|
|
30
|
+
headers: extra headers (e.g. {"Authorization": "Bearer ..."})
|
|
31
|
+
timeout: request timeout seconds
|
|
32
|
+
retries: number of attempts for network errors
|
|
33
|
+
"""
|
|
34
|
+
self.base_url = base_url.rstrip("/")
|
|
35
|
+
self.headers = DEFAULT_HEADERS.copy()
|
|
36
|
+
if headers:
|
|
37
|
+
self.headers.update(headers)
|
|
38
|
+
|
|
39
|
+
self.timeout = timeout
|
|
40
|
+
self.retries = max(1, retries)
|
|
41
|
+
|
|
42
|
+
self._client = httpx.AsyncClient(
|
|
43
|
+
timeout=self.timeout,
|
|
44
|
+
headers=self.headers,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# local session cache
|
|
48
|
+
self._sessions: Dict[str, Dict[str, Any]] = {}
|
|
49
|
+
|
|
50
|
+
async def _rpc(self, method: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
51
|
+
payload = {
|
|
52
|
+
"jsonrpc": "2.0",
|
|
53
|
+
"id": str(uuid.uuid4()),
|
|
54
|
+
"method": method,
|
|
55
|
+
"params": params,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
last_exc = None
|
|
59
|
+
for attempt in range(self.retries):
|
|
60
|
+
try:
|
|
61
|
+
r = await self._client.post(
|
|
62
|
+
self.base_url,
|
|
63
|
+
json=payload,
|
|
64
|
+
)
|
|
65
|
+
r.raise_for_status()
|
|
66
|
+
except Exception as e:
|
|
67
|
+
last_exc = e
|
|
68
|
+
await asyncio.sleep(0.2 * (attempt + 1))
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
data = r.json()
|
|
73
|
+
except ValueError:
|
|
74
|
+
raise MCPError(
|
|
75
|
+
f"Invalid JSON response (status {r.status_code}): {r.text}"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if err := data.get("error"):
|
|
79
|
+
raise MCPError(
|
|
80
|
+
{
|
|
81
|
+
"code": err.get("code"),
|
|
82
|
+
"message": err.get("message"),
|
|
83
|
+
"data": err.get("data"),
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return data.get("result", {})
|
|
88
|
+
|
|
89
|
+
raise MCPError(f"Request failed after {self.retries} attempts: {last_exc}")
|
|
90
|
+
|
|
91
|
+
# -------------------------
|
|
92
|
+
# Generic tool wrappers
|
|
93
|
+
# -------------------------
|
|
94
|
+
|
|
95
|
+
async def call_tool_structured(
|
|
96
|
+
self, tool: str, arguments: Optional[Dict[str, Any]] = None
|
|
97
|
+
) -> Dict[str, Any]:
|
|
98
|
+
res = await self._rpc("callTool", {"tool": tool, "arguments": arguments or {}})
|
|
99
|
+
|
|
100
|
+
if isinstance(res, dict) and "structuredContent" in res:
|
|
101
|
+
return res["structuredContent"]
|
|
102
|
+
|
|
103
|
+
return res
|
|
104
|
+
|
|
105
|
+
async def call_tool(
|
|
106
|
+
self, tool: str, arguments: Optional[Dict[str, Any]] = None
|
|
107
|
+
) -> Dict[str, Any]:
|
|
108
|
+
return await self._rpc("callTool", {"tool": tool, "arguments": arguments or {}})
|
|
109
|
+
|
|
110
|
+
# -------------------------
|
|
111
|
+
# Session helpers
|
|
112
|
+
# -------------------------
|
|
113
|
+
|
|
114
|
+
async def create_session(self, cdpUrl: str) -> str:
|
|
115
|
+
result = await self.call_tool_structured("createSession", {"cdpUrl": cdpUrl})
|
|
116
|
+
|
|
117
|
+
session_id = (
|
|
118
|
+
result.get("sessionId") or result.get("session_id") or result.get("id")
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if not session_id:
|
|
122
|
+
raise MCPError("createSession did not return sessionId")
|
|
123
|
+
|
|
124
|
+
self._sessions[session_id] = {"created_at": time.time()}
|
|
125
|
+
return session_id
|
|
126
|
+
|
|
127
|
+
async def close_session(self, session_id: str) -> bool:
|
|
128
|
+
try:
|
|
129
|
+
await self.call_tool_structured("closeSession", {"sessionId": session_id})
|
|
130
|
+
self._sessions.pop(session_id, None)
|
|
131
|
+
return True
|
|
132
|
+
except MCPError:
|
|
133
|
+
self._sessions.pop(session_id, None)
|
|
134
|
+
raise
|
|
135
|
+
|
|
136
|
+
def list_local_sessions(self) -> List[str]:
|
|
137
|
+
return list(self._sessions.keys())
|
|
138
|
+
|
|
139
|
+
async def aclose(self):
|
|
140
|
+
"""Close the httpx async client"""
|
|
141
|
+
await self._client.aclose()
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# async_tools.py
|
|
2
|
+
from typing import Any, Dict, Optional
|
|
3
|
+
|
|
4
|
+
from .client import MCPClient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MCPTools:
|
|
8
|
+
"""
|
|
9
|
+
Async wrapper cho Google ADK + MCP Server.
|
|
10
|
+
- Dùng MCPClientAsync (httpx async)
|
|
11
|
+
- Tất cả method đều async
|
|
12
|
+
- Screenshot trả về đúng format ADK Web yêu cầu
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, client: MCPClient):
|
|
16
|
+
self.client = client
|
|
17
|
+
|
|
18
|
+
# ======================================================
|
|
19
|
+
# SESSION MANAGEMENT
|
|
20
|
+
# ======================================================
|
|
21
|
+
|
|
22
|
+
async def create_session(self, cdpUrl: str) -> Dict[str, Any]:
|
|
23
|
+
sid = await self.client.create_session(cdpUrl)
|
|
24
|
+
return {"sessionId": sid}
|
|
25
|
+
|
|
26
|
+
async def close_session(self, sessionId: str) -> Dict[str, Any]:
|
|
27
|
+
ok = await self.client.close_session(sessionId)
|
|
28
|
+
return {"ok": bool(ok)}
|
|
29
|
+
|
|
30
|
+
async def list_sessions(self) -> Dict[str, Any]:
|
|
31
|
+
return {"sessions": self.client.list_local_sessions()}
|
|
32
|
+
|
|
33
|
+
# ======================================================
|
|
34
|
+
# NAVIGATION & DOM
|
|
35
|
+
# ======================================================
|
|
36
|
+
|
|
37
|
+
async def open_page(self, sessionId: str, url: str) -> Dict[str, Any]:
|
|
38
|
+
res = await self.client.call_tool(
|
|
39
|
+
"openPage", {"sessionId": sessionId, "url": url}
|
|
40
|
+
)
|
|
41
|
+
return res.get("structuredContent", {})
|
|
42
|
+
|
|
43
|
+
async def get_html(self, sessionId: str) -> Dict[str, Any]:
|
|
44
|
+
res = await self.client.call_tool("getHTML", {"sessionId": sessionId})
|
|
45
|
+
return res.get("structuredContent", {})
|
|
46
|
+
|
|
47
|
+
async def screenshot(self, sessionId: str) -> Dict[str, Any]:
|
|
48
|
+
"""
|
|
49
|
+
ADK Web expects this structure:
|
|
50
|
+
{
|
|
51
|
+
"type": "image",
|
|
52
|
+
"mimeType": "image/png",
|
|
53
|
+
"data": "<base64>"
|
|
54
|
+
}
|
|
55
|
+
"""
|
|
56
|
+
res = await self.client.call_tool("screenshot", {"sessionId": sessionId})
|
|
57
|
+
return res["content"][0]
|
|
58
|
+
|
|
59
|
+
async def click(self, sessionId: str, selector: str) -> Dict[str, Any]:
|
|
60
|
+
res = await self.client.call_tool(
|
|
61
|
+
"click", {"sessionId": sessionId, "selector": selector}
|
|
62
|
+
)
|
|
63
|
+
return res.get("structuredContent", {})
|
|
64
|
+
|
|
65
|
+
async def type(self, sessionId: str, selector: str, text: str) -> Dict[str, Any]:
|
|
66
|
+
res = await self.client.call_tool(
|
|
67
|
+
"type", {"sessionId": sessionId, "selector": selector, "text": text}
|
|
68
|
+
)
|
|
69
|
+
return res.get("structuredContent", {})
|
|
70
|
+
|
|
71
|
+
async def evaluate(self, sessionId: str, expression: str) -> Dict[str, Any]:
|
|
72
|
+
res = await self.client.call_tool(
|
|
73
|
+
"evaluate", {"sessionId": sessionId, "expression": expression}
|
|
74
|
+
)
|
|
75
|
+
return res.get("structuredContent", {})
|
|
76
|
+
|
|
77
|
+
# ======================================================
|
|
78
|
+
# ELEMENT UTILITIES
|
|
79
|
+
# ======================================================
|
|
80
|
+
|
|
81
|
+
async def find_element(self, sessionId: str, selector: str) -> Dict[str, Any]:
|
|
82
|
+
res = await self.client.call_tool(
|
|
83
|
+
"findElement", {"sessionId": sessionId, "selector": selector}
|
|
84
|
+
)
|
|
85
|
+
return res.get("structuredContent", {})
|
|
86
|
+
|
|
87
|
+
async def find_all(self, sessionId: str, selector: str) -> Dict[str, Any]:
|
|
88
|
+
res = await self.client.call_tool(
|
|
89
|
+
"findAll", {"sessionId": sessionId, "selector": selector}
|
|
90
|
+
)
|
|
91
|
+
return res.get("structuredContent", {})
|
|
92
|
+
|
|
93
|
+
async def get_bounding_box(self, sessionId: str, selector: str) -> Dict[str, Any]:
|
|
94
|
+
res = await self.client.call_tool(
|
|
95
|
+
"getBoundingBox", {"sessionId": sessionId, "selector": selector}
|
|
96
|
+
)
|
|
97
|
+
return res.get("structuredContent", {})
|
|
98
|
+
|
|
99
|
+
async def click_bounding_box(self, sessionId: str, selector: str) -> Dict[str, Any]:
|
|
100
|
+
res = await self.client.call_tool(
|
|
101
|
+
"clickBoundingBox", {"sessionId": sessionId, "selector": selector}
|
|
102
|
+
)
|
|
103
|
+
return res.get("structuredContent", {})
|
|
104
|
+
|
|
105
|
+
async def upload_file(
|
|
106
|
+
self, sessionId: str, selector: str, filename: str, base64data: str
|
|
107
|
+
) -> Dict[str, Any]:
|
|
108
|
+
res = await self.client.call_tool(
|
|
109
|
+
"uploadFile",
|
|
110
|
+
{
|
|
111
|
+
"sessionId": sessionId,
|
|
112
|
+
"selector": selector,
|
|
113
|
+
"filename": filename,
|
|
114
|
+
"data": base64data,
|
|
115
|
+
},
|
|
116
|
+
)
|
|
117
|
+
return res.get("structuredContent", {})
|
|
118
|
+
|
|
119
|
+
async def wait_for_selector(
|
|
120
|
+
self, sessionId: str, selector: str, timeoutMs: Optional[int] = None
|
|
121
|
+
) -> Dict[str, Any]:
|
|
122
|
+
args = {"sessionId": sessionId, "selector": selector}
|
|
123
|
+
if timeoutMs is not None:
|
|
124
|
+
args["timeoutMs"] = timeoutMs
|
|
125
|
+
|
|
126
|
+
res = await self.client.call_tool("waitForSelector", args)
|
|
127
|
+
return res.get("structuredContent", {})
|
|
128
|
+
|
|
129
|
+
# ======================================================
|
|
130
|
+
# TAB MANAGEMENT
|
|
131
|
+
# ======================================================
|
|
132
|
+
|
|
133
|
+
async def new_tab(
|
|
134
|
+
self, sessionId: str, url: Optional[str] = "about:blank"
|
|
135
|
+
) -> Dict[str, Any]:
|
|
136
|
+
res = await self.client.call_tool(
|
|
137
|
+
"newTab", {"sessionId": sessionId, "url": url}
|
|
138
|
+
)
|
|
139
|
+
return res.get("structuredContent", {})
|
|
140
|
+
|
|
141
|
+
async def switch_tab(self, sessionId: str, targetId: str) -> Dict[str, Any]:
|
|
142
|
+
res = await self.client.call_tool(
|
|
143
|
+
"switchTab", {"sessionId": sessionId, "targetId": targetId}
|
|
144
|
+
)
|
|
145
|
+
return res.get("structuredContent", {})
|
|
146
|
+
|
|
147
|
+
# ======================================================
|
|
148
|
+
# ADVANCED
|
|
149
|
+
# ======================================================
|
|
150
|
+
|
|
151
|
+
async def click_to_text(self, sessionId: str, text: str) -> dict:
|
|
152
|
+
res = await self.client.call_tool(
|
|
153
|
+
"clickToText", {"sessionId": sessionId, "text": text}
|
|
154
|
+
)
|
|
155
|
+
return res.get("structuredContent", {})
|
|
156
|
+
|
|
157
|
+
async def find_element_xpath(self, sessionId: str, xpath: str) -> Dict[str, Any]:
|
|
158
|
+
res = await self.client.call_tool(
|
|
159
|
+
"findElementByXPath", {"sessionId": sessionId, "xpath": xpath}
|
|
160
|
+
)
|
|
161
|
+
return res.get("structuredContent", {})
|
|
162
|
+
|
|
163
|
+
async def find_element_by_text(self, sessionId: str, text: str) -> Dict[str, Any]:
|
|
164
|
+
res = await self.client.call_tool(
|
|
165
|
+
"findElementByText", {"sessionId": sessionId, "text": text}
|
|
166
|
+
)
|
|
167
|
+
return res.get("structuredContent", {})
|
|
168
|
+
|
|
169
|
+
async def click_by_node_id(self, sessionId: str, nodeId: int) -> Dict[str, Any]:
|
|
170
|
+
res = await self.client.call_tool(
|
|
171
|
+
"clickByNodeId", {"sessionId": sessionId, "nodeId": nodeId}
|
|
172
|
+
)
|
|
173
|
+
return res.get("structuredContent", {})
|
|
174
|
+
|
|
175
|
+
async def import_cookies(self, sessionId: str, cookies: dict) -> Dict[str, Any]:
|
|
176
|
+
res = await self.client.call_tool(
|
|
177
|
+
"importCookies", {"sessionId": sessionId, "cookies": cookies}
|
|
178
|
+
)
|
|
179
|
+
return res.get("structuredContent", {})
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kl-mcp-client
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: MCP Client
|
|
5
|
+
Author-email: Kyle <hngan.it@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: requests>=2.32.5
|
|
10
|
+
Requires-Dist: httpx>=0.28.1
|
|
11
|
+
|
|
12
|
+
# 📦 kl-mcp-client
|
|
13
|
+
|
|
14
|
+
**kl-mcp-client** là SDK Python giúp giao tiếp với **MCP (Model Context Protocol) Browser Server** — một server trung gian điều khiển Chrome/Chromium qua CDP.
|
|
15
|
+
|
|
16
|
+
Thư viện này được thiết kế để:
|
|
17
|
+
|
|
18
|
+
- Điều khiển Chrome tự động: click, nhập liệu, screenshot, đọc DOM…
|
|
19
|
+
- Kết nối trình duyệt Chrome Remote qua CDP (Chrome DevTools Protocol)
|
|
20
|
+
- Tích hợp làm Web Automation Agent trong **Google ADK**
|
|
21
|
+
- Hoạt động độc lập như một browser automation SDK
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# 🚀 Cài đặt
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install kl-mcp-client
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
# 🧩 Thành phần của thư viện
|
|
34
|
+
|
|
35
|
+
Package gồm 2 module chính:
|
|
36
|
+
|
|
37
|
+
| File | Vai trò |
|
|
38
|
+
|------|---------|
|
|
39
|
+
| `client.py` | JSON-RPC HTTP Client giao tiếp với MCP Server |
|
|
40
|
+
| `tools.py` | Wrapper cấp cao, cung cấp API tiện dụng cho các Agent & automation |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# ✨ Tính năng chính
|
|
45
|
+
|
|
46
|
+
### ✔ Điều khiển trình duyệt
|
|
47
|
+
- Mở tab, load URL
|
|
48
|
+
- Click CSS selector, click bằng text, click bằng nodeId
|
|
49
|
+
- Nhập text vào input
|
|
50
|
+
- Screenshot (chuẩn ADK hiển thị được)
|
|
51
|
+
|
|
52
|
+
### ✔ DOM Tools nâng cao
|
|
53
|
+
- Find element by selector / text / XPath
|
|
54
|
+
- Lấy bounding box
|
|
55
|
+
- Lấy toàn bộ DOM Tree
|
|
56
|
+
- Lấy danh sách clickable elements
|
|
57
|
+
- Highlight elements (nếu server hỗ trợ)
|
|
58
|
+
|
|
59
|
+
### ✔ Tương tác hệ thống
|
|
60
|
+
- Upload file bằng base64
|
|
61
|
+
- Import cookies
|
|
62
|
+
- Evaluate JavaScript
|
|
63
|
+
|
|
64
|
+
### ✔ Chrome Remote Debugging
|
|
65
|
+
- Tạo session từ địa chỉ:
|
|
66
|
+
`http://localhost:9222/json/version`
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
# 🧭 Cách sử dụng
|
|
71
|
+
|
|
72
|
+
## 1. Import client & tools
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from kl_mcp_client.client import MCPClient
|
|
76
|
+
from kl_mcp_client.tools import MCPTools
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 2. Tạo kết nối tới MCP Server
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
mcp = MCPClient(
|
|
85
|
+
base_url="http://localhost:3000/mcp",
|
|
86
|
+
timeout=30,
|
|
87
|
+
retries=2
|
|
88
|
+
)
|
|
89
|
+
tools = MCPTools(mcp)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 3. Tạo session (Chrome Remote)
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
session = tools.create_session("http://localhost:9222/json/version")
|
|
98
|
+
sid = session["sessionId"]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 4. Mở URL
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
tools.open_page(sid, "https://google.com")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 5. Screenshot (hiển thị được trong ADK Web)
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
img = tools.screenshot(sid)
|
|
115
|
+
print(img)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Trả về:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"type": "image",
|
|
123
|
+
"mimeType": "image/png",
|
|
124
|
+
"data": "<base64>"
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 6. Click & Type
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
tools.click(sid, "#login")
|
|
134
|
+
tools.type(sid, "input[name=q]", "Hello world")
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 7. Find Elements
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
tools.find_element(sid, "#content")
|
|
143
|
+
tools.find_element_by_text(sid, "Đăng nhập")
|
|
144
|
+
tools.find_element_xpath(sid, "//input[@type='email']")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 8. Upload File
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
import base64
|
|
153
|
+
data = base64.b64encode(open("test.pdf", "rb").read()).decode()
|
|
154
|
+
|
|
155
|
+
tools.upload_file(
|
|
156
|
+
sid,
|
|
157
|
+
selector="input[type=file]",
|
|
158
|
+
filename="test.pdf",
|
|
159
|
+
base64Data=data
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 9. Import Cookies
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
tools.import_cookies(sid, [
|
|
169
|
+
{"name": "token", "value": "abc", "domain": "example.com", "path": "/"}
|
|
170
|
+
])
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 10. Đóng session
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
tools.close_session(sid)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
# 🧪 Ví dụ đầy đủ
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from kl_mcp_client.client import MCPClient
|
|
187
|
+
from kl_mcp_client.tools import MCPTools
|
|
188
|
+
import base64
|
|
189
|
+
|
|
190
|
+
mcp = MCPClient("http://localhost:3000/mcp")
|
|
191
|
+
tools = MCPTools(mcp)
|
|
192
|
+
|
|
193
|
+
# Create session
|
|
194
|
+
sid = tools.create_session("http://localhost:9222/json/version")["sessionId"]
|
|
195
|
+
|
|
196
|
+
# Navigate
|
|
197
|
+
tools.open_page(sid, "https://google.com")
|
|
198
|
+
|
|
199
|
+
# Screenshot
|
|
200
|
+
img = tools.screenshot(sid)
|
|
201
|
+
print("Screenshot returned:", img["mimeType"])
|
|
202
|
+
|
|
203
|
+
# Search
|
|
204
|
+
tools.type(sid, "input[name=q]", "Hello MCP")
|
|
205
|
+
tools.click_to_text(sid, "Google Search")
|
|
206
|
+
|
|
207
|
+
# Close
|
|
208
|
+
tools.close_session(sid)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
# 🏗 Kiến trúc
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
Python App / ADK Agent
|
|
217
|
+
↓
|
|
218
|
+
kl-mcp-client
|
|
219
|
+
↓
|
|
220
|
+
MCP Browser Server
|
|
221
|
+
↓
|
|
222
|
+
Chrome / CDP
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
# 📘 Yêu cầu
|
|
228
|
+
|
|
229
|
+
- Python ≥ 3.8
|
|
230
|
+
- MCP Server chạy sẵn (Chromedp backend)
|
|
231
|
+
- Chrome/Chromium với cờ:
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
chrome.exe --remote-debugging-port=9222
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
# 📝 License
|
|
240
|
+
|
|
241
|
+
MIT License.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
# 📚 Liên hệ
|
|
246
|
+
|
|
247
|
+
Nếu bạn cần:
|
|
248
|
+
|
|
249
|
+
- MCP Server đầy đủ
|
|
250
|
+
- Hỗ trợ tích hợp ADK Web Agent
|
|
251
|
+
- Thêm tool: DOM Tree, highlight, selector map…
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
kl_mcp_client/__init__.py
|
|
4
|
+
kl_mcp_client/__version__.py
|
|
5
|
+
kl_mcp_client/client.py
|
|
6
|
+
kl_mcp_client/tools.py
|
|
7
|
+
kl_mcp_client.egg-info/PKG-INFO
|
|
8
|
+
kl_mcp_client.egg-info/SOURCES.txt
|
|
9
|
+
kl_mcp_client.egg-info/dependency_links.txt
|
|
10
|
+
kl_mcp_client.egg-info/requires.txt
|
|
11
|
+
kl_mcp_client.egg-info/top_level.txt
|
|
12
|
+
kl_mcp_client/async/__init__.py
|
|
13
|
+
kl_mcp_client/async/client.py
|
|
14
|
+
kl_mcp_client/async/tools.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
kl_mcp_client
|
|
@@ -4,11 +4,15 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "kl-mcp-client"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.2"
|
|
8
8
|
description = "MCP Client"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name="Kyle", email="hngan.it@gmail.com" }
|
|
11
11
|
]
|
|
12
|
+
dependencies = [
|
|
13
|
+
"requests>=2.32.5",
|
|
14
|
+
"httpx>=0.28.1"
|
|
15
|
+
]
|
|
12
16
|
readme = "README.md"
|
|
13
17
|
license = { text = "MIT" }
|
|
14
|
-
requires-python = ">=3.
|
|
18
|
+
requires-python = ">=3.8"
|
kl_mcp_client-1.0.0/PKG-INFO
DELETED
kl_mcp_client-1.0.0/README.md
DELETED
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
mcp_client
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|