capguru-mcp 0.2.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.
- capguru_mcp-0.2.0/PKG-INFO +183 -0
- capguru_mcp-0.2.0/README.md +167 -0
- capguru_mcp-0.2.0/capguru_mcp/__init__.py +1 -0
- capguru_mcp-0.2.0/capguru_mcp/__main__.py +3 -0
- capguru_mcp-0.2.0/capguru_mcp/server.py +591 -0
- capguru_mcp-0.2.0/capguru_mcp.egg-info/PKG-INFO +183 -0
- capguru_mcp-0.2.0/capguru_mcp.egg-info/SOURCES.txt +11 -0
- capguru_mcp-0.2.0/capguru_mcp.egg-info/dependency_links.txt +1 -0
- capguru_mcp-0.2.0/capguru_mcp.egg-info/entry_points.txt +2 -0
- capguru_mcp-0.2.0/capguru_mcp.egg-info/requires.txt +3 -0
- capguru_mcp-0.2.0/capguru_mcp.egg-info/top_level.txt +1 -0
- capguru_mcp-0.2.0/pyproject.toml +31 -0
- capguru_mcp-0.2.0/setup.cfg +4 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: capguru-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: MCP server for the Cap.Guru captcha solving service: lets AI assistants open pages and solve reCAPTCHA v2/v3, Cloudflare Turnstile, text captchas, hCaptcha, GeeTest, FunCaptcha, TikTok, AWS WAF and Yandex SmartCaptcha
|
|
5
|
+
Author: Cap.Guru
|
|
6
|
+
Project-URL: Homepage, https://cap.guru
|
|
7
|
+
Project-URL: Documentation, https://docs.cap.guru
|
|
8
|
+
Keywords: mcp,captcha,solver,playwright,cap.guru,claude,cursor,turnstile,recaptcha
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: mcp>=1.2
|
|
14
|
+
Requires-Dist: capguru>=0.4.0
|
|
15
|
+
Requires-Dist: playwright>=1.40
|
|
16
|
+
|
|
17
|
+
# capguru-mcp
|
|
18
|
+
|
|
19
|
+
**English** | [Русский](#русский)
|
|
20
|
+
|
|
21
|
+
MCP server for the [Cap.Guru](https://cap.guru) captcha solving service.
|
|
22
|
+
It lets AI assistants (Claude Desktop, Claude Code, Cursor and other MCP clients) open web pages in a browser and solve captchas on them.
|
|
23
|
+
|
|
24
|
+
Supported: reCAPTCHA v2/v3, Cloudflare Turnstile, text (image) captchas, hCaptcha, GeeTest v3/v4, FunCaptcha (Arkose Labs), TikTok, AWS WAF, Yandex SmartCaptcha.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
pip install capguru-mcp
|
|
30
|
+
playwright install chromium
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Connecting to an MCP client
|
|
34
|
+
|
|
35
|
+
Claude Desktop (`claude_desktop_config.json`), Cursor (`.cursor/mcp.json`) and most other clients:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"capguru": {
|
|
41
|
+
"command": "capguru-mcp",
|
|
42
|
+
"env": {
|
|
43
|
+
"CAPGURU_API_KEY": "YOUR_KEY"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Claude Code:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
claude mcp add capguru --env CAPGURU_API_KEY=YOUR_KEY -- capguru-mcp
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Without installing (via [uv](https://docs.astral.sh/uv/)): use `"command": "uvx", "args": ["capguru-mcp"]`.
|
|
57
|
+
|
|
58
|
+
## Tools
|
|
59
|
+
|
|
60
|
+
| Tool | Description |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `open_browser` | Launch a browser (`headless`, `channel`: `chrome`/`msedge`, `proxy`) |
|
|
63
|
+
| `connect_browser` | Connect to a running Chrome started with `--remote-debugging-port` |
|
|
64
|
+
| `goto` | Open a URL |
|
|
65
|
+
| `click`, `fill` | Click an element, type text into an input |
|
|
66
|
+
| `screenshot` | Screenshot of the page |
|
|
67
|
+
| `page_info` | Current URL and title |
|
|
68
|
+
| `detect_captcha` | Detect the captcha type without solving |
|
|
69
|
+
| `solve_captcha` | Solve the captcha: `auto` or a specific type |
|
|
70
|
+
| `solve_recaptcha_token` | Get a reCAPTCHA v2 / v2 Invisible / v3 token (`sitekey`, `pageurl`, `version`, `min_score`); works without a browser too |
|
|
71
|
+
| `solve_turnstile_token` | Get a Cloudflare Turnstile token (`sitekey`, `pageurl`); works without a browser too |
|
|
72
|
+
| `solve_image_captcha` | Recognize a text captcha from an image: element `selector`, `image_base64`, `image_path` or `image_url`; optional `vernet`, `fill_selector` |
|
|
73
|
+
| `close_browser` | Close the browser |
|
|
74
|
+
|
|
75
|
+
Example requests to the assistant:
|
|
76
|
+
|
|
77
|
+
- *"Open https://site.com/login, solve the captcha and take a screenshot."*
|
|
78
|
+
- *"Get a reCAPTCHA token for sitekey 6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ- on https://www.google.com/recaptcha/api2/demo."*
|
|
79
|
+
- *"Get a reCAPTCHA v3 token for sitekey 6Ldb4ccaAAAAACMxv8uxUIMckZy1og98rs_h3AmO on http://learn.captcha.guru/ln/recap3/."*
|
|
80
|
+
- *"Open https://react-turnstile.vercel.app/basic and solve the Turnstile."*
|
|
81
|
+
- *"Recognize the captcha image #captcha_img and type the answer into #captcha_input."*
|
|
82
|
+
|
|
83
|
+
`solve_recaptcha_token` uses the Cap.Guru token method (`userrecaptcha`): it returns the `g-recaptcha-response` token. If a page is open, the sitekey, URL and User-Agent are taken from it; for v2 the token is inserted into the page and the site callback is called. For v3 pass `version: v3` (optionally `min_score`, service default 0.1); the token is only returned, because v3 sites request it themselves.
|
|
84
|
+
|
|
85
|
+
`solve_turnstile_token` works the same way for Cloudflare Turnstile (`method=turnstile`): with an open page the token is put into `cf-turnstile-response` and the Turnstile callback is called. Typical solve time is 10–30 seconds.
|
|
86
|
+
|
|
87
|
+
## Settings (environment variables)
|
|
88
|
+
|
|
89
|
+
| Variable | Default | Description |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| `CAPGURU_API_KEY` | — | Cap.Guru API key (required) |
|
|
92
|
+
| `CAPGURU_SERVER` | `https://api.cap.guru` | API server |
|
|
93
|
+
| `CAPGURU_ATTEMPTS` | `5` | Max number of solving attempts |
|
|
94
|
+
| `CAPGURU_DEBUG` | `1` | Include the solver log in tool results |
|
|
95
|
+
| `CAPGURU_BROWSER_CHANNEL` | — | `chrome` or `msedge` to use an installed browser |
|
|
96
|
+
| `CAPGURU_BROWSER_PATH` | — | Path to a browser executable |
|
|
97
|
+
| `CAPGURU_BROWSER_ARGS` | — | Extra browser arguments, space separated |
|
|
98
|
+
|
|
99
|
+
Full documentation: https://docs.cap.guru
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Русский
|
|
104
|
+
|
|
105
|
+
MCP-сервер для сервиса решения капчи [Cap.Guru](https://cap.guru).
|
|
106
|
+
Позволяет ИИ-ассистентам (Claude Desktop, Claude Code, Cursor и другим MCP-клиентам) открывать страницы в браузере и решать на них капчу.
|
|
107
|
+
|
|
108
|
+
Поддерживаются: reCAPTCHA v2/v3, Cloudflare Turnstile, текстовые капчи (картинкой), hCaptcha, GeeTest v3/v4, FunCaptcha (Arkose Labs), TikTok, AWS WAF, Яндекс SmartCaptcha.
|
|
109
|
+
|
|
110
|
+
### Установка
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
pip install capguru-mcp
|
|
114
|
+
playwright install chromium
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Подключение к MCP-клиенту
|
|
118
|
+
|
|
119
|
+
Claude Desktop (`claude_desktop_config.json`), Cursor (`.cursor/mcp.json`) и большинство других клиентов:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"mcpServers": {
|
|
124
|
+
"capguru": {
|
|
125
|
+
"command": "capguru-mcp",
|
|
126
|
+
"env": {
|
|
127
|
+
"CAPGURU_API_KEY": "ВАШ_КЛЮЧ"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Claude Code:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
claude mcp add capguru --env CAPGURU_API_KEY=ВАШ_КЛЮЧ -- capguru-mcp
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Без установки (через [uv](https://docs.astral.sh/uv/)): `"command": "uvx", "args": ["capguru-mcp"]`.
|
|
141
|
+
|
|
142
|
+
### Инструменты
|
|
143
|
+
|
|
144
|
+
| Инструмент | Описание |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `open_browser` | Запустить браузер (`headless`, `channel`: `chrome`/`msedge`, `proxy`) |
|
|
147
|
+
| `connect_browser` | Подключиться к запущенному Chrome с `--remote-debugging-port` |
|
|
148
|
+
| `goto` | Открыть адрес |
|
|
149
|
+
| `click`, `fill` | Нажать на элемент, ввести текст в поле |
|
|
150
|
+
| `screenshot` | Скриншот страницы |
|
|
151
|
+
| `page_info` | Текущий адрес и заголовок |
|
|
152
|
+
| `detect_captcha` | Определить тип капчи, не решая |
|
|
153
|
+
| `solve_captcha` | Решить капчу: `auto` или конкретный тип |
|
|
154
|
+
| `solve_recaptcha_token` | Получить токен reCAPTCHA v2 / v2 Invisible / v3 (`sitekey`, `pageurl`, `version`, `min_score`); работает и без браузера |
|
|
155
|
+
| `solve_turnstile_token` | Получить токен Cloudflare Turnstile (`sitekey`, `pageurl`); работает и без браузера |
|
|
156
|
+
| `solve_image_captcha` | Распознать текстовую капчу с картинки: элемент `selector`, `image_base64`, `image_path` или `image_url`; по желанию `vernet`, `fill_selector` |
|
|
157
|
+
| `close_browser` | Закрыть браузер |
|
|
158
|
+
|
|
159
|
+
Примеры запросов ассистенту:
|
|
160
|
+
|
|
161
|
+
- *«Открой https://site.com/login, реши капчу и сделай скриншот».*
|
|
162
|
+
- *«Получи токен reCAPTCHA для sitekey 6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ- на https://www.google.com/recaptcha/api2/demo».*
|
|
163
|
+
- *«Получи токен reCAPTCHA v3 для sitekey 6Ldb4ccaAAAAACMxv8uxUIMckZy1og98rs_h3AmO на http://learn.captcha.guru/ln/recap3/».*
|
|
164
|
+
- *«Открой https://react-turnstile.vercel.app/basic и реши Turnstile».*
|
|
165
|
+
- *«Распознай капчу с картинки #captcha_img и введи ответ в #captcha_input».*
|
|
166
|
+
|
|
167
|
+
`solve_recaptcha_token` использует токенный метод Cap.Guru (`userrecaptcha`) и возвращает токен `g-recaptcha-response`. Если страница открыта, sitekey, адрес и User-Agent берутся с неё; для v2 токен вставляется на страницу и вызывается callback сайта. Для v3 укажите `version: v3` (и при необходимости `min_score`, по умолчанию у сервиса 0.1); токен только возвращается, потому что сайты с v3 запрашивают его сами.
|
|
168
|
+
|
|
169
|
+
`solve_turnstile_token` так же работает для Cloudflare Turnstile (`method=turnstile`): если страница открыта, токен вставляется в `cf-turnstile-response` и вызывается callback Turnstile. Обычное время решения — 10–30 секунд.
|
|
170
|
+
|
|
171
|
+
### Настройки (переменные окружения)
|
|
172
|
+
|
|
173
|
+
| Переменная | По умолчанию | Описание |
|
|
174
|
+
|---|---|---|
|
|
175
|
+
| `CAPGURU_API_KEY` | — | API-ключ Cap.Guru (обязательно) |
|
|
176
|
+
| `CAPGURU_SERVER` | `https://api.cap.guru` | Сервер API |
|
|
177
|
+
| `CAPGURU_ATTEMPTS` | `5` | Макс. количество попыток |
|
|
178
|
+
| `CAPGURU_DEBUG` | `1` | Добавлять лог солвера в ответ инструмента |
|
|
179
|
+
| `CAPGURU_BROWSER_CHANNEL` | — | `chrome` или `msedge`, чтобы использовать установленный браузер |
|
|
180
|
+
| `CAPGURU_BROWSER_PATH` | — | Путь к исполняемому файлу браузера |
|
|
181
|
+
| `CAPGURU_BROWSER_ARGS` | — | Дополнительные аргументы браузера через пробел |
|
|
182
|
+
|
|
183
|
+
Полная документация: https://docs.cap.guru
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# capguru-mcp
|
|
2
|
+
|
|
3
|
+
**English** | [Русский](#русский)
|
|
4
|
+
|
|
5
|
+
MCP server for the [Cap.Guru](https://cap.guru) captcha solving service.
|
|
6
|
+
It lets AI assistants (Claude Desktop, Claude Code, Cursor and other MCP clients) open web pages in a browser and solve captchas on them.
|
|
7
|
+
|
|
8
|
+
Supported: reCAPTCHA v2/v3, Cloudflare Turnstile, text (image) captchas, hCaptcha, GeeTest v3/v4, FunCaptcha (Arkose Labs), TikTok, AWS WAF, Yandex SmartCaptcha.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
pip install capguru-mcp
|
|
14
|
+
playwright install chromium
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Connecting to an MCP client
|
|
18
|
+
|
|
19
|
+
Claude Desktop (`claude_desktop_config.json`), Cursor (`.cursor/mcp.json`) and most other clients:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"capguru": {
|
|
25
|
+
"command": "capguru-mcp",
|
|
26
|
+
"env": {
|
|
27
|
+
"CAPGURU_API_KEY": "YOUR_KEY"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Claude Code:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
claude mcp add capguru --env CAPGURU_API_KEY=YOUR_KEY -- capguru-mcp
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Without installing (via [uv](https://docs.astral.sh/uv/)): use `"command": "uvx", "args": ["capguru-mcp"]`.
|
|
41
|
+
|
|
42
|
+
## Tools
|
|
43
|
+
|
|
44
|
+
| Tool | Description |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `open_browser` | Launch a browser (`headless`, `channel`: `chrome`/`msedge`, `proxy`) |
|
|
47
|
+
| `connect_browser` | Connect to a running Chrome started with `--remote-debugging-port` |
|
|
48
|
+
| `goto` | Open a URL |
|
|
49
|
+
| `click`, `fill` | Click an element, type text into an input |
|
|
50
|
+
| `screenshot` | Screenshot of the page |
|
|
51
|
+
| `page_info` | Current URL and title |
|
|
52
|
+
| `detect_captcha` | Detect the captcha type without solving |
|
|
53
|
+
| `solve_captcha` | Solve the captcha: `auto` or a specific type |
|
|
54
|
+
| `solve_recaptcha_token` | Get a reCAPTCHA v2 / v2 Invisible / v3 token (`sitekey`, `pageurl`, `version`, `min_score`); works without a browser too |
|
|
55
|
+
| `solve_turnstile_token` | Get a Cloudflare Turnstile token (`sitekey`, `pageurl`); works without a browser too |
|
|
56
|
+
| `solve_image_captcha` | Recognize a text captcha from an image: element `selector`, `image_base64`, `image_path` or `image_url`; optional `vernet`, `fill_selector` |
|
|
57
|
+
| `close_browser` | Close the browser |
|
|
58
|
+
|
|
59
|
+
Example requests to the assistant:
|
|
60
|
+
|
|
61
|
+
- *"Open https://site.com/login, solve the captcha and take a screenshot."*
|
|
62
|
+
- *"Get a reCAPTCHA token for sitekey 6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ- on https://www.google.com/recaptcha/api2/demo."*
|
|
63
|
+
- *"Get a reCAPTCHA v3 token for sitekey 6Ldb4ccaAAAAACMxv8uxUIMckZy1og98rs_h3AmO on http://learn.captcha.guru/ln/recap3/."*
|
|
64
|
+
- *"Open https://react-turnstile.vercel.app/basic and solve the Turnstile."*
|
|
65
|
+
- *"Recognize the captcha image #captcha_img and type the answer into #captcha_input."*
|
|
66
|
+
|
|
67
|
+
`solve_recaptcha_token` uses the Cap.Guru token method (`userrecaptcha`): it returns the `g-recaptcha-response` token. If a page is open, the sitekey, URL and User-Agent are taken from it; for v2 the token is inserted into the page and the site callback is called. For v3 pass `version: v3` (optionally `min_score`, service default 0.1); the token is only returned, because v3 sites request it themselves.
|
|
68
|
+
|
|
69
|
+
`solve_turnstile_token` works the same way for Cloudflare Turnstile (`method=turnstile`): with an open page the token is put into `cf-turnstile-response` and the Turnstile callback is called. Typical solve time is 10–30 seconds.
|
|
70
|
+
|
|
71
|
+
## Settings (environment variables)
|
|
72
|
+
|
|
73
|
+
| Variable | Default | Description |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| `CAPGURU_API_KEY` | — | Cap.Guru API key (required) |
|
|
76
|
+
| `CAPGURU_SERVER` | `https://api.cap.guru` | API server |
|
|
77
|
+
| `CAPGURU_ATTEMPTS` | `5` | Max number of solving attempts |
|
|
78
|
+
| `CAPGURU_DEBUG` | `1` | Include the solver log in tool results |
|
|
79
|
+
| `CAPGURU_BROWSER_CHANNEL` | — | `chrome` or `msedge` to use an installed browser |
|
|
80
|
+
| `CAPGURU_BROWSER_PATH` | — | Path to a browser executable |
|
|
81
|
+
| `CAPGURU_BROWSER_ARGS` | — | Extra browser arguments, space separated |
|
|
82
|
+
|
|
83
|
+
Full documentation: https://docs.cap.guru
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Русский
|
|
88
|
+
|
|
89
|
+
MCP-сервер для сервиса решения капчи [Cap.Guru](https://cap.guru).
|
|
90
|
+
Позволяет ИИ-ассистентам (Claude Desktop, Claude Code, Cursor и другим MCP-клиентам) открывать страницы в браузере и решать на них капчу.
|
|
91
|
+
|
|
92
|
+
Поддерживаются: reCAPTCHA v2/v3, Cloudflare Turnstile, текстовые капчи (картинкой), hCaptcha, GeeTest v3/v4, FunCaptcha (Arkose Labs), TikTok, AWS WAF, Яндекс SmartCaptcha.
|
|
93
|
+
|
|
94
|
+
### Установка
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
pip install capguru-mcp
|
|
98
|
+
playwright install chromium
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Подключение к MCP-клиенту
|
|
102
|
+
|
|
103
|
+
Claude Desktop (`claude_desktop_config.json`), Cursor (`.cursor/mcp.json`) и большинство других клиентов:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"mcpServers": {
|
|
108
|
+
"capguru": {
|
|
109
|
+
"command": "capguru-mcp",
|
|
110
|
+
"env": {
|
|
111
|
+
"CAPGURU_API_KEY": "ВАШ_КЛЮЧ"
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Claude Code:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
claude mcp add capguru --env CAPGURU_API_KEY=ВАШ_КЛЮЧ -- capguru-mcp
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Без установки (через [uv](https://docs.astral.sh/uv/)): `"command": "uvx", "args": ["capguru-mcp"]`.
|
|
125
|
+
|
|
126
|
+
### Инструменты
|
|
127
|
+
|
|
128
|
+
| Инструмент | Описание |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `open_browser` | Запустить браузер (`headless`, `channel`: `chrome`/`msedge`, `proxy`) |
|
|
131
|
+
| `connect_browser` | Подключиться к запущенному Chrome с `--remote-debugging-port` |
|
|
132
|
+
| `goto` | Открыть адрес |
|
|
133
|
+
| `click`, `fill` | Нажать на элемент, ввести текст в поле |
|
|
134
|
+
| `screenshot` | Скриншот страницы |
|
|
135
|
+
| `page_info` | Текущий адрес и заголовок |
|
|
136
|
+
| `detect_captcha` | Определить тип капчи, не решая |
|
|
137
|
+
| `solve_captcha` | Решить капчу: `auto` или конкретный тип |
|
|
138
|
+
| `solve_recaptcha_token` | Получить токен reCAPTCHA v2 / v2 Invisible / v3 (`sitekey`, `pageurl`, `version`, `min_score`); работает и без браузера |
|
|
139
|
+
| `solve_turnstile_token` | Получить токен Cloudflare Turnstile (`sitekey`, `pageurl`); работает и без браузера |
|
|
140
|
+
| `solve_image_captcha` | Распознать текстовую капчу с картинки: элемент `selector`, `image_base64`, `image_path` или `image_url`; по желанию `vernet`, `fill_selector` |
|
|
141
|
+
| `close_browser` | Закрыть браузер |
|
|
142
|
+
|
|
143
|
+
Примеры запросов ассистенту:
|
|
144
|
+
|
|
145
|
+
- *«Открой https://site.com/login, реши капчу и сделай скриншот».*
|
|
146
|
+
- *«Получи токен reCAPTCHA для sitekey 6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ- на https://www.google.com/recaptcha/api2/demo».*
|
|
147
|
+
- *«Получи токен reCAPTCHA v3 для sitekey 6Ldb4ccaAAAAACMxv8uxUIMckZy1og98rs_h3AmO на http://learn.captcha.guru/ln/recap3/».*
|
|
148
|
+
- *«Открой https://react-turnstile.vercel.app/basic и реши Turnstile».*
|
|
149
|
+
- *«Распознай капчу с картинки #captcha_img и введи ответ в #captcha_input».*
|
|
150
|
+
|
|
151
|
+
`solve_recaptcha_token` использует токенный метод Cap.Guru (`userrecaptcha`) и возвращает токен `g-recaptcha-response`. Если страница открыта, sitekey, адрес и User-Agent берутся с неё; для v2 токен вставляется на страницу и вызывается callback сайта. Для v3 укажите `version: v3` (и при необходимости `min_score`, по умолчанию у сервиса 0.1); токен только возвращается, потому что сайты с v3 запрашивают его сами.
|
|
152
|
+
|
|
153
|
+
`solve_turnstile_token` так же работает для Cloudflare Turnstile (`method=turnstile`): если страница открыта, токен вставляется в `cf-turnstile-response` и вызывается callback Turnstile. Обычное время решения — 10–30 секунд.
|
|
154
|
+
|
|
155
|
+
### Настройки (переменные окружения)
|
|
156
|
+
|
|
157
|
+
| Переменная | По умолчанию | Описание |
|
|
158
|
+
|---|---|---|
|
|
159
|
+
| `CAPGURU_API_KEY` | — | API-ключ Cap.Guru (обязательно) |
|
|
160
|
+
| `CAPGURU_SERVER` | `https://api.cap.guru` | Сервер API |
|
|
161
|
+
| `CAPGURU_ATTEMPTS` | `5` | Макс. количество попыток |
|
|
162
|
+
| `CAPGURU_DEBUG` | `1` | Добавлять лог солвера в ответ инструмента |
|
|
163
|
+
| `CAPGURU_BROWSER_CHANNEL` | — | `chrome` или `msedge`, чтобы использовать установленный браузер |
|
|
164
|
+
| `CAPGURU_BROWSER_PATH` | — | Путь к исполняемому файлу браузера |
|
|
165
|
+
| `CAPGURU_BROWSER_ARGS` | — | Дополнительные аргументы браузера через пробел |
|
|
166
|
+
|
|
167
|
+
Полная документация: https://docs.cap.guru
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.2.0'
|
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import contextlib
|
|
5
|
+
import functools
|
|
6
|
+
import io
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from mcp.server.mcpserver import MCPServer as _Server, Image
|
|
13
|
+
from mcp.server.mcpserver.exceptions import ToolError
|
|
14
|
+
except ImportError:
|
|
15
|
+
from mcp.server.fastmcp import FastMCP as _Server, Image
|
|
16
|
+
from mcp.server.fastmcp.exceptions import ToolError
|
|
17
|
+
|
|
18
|
+
from captcha_solver import CaptchaSolver, CapGuruFatalError
|
|
19
|
+
from captcha_solver.capguru_client import CapGuruClient
|
|
20
|
+
|
|
21
|
+
INSTRUCTIONS = (
|
|
22
|
+
'Cap.Guru captcha solver. Typical flow: open_browser (or connect_browser) -> goto(url) -> '
|
|
23
|
+
'interact with the page (click/fill) until a captcha appears -> solve_captcha(). '
|
|
24
|
+
'Use screenshot() to see the page. Supported: reCAPTCHA v2, hCaptcha, GeeTest v3/v4, '
|
|
25
|
+
'FunCaptcha, TikTok, AWS WAF, Yandex SmartCaptcha. '
|
|
26
|
+
'For reCAPTCHA v2 / v2 Invisible / v3 you can also get a token without solving images: '
|
|
27
|
+
'solve_recaptcha_token(sitekey, pageurl, version), it works with or without an open browser. '
|
|
28
|
+
'Cloudflare Turnstile is solved the same way with solve_turnstile_token(sitekey, pageurl). '
|
|
29
|
+
'Text (image) captchas: solve_image_captcha(selector or image), optionally fill_selector to type the answer.'
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
mcp = _Server('capguru', instructions=INSTRUCTIONS)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def tool(fn):
|
|
36
|
+
@functools.wraps(fn)
|
|
37
|
+
async def wrapper(*args, **kwargs):
|
|
38
|
+
try:
|
|
39
|
+
return await fn(*args, **kwargs)
|
|
40
|
+
except ToolError:
|
|
41
|
+
raise
|
|
42
|
+
except Exception as e:
|
|
43
|
+
raise ToolError(f'{type(e).__name__}: {e}') from e
|
|
44
|
+
return mcp.tool()(wrapper)
|
|
45
|
+
|
|
46
|
+
SOLVE_METHODS = {
|
|
47
|
+
'auto': None,
|
|
48
|
+
'recaptcha2': 'solve_recaptcha2',
|
|
49
|
+
'hcaptcha': 'solve_hcaptcha',
|
|
50
|
+
'yandex': 'solve_other',
|
|
51
|
+
'geetest': 'solve_geetest',
|
|
52
|
+
'funcaptcha': 'solve_funcaptcha',
|
|
53
|
+
'tiktok': 'solve_tiktok',
|
|
54
|
+
'aws': 'solve_aws',
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class _State:
|
|
59
|
+
def __init__(self) -> None:
|
|
60
|
+
self.pw = None
|
|
61
|
+
self.browser = None
|
|
62
|
+
self.context = None
|
|
63
|
+
self.page = None
|
|
64
|
+
self.solver: Optional[CaptchaSolver] = None
|
|
65
|
+
self.owns_browser = False
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
S = _State()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class _Tee(io.TextIOBase):
|
|
72
|
+
def __init__(self) -> None:
|
|
73
|
+
self.buf = io.StringIO()
|
|
74
|
+
|
|
75
|
+
def write(self, s: str) -> int:
|
|
76
|
+
self.buf.write(s)
|
|
77
|
+
sys.stderr.write(s)
|
|
78
|
+
return len(s)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@contextlib.contextmanager
|
|
82
|
+
def _capture():
|
|
83
|
+
tee = _Tee()
|
|
84
|
+
with contextlib.redirect_stdout(tee):
|
|
85
|
+
yield tee
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _api_key() -> str:
|
|
89
|
+
key = os.environ.get('CAPGURU_API_KEY', '').strip()
|
|
90
|
+
if not key:
|
|
91
|
+
raise RuntimeError('CAPGURU_API_KEY environment variable is not set in the MCP server config')
|
|
92
|
+
return key
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _server_url() -> str:
|
|
96
|
+
return os.environ.get('CAPGURU_SERVER', '').strip() or 'https://api.cap.guru'
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _debug() -> bool:
|
|
100
|
+
return os.environ.get('CAPGURU_DEBUG', '1').strip().lower() not in ('0', 'false', 'no', '')
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _attempts() -> int:
|
|
104
|
+
try:
|
|
105
|
+
return max(1, int(os.environ.get('CAPGURU_ATTEMPTS', '5')))
|
|
106
|
+
except ValueError:
|
|
107
|
+
return 5
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def _ensure_playwright():
|
|
111
|
+
if S.pw is None:
|
|
112
|
+
from playwright.async_api import async_playwright
|
|
113
|
+
S.pw = await async_playwright().start()
|
|
114
|
+
return S.pw
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
TURNSTILE_HOOK_JS = """(() => {
|
|
118
|
+
if (window.__cgTurnstileHooked) return;
|
|
119
|
+
window.__cgTurnstileHooked = true;
|
|
120
|
+
window.__cgTurnstile = [];
|
|
121
|
+
const wrap = (ts) => {
|
|
122
|
+
if (!ts || ts.__cgWrapped || typeof ts.render !== 'function') return ts;
|
|
123
|
+
const orig = ts.render.bind(ts);
|
|
124
|
+
ts.render = function (el, params) {
|
|
125
|
+
try { window.__cgTurnstile.push(Object.assign({}, params || {})); } catch (e) {}
|
|
126
|
+
return orig(el, params);
|
|
127
|
+
};
|
|
128
|
+
ts.__cgWrapped = true;
|
|
129
|
+
return ts;
|
|
130
|
+
};
|
|
131
|
+
let current = window.turnstile;
|
|
132
|
+
try {
|
|
133
|
+
Object.defineProperty(window, 'turnstile', {
|
|
134
|
+
configurable: true,
|
|
135
|
+
get() { return current; },
|
|
136
|
+
set(v) { current = wrap(v); },
|
|
137
|
+
});
|
|
138
|
+
} catch (e) {}
|
|
139
|
+
if (current) wrap(current);
|
|
140
|
+
})();"""
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
async def _attach(page) -> None:
|
|
144
|
+
S.page = page
|
|
145
|
+
try:
|
|
146
|
+
await page.context.add_init_script(TURNSTILE_HOOK_JS)
|
|
147
|
+
except Exception:
|
|
148
|
+
pass
|
|
149
|
+
with _capture():
|
|
150
|
+
S.solver = CaptchaSolver(
|
|
151
|
+
page=page,
|
|
152
|
+
api_key=_api_key(),
|
|
153
|
+
server=_server_url(),
|
|
154
|
+
attempts=_attempts(),
|
|
155
|
+
debug=_debug(),
|
|
156
|
+
)
|
|
157
|
+
await S.solver.setup()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _require_page():
|
|
161
|
+
if S.page is None or S.page.is_closed():
|
|
162
|
+
raise RuntimeError('No browser page. Call open_browser or connect_browser first.')
|
|
163
|
+
return S.page
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
async def _close() -> None:
|
|
167
|
+
try:
|
|
168
|
+
if S.solver is not None:
|
|
169
|
+
S.solver.close()
|
|
170
|
+
except Exception:
|
|
171
|
+
pass
|
|
172
|
+
try:
|
|
173
|
+
if S.owns_browser and S.browser is not None:
|
|
174
|
+
await S.browser.close()
|
|
175
|
+
except Exception:
|
|
176
|
+
pass
|
|
177
|
+
S.browser = S.context = S.page = S.solver = None
|
|
178
|
+
S.owns_browser = False
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@tool
|
|
182
|
+
async def open_browser(headless: bool = False, channel: str = '', proxy: str = '') -> str:
|
|
183
|
+
"""Launch a new Chromium browser with one page and attach the captcha solver to it.
|
|
184
|
+
|
|
185
|
+
headless: run without a visible window.
|
|
186
|
+
channel: 'chrome' or 'msedge' to use an installed browser instead of bundled Chromium.
|
|
187
|
+
proxy: optional proxy, e.g. 'http://user:pass@host:port'.
|
|
188
|
+
"""
|
|
189
|
+
await _close()
|
|
190
|
+
pw = await _ensure_playwright()
|
|
191
|
+
kwargs = {'headless': headless}
|
|
192
|
+
channel = channel or os.environ.get('CAPGURU_BROWSER_CHANNEL', '')
|
|
193
|
+
if channel:
|
|
194
|
+
kwargs['channel'] = channel
|
|
195
|
+
exe = os.environ.get('CAPGURU_BROWSER_PATH', '')
|
|
196
|
+
if exe:
|
|
197
|
+
kwargs['executable_path'] = exe
|
|
198
|
+
args = [a for a in os.environ.get('CAPGURU_BROWSER_ARGS', '').split() if a]
|
|
199
|
+
if args:
|
|
200
|
+
kwargs['args'] = args
|
|
201
|
+
if proxy:
|
|
202
|
+
from urllib.parse import urlsplit
|
|
203
|
+
u = urlsplit(proxy)
|
|
204
|
+
p = {'server': f'{u.scheme}://{u.hostname}:{u.port}' if u.port else f'{u.scheme}://{u.hostname}'}
|
|
205
|
+
if u.username:
|
|
206
|
+
p['username'] = u.username
|
|
207
|
+
p['password'] = u.password or ''
|
|
208
|
+
kwargs['proxy'] = p
|
|
209
|
+
S.browser = await pw.chromium.launch(**kwargs)
|
|
210
|
+
S.owns_browser = True
|
|
211
|
+
S.context = await S.browser.new_context()
|
|
212
|
+
page = await S.context.new_page()
|
|
213
|
+
await _attach(page)
|
|
214
|
+
return 'Browser opened. Use goto(url) to open a page.'
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@tool
|
|
218
|
+
async def connect_browser(cdp_url: str = 'http://127.0.0.1:9222') -> str:
|
|
219
|
+
"""Connect to an already running Chrome started with --remote-debugging-port and attach the solver
|
|
220
|
+
to its first tab. Use this to solve captchas in a browser controlled by another tool."""
|
|
221
|
+
await _close()
|
|
222
|
+
pw = await _ensure_playwright()
|
|
223
|
+
S.browser = await pw.chromium.connect_over_cdp(cdp_url)
|
|
224
|
+
S.owns_browser = False
|
|
225
|
+
S.context = S.browser.contexts[0] if S.browser.contexts else await S.browser.new_context()
|
|
226
|
+
page = S.context.pages[0] if S.context.pages else await S.context.new_page()
|
|
227
|
+
await _attach(page)
|
|
228
|
+
return f'Connected to {cdp_url}. Current page: {page.url}'
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@tool
|
|
232
|
+
async def goto(url: str, wait_until: str = 'load', timeout: int = 60) -> str:
|
|
233
|
+
"""Open a URL in the browser page. wait_until: load | domcontentloaded | networkidle."""
|
|
234
|
+
page = _require_page()
|
|
235
|
+
await page.goto(url, wait_until=wait_until, timeout=timeout * 1000)
|
|
236
|
+
return f'Opened {page.url} — title: {await page.title()}'
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@tool
|
|
240
|
+
async def detect_captcha() -> str:
|
|
241
|
+
"""Detect which captcha is visible on the page without solving it.
|
|
242
|
+
Returns recaptcha2, hcaptcha, yandex, geetest, funcaptcha, tiktok, aws or 'none'."""
|
|
243
|
+
_require_page()
|
|
244
|
+
with _capture():
|
|
245
|
+
kind = await S.solver.detect()
|
|
246
|
+
return kind or 'none'
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@tool
|
|
250
|
+
async def solve_captcha(captcha_type: str = 'auto', timeout: int = 30) -> str:
|
|
251
|
+
"""Solve the captcha on the current page through the Cap.Guru service.
|
|
252
|
+
|
|
253
|
+
captcha_type: auto (detect automatically) | recaptcha2 | hcaptcha | yandex | geetest | funcaptcha | tiktok | aws.
|
|
254
|
+
timeout: for auto, how many seconds to wait for a captcha to appear.
|
|
255
|
+
"""
|
|
256
|
+
_require_page()
|
|
257
|
+
captcha_type = (captcha_type or 'auto').strip().lower()
|
|
258
|
+
if captcha_type not in SOLVE_METHODS:
|
|
259
|
+
return f'Unknown captcha_type "{captcha_type}". Use one of: {", ".join(SOLVE_METHODS)}'
|
|
260
|
+
with _capture() as log:
|
|
261
|
+
try:
|
|
262
|
+
if captcha_type == 'auto':
|
|
263
|
+
kind = await S.solver.auto(timeout=timeout)
|
|
264
|
+
result = f'Solved: {kind}' if kind else f'No captcha found within {timeout} s'
|
|
265
|
+
else:
|
|
266
|
+
await getattr(S.solver, SOLVE_METHODS[captcha_type])()
|
|
267
|
+
result = f'Solved: {captcha_type}'
|
|
268
|
+
except CapGuruFatalError as e:
|
|
269
|
+
result = f'Cap.Guru account error (API key or balance): {e}'
|
|
270
|
+
except Exception as e:
|
|
271
|
+
result = f'Not solved: {e}'
|
|
272
|
+
tail = log.buf.getvalue().strip().splitlines()[-15:]
|
|
273
|
+
return result + ('\n\nLog:\n' + '\n'.join(tail) if tail else '')
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
RECAPTCHA_INFO_JS = """() => {
|
|
277
|
+
const out = { sitekey: '', callback: '', dataS: '' };
|
|
278
|
+
const el = document.querySelector('.g-recaptcha[data-sitekey], [data-sitekey]');
|
|
279
|
+
if (el) {
|
|
280
|
+
out.sitekey = el.getAttribute('data-sitekey') || '';
|
|
281
|
+
out.callback = el.getAttribute('data-callback') || '';
|
|
282
|
+
out.dataS = el.getAttribute('data-s') || '';
|
|
283
|
+
}
|
|
284
|
+
if (!out.sitekey) {
|
|
285
|
+
for (const sc of document.querySelectorAll('script[src*="recaptcha"]')) {
|
|
286
|
+
const m = /[?&]render=([^&]+)/.exec(sc.src);
|
|
287
|
+
if (m && m[1] !== 'explicit') { out.sitekey = decodeURIComponent(m[1]); out.v3 = true; break; }
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (!out.sitekey) {
|
|
291
|
+
for (const f of document.querySelectorAll('iframe[src*="recaptcha"]')) {
|
|
292
|
+
const m = /[?&]k=([^&]+)/.exec(f.src);
|
|
293
|
+
if (m) { out.sitekey = decodeURIComponent(m[1]); break; }
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return out;
|
|
297
|
+
}"""
|
|
298
|
+
|
|
299
|
+
APPLY_TOKEN_JS = """([token, callback]) => {
|
|
300
|
+
const done = [];
|
|
301
|
+
for (const ta of document.querySelectorAll('textarea[name="g-recaptcha-response"], #g-recaptcha-response')) {
|
|
302
|
+
ta.value = token;
|
|
303
|
+
ta.innerHTML = token;
|
|
304
|
+
done.push('textarea');
|
|
305
|
+
}
|
|
306
|
+
const call = (fn) => { try { fn(token); return true; } catch (e) { return false; } };
|
|
307
|
+
if (callback && typeof window[callback] === 'function' && call(window[callback])) done.push('callback:' + callback);
|
|
308
|
+
else {
|
|
309
|
+
const cfg = window.___grecaptcha_cfg && window.___grecaptcha_cfg.clients;
|
|
310
|
+
const seen = new Set();
|
|
311
|
+
const walk = (o, depth) => {
|
|
312
|
+
if (!o || typeof o !== 'object' || depth > 5 || seen.has(o)) return;
|
|
313
|
+
seen.add(o);
|
|
314
|
+
for (const k of Object.keys(o)) {
|
|
315
|
+
const v = o[k];
|
|
316
|
+
if (k === 'callback') {
|
|
317
|
+
if (typeof v === 'function' && call(v)) done.push('callback');
|
|
318
|
+
else if (typeof v === 'string' && typeof window[v] === 'function' && call(window[v])) done.push('callback:' + v);
|
|
319
|
+
} else walk(v, depth + 1);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
if (cfg) walk(cfg, 0);
|
|
323
|
+
}
|
|
324
|
+
return done.join(', ') || 'nothing found';
|
|
325
|
+
}"""
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@tool
|
|
329
|
+
async def solve_recaptcha_token(
|
|
330
|
+
sitekey: str = '',
|
|
331
|
+
pageurl: str = '',
|
|
332
|
+
version: str = 'v2',
|
|
333
|
+
min_score: float = 0.0,
|
|
334
|
+
data_s: str = '',
|
|
335
|
+
user_agent: str = '',
|
|
336
|
+
cookies: str = '',
|
|
337
|
+
proxy: str = '',
|
|
338
|
+
proxytype: str = '',
|
|
339
|
+
apply_to_page: bool = True,
|
|
340
|
+
) -> str:
|
|
341
|
+
"""Get a reCAPTCHA token from Cap.Guru (method userrecaptcha) without solving images.
|
|
342
|
+
|
|
343
|
+
version: v2 (also v2 Invisible) or v3. min_score: for v3 only, required score (service default 0.1).
|
|
344
|
+
sitekey: the k / data-sitekey value (for v3 also the render= value of recaptcha/api.js).
|
|
345
|
+
pageurl: full URL of the page with the captcha.
|
|
346
|
+
If a browser page is open, empty sitekey/pageurl/user_agent are taken from it, and with
|
|
347
|
+
apply_to_page=true the token is put into g-recaptcha-response and the site callback is called (v2).
|
|
348
|
+
For v3 the site requests the token itself, so the token is only returned.
|
|
349
|
+
data_s: data-s value (Google services). cookies, user_agent: passed to the worker.
|
|
350
|
+
proxy: 'login:password@ip:port', proxytype: HTTP | HTTPS | SOCKS4 | SOCKS5.
|
|
351
|
+
"""
|
|
352
|
+
page = S.page if (S.page is not None and not S.page.is_closed()) else None
|
|
353
|
+
callback = ''
|
|
354
|
+
if page is not None:
|
|
355
|
+
info = await page.evaluate(RECAPTCHA_INFO_JS)
|
|
356
|
+
sitekey = sitekey or info.get('sitekey', '')
|
|
357
|
+
data_s = data_s or info.get('dataS', '')
|
|
358
|
+
callback = info.get('callback', '')
|
|
359
|
+
pageurl = pageurl or page.url
|
|
360
|
+
if not user_agent:
|
|
361
|
+
user_agent = await page.evaluate('() => navigator.userAgent')
|
|
362
|
+
version = (version or 'v2').strip().lower()
|
|
363
|
+
if version not in ('v2', 'v3'):
|
|
364
|
+
return 'version must be v2 or v3'
|
|
365
|
+
if not sitekey:
|
|
366
|
+
return 'sitekey not found: pass sitekey (the k / data-sitekey value) explicitly'
|
|
367
|
+
if not pageurl:
|
|
368
|
+
return 'pageurl is required when no browser page is open'
|
|
369
|
+
payload = {'method': 'userrecaptcha', 'googlekey': sitekey, 'pageurl': pageurl}
|
|
370
|
+
if version == 'v3':
|
|
371
|
+
payload['version'] = 'v3'
|
|
372
|
+
if min_score and min_score > 0:
|
|
373
|
+
payload['min_score'] = min_score
|
|
374
|
+
for k, v in (('data-s', data_s), ('userAgent', user_agent), ('cookies', cookies),
|
|
375
|
+
('proxy', proxy), ('proxytype', proxytype)):
|
|
376
|
+
if v:
|
|
377
|
+
payload[k] = v
|
|
378
|
+
client = CapGuruClient(_api_key(), _server_url())
|
|
379
|
+
with _capture():
|
|
380
|
+
try:
|
|
381
|
+
token = await client.click(payload, debug=_debug())
|
|
382
|
+
except CapGuruFatalError as e:
|
|
383
|
+
return f'Cap.Guru account error (API key or balance): {e}'
|
|
384
|
+
if not token or 'ERROR' in str(token).upper():
|
|
385
|
+
return f'Not solved: {token or "timeout"}'
|
|
386
|
+
token = str(token).strip()
|
|
387
|
+
applied = ''
|
|
388
|
+
if apply_to_page and page is not None and version == 'v2':
|
|
389
|
+
applied = await page.evaluate(APPLY_TOKEN_JS, [token, callback])
|
|
390
|
+
lines = [f'Token: {token}', f'version: {version}', f'sitekey: {sitekey}', f'pageurl: {pageurl}']
|
|
391
|
+
if applied:
|
|
392
|
+
lines.append(f'Applied to page: {applied}')
|
|
393
|
+
return '\n'.join(lines)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
TURNSTILE_INFO_JS = r"""() => {
|
|
397
|
+
const out = { sitekey: '', callback: '' };
|
|
398
|
+
const el = document.querySelector('.cf-turnstile[data-sitekey]');
|
|
399
|
+
if (el) {
|
|
400
|
+
out.sitekey = el.getAttribute('data-sitekey') || '';
|
|
401
|
+
out.callback = el.getAttribute('data-callback') || '';
|
|
402
|
+
}
|
|
403
|
+
const hooked = (window.__cgTurnstile || []).filter(p => p && p.sitekey);
|
|
404
|
+
if (!out.sitekey && hooked.length) out.sitekey = hooked[hooked.length - 1].sitekey;
|
|
405
|
+
if (!out.sitekey) {
|
|
406
|
+
for (const f of document.querySelectorAll('iframe[src*="challenges.cloudflare.com"]')) {
|
|
407
|
+
const m = /\/([0-3]x[0-9A-Za-z_-]{10,})\//.exec(f.src);
|
|
408
|
+
if (m) { out.sitekey = m[1]; break; }
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (!out.sitekey) {
|
|
412
|
+
const any = document.querySelector('[data-sitekey^="0x"], [data-sitekey^="1x"], [data-sitekey^="2x"], [data-sitekey^="3x"]');
|
|
413
|
+
if (any) { out.sitekey = any.getAttribute('data-sitekey'); out.callback = out.callback || any.getAttribute('data-callback') || ''; }
|
|
414
|
+
}
|
|
415
|
+
return out;
|
|
416
|
+
}"""
|
|
417
|
+
|
|
418
|
+
APPLY_TURNSTILE_JS = """([token, callback, sitekey]) => {
|
|
419
|
+
const done = [];
|
|
420
|
+
for (const el of document.querySelectorAll('input[name="cf-turnstile-response"], textarea[name="cf-turnstile-response"]')) {
|
|
421
|
+
el.value = token;
|
|
422
|
+
done.push(el.tagName.toLowerCase());
|
|
423
|
+
}
|
|
424
|
+
const call = (fn) => { try { fn(token); return true; } catch (e) { return false; } };
|
|
425
|
+
let called = false;
|
|
426
|
+
if (callback && typeof window[callback] === 'function') called = call(window[callback]) && !!done.push('callback:' + callback);
|
|
427
|
+
if (!called) {
|
|
428
|
+
for (const p of (window.__cgTurnstile || [])) {
|
|
429
|
+
if (sitekey && p.sitekey && p.sitekey !== sitekey) continue;
|
|
430
|
+
const cb = p.callback;
|
|
431
|
+
if (typeof cb === 'function' && call(cb)) { done.push('callback (render)'); called = true; break; }
|
|
432
|
+
if (typeof cb === 'string' && typeof window[cb] === 'function' && call(window[cb])) { done.push('callback:' + cb); called = true; break; }
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return done.join(', ') || 'nothing found';
|
|
436
|
+
}"""
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
@tool
|
|
440
|
+
async def solve_turnstile_token(sitekey: str = '', pageurl: str = '', apply_to_page: bool = True) -> str:
|
|
441
|
+
"""Get a Cloudflare Turnstile token from Cap.Guru (method turnstile).
|
|
442
|
+
|
|
443
|
+
sitekey: the data-sitekey value (e.g. 0x4AAAA... or 1x000...). pageurl: URL of the page with Turnstile.
|
|
444
|
+
If a browser page is open, empty sitekey/pageurl are taken from it, and with apply_to_page=true the
|
|
445
|
+
token is put into cf-turnstile-response and the Turnstile callback is called.
|
|
446
|
+
Typical solve time is 10-30 seconds.
|
|
447
|
+
"""
|
|
448
|
+
page = S.page if (S.page is not None and not S.page.is_closed()) else None
|
|
449
|
+
callback = ''
|
|
450
|
+
if page is not None:
|
|
451
|
+
info = await page.evaluate(TURNSTILE_INFO_JS)
|
|
452
|
+
sitekey = sitekey or info.get('sitekey', '')
|
|
453
|
+
callback = info.get('callback', '')
|
|
454
|
+
pageurl = pageurl or page.url
|
|
455
|
+
if not sitekey:
|
|
456
|
+
return 'sitekey not found: pass sitekey (the data-sitekey value) explicitly'
|
|
457
|
+
if not pageurl:
|
|
458
|
+
return 'pageurl is required when no browser page is open'
|
|
459
|
+
payload = {'method': 'turnstile', 'sitekey': sitekey, 'pageurl': pageurl}
|
|
460
|
+
client = CapGuruClient(_api_key(), _server_url())
|
|
461
|
+
with _capture():
|
|
462
|
+
try:
|
|
463
|
+
token = await client.click(payload, debug=_debug())
|
|
464
|
+
except CapGuruFatalError as e:
|
|
465
|
+
return f'Cap.Guru account error (API key or balance): {e}'
|
|
466
|
+
if not token or 'ERROR' in str(token).upper():
|
|
467
|
+
return f'Not solved: {token or "timeout"}'
|
|
468
|
+
token = str(token).strip()
|
|
469
|
+
applied = ''
|
|
470
|
+
if apply_to_page and page is not None:
|
|
471
|
+
applied = await page.evaluate(APPLY_TURNSTILE_JS, [token, callback, sitekey])
|
|
472
|
+
lines = [f'Token: {token}', f'sitekey: {sitekey}', f'pageurl: {pageurl}']
|
|
473
|
+
if applied:
|
|
474
|
+
lines.append(f'Applied to page: {applied}')
|
|
475
|
+
return '\n'.join(lines)
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
@tool
|
|
479
|
+
async def solve_image_captcha(
|
|
480
|
+
selector: str = '',
|
|
481
|
+
image_base64: str = '',
|
|
482
|
+
image_path: str = '',
|
|
483
|
+
image_url: str = '',
|
|
484
|
+
vernet: int = 0,
|
|
485
|
+
fill_selector: str = '',
|
|
486
|
+
) -> str:
|
|
487
|
+
"""Recognize a text (image) captcha through Cap.Guru (method base64) and return the text.
|
|
488
|
+
|
|
489
|
+
Image source, one of:
|
|
490
|
+
selector: the captcha image element on the open page (screenshot of the element is sent),
|
|
491
|
+
image_base64: image in base64 (a data: URL is also accepted),
|
|
492
|
+
image_path: path to an image file,
|
|
493
|
+
image_url: image URL (downloaded with the page cookies if a page is open).
|
|
494
|
+
vernet: optional captcha kind: 2 General v1, 6 General v2, 22 Arithmetic, 12 Rambler, 18 General Yandex,
|
|
495
|
+
20 Yandex 6 ch., 16 VKontakte, 14 SolveMedia, 13 Gibdd, 9 Ok.ru, 17 World of Tanks, 5 Microsoft Hotmail.
|
|
496
|
+
fill_selector: if set and a page is open, the recognized text is typed into this input.
|
|
497
|
+
"""
|
|
498
|
+
page = S.page if (S.page is not None and not S.page.is_closed()) else None
|
|
499
|
+
body = ''
|
|
500
|
+
if image_base64:
|
|
501
|
+
body = image_base64.split(',', 1)[1] if image_base64.startswith('data:') else image_base64
|
|
502
|
+
elif image_path:
|
|
503
|
+
with open(os.path.expanduser(image_path), 'rb') as f:
|
|
504
|
+
body = base64.b64encode(f.read()).decode()
|
|
505
|
+
elif image_url:
|
|
506
|
+
if page is not None:
|
|
507
|
+
resp = await page.context.request.get(image_url)
|
|
508
|
+
data = await resp.body()
|
|
509
|
+
else:
|
|
510
|
+
import httpx
|
|
511
|
+
async with httpx.AsyncClient(follow_redirects=True, timeout=30) as http:
|
|
512
|
+
data = (await http.get(image_url)).content
|
|
513
|
+
body = base64.b64encode(data).decode()
|
|
514
|
+
elif selector:
|
|
515
|
+
if page is None:
|
|
516
|
+
return 'selector requires an open page (open_browser / connect_browser)'
|
|
517
|
+
el = await page.wait_for_selector(selector, timeout=15000)
|
|
518
|
+
src = await el.get_attribute('src') or ''
|
|
519
|
+
if src.startswith('data:image') and ',' in src:
|
|
520
|
+
body = src.split(',', 1)[1]
|
|
521
|
+
else:
|
|
522
|
+
body = base64.b64encode(await el.screenshot(type='png')).decode()
|
|
523
|
+
else:
|
|
524
|
+
return 'Pass one image source: selector, image_base64, image_path or image_url'
|
|
525
|
+
if not body:
|
|
526
|
+
return 'Image is empty'
|
|
527
|
+
payload = {'method': 'base64', 'body': body}
|
|
528
|
+
if vernet:
|
|
529
|
+
payload['vernet'] = int(vernet)
|
|
530
|
+
client = CapGuruClient(_api_key(), _server_url())
|
|
531
|
+
with _capture():
|
|
532
|
+
try:
|
|
533
|
+
text = await client.click(payload, debug=_debug())
|
|
534
|
+
except CapGuruFatalError as e:
|
|
535
|
+
return f'Cap.Guru account error (API key or balance): {e}'
|
|
536
|
+
if not text or 'ERROR' in str(text).upper():
|
|
537
|
+
return f'Not solved: {text or "timeout"}'
|
|
538
|
+
text = str(text).strip()
|
|
539
|
+
lines = [f'Text: {text}']
|
|
540
|
+
if fill_selector:
|
|
541
|
+
if page is None:
|
|
542
|
+
lines.append('fill_selector ignored: no open page')
|
|
543
|
+
else:
|
|
544
|
+
await page.fill(fill_selector, text, timeout=15000)
|
|
545
|
+
lines.append(f'Filled {fill_selector}')
|
|
546
|
+
return '\n'.join(lines)
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
@tool
|
|
550
|
+
async def screenshot(full_page: bool = False) -> Image:
|
|
551
|
+
"""Take a screenshot of the current page."""
|
|
552
|
+
page = _require_page()
|
|
553
|
+
return Image(data=await page.screenshot(full_page=full_page, type='png'), format='png')
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
@tool
|
|
557
|
+
async def click(selector: str) -> str:
|
|
558
|
+
"""Click an element by CSS selector (or Playwright selector such as text=Sign in)."""
|
|
559
|
+
page = _require_page()
|
|
560
|
+
await page.click(selector, timeout=15000)
|
|
561
|
+
return f'Clicked {selector}'
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
@tool
|
|
565
|
+
async def fill(selector: str, text: str) -> str:
|
|
566
|
+
"""Type text into an input found by selector."""
|
|
567
|
+
page = _require_page()
|
|
568
|
+
await page.fill(selector, text, timeout=15000)
|
|
569
|
+
return f'Filled {selector}'
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
@tool
|
|
573
|
+
async def page_info() -> str:
|
|
574
|
+
"""Return the current URL and title of the page."""
|
|
575
|
+
page = _require_page()
|
|
576
|
+
return f'URL: {page.url}\nTitle: {await page.title()}'
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
@tool
|
|
580
|
+
async def close_browser() -> str:
|
|
581
|
+
"""Close the browser opened by open_browser (or disconnect from a connected one)."""
|
|
582
|
+
await _close()
|
|
583
|
+
return 'Browser closed.'
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def main() -> None:
|
|
587
|
+
mcp.run()
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
if __name__ == '__main__':
|
|
591
|
+
main()
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: capguru-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: MCP server for the Cap.Guru captcha solving service: lets AI assistants open pages and solve reCAPTCHA v2/v3, Cloudflare Turnstile, text captchas, hCaptcha, GeeTest, FunCaptcha, TikTok, AWS WAF and Yandex SmartCaptcha
|
|
5
|
+
Author: Cap.Guru
|
|
6
|
+
Project-URL: Homepage, https://cap.guru
|
|
7
|
+
Project-URL: Documentation, https://docs.cap.guru
|
|
8
|
+
Keywords: mcp,captcha,solver,playwright,cap.guru,claude,cursor,turnstile,recaptcha
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: mcp>=1.2
|
|
14
|
+
Requires-Dist: capguru>=0.4.0
|
|
15
|
+
Requires-Dist: playwright>=1.40
|
|
16
|
+
|
|
17
|
+
# capguru-mcp
|
|
18
|
+
|
|
19
|
+
**English** | [Русский](#русский)
|
|
20
|
+
|
|
21
|
+
MCP server for the [Cap.Guru](https://cap.guru) captcha solving service.
|
|
22
|
+
It lets AI assistants (Claude Desktop, Claude Code, Cursor and other MCP clients) open web pages in a browser and solve captchas on them.
|
|
23
|
+
|
|
24
|
+
Supported: reCAPTCHA v2/v3, Cloudflare Turnstile, text (image) captchas, hCaptcha, GeeTest v3/v4, FunCaptcha (Arkose Labs), TikTok, AWS WAF, Yandex SmartCaptcha.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
pip install capguru-mcp
|
|
30
|
+
playwright install chromium
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Connecting to an MCP client
|
|
34
|
+
|
|
35
|
+
Claude Desktop (`claude_desktop_config.json`), Cursor (`.cursor/mcp.json`) and most other clients:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"capguru": {
|
|
41
|
+
"command": "capguru-mcp",
|
|
42
|
+
"env": {
|
|
43
|
+
"CAPGURU_API_KEY": "YOUR_KEY"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Claude Code:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
claude mcp add capguru --env CAPGURU_API_KEY=YOUR_KEY -- capguru-mcp
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Without installing (via [uv](https://docs.astral.sh/uv/)): use `"command": "uvx", "args": ["capguru-mcp"]`.
|
|
57
|
+
|
|
58
|
+
## Tools
|
|
59
|
+
|
|
60
|
+
| Tool | Description |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `open_browser` | Launch a browser (`headless`, `channel`: `chrome`/`msedge`, `proxy`) |
|
|
63
|
+
| `connect_browser` | Connect to a running Chrome started with `--remote-debugging-port` |
|
|
64
|
+
| `goto` | Open a URL |
|
|
65
|
+
| `click`, `fill` | Click an element, type text into an input |
|
|
66
|
+
| `screenshot` | Screenshot of the page |
|
|
67
|
+
| `page_info` | Current URL and title |
|
|
68
|
+
| `detect_captcha` | Detect the captcha type without solving |
|
|
69
|
+
| `solve_captcha` | Solve the captcha: `auto` or a specific type |
|
|
70
|
+
| `solve_recaptcha_token` | Get a reCAPTCHA v2 / v2 Invisible / v3 token (`sitekey`, `pageurl`, `version`, `min_score`); works without a browser too |
|
|
71
|
+
| `solve_turnstile_token` | Get a Cloudflare Turnstile token (`sitekey`, `pageurl`); works without a browser too |
|
|
72
|
+
| `solve_image_captcha` | Recognize a text captcha from an image: element `selector`, `image_base64`, `image_path` or `image_url`; optional `vernet`, `fill_selector` |
|
|
73
|
+
| `close_browser` | Close the browser |
|
|
74
|
+
|
|
75
|
+
Example requests to the assistant:
|
|
76
|
+
|
|
77
|
+
- *"Open https://site.com/login, solve the captcha and take a screenshot."*
|
|
78
|
+
- *"Get a reCAPTCHA token for sitekey 6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ- on https://www.google.com/recaptcha/api2/demo."*
|
|
79
|
+
- *"Get a reCAPTCHA v3 token for sitekey 6Ldb4ccaAAAAACMxv8uxUIMckZy1og98rs_h3AmO on http://learn.captcha.guru/ln/recap3/."*
|
|
80
|
+
- *"Open https://react-turnstile.vercel.app/basic and solve the Turnstile."*
|
|
81
|
+
- *"Recognize the captcha image #captcha_img and type the answer into #captcha_input."*
|
|
82
|
+
|
|
83
|
+
`solve_recaptcha_token` uses the Cap.Guru token method (`userrecaptcha`): it returns the `g-recaptcha-response` token. If a page is open, the sitekey, URL and User-Agent are taken from it; for v2 the token is inserted into the page and the site callback is called. For v3 pass `version: v3` (optionally `min_score`, service default 0.1); the token is only returned, because v3 sites request it themselves.
|
|
84
|
+
|
|
85
|
+
`solve_turnstile_token` works the same way for Cloudflare Turnstile (`method=turnstile`): with an open page the token is put into `cf-turnstile-response` and the Turnstile callback is called. Typical solve time is 10–30 seconds.
|
|
86
|
+
|
|
87
|
+
## Settings (environment variables)
|
|
88
|
+
|
|
89
|
+
| Variable | Default | Description |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| `CAPGURU_API_KEY` | — | Cap.Guru API key (required) |
|
|
92
|
+
| `CAPGURU_SERVER` | `https://api.cap.guru` | API server |
|
|
93
|
+
| `CAPGURU_ATTEMPTS` | `5` | Max number of solving attempts |
|
|
94
|
+
| `CAPGURU_DEBUG` | `1` | Include the solver log in tool results |
|
|
95
|
+
| `CAPGURU_BROWSER_CHANNEL` | — | `chrome` or `msedge` to use an installed browser |
|
|
96
|
+
| `CAPGURU_BROWSER_PATH` | — | Path to a browser executable |
|
|
97
|
+
| `CAPGURU_BROWSER_ARGS` | — | Extra browser arguments, space separated |
|
|
98
|
+
|
|
99
|
+
Full documentation: https://docs.cap.guru
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Русский
|
|
104
|
+
|
|
105
|
+
MCP-сервер для сервиса решения капчи [Cap.Guru](https://cap.guru).
|
|
106
|
+
Позволяет ИИ-ассистентам (Claude Desktop, Claude Code, Cursor и другим MCP-клиентам) открывать страницы в браузере и решать на них капчу.
|
|
107
|
+
|
|
108
|
+
Поддерживаются: reCAPTCHA v2/v3, Cloudflare Turnstile, текстовые капчи (картинкой), hCaptcha, GeeTest v3/v4, FunCaptcha (Arkose Labs), TikTok, AWS WAF, Яндекс SmartCaptcha.
|
|
109
|
+
|
|
110
|
+
### Установка
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
pip install capguru-mcp
|
|
114
|
+
playwright install chromium
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Подключение к MCP-клиенту
|
|
118
|
+
|
|
119
|
+
Claude Desktop (`claude_desktop_config.json`), Cursor (`.cursor/mcp.json`) и большинство других клиентов:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"mcpServers": {
|
|
124
|
+
"capguru": {
|
|
125
|
+
"command": "capguru-mcp",
|
|
126
|
+
"env": {
|
|
127
|
+
"CAPGURU_API_KEY": "ВАШ_КЛЮЧ"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Claude Code:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
claude mcp add capguru --env CAPGURU_API_KEY=ВАШ_КЛЮЧ -- capguru-mcp
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Без установки (через [uv](https://docs.astral.sh/uv/)): `"command": "uvx", "args": ["capguru-mcp"]`.
|
|
141
|
+
|
|
142
|
+
### Инструменты
|
|
143
|
+
|
|
144
|
+
| Инструмент | Описание |
|
|
145
|
+
|---|---|
|
|
146
|
+
| `open_browser` | Запустить браузер (`headless`, `channel`: `chrome`/`msedge`, `proxy`) |
|
|
147
|
+
| `connect_browser` | Подключиться к запущенному Chrome с `--remote-debugging-port` |
|
|
148
|
+
| `goto` | Открыть адрес |
|
|
149
|
+
| `click`, `fill` | Нажать на элемент, ввести текст в поле |
|
|
150
|
+
| `screenshot` | Скриншот страницы |
|
|
151
|
+
| `page_info` | Текущий адрес и заголовок |
|
|
152
|
+
| `detect_captcha` | Определить тип капчи, не решая |
|
|
153
|
+
| `solve_captcha` | Решить капчу: `auto` или конкретный тип |
|
|
154
|
+
| `solve_recaptcha_token` | Получить токен reCAPTCHA v2 / v2 Invisible / v3 (`sitekey`, `pageurl`, `version`, `min_score`); работает и без браузера |
|
|
155
|
+
| `solve_turnstile_token` | Получить токен Cloudflare Turnstile (`sitekey`, `pageurl`); работает и без браузера |
|
|
156
|
+
| `solve_image_captcha` | Распознать текстовую капчу с картинки: элемент `selector`, `image_base64`, `image_path` или `image_url`; по желанию `vernet`, `fill_selector` |
|
|
157
|
+
| `close_browser` | Закрыть браузер |
|
|
158
|
+
|
|
159
|
+
Примеры запросов ассистенту:
|
|
160
|
+
|
|
161
|
+
- *«Открой https://site.com/login, реши капчу и сделай скриншот».*
|
|
162
|
+
- *«Получи токен reCAPTCHA для sitekey 6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ- на https://www.google.com/recaptcha/api2/demo».*
|
|
163
|
+
- *«Получи токен reCAPTCHA v3 для sitekey 6Ldb4ccaAAAAACMxv8uxUIMckZy1og98rs_h3AmO на http://learn.captcha.guru/ln/recap3/».*
|
|
164
|
+
- *«Открой https://react-turnstile.vercel.app/basic и реши Turnstile».*
|
|
165
|
+
- *«Распознай капчу с картинки #captcha_img и введи ответ в #captcha_input».*
|
|
166
|
+
|
|
167
|
+
`solve_recaptcha_token` использует токенный метод Cap.Guru (`userrecaptcha`) и возвращает токен `g-recaptcha-response`. Если страница открыта, sitekey, адрес и User-Agent берутся с неё; для v2 токен вставляется на страницу и вызывается callback сайта. Для v3 укажите `version: v3` (и при необходимости `min_score`, по умолчанию у сервиса 0.1); токен только возвращается, потому что сайты с v3 запрашивают его сами.
|
|
168
|
+
|
|
169
|
+
`solve_turnstile_token` так же работает для Cloudflare Turnstile (`method=turnstile`): если страница открыта, токен вставляется в `cf-turnstile-response` и вызывается callback Turnstile. Обычное время решения — 10–30 секунд.
|
|
170
|
+
|
|
171
|
+
### Настройки (переменные окружения)
|
|
172
|
+
|
|
173
|
+
| Переменная | По умолчанию | Описание |
|
|
174
|
+
|---|---|---|
|
|
175
|
+
| `CAPGURU_API_KEY` | — | API-ключ Cap.Guru (обязательно) |
|
|
176
|
+
| `CAPGURU_SERVER` | `https://api.cap.guru` | Сервер API |
|
|
177
|
+
| `CAPGURU_ATTEMPTS` | `5` | Макс. количество попыток |
|
|
178
|
+
| `CAPGURU_DEBUG` | `1` | Добавлять лог солвера в ответ инструмента |
|
|
179
|
+
| `CAPGURU_BROWSER_CHANNEL` | — | `chrome` или `msedge`, чтобы использовать установленный браузер |
|
|
180
|
+
| `CAPGURU_BROWSER_PATH` | — | Путь к исполняемому файлу браузера |
|
|
181
|
+
| `CAPGURU_BROWSER_ARGS` | — | Дополнительные аргументы браузера через пробел |
|
|
182
|
+
|
|
183
|
+
Полная документация: https://docs.cap.guru
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
capguru_mcp/__init__.py
|
|
4
|
+
capguru_mcp/__main__.py
|
|
5
|
+
capguru_mcp/server.py
|
|
6
|
+
capguru_mcp.egg-info/PKG-INFO
|
|
7
|
+
capguru_mcp.egg-info/SOURCES.txt
|
|
8
|
+
capguru_mcp.egg-info/dependency_links.txt
|
|
9
|
+
capguru_mcp.egg-info/entry_points.txt
|
|
10
|
+
capguru_mcp.egg-info/requires.txt
|
|
11
|
+
capguru_mcp.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
capguru_mcp
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "capguru-mcp"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "MCP server for the Cap.Guru captcha solving service: lets AI assistants open pages and solve reCAPTCHA v2/v3, Cloudflare Turnstile, text captchas, hCaptcha, GeeTest, FunCaptcha, TikTok, AWS WAF and Yandex SmartCaptcha"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
authors = [{ name = "Cap.Guru" }]
|
|
12
|
+
dependencies = [
|
|
13
|
+
"mcp>=1.2",
|
|
14
|
+
"capguru>=0.4.0",
|
|
15
|
+
"playwright>=1.40",
|
|
16
|
+
]
|
|
17
|
+
keywords = ["mcp", "captcha", "solver", "playwright", "cap.guru", "claude", "cursor", "turnstile", "recaptcha"]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://cap.guru"
|
|
25
|
+
Documentation = "https://docs.cap.guru"
|
|
26
|
+
|
|
27
|
+
[project.scripts]
|
|
28
|
+
capguru-mcp = "capguru_mcp.server:main"
|
|
29
|
+
|
|
30
|
+
[tool.setuptools]
|
|
31
|
+
packages = ["capguru_mcp"]
|