سرور chrome-devtools-mcp نسخه‌ی ۱٫۱۰٫۱ روی کروم ۱۴۵ واقعی وصل شد و شش فراخوانی زد: صفحه باز شد، پنج پیام کنسول خوانده شد که خود صفحه ساخته بود، دو درخواست شبکه فهرست شد، و یک فراخوانی عمداً نادرست با پیام خطای دقیق برگشت. سرور ۳۰ ابزار و ۶٬۴۵۸ توکن دارد، اما در حالت باریک همان سه ابزار ۲۱۲ توکن‌اند. زمان خواندن داده: ۲۶ سپتامبر ۲۰۲۶.

نصب و آنچه پیش از اولین فراخوانی باید بدانید

بسته در رجیستری با همین نسخه موجود بود و با یک فرمان نصب شد. مسیر ورودی واقعی در فایل package.json خود بسته نوشته شده و حدس زدنش لازم نیست: build/src/bin/chrome-devtools-mcp.js، نه index.js در ریشه. حدس زدن این مسیر سه دقیقه هدر داد.

$ npm install chrome-devtools-mcp@1.10.1
added 1 package in 640ms
$ node -e "console.log(require('./node_modules/chrome-devtools-mcp/package.json').version)"
1.10.1
$ du -sh node_modules/chrome-devtools-mcp
15M

راهنمای خط فرمان ۵۲ پرچم دارد، که ۱۱ تای آن دسته‌بندی ابزار است. بیشترشان برای این پست لازم نیست. چهار تای اول را بخوانید چون هرکدام یک دام دارند: --headless، --isolated که یک پوشه‌ی کاربر موقت می‌سازد و در پایان پاکش می‌کند، --executablePath برای اشاره به کروم خودتان، و --no-page-id-routing.

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

[2] navigate_page -> Input validation error: Invalid arguments for tool
    navigate_page: pageId: Invalid input: expected number, received undefined

این خطا دروغ نمی‌گوید، ولی راهنمایی هم نیست: شما فکر می‌کنید مرورگر خراب است، در حالی که فقط باید شماره‌ی صفحه را بدهید. اگر یک نشست بیشتر دارید، همین پرچم را روشن نگه دارید، چون وظیفه‌اش دقیقاً تفکیک نشست‌های هم‌زمان است. جدا کردن نشست‌های هم‌زمان فقط روی مرورگر متوقف نمی‌شود؛ همان الگو برای پوشه‌ی کاری هم در راهنمای کار درخت هرمز توضیح داده شده است.

دومین دام: کروم به‌عنوان ریشه بالا نمی‌آید

پس از رفع آن، خطای دوم آمد و این یکی واقعاً بن‌بست بود:

