capsolver-mcp 0.1.0__py3-none-any.whl
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.
- capsolver_mcp/__init__.py +9 -0
- capsolver_mcp/__main__.py +74 -0
- capsolver_mcp/py.typed +0 -0
- capsolver_mcp/server.py +351 -0
- capsolver_mcp-0.1.0.dist-info/METADATA +157 -0
- capsolver_mcp-0.1.0.dist-info/RECORD +9 -0
- capsolver_mcp-0.1.0.dist-info/WHEEL +4 -0
- capsolver_mcp-0.1.0.dist-info/entry_points.txt +2 -0
- capsolver_mcp-0.1.0.dist-info/licenses/LICENSE +15 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Entry point for ``python -m capsolver_mcp`` and the ``capsolver-mcp`` console script.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
# stdio transport (default, for local MCP clients like Claude Desktop)
|
|
5
|
+
capsolver-mcp
|
|
6
|
+
|
|
7
|
+
# SSE transport (for remote access)
|
|
8
|
+
capsolver-mcp --transport sse --host 0.0.0.0 --port 8000
|
|
9
|
+
|
|
10
|
+
# Streamable HTTP transport (MCP 2025-03-26 spec)
|
|
11
|
+
capsolver-mcp --transport streamable-http --host 0.0.0.0 --port 8000
|
|
12
|
+
|
|
13
|
+
Environment variables:
|
|
14
|
+
CAPSOLVER_API_KEY — your CapSolver API key (required for solving tools)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import argparse
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main() -> None:
|
|
23
|
+
parser = argparse.ArgumentParser(
|
|
24
|
+
description="CapSolver MCP Server — expose captcha-solving tools via Model Context Protocol.",
|
|
25
|
+
)
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"--transport",
|
|
28
|
+
choices=["stdio", "sse", "streamable-http"],
|
|
29
|
+
default="stdio",
|
|
30
|
+
help="Transport protocol: stdio (default), sse, or streamable-http.",
|
|
31
|
+
)
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"--host",
|
|
34
|
+
default="127.0.0.1",
|
|
35
|
+
help="Host to bind for SSE/HTTP transports (default: 127.0.0.1).",
|
|
36
|
+
)
|
|
37
|
+
parser.add_argument(
|
|
38
|
+
"--port",
|
|
39
|
+
type=int,
|
|
40
|
+
default=8000,
|
|
41
|
+
help="Port to bind for SSE/HTTP transports (default: 8000).",
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"--api-key",
|
|
45
|
+
default=None,
|
|
46
|
+
help="CapSolver API key. Falls back to CAPSOLVER_API_KEY env var.",
|
|
47
|
+
)
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"--name",
|
|
50
|
+
default="capsolver",
|
|
51
|
+
help="Server name advertised to MCP clients (default: capsolver).",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
args = parser.parse_args()
|
|
55
|
+
|
|
56
|
+
from capsolver_mcp.server import create_server
|
|
57
|
+
|
|
58
|
+
server = create_server(
|
|
59
|
+
api_key=args.api_key,
|
|
60
|
+
server_name=args.name,
|
|
61
|
+
host=args.host,
|
|
62
|
+
port=args.port,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if args.transport == "sse":
|
|
66
|
+
server.run(transport="sse")
|
|
67
|
+
elif args.transport == "streamable-http":
|
|
68
|
+
server.run(transport="streamable-http")
|
|
69
|
+
else:
|
|
70
|
+
server.run(transport="stdio")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
main()
|
capsolver_mcp/py.typed
ADDED
|
File without changes
|
capsolver_mcp/server.py
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"""CapSolver MCP Server — tool definitions and server factory.
|
|
2
|
+
|
|
3
|
+
Exposes five tools via MCP:
|
|
4
|
+
- solve_captcha: Token-mode solve (no browser required)
|
|
5
|
+
- detect_captchas: Detect captcha types on a page (requires browser session)
|
|
6
|
+
- solve_on_page: Detect + solve + autofill on a page (requires browser session)
|
|
7
|
+
- get_balance: Check account balance
|
|
8
|
+
- get_supported_captchas: List supported captcha types
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from mcp.server.fastmcp import FastMCP
|
|
17
|
+
|
|
18
|
+
from capsolver_core import Capsolver, CaptchaInfo, CaptchaType
|
|
19
|
+
from capsolver_core.captcha.types import Solution
|
|
20
|
+
from capsolver_core.core.errors import CapsolverError, CapsolverTimeoutError, NetworkError, RateLimitError
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _get_capsolver(api_key: str | None = None) -> Capsolver:
|
|
24
|
+
"""Construct a Capsolver instance, reading API key from env if not provided."""
|
|
25
|
+
key = api_key or os.environ.get("CAPSOLVER_API_KEY", "")
|
|
26
|
+
return Capsolver(api_key=key)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _solution_to_dict(sol: Solution) -> dict[str, Any]:
|
|
30
|
+
"""Serialize a Solution to a JSON-friendly dict."""
|
|
31
|
+
return {
|
|
32
|
+
"captcha_type": sol.captcha_type.value,
|
|
33
|
+
"token": sol.token,
|
|
34
|
+
"expire_time": sol.expire_time,
|
|
35
|
+
"user_agent": sol.user_agent,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _error_response(exc: Exception) -> dict[str, Any]:
|
|
40
|
+
"""Build a consistent error dict from an exception.
|
|
41
|
+
|
|
42
|
+
Extracts structured fields from CapsolverError when available so that
|
|
43
|
+
AI agents can programmatically inspect error_id, error_code, http_status
|
|
44
|
+
and task_id instead of parsing free-form error strings.
|
|
45
|
+
"""
|
|
46
|
+
base: dict[str, Any] = {"success": False, "error": str(exc)}
|
|
47
|
+
if isinstance(exc, CapsolverTimeoutError) and exc.task_id is not None:
|
|
48
|
+
base["task_id"] = exc.task_id
|
|
49
|
+
if isinstance(exc, NetworkError) and exc.cause is not None:
|
|
50
|
+
base["cause"] = str(exc.cause)
|
|
51
|
+
if isinstance(exc, RateLimitError):
|
|
52
|
+
base["error_type"] = "rate_limit"
|
|
53
|
+
if isinstance(exc, CapsolverError):
|
|
54
|
+
if exc.error_id is not None:
|
|
55
|
+
base["error_id"] = exc.error_id
|
|
56
|
+
if exc.error_code:
|
|
57
|
+
base["error_code"] = exc.error_code
|
|
58
|
+
if exc.error_description:
|
|
59
|
+
base["error_description"] = exc.error_description
|
|
60
|
+
if exc.http_status is not None:
|
|
61
|
+
base["http_status"] = exc.http_status
|
|
62
|
+
return base
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def create_server(
|
|
66
|
+
api_key: str | None = None,
|
|
67
|
+
server_name: str = "capsolver",
|
|
68
|
+
host: str = "127.0.0.1",
|
|
69
|
+
port: int = 8000,
|
|
70
|
+
) -> FastMCP:
|
|
71
|
+
"""Create and configure the MCP server with all CapSolver tools.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
api_key: CapSolver API key. Falls back to CAPSOLVER_API_KEY env var.
|
|
75
|
+
server_name: Name advertised to MCP clients.
|
|
76
|
+
host: Bind host for SSE / HTTP transports.
|
|
77
|
+
port: Bind port for SSE / HTTP transports.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
A configured FastMCP server instance.
|
|
81
|
+
"""
|
|
82
|
+
server = FastMCP(server_name, host=host, port=port)
|
|
83
|
+
capsolver = _get_capsolver(api_key)
|
|
84
|
+
|
|
85
|
+
# ── Tool 1: solve_captcha (token mode) ────────────────────────
|
|
86
|
+
|
|
87
|
+
@server.tool()
|
|
88
|
+
async def solve_captcha(
|
|
89
|
+
captcha_type: str,
|
|
90
|
+
website_url: str,
|
|
91
|
+
website_key: str,
|
|
92
|
+
version: str | None = None,
|
|
93
|
+
page_action: str | None = None,
|
|
94
|
+
min_score: float | None = None,
|
|
95
|
+
invisible: bool | None = None,
|
|
96
|
+
enterprise: bool | None = None,
|
|
97
|
+
s_token: str | None = None,
|
|
98
|
+
cdata: str | None = None,
|
|
99
|
+
proxy: str | None = None,
|
|
100
|
+
user_agent: str | None = None,
|
|
101
|
+
timeout: float | None = None,
|
|
102
|
+
polling_interval: float | None = None,
|
|
103
|
+
) -> dict[str, Any]:
|
|
104
|
+
"""Solve a captcha in token mode — no browser required.
|
|
105
|
+
|
|
106
|
+
CapSolver will create a task on its server, solve the captcha challenge,
|
|
107
|
+
and return the solution token. Use this when you only need the token to
|
|
108
|
+
submit to a target website (e.g. via form POST or API call).
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
captcha_type: One of "reCaptchaV2", "reCaptchaV3", "cloudflare".
|
|
112
|
+
website_url: The full URL of the page where the captcha appears.
|
|
113
|
+
website_key: The site key / public key / data-sitekey of the captcha widget.
|
|
114
|
+
version: reCAPTCHA version hint ("v2" or "v3"). Usually auto-detected.
|
|
115
|
+
page_action: reCAPTCHA v3 action name (e.g. "login", "submit").
|
|
116
|
+
min_score: reCAPTCHA v3 minimum score threshold (0.0–1.0).
|
|
117
|
+
invisible: Whether the reCAPTCHA widget uses invisible mode.
|
|
118
|
+
enterprise: Whether to use the Enterprise API variant.
|
|
119
|
+
s_token: Enterprise s_token for stoken-based verification.
|
|
120
|
+
cdata: Cloudflare Turnstile custom data parameter.
|
|
121
|
+
proxy: Proxy in "user:pass@host:port" or "host:port" format.
|
|
122
|
+
user_agent: Custom User-Agent string to use during solving.
|
|
123
|
+
timeout: Maximum seconds to wait for a solution (default: 120).
|
|
124
|
+
polling_interval: Seconds between status polls (default: 5).
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
{"success": True, "solution": {...}} on success.
|
|
128
|
+
{"success": False, "error": "...", "error_id": ..., "error_code": "...", "http_status": ...} on failure.
|
|
129
|
+
"""
|
|
130
|
+
try:
|
|
131
|
+
ct = CaptchaType(captcha_type)
|
|
132
|
+
except ValueError:
|
|
133
|
+
return {
|
|
134
|
+
"success": False,
|
|
135
|
+
"error": f"Unsupported captcha type: {captcha_type}. Supported: {[t.value for t in CaptchaType]}",
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
info = CaptchaInfo(
|
|
139
|
+
type=ct,
|
|
140
|
+
website_url=website_url,
|
|
141
|
+
website_key=website_key,
|
|
142
|
+
version=version,
|
|
143
|
+
page_action=page_action,
|
|
144
|
+
min_score=min_score,
|
|
145
|
+
invisible=invisible,
|
|
146
|
+
enterprise=enterprise,
|
|
147
|
+
s=s_token,
|
|
148
|
+
cdata=cdata,
|
|
149
|
+
proxy=proxy,
|
|
150
|
+
user_agent=user_agent,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
from capsolver_core.core.client import WaitOptions
|
|
154
|
+
|
|
155
|
+
wait_opts = None
|
|
156
|
+
if timeout is not None or polling_interval is not None:
|
|
157
|
+
wait_opts = WaitOptions(timeout=timeout, polling_interval=polling_interval)
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
solution = await capsolver.solve(info, wait_options=wait_opts)
|
|
161
|
+
return {"success": True, "solution": _solution_to_dict(solution)}
|
|
162
|
+
except Exception as e:
|
|
163
|
+
return _error_response(e)
|
|
164
|
+
|
|
165
|
+
# ── Tool 2: detect_captchas (browser mode) ────────────────────
|
|
166
|
+
|
|
167
|
+
@server.tool()
|
|
168
|
+
async def detect_captchas(page_url: str) -> dict[str, Any]:
|
|
169
|
+
"""Detect which captcha types are present on a given page URL.
|
|
170
|
+
|
|
171
|
+
Opens the page in a headless browser and inspects the DOM to identify
|
|
172
|
+
captcha widgets (reCAPTCHA, Cloudflare Turnstile).
|
|
173
|
+
Requires playwright to be installed.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
page_url: The full URL of the page to inspect.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
{"success": True, "url": "...", "detected_captchas": ["reCaptchaV2", ...]} on success.
|
|
180
|
+
{"success": False, "error": "..."} on failure.
|
|
181
|
+
"""
|
|
182
|
+
try:
|
|
183
|
+
driver = await _launch_browser_session(page_url)
|
|
184
|
+
except ImportError:
|
|
185
|
+
return {
|
|
186
|
+
"success": False,
|
|
187
|
+
"error": "Browser automation not available. Install with: pip install capsolver-mcp[browser]",
|
|
188
|
+
}
|
|
189
|
+
except Exception as e:
|
|
190
|
+
return {"success": False, "error": f"Failed to open page: {e}"}
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
detected = await capsolver.detect(driver)
|
|
194
|
+
return {
|
|
195
|
+
"success": True,
|
|
196
|
+
"url": page_url,
|
|
197
|
+
"detected_captchas": [t.value for t in detected],
|
|
198
|
+
}
|
|
199
|
+
except Exception as e:
|
|
200
|
+
return _error_response(e)
|
|
201
|
+
finally:
|
|
202
|
+
await _close_browser_session(driver)
|
|
203
|
+
|
|
204
|
+
# ── Tool 3: solve_on_page (browser mode) ──────────────────────
|
|
205
|
+
|
|
206
|
+
@server.tool()
|
|
207
|
+
async def solve_on_page(
|
|
208
|
+
page_url: str,
|
|
209
|
+
autofill: bool = True,
|
|
210
|
+
timeout: float | None = None,
|
|
211
|
+
polling_interval: float | None = None,
|
|
212
|
+
) -> dict[str, Any]:
|
|
213
|
+
"""Detect, solve, and optionally autofill all captchas on a page.
|
|
214
|
+
|
|
215
|
+
One-shot operation: opens the page in a headless browser, detects captcha
|
|
216
|
+
widgets, solves them via the CapSolver API, and injects the solution tokens
|
|
217
|
+
back into the page DOM.
|
|
218
|
+
|
|
219
|
+
Requires playwright to be installed.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
page_url: The full URL of the page containing captchas.
|
|
223
|
+
autofill: If True, inject solved tokens into the page (default: True).
|
|
224
|
+
timeout: Maximum seconds to wait per captcha (default: 120).
|
|
225
|
+
polling_interval: Seconds between status polls (default: 5).
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
{"success": True, "url": "...", "results": [{"captcha_type": "...", "solved": true, "token": "...", "filled": true}, ...]}
|
|
229
|
+
{"success": False, "error": "..."} on failure.
|
|
230
|
+
"""
|
|
231
|
+
try:
|
|
232
|
+
driver = await _launch_browser_session(page_url)
|
|
233
|
+
except ImportError:
|
|
234
|
+
return {
|
|
235
|
+
"success": False,
|
|
236
|
+
"error": "Browser automation not available. Install with: pip install capsolver-mcp[browser]",
|
|
237
|
+
}
|
|
238
|
+
except Exception as e:
|
|
239
|
+
return {"success": False, "error": f"Failed to open page: {e}"}
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
from capsolver_core.capsolver import SolveOnPageOptions
|
|
243
|
+
|
|
244
|
+
opts = SolveOnPageOptions(
|
|
245
|
+
autofill=autofill,
|
|
246
|
+
throw_on_error=False,
|
|
247
|
+
timeout=timeout,
|
|
248
|
+
polling_interval=polling_interval,
|
|
249
|
+
)
|
|
250
|
+
results = await capsolver.solve_on_page(driver, options=opts)
|
|
251
|
+
return {
|
|
252
|
+
"success": True,
|
|
253
|
+
"url": page_url,
|
|
254
|
+
"results": [
|
|
255
|
+
{
|
|
256
|
+
"captcha_type": (r.info.type.value),
|
|
257
|
+
"solved": r.solution is not None,
|
|
258
|
+
"token": r.solution.token if r.solution else None,
|
|
259
|
+
"filled": r.filled,
|
|
260
|
+
"error": r.error,
|
|
261
|
+
}
|
|
262
|
+
for r in results
|
|
263
|
+
],
|
|
264
|
+
}
|
|
265
|
+
except Exception as e:
|
|
266
|
+
return _error_response(e)
|
|
267
|
+
finally:
|
|
268
|
+
await _close_browser_session(driver)
|
|
269
|
+
|
|
270
|
+
# ── Tool 4: get_balance ───────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
@server.tool()
|
|
273
|
+
async def get_balance() -> dict[str, Any]:
|
|
274
|
+
"""Get the current CapSolver account balance.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
{"success": True, "balance": 5.67, "packages": [...]} on success.
|
|
278
|
+
{"success": False, "error": "...", "error_id": ..., "error_code": "..."} on failure.
|
|
279
|
+
"""
|
|
280
|
+
try:
|
|
281
|
+
balance = await capsolver.get_balance()
|
|
282
|
+
return {"success": True, "balance": balance.balance, "packages": balance.packages}
|
|
283
|
+
except Exception as e:
|
|
284
|
+
return _error_response(e)
|
|
285
|
+
|
|
286
|
+
# ── Tool 5: get_supported_captchas ────────────────────────────
|
|
287
|
+
|
|
288
|
+
@server.tool()
|
|
289
|
+
async def get_supported_captchas() -> dict[str, Any]:
|
|
290
|
+
"""List all captcha types supported by this CapSolver instance.
|
|
291
|
+
|
|
292
|
+
Returns the registered handler names and all available captcha type values.
|
|
293
|
+
No parameters required.
|
|
294
|
+
"""
|
|
295
|
+
handlers = capsolver.get_supported_captchas()
|
|
296
|
+
captcha_types = [t.value for t in CaptchaType]
|
|
297
|
+
return {
|
|
298
|
+
"success": True,
|
|
299
|
+
"registered_handlers": handlers,
|
|
300
|
+
"captcha_types": captcha_types,
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return server
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
# ── Browser session helpers ───────────────────────────────────────
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
async def _launch_browser_session(page_url: str) -> Any:
|
|
310
|
+
"""Launch a headless browser and navigate to the given URL.
|
|
311
|
+
|
|
312
|
+
Returns a PageDriver wrapping the Playwright page.
|
|
313
|
+
Raises ImportError if playwright is not installed.
|
|
314
|
+
"""
|
|
315
|
+
try:
|
|
316
|
+
from playwright.async_api import async_playwright
|
|
317
|
+
except ImportError:
|
|
318
|
+
raise
|
|
319
|
+
|
|
320
|
+
from capsolver_core.browser.adapter import from_playwright_page
|
|
321
|
+
|
|
322
|
+
pw = await async_playwright().start()
|
|
323
|
+
browser = await pw.chromium.launch(headless=True)
|
|
324
|
+
page = await browser.new_page()
|
|
325
|
+
await page.goto(page_url, wait_until="domcontentloaded", timeout=30_000)
|
|
326
|
+
|
|
327
|
+
# Captcha widgets (reCAPTCHA api.js, Turnstile) load asynchronously after
|
|
328
|
+
# DOMContentLoaded. Wait for the network to settle so their scripts can
|
|
329
|
+
# register before we detect — bounded so pages with long-lived
|
|
330
|
+
# connections don't hang. Best-effort: ignore timeout.
|
|
331
|
+
try:
|
|
332
|
+
await page.wait_for_load_state("networkidle", timeout=5_000)
|
|
333
|
+
except Exception:
|
|
334
|
+
pass
|
|
335
|
+
|
|
336
|
+
driver = from_playwright_page(page)
|
|
337
|
+
# Stash references for cleanup
|
|
338
|
+
setattr(driver, "_pw", pw)
|
|
339
|
+
setattr(driver, "_browser", browser)
|
|
340
|
+
return driver
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
async def _close_browser_session(driver: Any) -> None:
|
|
344
|
+
"""Clean up browser resources."""
|
|
345
|
+
try:
|
|
346
|
+
if hasattr(driver, "_browser") and driver._browser:
|
|
347
|
+
await driver._browser.close()
|
|
348
|
+
if hasattr(driver, "_pw") and driver._pw:
|
|
349
|
+
await driver._pw.stop()
|
|
350
|
+
except Exception:
|
|
351
|
+
pass
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: capsolver-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP Server for CapSolver — expose captcha-solving capabilities to AI agents via Model Context Protocol.
|
|
5
|
+
Project-URL: Homepage, https://capsolver.com
|
|
6
|
+
Project-URL: Repository, https://github.com/capsolver-ai/mcp-capsolver
|
|
7
|
+
Project-URL: Issues, https://github.com/capsolver-ai/mcp-capsolver/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/capsolver-ai/mcp-capsolver/blob/main/docs/mcp-integration.md
|
|
9
|
+
Project-URL: Changelog, https://github.com/capsolver-ai/mcp-capsolver/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Security, https://github.com/capsolver-ai/mcp-capsolver/blob/main/SECURITY.md
|
|
11
|
+
Author-email: capsolver-ai <dev@capsolver.ai>
|
|
12
|
+
License-Expression: ISC
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: ai-agent,capsolver,captcha,mcp,model-context-protocol
|
|
15
|
+
Classifier: Development Status :: 3 - Alpha
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: capsolver-core>=0.1.0
|
|
28
|
+
Requires-Dist: mcp<2,>=1.0.0
|
|
29
|
+
Provides-Extra: browser
|
|
30
|
+
Requires-Dist: playwright>=1.40; extra == 'browser'
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-asyncio>=1.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# capsolver-mcp
|
|
39
|
+
|
|
40
|
+
MCP Server for [CapSolver](https://capsolver.com) — expose captcha-solving capabilities to AI agents via the [Model Context Protocol](https://modelcontextprotocol.io).
|
|
41
|
+
|
|
42
|
+
See the [capsolver-ai](https://github.com/capsolver-ai/capsolver-ai) hub repo for integration examples and the full documentation.
|
|
43
|
+
|
|
44
|
+
For detailed MCP client setup (Claude Desktop, Claude Code, Cursor, Windsurf, Cline, and more), see [docs/mcp-integration.md](docs/mcp-integration.md).
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install capsolver-mcp
|
|
50
|
+
pip install capsolver-mcp[browser] # with Playwright support (for detect/solve_on_page)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
All tools read the API key from the environment:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# bash / zsh
|
|
57
|
+
export CAPSOLVER_API_KEY="your-capsolver-api-key"
|
|
58
|
+
|
|
59
|
+
# PowerShell
|
|
60
|
+
$env:CAPSOLVER_API_KEY = "your-capsolver-api-key"
|
|
61
|
+
|
|
62
|
+
# cmd
|
|
63
|
+
set CAPSOLVER_API_KEY=your-capsolver-api-key
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Usage
|
|
67
|
+
|
|
68
|
+
### CLI
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# stdio (default — for local MCP clients like Claude Desktop)
|
|
72
|
+
capsolver-mcp
|
|
73
|
+
|
|
74
|
+
# SSE (for remote / HTTP access)
|
|
75
|
+
capsolver-mcp --transport sse --host 0.0.0.0 --port 8000
|
|
76
|
+
|
|
77
|
+
# Streamable HTTP (MCP 2025-03-26 spec)
|
|
78
|
+
capsolver-mcp --transport streamable-http --host 0.0.0.0 --port 8000
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### CLI options
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
capsolver-mcp [OPTIONS]
|
|
85
|
+
|
|
86
|
+
--transport {stdio,sse,streamable-http}
|
|
87
|
+
Transport protocol (default: stdio)
|
|
88
|
+
--host HOST Bind host for SSE/HTTP transports (default: 127.0.0.1)
|
|
89
|
+
--port PORT Bind port for SSE/HTTP transports (default: 8000)
|
|
90
|
+
--api-key KEY API key (fallback: CAPSOLVER_API_KEY env)
|
|
91
|
+
--name NAME Server name (default: capsolver)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Programmatic
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from capsolver_mcp.server import create_server
|
|
98
|
+
|
|
99
|
+
server = create_server(
|
|
100
|
+
api_key="your-key", # or set CAPSOLVER_API_KEY env var
|
|
101
|
+
server_name="capsolver", # name advertised to MCP clients
|
|
102
|
+
host="127.0.0.1", # bind host for SSE / HTTP transports
|
|
103
|
+
port=8000, # bind port for SSE / HTTP transports
|
|
104
|
+
)
|
|
105
|
+
server.run(transport="sse") # or "stdio" or "streamable-http"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
> **Note:** `host` and `port` are constructor parameters on `create_server()`
|
|
109
|
+
> (forwarded to `FastMCP`), matching the MCP Python SDK 1.x API.
|
|
110
|
+
|
|
111
|
+
## Configure in Claude Desktop
|
|
112
|
+
|
|
113
|
+
Add to your `claude_desktop_config.json`:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"mcpServers": {
|
|
118
|
+
"capsolver": {
|
|
119
|
+
"command": "capsolver-mcp",
|
|
120
|
+
"env": {
|
|
121
|
+
"CAPSOLVER_API_KEY": "your-key"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Available tools
|
|
129
|
+
|
|
130
|
+
| Tool | Browser? | Description |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| `solve_captcha` | No | Solve a captcha by type + site params (token mode) |
|
|
133
|
+
| `detect_captchas` | Yes | Scan a page URL and list present captcha types |
|
|
134
|
+
| `solve_on_page` | Yes | Detect + solve + autofill all captchas on a page |
|
|
135
|
+
| `get_balance` | No | Check account balance and packages |
|
|
136
|
+
| `get_supported_captchas` | No | List all supported captcha types and handlers |
|
|
137
|
+
|
|
138
|
+
Browser-based tools (`detect_captchas`, `solve_on_page`) require the `browser` extra:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
pip install capsolver-mcp[browser]
|
|
142
|
+
playwright install chromium
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Development
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
git clone https://github.com/capsolver-ai/mcp-capsolver.git
|
|
149
|
+
cd mcp-capsolver
|
|
150
|
+
uv sync --all-extras # or: pip install -r requirements-dev.txt
|
|
151
|
+
uv run pytest # run tests
|
|
152
|
+
uv run ruff check src tests # lint
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
ISC
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
capsolver_mcp/__init__.py,sha256=aRttRvodWnYgwfOZ87mkX2E17sAZXEwdQkkXsZaT4e0,262
|
|
2
|
+
capsolver_mcp/__main__.py,sha256=UG53cr-xfY8rZXfU9hQoQ0xz9cxc15k-UDNIFOYgxD8,2131
|
|
3
|
+
capsolver_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
capsolver_mcp/server.py,sha256=2R9m3gnq3uQhf2ora_N625bC3r4DvJc-YKxde9c3ZI0,13904
|
|
5
|
+
capsolver_mcp-0.1.0.dist-info/METADATA,sha256=AabIyI07Rh2z0DHOCb2NvmIUZo2pjIaO0Vo8fhSpEPc,5065
|
|
6
|
+
capsolver_mcp-0.1.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
7
|
+
capsolver_mcp-0.1.0.dist-info/entry_points.txt,sha256=AfxYxVI3Ai7HfGzAR-3mMCSMiMmtxrz5fnR0czEEGKY,62
|
|
8
|
+
capsolver_mcp-0.1.0.dist-info/licenses/LICENSE,sha256=p65mlslnCknoxoEoP3lESj40mUaZRt2xzgMlrGE72hM,763
|
|
9
|
+
capsolver_mcp-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 capsolver-ai
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|