dots یک ریپوی ۷۹۷ بایتی است که هیچ کد تازه‌ای ندارد: کل cli.py در آن یک تابع است که فرمان ui از بسته‌ی invisible-playwright-mcp را صدا می‌زند و یک پین دقیق روی نسخه‌ی 0.70.2 می‌گذارد. آنچه در عمل نصب می‌شود ۲۲۹ کیلوبایت کد و ۲۶۱ مگابایت موتور فایرفاکس وصله‌خورده است. در این نوشته نصب واقعی، اجرای واقعی، ۱۶ ابزاری که سرور MCP برمی‌گرداند، و پیش‌نیازی که README از آن ساکت است را نشان می‌دهم.

این ریپو دقیقاً چه چیزی هست و چه چیزی نیست

مخزن feder-cr/dots در ۲۹ سپتامبر ۲۰۲۶ ساعت ۲۳:۰۶ به‌وقت جهانی ساخته شد و لحظه‌ی خواندن این نوشته ۱٫۸۲۴ ستاره و ۲۹۴ فورک داشت، با لایسنس MIT. کل مخزن پنج فایل دارد: README.md، دو فایل __init__ و __main__ که تقریباً خالی‌اند، cli.py به طول ۷۹۷ بایت، و یک فایل تست.

آن ۷۹۷ بایت تمام محصول‌اند و سه کار می‌کنند. فرمان ui به گروه فرمان‌های بسته‌ی پایین می‌رود، نه مستقیم به خود فرمان، چون همان گروه است که فایل .env کنار دستور را می‌خواند و پوشه‌ی نشست قبلی را منتقل می‌کند. نام برنامه هم dots می‌ماند. یعنی هر گزینه‌ای که در dots --help می‌بینی، گزینه‌ی همان رابط است و چیزی به آن اضافه نشده.

وابستگی‌اش یک خط است و عمداً پین شده، نه کف: invisible-playwright-mcp==0.70.2. خود مخزن توضیح می‌دهد که این بسته یک API منتشرشده ندارد، پس هر نسخه‌ی تازه باید آگاهانه انتخاب و با tests/test_entry.py آزموده شود، نه اینکه یک حل‌کننده‌ی وابستگی خودش آن را بردارد. آخرین نسخه‌ی منتشرشده در پایتون هم دقیقاً همان 0.70.2 است که در ۲۵ سپتامبر ۲۰۲۶ ساعت ۱۳:۵۶ به‌وقت جهانی روی PyPI نشسته و کمینه‌ی نسخه‌ی پایتونش 3.11 است.

عددمقدارمنبع
حجم چرخ کد229,281 بایتPyPI
حجم موتور دانلودی261 MBخروجی نصب
نسبت موتور به کدبیش از ۱۱۰۰ برابرمحاسبه‌شده
تعداد بسته‌های نصب‌شده۵۰خروجی uv
تعداد ابزارهای سرور MCP۱۶پاسخ واقعی list_tools

نسبت ۱۱۰۰ برابر از تقسیم ۲۶۱ مگابایت بر ۲۲۹,۲۸۱ بایت به دست می‌آید. این عدد پیام اصلی است: چیزی که این ریپو اضافه می‌کند یک نام تازه و یک پین است، و باری که دانلود می‌کنی تقریباً تماماً موتور است. بسته‌ی پایه‌ی invisible_playwright_mcp با ۳۱٫۷۲۵ ستاره و invisible_playwright با ۲٫۹۸۲ ستاره، هر دو از قبل همان کار را می‌کنند. لایسنس بسته‌ی پایه هم در ۲ سپتامبر ۲۰۲۶ از AGPL-3.0 به MIT عوض شده و آنچه پیش از آن تاریخ منتشر شده، زیر AGPL می‌ماند.

نصب در یک خط، و اولین اجرای واقعی

پیش‌نیاز فقط uv است. نصب را از همان اسکریپتی بگیر که README می‌دهد و بعد خط فرمان خودِ مخزن را اجرا کن.

