# kl-mcp-client

SDK Python để gọi MCP Browser Server qua JSON-RPC HTTP.

## Cài đặt

```bash
pip install kl-mcp-client
```

## Thành phần

- `kl_mcp_client.client.MCPClient`: sync JSON-RPC client
- `kl_mcp_client.tools.MCPTools`: sync tools wrapper
- `kl_mcp_client.asyncio.client.MCPClient`: async JSON-RPC client
- `kl_mcp_client.asyncio.tools.MCPTools`: async tools wrapper

## Quickstart (sync)

```python
from kl_mcp_client.tools import MCPTools

tools = MCPTools()
tools.connect_mcp("http://localhost:3000/mcp")

sid = tools.create_session("http://localhost:9222/json/version")["sessionId"]
tools.open_page(sid, "https://example.com")
print(tools.get_html(sid))
```

## Quickstart (async)

```python
import asyncio
from kl_mcp_client.asyncio.tools import MCPTools


async def main():
    tools = MCPTools()
    tools.connect_mcp("http://localhost:3000/mcp")

    sid = (await tools.create_session("http://localhost:9222/json/version"))["sessionId"]
    await tools.open_page(sid, "https://example.com")
    print(await tools.get_html(sid))
    await tools.close()


asyncio.run(main())
```

## API chính

### Kết nối / core
- `connect_mcp(mcp_url=None, mcpUrl=None, headers=None, timeout=30, retries=2, proxies=None)`
- `close()`

### Session / navigation
- `create_session(cdpUrl)`
- `close_session(sessionId)`
- `list_sessions()`
- `open_page(sessionId, url)`
- `get_html(sessionId)`
- `screenshot(sessionId)`
- `wait_for_selector(sessionId, selector, timeout=None, timeoutMs=None)`

### Element / action
- `click(sessionId, selector)`
- `type(sessionId, selector, text)`
- `click_to_text(sessionId, text)`
- `find_element(sessionId, selector)`
- `find_all(sessionId, selector)`
- `find_element_by_xpath(sessionId, xpath)`
- `find_element_by_text(sessionId, text)`
- `find_element_by_prompt(sessionId, prompt)`
- `get_bounding_box(sessionId, selector)`
- `click_bounding_box(sessionId, selector)`
- `perform(...)`
- `scroll(...)`

### File / cookie
- `upload_file_to_server(file_path)`: upload file local lên MCP server và nhận `uploadId`
- `set_file_for_input(sessionId, selector, uploadId)`: gắn `uploadId` vào `input[type=file]`
- `upload_file(sessionId, selector, file_path)`: wrapper tương thích ngược, tự chạy cả 2 bước trên
- `import_cookies(sessionId, cookies)`
- `get_cookies(sessionId)`
- `cookies(sessionId)`: alias của `get_cookies`

### Stream / parsing / viewport
- `evaluate(sessionId, expression)`
- `evaluate_stream(sessionId, expression, chunkSize=100)`
- `stream_pull(stream_id, offset=0, limit=100)`
- `evaluate_stream_all(sessionId, expression, chunkSize=100, max_items=None)`
- `parse_html_by_prompt(html, prompt)`
- `viewport(sessionId, viewport=None)`
- `set_viewport(...)`
- `get_viewport(sessionId)`

### Tab / browser helpers
- `get_current_url(sessionId)`
- `current_url(sessionId)`: alias của `get_current_url`
- `list_tabs(sessionId)`
- `switch_tab(sessionId, tabId=None, targetId=None)`
- `close_tab(sessionId, tabId)`
- `new_tab(sessionId, url=None)`
- `switch_tab_by_title(sessionId, title)`
- `switch_tab_by_url(sessionId, url)`
- `current_tab(sessionId)`
- `create_browser(payload=None)`
- `release_browser(pod_name)`
- `drag_and_drop(...)`
- `hover(...)`

## Upload file 2 bước

```python
upload = tools.upload_file_to_server("/absolute/path/to/test.pdf")

tools.set_file_for_input(
    sessionId=sid,
    selector="input[type=file]",
    uploadId=upload["uploadId"],
)
```

Hoặc dùng wrapper cũ:

```python
tools.upload_file(
    sessionId=sid,
    selector="input[type=file]",
    file_path="/absolute/path/to/test.pdf",
)
```

## Lưu ý

- Cần gọi `connect_mcp(...)` trước khi gọi tool khác.
- Flow upload dùng endpoint HTTP `/upload` để lấy `uploadId`, sau đó gọi tool `uploadFile`.
- Bản async hỗ trợ cùng semantics với bản sync, nhưng tất cả lời gọi là bất đồng bộ (`await`).

## License

MIT License.
