# 2Captcha MCP [![PyPI version](https://img.shields.io/pypi/v/twocaptcha-mcp.svg)](https://pypi.org/project/twocaptcha-mcp/) [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/) [![CI](https://github.com/aruxojuyu665/2Captcha-MCP/actions/workflows/ci.yml/badge.svg)](https://github.com/aruxojuyu665/2Captcha-MCP/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/aruxojuyu665/2Captcha-MCP/branch/main/graph/badge.svg)](https://codecov.io/gh/aruxojuyu665/2Captcha-MCP) [![mypy: strict](https://img.shields.io/badge/mypy-strict-success.svg)](https://mypy.readthedocs.io/) [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](#license) MCP server exposing the **full 2Captcha API surface** — 31 captcha types, 5 management tools, 3 pingback CRUD tools, 3 webhook event-store tools, and 1 composite solve-and-wait tool (**43 tools total**) — to Claude Code via stdio. Built on the official `2captcha-python` SDK (`AsyncTwoCaptcha`), so retry/polling/parsing are handled upstream and every captcha type the SDK supports is available the moment the SDK adds it. ## Quick start ```bash pip install twocaptcha-mcp # or for development: pip install -e ".[dev]" ``` Create a `.env` next to the project root: ```bash 2Captcha_API_KEY=your_2captcha_key_here ``` Verify it works: ```bash python -m twocaptcha_mcp # starts the stdio server (Ctrl+C to stop) ``` Register the server with Claude Code: ```bash claude mcp add 2captcha -e 2Captcha_API_KEY=your_2captcha_key_here -- python -m twocaptcha_mcp ``` After a `claude` restart, prompt the model with: > "Use twocaptcha_balance to check my account." ## Configuration All settings are environment-driven. Aliases in priority order: | Setting | Aliases | Default | Range | |---|---|---|---| | API key (required) | `2Captcha_API_KEY`, `TWOCAPTCHA_API_KEY`, `2CAPTCHA_API_KEY`, `API_KEY_2CAPTCHA` | — | — | | Server | `TWOCAPTCHA_SERVER` | `2captcha.com` | `2captcha.com`, `rucaptcha.com` | | Default timeout | `TWOCAPTCHA_DEFAULT_TIMEOUT` | `120` s | 1..600 | | reCAPTCHA timeout | `TWOCAPTCHA_RECAPTCHA_TIMEOUT` | `600` s | 1..1200 | | Polling interval | `TWOCAPTCHA_POLLING_INTERVAL` | `10` s | 1..120 | | Soft id | `TWOCAPTCHA_SOFT_ID` | `4580` | — | | Log level | `TWOCAPTCHA_LOG_LEVEL` | `INFO` | DEBUG/INFO/WARNING/ERROR/CRITICAL | | Default pingback URL | `TWOCAPTCHA_DEFAULT_CALLBACK` | _unset_ | https URL | ## Tools ### Captcha solvers (31) | Tool | SDK method | Required arguments | |---|---|---| | `twocaptcha_solve_normal` | `normal` | `file_path` xor `file_base64` | | `twocaptcha_solve_text` | `text` | `text` | | `twocaptcha_solve_audio` | `audio` | file (xor), `audio_lang` | | `twocaptcha_solve_grid` | `grid` | file (xor) | | `twocaptcha_solve_canvas` | `canvas` | file (xor) | | `twocaptcha_solve_coordinates` | `coordinates` | file (xor) | | `twocaptcha_solve_rotate` | `rotate` | `files: list` | | `twocaptcha_solve_vkimage` | `vkimage` | `files`, `steps` | | `twocaptcha_solve_recaptcha` | `recaptcha` | `sitekey`, `url`, `version`, `enterprise` | | `twocaptcha_solve_hcaptcha` | `hcaptcha` | `sitekey`, `url` | | `twocaptcha_solve_turnstile` | `turnstile` | `sitekey`, `url` (+ `data`/`pagedata` for CF Challenge) | | `twocaptcha_solve_funcaptcha` | `funcaptcha` | `sitekey`, `url` | | `twocaptcha_solve_geetest` | `geetest` | `gt`, `challenge`, `url` | | `twocaptcha_solve_geetest_v4` | `geetest_v4` | `captcha_id`, `url` | | `twocaptcha_solve_capy` | `capy` | `sitekey`, `url` | | `twocaptcha_solve_keycaptcha` | `keycaptcha` | `s_s_c_user_id`, `s_s_c_session_id`, two signs, `url` | | `twocaptcha_solve_lemin` | `lemin` | `captcha_id`, `div_id`, `url` | | `twocaptcha_solve_mtcaptcha` | `mtcaptcha` | `sitekey`, `url` | | `twocaptcha_solve_friendly_captcha` | `friendly_captcha` | `sitekey`, `url` | | `twocaptcha_solve_cutcaptcha` | `cutcaptcha` | `misery_key`, `apikey`, `url` | | `twocaptcha_solve_amazon_waf` | `amazon_waf` | `sitekey`, `iv`, `context`, `url` | | `twocaptcha_solve_tencent` | `tencent` | `app_id`, `url` | | `twocaptcha_solve_atb_captcha` | `atb_captcha` | `app_id`, `api_server`, `url` | | `twocaptcha_solve_datadome` ⚑ | `datadome` | `proxy`, `user_agent`, `captcha_url`, `pageurl` | | `twocaptcha_solve_captchafox` ⚑ | `captchafox` | `proxy`, `user_agent`, `sitekey`, `pageurl` | | `twocaptcha_solve_vkcaptcha` ⚑ | `vkcaptcha` | `proxy`, `user_agent`, `redirect_uri` | | `twocaptcha_solve_prosopo` | `prosopo` | `sitekey`, `pageurl` | | `twocaptcha_solve_temu` | `temu` | `body`, `part1..3` | | `twocaptcha_solve_altcha` | `altcha` | `pageurl` | | `twocaptcha_solve_cybersiara` | `cybersiara` | `master_url_id`, `pageurl`, `cyber_user_agent` | | `twocaptcha_solve_yandex_smart` | `yandex_smart` | `sitekey`, `url` | ⚑ — `proxy` and `user_agent` are **required** by the SDK signature. All solver tools accept the same set of optional kwargs (mixin `SolverKwargs`): - `proxy: {type, uri}` (HTTP/HTTPS/SOCKS4/SOCKS5) - `pingback: HttpUrl` — per-call pingback URL (must be pre-registered) - `soft_id: int` - `cookies: str` - `user_agent: str` - `lang: str` - `header_acao: 0|1` ### Management (5) - `twocaptcha_balance` — current balance in USD - `twocaptcha_report_good` / `twocaptcha_report_bad` — report a captcha id within 15 minutes - `twocaptcha_get_result` — poll a captcha id manually (paired with `send_raw`) - `twocaptcha_send_raw` — escape hatch for SDK methods this server does not yet expose ### Pingback CRUD (3) - `twocaptcha_register_pingback` — whitelist a callback URL - `twocaptcha_list_pingbacks` — list whitelisted addresses - `twocaptcha_delete_pingback` — delete one or `addr="all"` ## Development ```bash pytest --cov=twocaptcha_mcp --cov-branch --cov-fail-under=92 -v ruff check . && ruff format --check . mypy --strict twocaptcha_mcp ``` Run live tests against the real 2Captcha API (consumes credits): ```bash pytest -m live --run-live # balance + solve_normal + concurrent_balance pytest -m e2e --run-e2e # full MCP protocol roundtrip via mcp.client.session ``` ## Architecture ``` twocaptcha_mcp/ ├── __main__.py # stdio entrypoint ├── server.py # build_server + dispatch_tool_call ├── config.py # pydantic-settings (.env) ├── logger.py # stderr-only logger (stdout is reserved for stdio MCP) ├── client/ │ ├── solver.py # SolverClient — async wrapper over AsyncTwoCaptcha │ ├── pingback.py # httpx wrapper for legacy res.php │ ├── rate_limiter.py # async sliding-window per-method rate limiter │ └── errors.py # internal CaptchaError hierarchy ├── schemas/ # Pydantic request/response models per captcha family ├── tools/ # @captcha_tool handlers per captcha family + composite └── webhook_receiver/ # Starlette app + SQLite store (twocaptcha-mcp-webhook) ``` Tool handlers depend only on the `SolverClient` / `PingbackClient` abstractions — the SDK is never imported from a handler. Adding a new captcha type is a 3-step process: schema → tool handler → snapshot test count bump. ## Webhook receiver (optional, since 0.3.0) For long-running solves (recaptcha v3 up to 600 s), the polling-mode SDK call holds the MCP request slot for the entire duration. Run the optional `twocaptcha-mcp-webhook` server to flip into pingback mode — 2Captcha POSTs the result to your URL, the receiver writes it to SQLite, and the MCP tools read asynchronously without blocking. - **Composite tool**: [`twocaptcha_solve_and_wait_pingback`](TROUBLESHOOTING.md#server-hangs-forever-on-a-solve_recaptcha-call) sends the captcha and waits on the local store - **Read tools**: `twocaptcha_pingback_events_list/get/clear` - **Deployment**: [docs/DEPLOY-WEBHOOK.md](docs/DEPLOY-WEBHOOK.md) - **Troubleshooting**: [TROUBLESHOOTING.md](TROUBLESHOOTING.md#webhook-receiver) ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) for the dev loop, code conventions, and the SemVer policy for the MCP tool surface. ## Security See [SECURITY.md](SECURITY.md). Vulnerabilities are reported privately via GitHub Security Advisory. ## License MIT — see [LICENSE](LICENSE). --- Registry verification — `mcp-name: io.github.aruxojuyu665/twocaptcha-mcp`