# نصب uv؛ این تنها پیش‌نیاز اعلام‌شده است و نسخه‌ی تازه را خودش پیدا می‌کند
$ curl -LsSf https://astral.sh/uv/install.sh | sh
$ source $HOME/.local/bin/env
$ uvx --version
uvx 0.12.21 (x86_64-unknown-linux-gnu)

# اجرای خط فرمان خودِ مخزن: از گیت می‌سازد، نه از PyPI
$ uvx --from git+https://github.com/feder-cr/dots dots --help
Usage: dots ui [OPTIONS]

  Serve the two-pane interface: conversation left, live browser right.

در اجرای من، همین فرمان ۵۰ بسته را در ۵۶ میلی‌ثانیه نصب کرد و مجموعاً ۱۷٫۴ ثانیه طول کشید. عدد ۱۷٫۴ ثانیه فقط برای بار اول است، چون هم کد از گیت ساخته می‌شود و هم موتور دانلود می‌شود؛ اجراهای بعدی روی همان کش، چند ثانیه بیشتر نیست.

برای اجرای واقعی یک کلید OpenRouter لازم است. بدون آن برنامه پیش از هر کار دیگری می‌ایستد و کد خروج ۱ می‌دهد، و پیامش راه فرار را هم نام می‌برد.

$ uvx --from git+https://github.com/feder-cr/dots dots
Error: no OpenRouter key. Pass --openrouter-key or set OPENROUTER_API_KEY.
To drive the browser without a model, use the invisible_playwright library
directly: same engine, Playwright's whole API.

با کلید، چهار سطر چاپ می‌شود که هرکدام یک مرحله را تأیید می‌کند: مدل، دانلود موتور، اتصال سرور، و نشانی رابط.

$ uvx --from git+https://github.com/feder-cr/dots dots --openrouter-key sk-or-...
model    z-ai/glm-5.3-flash via https://openrouter.ai/api/v1
browser  downloading   0%  0 MB                  browser  downloading  50%  131 MB
browser  downloading 100%  261 MB                browser  verifying
browser  extracting                             browser  ready
Processing request of type ListToolsRequest
server   connected, 16 tools
open     http://127.0.0.1:8765

سطر server connected, 16 tools همان چیزی است که در بخش بعد می‌شماریم. اگر به‌جای آن خطا گرفتی، هنوز موتور بالا نیامده و باید بخش پیش‌نیازها را بخوانی.

همان مرورگر، داخل کلاد کد و کدکس

اگر رابط دو‌ستونی را نمی‌خواهی، همان موتور یک سرور MCP است و ابزارهایش را بدون هیچ مدلی می‌شود شمرد. کوچک‌ترین کلاینت این است، با یک بسته‌ی mcp و بدون کلید.

# کلاینت کوتاه: سرور را بالا می‌آورد و ابزارها را می‌شمارد
$ uvx --with mcp python3 mcp_client.py
server: stealth 0.70.2
tools: 16
 - browser_open
 - browser_close
 - browser_list
 - browser_status
 - browser_navigate
 - browser_read_text
 - browser_snapshot
 - browser_read_html
 - browser_take_screenshot
 - browser_watch
 - browser_click
 - browser_click_at
 - browser_type
 - browser_select_option
 - browser_press_key
 - browser_evaluate

پاسخ واقعی همان ۱۶ نامی است که مستند ویکی هم فهرست می‌کند، و نام‌ها عمداً آینه‌ی MCP مایکروسافت پلی‌رایت‌اند تا پرامپت‌های نوشته‌شده برای آن کار کند. سه گروه‌اند: مدیریت مرورگر، خواندن صفحه، و کار روی صفحه.

برای وصل‌کردن به کلاینتی که فایل تنظیمات می‌گیرد، بلوک زیر را با کلید mcpServers بگذار. کدکس همان را با TOML می‌خواهد و کلیدش servers است.

{
  "mcpServers": {
    "stealth": {
      "command": "uvx",
      "args": ["invisible-playwright-mcp"]
    }
  }
}