Chrome failed to start: Protocol error (Target.setDiscoverTargets): Target closed
chrome-devtools-mcp is running as root and Chrome does not start as root
(https://crbug.com/638180). Run chrome-devtools-mcp as a user that is not root.

راه‌حل نیست که کروم را با پرچمی دور بزنید، بلکه این است که سرور را با کاربری اجرا کنید که ریشه نیست. سه چیز باید درست باشد: خود کروم در مسیری باشد که آن کاربر بتواند بخواند، و پوشه‌ی کاری هم مال همان کاربر باشد. اولین تلاش من هم شکست خورد، چون /root با دسترسی هفده‌دهی بسته است و کاربر عادی اصلاً نمی‌تواند به درون آن برود؛ کروم را کپی کردم تا مسیرش خوانا شود.

پس از آن، همان چهار فراخوانی بدون هیچ تغییر دیگری کار کرد. نسخه‌ی کرومی که اجرا شد ۱۴۵٫۰٫۷۶۳۲٫۶ بود. نکته‌ی عملی برای محیط‌های سرور: اگر ایجنت شما با کاربر ریشه اجرا می‌شود، این سرور را باید به کاربر دیگری بسپارید، و این یعنی یک لایه‌ی بیشتر در استقرار. مسیر، پوشه و هویت اجرای نشست از متغیرهای محیطی می‌آید و فهرست کامل آن‌ها در مرجع متغیرهای محیطی آمده است.

سه فراخوانی واقعی روی یک صفحه‌ی واقعی

صفحه‌ی آزمایشی عمداً سه پیام کنسول و یک درخواست شبکه می‌سازد. کلاینت هم یکسان است: کتابخانه‌ی mcp نسخه‌ی ۲٫۲٫۰ و نسخه‌ی قرارداد ۲۰۲۵٬۱۱٬۲۵. شماره‌ی همین نسخه از صفحه‌ی بسته‌ی mcp در رجیستری پایتون خوانده شد و همان بسته، بسته‌ای است که راهنمای نصب کتابخانه نصبش را توضیح می‌دهد.

server   : chrome_devtools 1.10.1
protocol : 2025-11-25
tools cap: list_changed=True

=== 1. new_page  (is_error: False)
    1: about:blank
    2: probe page (file:///.../site/index.html) [selected]

=== 2. navigate_page  (is_error: False)
    Successfully reloaded the page.

=== 3. list_console_messages  (is_error: False)
    Showing 1-5 of 5 (Page 1 of 1).
    msgid=6 [log]   page loaded (1 args)
    msgid=7 [warn]  careful (1 args)
    msgid=8 [error] boom (1 args)
    msgid=9 [error] Access to fetch at 'file:///.../data.json' from origin 'null' has been blocked
    msgid=10 [error] Failed to load resource: net::ERR_FAILED (0 args)

=== 4. list_network_requests  (is_error: False)
    Showing 1-2 of 2 (Page 1 of 1).
    reqid=3 GET file:///.../index.html [200]
    reqid=4 GET file:///.../data.json [net::ERR_FAILED]

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

فراخوانی پنجم، درخت دسترسی‌پذیری صفحه بود و متن فارسی در آن سالم برگشت:

=== 5. take_snapshot
    uid=1_0 RootWebArea "probe page" url="file:///.../index.html"
      uid=1_1 heading "صفحه‌ی آزمایشی" level="1"
      uid=1_2 StaticText "یک پاراگراف کوتاه برای اندازه‌گیری."
      uid=1_3 button "کلیک"

این درخت به‌جای مختصات مختصات‌گونه می‌دهد: هر گره یک uid دارد و همان uid ورودی ابزارهای کنش است. برای ایجنتی که مدل بینایی ندارد، این تفاوت میان «کلیک کن» و «کلیک کن روی دکمه‌ی با شناسه‌ی ۱٬۳» است.

فراخوانی ششم: تست منفی واقعی

ششمین فراخوانی عمداً نادرست بود، با شناسه‌ای که وجود ندارد:

=== 6. click with a uid that does not exist
    is_error: True
    Error: Element uid "nope" not found on page 2.

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

  • --slim — فقط سه ابزار پایه: پیمایش، اجرای اسکریپت و عکس‌برداری.
  • --screenshotMaxWidth و --screenshotMaxHeight — کوچک کردن تصویر، چون هزینه‌ی توکن تصویر با ابعاد زیاد می‌شود نه با حجم فایل.
  • --redactNetworkHeaders — پاک کردن سرآیندهای حساس درخواست‌ها پیش از برگشت به کلاینت.
  • --javascriptEvaluation false — خاموش کردن اجرای اسکریپت، که هم‌زمان راه‌روی به نشانی‌های اسکریپتی را می‌بندد.

برای اطمینان از اینکه خطا واقعاً از صفحه می‌آید و نه از اعتبارسنجی ورودی، مقایسه کنید: خطای pageId در بخش قبل با عبارت expected number, received undefined آمد، یعنی پیش از رسیدن به مرورگر. خطای این بخش با نام صفحه آمد، یعنی مرورگر بالا بود و واقعاً نگاه کرد. همین دو شکل خطا در راهنمای مهاجرت کتابخانه‌ی پایتون هم از هم جدا شده‌اند: یکی اعتبارسنجی ورودی است و دیگری نتیجه‌ی یک فراخوانی. آنچه در فهرست تغییرهای نسخه‌ی دو اعلام شده، از جمله همین تفکیک است.

هزینه در زمینه و مرزهای ادعا

همان‌طور که در پست ارزیابی اندازه گرفتم، فهرست ابزارهای این سرور دو چهره دارد. حالت کامل ۳۰ ابزار و ۶٬۴۵۸ توکن، میانگین ۲۱۵ توکن به‌ازای هر ابزار. حالت باریک با پرچم --slim سه ابزار و ۲۱۲ توکن:

حالتابزارتوکنمیانگینگران‌ترین ابزار
کامل۳۰۶٬۴۵۸۲۱۵۴۵۲
باریک۳۲۱۲۷۰۸۲

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

ابزارتوکن ورودیکارش چیست
list_console_messages۴۰۲خواندن پیام‌های کنسول با فیلتر
evaluate_script۳۳۹اجرای اسکریپت در صفحه
list_network_requests۳۳۷فهرست درخواست‌های شبکه
navigate_page۳۱۲پیمایش و بارگذاری مجدد
get_css_styles۲۹۲خواندن سبک‌های محاسبه‌شده
fill_form۲۸۸پر کردن چند فیلد یک‌جا
performance_start_trace۲۵۹آغاز ردیابی کارایی
get_network_request۲۴۶جزئیات یک درخواست
new_page۲۴۷باز کردن صفحه‌ی تازه
take_snapshot۲۱۳درخت دسترسی‌پذیری صفحه

الگو روشن است: هرچه ورودی ابزار انعطاف بیشتری داشته باشد، توصیفش طولانی‌تر است. list_pages که فقط فهرست صفحه‌ها را می‌دهد، ۵۹ توکن است؛ list_console_messages که فیلتر نوع و محدوده دارد، ۴۰۲ توکن. یعنی هزینه‌ی ورودی را می‌شود حدس زد بدون آنکه ابزار را اجرا کنیم.

برای کاری که در این پست کردیم، حالت باریک کافی است: هر سه ابزاری که لازم داشتیم در آن هستند و سه هزار برابر ارزان‌ترند. تفاوت ۲۱۲ با ۶٬۴۵۸ یعنی سی برابر.

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

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

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

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

منابع

  1. صفحه‌ی بسته‌ی mcp در رجیستری پایتون — نسخه‌ی ۲٫۲٫۰ که کلاینت این آزمایش با آن وصل شد
  2. راهنمای نصب کتابخانه‌ی پایتون MCP — خط نصب و نسخه‌ی پیشنهادی
  3. راهنمای مهاجرت کتابخانه‌ی پایتون — تفاوت خطای اعتبارسنجی ورودی با خطای فراخوانی
  4. تغییرهای نسخه‌ی دو کتابخانه — آنچه در کلاینت و قرارداد عوض شد
  5. راهنمای کار درخت هرمز — جدا کردن نشست‌های هم‌زمان در سطح پوشه‌ی کاری
  6. مرجع متغیرهای محیطی هرمز — تعیین کاربر اجرا، مسیر و پوشه‌ی کاری
  7. راهنمای خط فرمان هرمز — اجرای نشست غیرتعاملی و مدیریت آن
  8. ارزیابی سرور MCP — همین سرور در جدول هزینه‌ی توکن، با دو حالت باریک و کامل