Skip to content

Commit e8cd720

Browse files
committed
mcp: add solve_captcha + get_egress_info tools (26 -> 28); proxy/captcha env vars
Sync the MCP server and docs with the framework: two new tools and the egress/captcha configuration. - mcp/server.py: solve_captcha(url) — detect + solve reCAPTCHA/hCaptcha/Turnstile (needs CAPTCHA_API_KEY); get_egress_info() — report proxy/region + the real public IP so residential egress can be verified. - mcp/README + README: 26 -> 28 tools; document the new tools and env vars (TILION_PROXY, TILION_REGION, CAPTCHA_API_KEY, CAPTCHA_PROVIDER). Note: fetch_protected_page auto-solves captchas when CAPTCHA_API_KEY is set. Proxy routing requires an engine build that honors --proxy-server; get_egress_info verifies it took effect.
1 parent 696e3c2 commit e8cd720

3 files changed

Lines changed: 62 additions & 8 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Stealth Chromium engine
1010
[![Chromium](https://img.shields.io/badge/chromium-151.0.7908.0-4285F4?logo=googlechrome&logoColor=white)](CHROMIUM_VERSION) [![pip](https://img.shields.io/badge/pip-3776AB?logo=pypi&logoColor=white)](https://pypi.org/project/tilion-fortress/) [![npm](https://img.shields.io/badge/npm-CB3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/tilion-fortress) [![Docker pulls](https://img.shields.io/docker/pulls/tilion/fortress?logo=docker&logoColor=white&label=pulls)](https://hub.docker.com/r/tilion/fortress)<br/>
1111
[![CreepJS](https://img.shields.io/badge/CreepJS-0%25%20headless-2ea44f)](docs/GAUNTLET_RESULTS.md) [![Runtime.enable leak](https://img.shields.io/badge/Runtime.enable-no%20leak-2ea44f)](docs/GAUNTLET_RESULTS.md)<br/>
1212
[![Copy for agent](https://img.shields.io/badge/Copy%20for%20agent-24292f?logo=readme&logoColor=white)](https://raw.githubusercontent.com/tiliondev/fortress/main/AGENTS.md) [![llms.txt](https://img.shields.io/badge/llms.txt-24292f?logo=readme&logoColor=white)](https://raw.githubusercontent.com/tiliondev/fortress/main/llms.txt)<br/>
13-
[![MCP server](https://img.shields.io/badge/MCP-fortress%20·%2026%20tools-6E56CF?logo=modelcontextprotocol&logoColor=white)](mcp/) [![npm tilion-mcp](https://img.shields.io/npm/v/tilion-mcp?logo=npm&logoColor=white&label=npx%20tilion-mcp&color=CB3837)](https://www.npmjs.com/package/tilion-mcp)
13+
[![MCP server](https://img.shields.io/badge/MCP-fortress%20·%2028%20tools-6E56CF?logo=modelcontextprotocol&logoColor=white)](mcp/) [![npm tilion-mcp](https://img.shields.io/npm/v/tilion-mcp?logo=npm&logoColor=white&label=npx%20tilion-mcp&color=CB3837)](https://www.npmjs.com/package/tilion-mcp)
1414

1515
**Fortress is a stealth Chromium engine that stops your scrapers and browser agents from getting blocked, with one line of code change.** Bot detectors flag automation by reading the browser fingerprint; Fortress corrects that fingerprint inside Chromium's C++, so the browser presents as an ordinary Chrome install. Scrapers finish their runs, agents reach the pages they were sent to, and CreepJS, Sannysoft, BrowserScan, and live Cloudflare Turnstile all read it as human. Point your existing Playwright or Puppeteer at Fortress over CDP, and nothing else in your code changes.
1616

@@ -94,7 +94,7 @@ pip install -U tilion-fortress # or: docker run --rm -p 9222:9222 tilion/
9494
| | |
9595
|---|---|
9696
| **[What it is](#what-it-is)** · **[Quick start](#quick-start)** | what it is, install, first script, AI-agent setup |
97-
| **[The Fortress MCP](#the-fortress-mcp--stealth-browsing-as-agent-tools)** | 26 stealth-browser tools for AI agents (Beta) |
97+
| **[The Fortress MCP](#the-fortress-mcp--stealth-browsing-as-agent-tools)** | 28 stealth-browser tools for AI agents (Beta) |
9898
| **[Why patch the engine, not the page](#why-patch-the-engine-not-the-page)** | the self-revealing-JS thesis + the three detection layers |
9999
| **[How Fortress compares](#how-fortress-compares)** | vs puppeteer-stealth · Camoufox · CloakBrowser · closed vendors |
100100
| **[Proof: live-detector results](#proof-live-detector-results)** | CreepJS / Sannysoft / BrowserScan / Cloudflare, with screenshots |
@@ -273,7 +273,7 @@ Full guide: https://github.com/tiliondev/fortress/blob/main/AGENTS.md
273273

274274
## The Fortress MCP — stealth browsing as agent tools &nbsp;<sub>Beta</sub>
275275

276-
Raw CDP is for code you write. The **Fortress MCP** is for agents that call **tools**: a [Model Context Protocol](https://modelcontextprotocol.io) server that hands Claude, Cursor, or any MCP client a stealth browser, so the moment a fetch is blocked it just calls a tool and gets the page. **26 tools, local and free**`fetch_protected_page`, `extract_page`, `crawl_site`, `recon_site_apis`, `search_web`, `run_browser_task`, `save_profile`, `get_stealth_cdp_endpoint`, and more.
276+
Raw CDP is for code you write. The **Fortress MCP** is for agents that call **tools**: a [Model Context Protocol](https://modelcontextprotocol.io) server that hands Claude, Cursor, or any MCP client a stealth browser, so the moment a fetch is blocked it just calls a tool and gets the page. **28 tools, local and free**`fetch_protected_page`, `extract_page`, `crawl_site`, `recon_site_apis`, `search_web`, `run_browser_task`, `save_profile`, `get_stealth_cdp_endpoint`, and more.
277277

278278
<p align="center"><img src="mcp/demo.gif" alt="Same site, same prompt: a vanilla browser is blocked by PerimeterX while an agent with the Fortress MCP returns clean JSON" width="760"/></p>
279279

@@ -324,7 +324,7 @@ Then just ask your agent — *“get the price off this StockX page”* — and
324324

325325
Tools are annotated (reads auto-approve, writes gate), **pre-warmed** on startup (~100 ms first call), concurrency-safe, and timeout- and SSRF-guarded. A hosted endpoint with **residential egress is coming soon**.
326326

327-
→ Full 26-tool table, benchmarks, and the agent skill: **[`mcp/`](mcp/README.md)**
327+
→ Full 28-tool table, benchmarks, and the agent skill: **[`mcp/`](mcp/README.md)**
328328

329329
---
330330

mcp/README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Fortress MCP — a stealth browser for AI agents
22

3-
> **Beta** · 26 tools · runs **local & free** · **hosted cloud coming soon**
3+
> **Beta** · 28 tools · runs **local & free** · **hosted cloud coming soon**
44
55
An [MCP](https://modelcontextprotocol.io) server that gives any AI agent the **Fortress
66
stealth engine** the moment it gets blocked. When a fetch hits Cloudflare, DataDome,
@@ -36,7 +36,7 @@ Fortress engine. The stealth Chromium downloads on first run and is cached local
3636

3737
If `tilion-mcp` isn't on PATH, use `"command": "python", "args": ["-m", "tilion.mcp"]`.
3838

39-
## The 26 tools
39+
## The 28 tools
4040

4141
| Tool | What the agent uses it for |
4242
|---|---|
@@ -55,6 +55,8 @@ If `tilion-mcp` isn't on PATH, use `"command": "python", "args": ["-m", "tilion.
5555
| `get_cookies` · `save_profile` · `load_profile` | read cookies · persist/restore an authenticated session |
5656
| `list_tabs` · `close_tab` | manage open tabs |
5757
| `get_stealth_cdp_endpoint` | a CDP url to point your OWN browser-use / Playwright / Puppeteer at |
58+
| `solve_captcha` | detect + solve a reCAPTCHA/hCaptcha/Turnstile (needs `CAPTCHA_API_KEY`) |
59+
| `get_egress_info` | report proxy/region + the real public IP the target sees (verify residential egress) |
5860

5961
Tools are **annotated** (`readOnlyHint` / `destructiveHint`) so clients auto-approve reads
6062
and gate writes. Every tool is **timeout- and SSRF-guarded**, caps its output, and returns a
@@ -84,6 +86,10 @@ Fingerprint suites: **Sannysoft all-green · CreepJS 0% headless · BrowserScan
8486
| `TILION_ALLOW_PRIVATE_EGRESS` | `0` | `1` to allow localhost / private IPs (SSRF guard off) |
8587
| `TILION_MCP_TOOL_TIMEOUT` | `120` | per-tool wall-clock cap (seconds) |
8688
| `TILION_BASE_URL` / `TILION_API_KEY` || hosted mode (**coming soon**) |
89+
| `TILION_PROXY` || egress proxy `http://user:pass@host:port` (residential/mobile) |
90+
| `TILION_REGION` || egress region hint (e.g. `us`) — aligns timezone/locale to the IP |
91+
| `CAPTCHA_API_KEY` || solver key; `fetch` then auto-solves + `solve_captcha` works |
92+
| `CAPTCHA_PROVIDER` | `2captcha` | `2captcha` \| `anticaptcha` \| `capsolver` |
8793

8894
## How it works
8995

mcp/server.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,17 @@ async def _t() -> Tilion:
200200
base_url = _env("BASE_URL")
201201
api_key = _env("API_KEY")
202202
headless = (_env("MCP_HEADLESS", "1") != "0")
203-
inst = Tilion(headless=headless, base_url=base_url, api_key=api_key)
203+
# Egress + captcha come from env so an agent host can configure a residential
204+
# proxy and a captcha key without changing the tool surface. TILION_PROXY /
205+
# TILION_REGION / CAPTCHA_API_KEY are read by the facade itself; pass any
206+
# explicit ones through here too.
207+
inst = Tilion(
208+
headless=headless, base_url=base_url, api_key=api_key,
209+
proxy=_env("PROXY"), # TILION_PROXY
210+
region=_env("REGION"), # TILION_REGION
211+
captcha_api_key=os.environ.get("CAPTCHA_API_KEY"),
212+
captcha_provider=os.environ.get("CAPTCHA_PROVIDER", "2captcha"),
213+
)
204214
await inst.start()
205215
_tilion = inst
206216
return _tilion
@@ -580,6 +590,44 @@ async def close_tab(index: int) -> dict:
580590
return await t.close_tab(int(index))
581591

582592

593+
@mcp.tool(annotations=_WRITE)
594+
@_safe
595+
async def solve_captcha(url: str | None = None) -> dict:
596+
"""Detect and solve a CAPTCHA on the current page (or a freshly-navigated `url`).
597+
598+
Use when a page is gated by reCAPTCHA/hCaptcha/Turnstile. Requires a solver key in the
599+
server env (`CAPTCHA_API_KEY`, provider `CAPTCHA_PROVIDER` = 2captcha|anticaptcha|
600+
capsolver). Returns {detected, kind, solvable, solved}. `fetch_protected_page` already
601+
auto-solves when a key is set; use this for an explicit retry on an interactive page.
602+
"""
603+
if url:
604+
await _check_url(url)
605+
t = await _t()
606+
return await t.solve_captcha(url)
607+
608+
609+
@mcp.tool(annotations=_READ)
610+
@_safe
611+
async def get_egress_info() -> dict:
612+
"""Report the current egress config: whether a proxy/region is set and the public IP
613+
the target actually sees. Use to confirm residential egress is active before hitting a
614+
hard target (a datacenter IP gets pre-classified as a bot regardless of fingerprint).
615+
Configure via env: TILION_PROXY=http://user:pass@host:port, TILION_REGION=us.
616+
"""
617+
t = await _t()
618+
info = {"proxy_configured": t._proxy is not None,
619+
"proxy_type": getattr(t._proxy, "type", None),
620+
"region": t._region,
621+
"captcha_solver": bool(t._captcha_api_key)}
622+
try:
623+
r = await t.fetch("https://api.ipify.org?format=json", timeout_ms=20000)
624+
import json as _json
625+
info["public_ip"] = _json.loads(r.get("text", "{}") or "{}").get("ip")
626+
except Exception:
627+
info["public_ip"] = None
628+
return info
629+
630+
583631
@mcp.tool(annotations=_READ)
584632
@_safe
585633
async def get_stealth_cdp_endpoint() -> dict:
@@ -611,7 +659,7 @@ def main() -> None:
611659
# Banner to STDERR only — stdout is the MCP stdio transport and must stay clean.
612660
print(
613661
"Tilion Fortress Stealth-Browser MCP [Beta]\n"
614-
" 26 tools | local & free | hosted cloud (residential egress) coming soon\n"
662+
" 28 tools | local & free | hosted cloud (residential egress) coming soon\n"
615663
" docs: DOCUMENTATION.md - more coming; benchmarks in the README\n"
616664
" listening on stdio...",
617665
file=sys.stderr, flush=True,

0 commit comments

Comments
 (0)