یک نشست کامل، بدون مدل و بدون هزینه‌ی توکن: مرورگر را باز کن، برو، بخوان، و ببین چه کسی دارد می‌گردد.

$ uvx --with mcp python3 mcp_drive.py
# همان seed در هر اجرا همان هویت را می‌سازد
browser_open       the main browser is open. identity: seed 4242 (you asked for it).
                   profile: none, so nothing survives this session. headless: yes.
browser_navigate   navigated to https://example.com/ (HTTP 200)
browser_read_text  This domain is for use in documentation examples...
                   [cut after 200 of 917 characters. Raise max_chars, or narrow
                    the selector to the part you need.]
browser_status     identity: seed 4242. page: https://example.com/.

این چهار سطر چهار قرارداد را نشان می‌دهند. اول اینکه browser_open تنها فراخوانی‌ای است که مرورگر باز می‌کند و بقیه بدون آن جواب the main browser is not open می‌دهند. دوم اینکه browser_navigate کد وضعیت HTTP را برمی‌گرداند، پس ۴۰۴ یا ریدایرکت به دیوار ورود دیده می‌شود و شبیه یک ورود عادی خوانده نمی‌شود. سوم اینکه برید متن با نشانه‌ی برش همراه است، پس متن بدون نشانه یعنی کل متن همان بخش است. چهارم اینکه browser_status هویت را می‌گوید: seed، خروجی، پروفایل و صفحه.

اگر اسنپ‌شات گرفتی، انتخابگر را عیناً بفرست و خودت ننویس. مستند ویکی اندازه گرفته که روی ۹۵۸ عنصر واقعی، ۸۸ درصد با یک انتخابگر قابل نشانه‌گذاری بودند اما فقط ۴۸ درصد بدون ابهام. پلی‌رایت روی اولین تطابق عمل می‌کند، پس انتخابگر دستی برای سومین لینکِ یکسان، بی‌صدا اولی را می‌زند.

پیش‌نیازی که README نمی‌گوید: موتور روی سرور بالا نمی‌آید

روی یک ماشین بدون نمایشگر، browser_open با خطای امن رد می‌شود و بقیه‌ی فراخوانی‌ها هم همان را تکرار می‌کنند. این خطا را گرفتم و ریشه‌اش را دنبال کردم: اول Xvfb لازم بود، و پس از نصب آن، موتور با پیام Broken pipe می‌افتاد. اجرای مستقیم باینری علت واقعی را نشان داد.

# اجرای مستقیم موتور، بدون لایه‌ی MCP، تا خطای اصلی دیده شود
$ /root/.cache/invisible-playwright/firefox-34_151.0_20260920230044/firefox --version
XPCOMGlueLoad error for file .../libmozgtk.so:
libgtk-3.so.0: cannot open shared object file: No such file or directory
Couldn't load XPCOM.

پس ترتیب درست روی سرور سه گام است: نصب Xvfb، نصب کتابخانه‌ی گرافیکی GTK3، و بالا آوردن یک نمایشگر مجازی. بعد از این سه، همان باینری بدون خطا نسخه‌اش را می‌گوید.

$ apt-get install -y xvfb
$ apt-get install -y libgtk-3-0
$ Xvfb :99 -screen 0 1280x800x24 &
$ export DISPLAY=:99
$ .../firefox-34_151.0_20260920230044/firefox --version
Mozilla Firefox 151.0

apt-get install را برای هر بسته جدا اجرا کن، نه یک‌جا: اگر یکی از نام‌ها کاندیدای نصب نداشته باشد، کل تراکنش برای بقیه هم لغو می‌شود و نشانه‌ای هم روی صفحه نمی‌ماند. در اجرای من همین اتفاق افتاد و برای همین libgtk-3-0 نصب نشد تا وقتی تنها اجرا شد.

محدودیت‌ها، و اینکه کِی سراغش بروی

