kl-mcp-client 2.0.4__tar.gz → 2.1.0__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-2.0.4 → kl_mcp_client-2.1.0}/PKG-INFO +1 -1
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/kl_mcp_client/asyncio/tools.py +88 -16
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/kl_mcp_client/tools.py +72 -56
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/kl_mcp_client.egg-info/PKG-INFO +1 -1
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/pyproject.toml +1 -1
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/README.md +0 -0
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/kl_mcp_client/__init__.py +0 -0
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/kl_mcp_client/__version__.py +0 -0
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/kl_mcp_client/asyncio/__init__.py +0 -0
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/kl_mcp_client/asyncio/client.py +0 -0
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/kl_mcp_client/client.py +0 -0
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/kl_mcp_client.egg-info/SOURCES.txt +0 -0
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/kl_mcp_client.egg-info/dependency_links.txt +0 -0
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/kl_mcp_client.egg-info/requires.txt +0 -0
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/kl_mcp_client.egg-info/top_level.txt +0 -0
- {kl_mcp_client-2.0.4 → kl_mcp_client-2.1.0}/setup.cfg +0 -0
|
@@ -46,7 +46,7 @@ class MCPTools:
|
|
|
46
46
|
|
|
47
47
|
async def screenshot(self, sessionId: str) -> Dict[str, Any]:
|
|
48
48
|
"""
|
|
49
|
-
ADK Web expects
|
|
49
|
+
ADK Web expects:
|
|
50
50
|
{
|
|
51
51
|
"type": "image",
|
|
52
52
|
"mimeType": "image/png",
|
|
@@ -121,7 +121,7 @@ class MCPTools:
|
|
|
121
121
|
) -> Dict[str, Any]:
|
|
122
122
|
args = {"sessionId": sessionId, "selector": selector}
|
|
123
123
|
if timeoutMs is not None:
|
|
124
|
-
args["timeoutMs"] = timeoutMs
|
|
124
|
+
args["timeoutMs"] = int(timeoutMs)
|
|
125
125
|
|
|
126
126
|
res = await self.client.call_tool("waitForSelector", args)
|
|
127
127
|
return res.get("structuredContent", {})
|
|
@@ -145,10 +145,10 @@ class MCPTools:
|
|
|
145
145
|
return res.get("structuredContent", {})
|
|
146
146
|
|
|
147
147
|
# ======================================================
|
|
148
|
-
# ADVANCED
|
|
148
|
+
# ADVANCED ACTIONS
|
|
149
149
|
# ======================================================
|
|
150
150
|
|
|
151
|
-
async def click_to_text(self, sessionId: str, text: str) ->
|
|
151
|
+
async def click_to_text(self, sessionId: str, text: str) -> Dict[str, Any]:
|
|
152
152
|
res = await self.client.call_tool(
|
|
153
153
|
"clickToText", {"sessionId": sessionId, "text": text}
|
|
154
154
|
)
|
|
@@ -178,25 +178,97 @@ class MCPTools:
|
|
|
178
178
|
)
|
|
179
179
|
return res.get("structuredContent", {})
|
|
180
180
|
|
|
181
|
+
async def get_dom_tree(self, sessionId: str, args: Optional[dict] = None):
|
|
182
|
+
return await self.client.call_tool(
|
|
183
|
+
"getDomTree", {"sessionId": sessionId, "args": args or {}}
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
async def get_clickable(self, sessionId: str, args: Optional[dict] = None):
|
|
187
|
+
return await self.client.call_tool(
|
|
188
|
+
"getClickable", {"sessionId": sessionId, "args": args or {}}
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
async def selector_map(
|
|
192
|
+
self, sessionId: str, selector: str, args: Optional[dict] = None
|
|
193
|
+
):
|
|
194
|
+
return await self.client.call_tool(
|
|
195
|
+
"selectorMap",
|
|
196
|
+
{"sessionId": sessionId, "selector": selector, "args": args or {}},
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# ======================================================
|
|
200
|
+
# AI / CONTENT PARSING
|
|
201
|
+
# ======================================================
|
|
202
|
+
|
|
181
203
|
async def parse_html_by_prompt(self, html: str, prompt: str) -> Dict[str, Any]:
|
|
204
|
+
res = await self.client.call_tool(
|
|
205
|
+
"parseHTMLByPrompt",
|
|
206
|
+
{"html": html, "prompt": prompt},
|
|
207
|
+
)
|
|
208
|
+
return res.get("structuredContent", {})
|
|
209
|
+
|
|
210
|
+
# ======================================================
|
|
211
|
+
# MOUSE / PERFORM ACTIONS
|
|
212
|
+
# ======================================================
|
|
213
|
+
|
|
214
|
+
async def perform_click_xy(
|
|
215
|
+
self,
|
|
216
|
+
sessionId: str,
|
|
217
|
+
x: float,
|
|
218
|
+
y: float,
|
|
219
|
+
) -> Dict[str, Any]:
|
|
220
|
+
"""
|
|
221
|
+
Move mouse smoothly to (x, y) and left click.
|
|
182
222
|
"""
|
|
183
|
-
|
|
223
|
+
res = await self.client.call_tool(
|
|
224
|
+
"perform",
|
|
225
|
+
{
|
|
226
|
+
"sessionId": sessionId,
|
|
227
|
+
"action": "click",
|
|
228
|
+
"x": float(x),
|
|
229
|
+
"y": float(y),
|
|
230
|
+
},
|
|
231
|
+
)
|
|
232
|
+
return res.get("structuredContent", {})
|
|
184
233
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
234
|
+
async def perform_drag(
|
|
235
|
+
self,
|
|
236
|
+
sessionId: str,
|
|
237
|
+
from_x: float,
|
|
238
|
+
from_y: float,
|
|
239
|
+
to_x: float,
|
|
240
|
+
to_y: float,
|
|
241
|
+
) -> Dict[str, Any]:
|
|
242
|
+
"""
|
|
243
|
+
Drag mouse from (from_x, from_y) to (to_x, to_y).
|
|
244
|
+
"""
|
|
245
|
+
res = await self.client.call_tool(
|
|
246
|
+
"perform",
|
|
247
|
+
{
|
|
248
|
+
"sessionId": sessionId,
|
|
249
|
+
"action": "drag",
|
|
250
|
+
"from": {"x": float(from_x), "y": float(from_y)},
|
|
251
|
+
"to": {"x": float(to_x), "y": float(to_y)},
|
|
252
|
+
},
|
|
253
|
+
)
|
|
254
|
+
return res.get("structuredContent", {})
|
|
191
255
|
|
|
192
|
-
|
|
193
|
-
|
|
256
|
+
async def perform_hover(
|
|
257
|
+
self,
|
|
258
|
+
sessionId: str,
|
|
259
|
+
x: float,
|
|
260
|
+
y: float,
|
|
261
|
+
) -> Dict[str, Any]:
|
|
262
|
+
"""
|
|
263
|
+
Move mouse smoothly to (x, y) without clicking.
|
|
194
264
|
"""
|
|
195
265
|
res = await self.client.call_tool(
|
|
196
|
-
"
|
|
266
|
+
"perform",
|
|
197
267
|
{
|
|
198
|
-
"
|
|
199
|
-
"
|
|
268
|
+
"sessionId": sessionId,
|
|
269
|
+
"action": "hover",
|
|
270
|
+
"x": float(x),
|
|
271
|
+
"y": float(y),
|
|
200
272
|
},
|
|
201
273
|
)
|
|
202
274
|
return res.get("structuredContent", {})
|
|
@@ -126,63 +126,27 @@ class MCPTools:
|
|
|
126
126
|
|
|
127
127
|
@_ensure_client
|
|
128
128
|
def upload_file(
|
|
129
|
-
self,
|
|
130
|
-
sessionId: str,
|
|
131
|
-
selector: str,
|
|
132
|
-
file_path: str,
|
|
129
|
+
self, sessionId: str, selector: str, filename: str, base64data: str
|
|
133
130
|
) -> Dict[str, Any]:
|
|
134
131
|
"""
|
|
135
|
-
Upload file
|
|
136
|
-
1. Multipart upload file lên MCP server
|
|
137
|
-
2. Nhận uploadId
|
|
138
|
-
3. Gọi MCP tool uploadFile với uploadId
|
|
139
|
-
|
|
132
|
+
Upload file vào input[type=file]
|
|
140
133
|
Args:
|
|
141
134
|
sessionId: MCP browser session
|
|
142
135
|
selector: CSS selector, ví dụ 'input[type=file]'
|
|
143
|
-
|
|
136
|
+
filename: tên file trên browser side
|
|
137
|
+
base64data: dữ liệu base64 (không kèm header)
|
|
138
|
+
Returns:
|
|
139
|
+
structured result từ server
|
|
144
140
|
"""
|
|
145
|
-
|
|
146
|
-
if not file_path:
|
|
147
|
-
return {"ok": False, "error": "file_path is required"}
|
|
148
|
-
|
|
149
|
-
# --------------------------------------------------
|
|
150
|
-
# 1️⃣ Multipart upload file lên MCP server
|
|
151
|
-
# --------------------------------------------------
|
|
152
|
-
try:
|
|
153
|
-
with open(file_path, "rb") as f:
|
|
154
|
-
resp = self.client.http.post(
|
|
155
|
-
"/upload",
|
|
156
|
-
files={"file": f},
|
|
157
|
-
timeout=300, # upload file lớn
|
|
158
|
-
)
|
|
159
|
-
except Exception as e:
|
|
160
|
-
return {"ok": False, "error": f"upload http failed: {e}"}
|
|
161
|
-
|
|
162
|
-
if resp.status_code != 200:
|
|
163
|
-
return {
|
|
164
|
-
"ok": False,
|
|
165
|
-
"error": f"upload http error {resp.status_code}: {resp.text}",
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
data = resp.json()
|
|
169
|
-
upload_id = data.get("uploadId")
|
|
170
|
-
if not upload_id:
|
|
171
|
-
return {"ok": False, "error": "uploadId not returned from server"}
|
|
172
|
-
|
|
173
|
-
# --------------------------------------------------
|
|
174
|
-
# 2️⃣ Gọi MCP tool uploadFile (PATH MODE)
|
|
175
|
-
# --------------------------------------------------
|
|
176
|
-
result = self.client.call_tool(
|
|
141
|
+
return self.client.call_tool(
|
|
177
142
|
"uploadFile",
|
|
178
143
|
{
|
|
179
144
|
"sessionId": sessionId,
|
|
180
145
|
"selector": selector,
|
|
181
|
-
"
|
|
146
|
+
"filename": filename,
|
|
147
|
+
"data": base64data,
|
|
182
148
|
},
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
return result.get("structuredContent", {})
|
|
149
|
+
).get("structuredContent", {})
|
|
186
150
|
|
|
187
151
|
@_ensure_client
|
|
188
152
|
def wait_for_selector(
|
|
@@ -265,16 +229,6 @@ class MCPTools:
|
|
|
265
229
|
{"sessionId": sessionId, "selector": selector, "args": args or {}},
|
|
266
230
|
)
|
|
267
231
|
|
|
268
|
-
@_ensure_client
|
|
269
|
-
def find_element_by_prompt(self, sessionId: str, prompt: str) -> Dict[str, Any]:
|
|
270
|
-
"""
|
|
271
|
-
Gọi tool findElementByPrompt trên MCP server.
|
|
272
|
-
Trả về structuredContent gồm: html, nodeId.
|
|
273
|
-
"""
|
|
274
|
-
return self.client.call_tool(
|
|
275
|
-
"findElementByPrompt", {"sessionId": sessionId, "prompt": prompt}
|
|
276
|
-
).get("structuredContent", {})
|
|
277
|
-
|
|
278
232
|
# ======================================================
|
|
279
233
|
# AI / CONTENT PARSING
|
|
280
234
|
# ======================================================
|
|
@@ -300,3 +254,65 @@ class MCPTools:
|
|
|
300
254
|
"prompt": prompt,
|
|
301
255
|
},
|
|
302
256
|
).get("structuredContent", {})
|
|
257
|
+
# ======================================================
|
|
258
|
+
# MOUSE / PERFORM ACTIONS
|
|
259
|
+
# ======================================================
|
|
260
|
+
@_ensure_client
|
|
261
|
+
def perform_click_xy(
|
|
262
|
+
self,
|
|
263
|
+
sessionId: str,
|
|
264
|
+
x: float,
|
|
265
|
+
y: float,
|
|
266
|
+
) -> Dict[str, Any]:
|
|
267
|
+
"""
|
|
268
|
+
Move mouse smoothly to (x, y) and left click.
|
|
269
|
+
"""
|
|
270
|
+
return self.client.call_tool(
|
|
271
|
+
"perform",
|
|
272
|
+
{
|
|
273
|
+
"sessionId": sessionId,
|
|
274
|
+
"action": "click",
|
|
275
|
+
"x": float(x),
|
|
276
|
+
"y": float(y),
|
|
277
|
+
},
|
|
278
|
+
).get("structuredContent", {})
|
|
279
|
+
@_ensure_client
|
|
280
|
+
def perform_drag(
|
|
281
|
+
self,
|
|
282
|
+
sessionId: str,
|
|
283
|
+
from_x: float,
|
|
284
|
+
from_y: float,
|
|
285
|
+
to_x: float,
|
|
286
|
+
to_y: float,
|
|
287
|
+
) -> Dict[str, Any]:
|
|
288
|
+
"""
|
|
289
|
+
Drag mouse from (from_x, from_y) to (to_x, to_y).
|
|
290
|
+
"""
|
|
291
|
+
return self.client.call_tool(
|
|
292
|
+
"perform",
|
|
293
|
+
{
|
|
294
|
+
"sessionId": sessionId,
|
|
295
|
+
"action": "drag",
|
|
296
|
+
"from": {"x": float(from_x), "y": float(from_y)},
|
|
297
|
+
"to": {"x": float(to_x), "y": float(to_y)},
|
|
298
|
+
},
|
|
299
|
+
).get("structuredContent", {})
|
|
300
|
+
@_ensure_client
|
|
301
|
+
def perform_hover(
|
|
302
|
+
self,
|
|
303
|
+
sessionId: str,
|
|
304
|
+
x: float,
|
|
305
|
+
y: float,
|
|
306
|
+
) -> Dict[str, Any]:
|
|
307
|
+
"""
|
|
308
|
+
Move mouse smoothly to (x, y) without clicking.
|
|
309
|
+
"""
|
|
310
|
+
return self.client.call_tool(
|
|
311
|
+
"perform",
|
|
312
|
+
{
|
|
313
|
+
"sessionId": sessionId,
|
|
314
|
+
"action": "hover",
|
|
315
|
+
"x": float(x),
|
|
316
|
+
"y": float(y),
|
|
317
|
+
},
|
|
318
|
+
).get("structuredContent", {})
|
|
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
|