این یک مرورگر است، نه حل‌کننده‌ی کپچا. کاری که می‌کند این است که یک نشست عادی فایرفاکس را شبیه نشست واقعی نشان می‌دهد. ویکی چهار لایه‌ی مسدودسازی را جدا می‌کند و لایه‌ی دوم، امتیاز شبکه، ربطی به این ابزار ندارد: یک آی‌پی بد، مرورگر بی‌نقص را هم رد می‌کند.

  • هر نشست حداکثر دو مرورگر دارد، به نام main و support، و هرکدام یک صفحه.
  • ابزار تب وجود ندارد. صفحه‌ی دوم یعنی همان support، چون تب دوم در main کوکی و اثر انگشت همان را با خود می‌برد.
  • browser_evaluate فقط می‌خواند. نوشتن در value یا صدا زدن click() رد می‌شود و نام ابزار درست را می‌گوید.
  • browser_type مقدار را می‌گذارد و کلید به کلید تایپ نمی‌کند، پس هندلرهای خودکاملیابی فعال نمی‌شوند؛ آنجا کلیک کن و browser_press_key بزن.
  • اگر خواستی این مرورگر به پرامپت‌های نوشته‌شده برای پلی‌رایت جواب بدهد، سه فراخوانی کافی است: browser_open، بعد browser_navigate، بعد browser_read_text.
  • مخزن تصریح می‌کند به OpenAI وابسته نیست، هرچند نامش یادآور آن است.

جمع‌بندی: اگر ایجنت تو به صفحه‌ای می‌رسد که به‌جای JSON یک فرم واقعی می‌بیند، این مسیر از نوشتن اسکریپت پلی‌رایت کوتاه‌تر است، چون رابطه‌ی نقش و موتور را یک‌بار برایت حل کرده. اگر فقط می‌خواهی داده بگیری، سراغ یک API برو؛ برای سرور MCP کروم نوشته‌ام چه چیزی را به دست می‌دهی و چه چیزی را نه. اگر می‌خواهی بدانی یک ریپوی ترند ارزش دنبال‌کردن دارد یا نه، معیار انتخاب ریپو همان معیار را می‌دهد. و اگر مرورگر داخل خود کروم می‌خواهی، BrowserKitten همان کار را از سمت دیگری انجام می‌دهد.

منابع

  1. مخزن dots در گیت‌هاب — شمارش ستاره و فورک، لایسنس و تاریخ ساخت در لحظه‌ی خواندن: ۳۰ سپتامبر ۲۰۲۶، ساعت ۱۷:۱۰ به‌وقت جهانی
  2. README مخزن — همان خط فرمان نصبی که در این نوشته اجرا شد
  3. فایل pyproject.toml — پین دقیق invisible-playwright-mcp==0.70.2 و دلیل پین بودنش
  4. فایل cli.py — همان ۷۹۷ بایتی که کل محصول است
  5. فایل تست ورود — آزمونی که ارتقای پین باید از آن رد شود
  6. داده‌های PyPI بسته‌ی invisible-playwright-mcp — نسخه‌ی 0.70.2، زمان انتشار ۲۵ سپتامبر ۲۰۲۶، حجم چرخ و کمینه‌ی نسخه‌ی پایتون
  7. صفحه‌ی بسته در PyPI — فهرست وابستگی‌ها و متن لایسنس
  8. مخزن invisible_playwright_mcp — بسته‌ی پایه، با ۳۱٫۷۲۵ ستاره در لحظه‌ی خواندن
  9. مخزن invisible_playwright — موتور به‌صورت کتابخانه، با ۲٫۹۸۲ ستاره
  10. ویکی: سرور MCP — تنظیمات، متغیرهای محیطی و جدول کامل ۱۶ ابزار
  11. ویکی: چرا ایجنت من مسدود می‌شود — چهار لایه‌ی مسدودسازی و اینکه کدام لایه به ابزار ربطی ندارد
  12. ویکی: مقایسه‌ی ابزارهای ایجنت مرورگری — جایگاه این خانواده در برابر گزینه‌های دیگر
  13. اسکریپت نصب uv — همان چیزی که اولین بلوک اجرا می‌کند