تصور کنید سرور پروتکل زمینهٔ مدل (MCP) شما کاملاً درست کار میکند، اما در چشم دنیا مرده است. اگر توسعهدهندگان تمام هندلرهای خود را پشت دیواری از احراز هویت OAuth 2.1 قرار دهند، بهطور ناخواسته باعث میشوند دایرکتوریهای اکتشافی مانند Smithery و mcp.directory سرور آنها را با لیست ابزارهای صفر ثبت کنند. این اتفاق عملاً سرویس را برای کاربران بالقوه نامرئی میکند.
این شکست بهدلیل سازوکار اکوسیستم MCP در مدیریت اکتشاف رخ میدهد. برای فهرست کردن قابلیتهای یک سرور، خزندهی دایرکتوری متد tools/list را فراخوانی میکند. اگر این متد پشت یک دیوار احرازی باشد، خزنده با پاسخ ۴۰۱ (Unauthorized) مواجه میشود. از آنجا که خزنده حساب کاربری ندارد، نمیتواند درخواست را تایید کند و صرفاً یک لیست خالی از قابلیتها را ثبت میکند.
به نقل از گزارش فنی منتشر شده توسط سازندگان FrameThrower، این یک شکست خاموش است. لاگهای سرور خطاهای ۴۰۱ را نشان میدهند، اما صفحه عمومی دایرکتوری صرفاً عبارت tools: [] را نمایش میدهد. در یک مورد، API شرکت glama خروجی را بهصورت زیر برگرداند: { "name": "FrameThrower MCP Server", "attributes": ["author:official", "hosting:remote-capable"], "tools": [] }. این وضعیت باعث ایجاد «لیستهای مرده» میشود که اغلب در سایر رجیستریها نیز تکثیر شده و تضمین میکند که سرور، فارغ از اینکه در چند دایرکتوری ثبت شده باشد، ناشناخته باقی بماند.
سازوکار شکست
bسیاری از توسعهدهندگان از مستنداتی پیروی میکنند که آنها را تشویق میکند برای محافظت از اعتبارها (Credits) و دادههای کاربر، هندلرها را در لایهی احراز هویت بپیچند. یک پیادهسازی اشتباه معمولاً به این شکل است:
- رپِر
withMcpAuthروی تمام درخواستهای GET، POST و DELETE اعمال میشود. - هر درخواستی بدون
userIdمعتبر (که از طریقsession?.userId ?? session?.user?.idبررسی میشود)، پاسخ ۴۰۱ میگیرد. - متد
tools/listکه یک متد غیرممتاز (non-privileged) است، در این تله گرفتار میشود.
در مورد FrameThrower، لاگ اسکنر Smithery فاش کرد که خزنده بهمحض برخورد با نیاز به احراز هویت متوقف شده است. لاگ نشان میداد: [scan] Discovering server metadata... و سپس [scan] Server metadata discovered (OAuth required). و در نهایت [scan] Authentication required. Please authorize at: https://connect.smithery.ai/...
تنها زمانی که یک انسان بهصورت دستی مراحل تعاملی OAuth را طی کرد، اسکنر توانست چهار ابزار موجود را شناسایی کند: [scan] Capabilities found: 4 tools. با این حال، این نتیجهی انسانی در صفحه عمومی دایرکتوری منعکس نمیشود و بازدیدکنندگان همچنان تعداد ابزارها را صفر میبینند.
راهکار فنی: تفکیک متدها
برای حل این مشکل، توسعهدهندگان باید «دستدادن» (Handshake) را از «اجرا» (Execution) جدا کنند. توصیف آنچه سرور ارائه میدهد یک عملیات عمومی است، اما فراخوانی آن ابزارها یک عملیات ممتاز و نیازمند سطح دسترسی است. راهکار پیشنهادی، ایجاد مجموعهای از متدهای عمومی (PUBLIC_METHODS) است که از گیت احرازی عبور کنند:
initializenotifications/initializedpingtools/list
برای جلوگیری از تبدیل این مسیر به یک حفره امنیتی (Authentication Bypass)، پیادهسازی باید از سه قانون سختگیرانه پیروی کند. اول، اگر هدر Authorization در درخواست وجود داشت، درخواست حتماً باید از مسیر احراز هویت عبور کند. بدون این قانون، کلاینتی که توکنی منقضیشده دارد، بهجای دریافت خطای ۴۰۱ (که باعث تمدید توکن میشود)، بهطور خاموش به دسترسی ناشناس سقوط میکند. شکست خاموش بدتر از شکست آشکار است.
دوم، در مسیر عمومی فقط درخواستهای POST مجاز هستند. در پروتکل HTTP Streamable، درخواستهای GET برای باز کردن جریان SSE و DELETE برای پایان دادن به نشست استفاده میشوند. هیچکدام از اینها متد JSON-RPC را حمل نمیکنند که بتوان آن را بازرسی کرد، بنابراین هیچکدام نمیتوانند «عمومی» طبقهبندی شوند و باید احراز هویت شوند.
سوم، سیستم باید هنگام بررسی درخواستهای دستهای (Batch)، از متد .every() بهجای .some() استفاده کند. پروتکل JSON-RPC اجازه دستهبندی درخواستها را میدهد. اگر یک دسته درخواست، متد tools/list را با tools/call ترکیب کند، کل درخواست باید بهعنوان یک درخواست نیازمند احراز هویت تلقی شود.
به عنوان خط دفاعی دوم، مسیر عمومی بدون هیچ فراخوانندهای (Caller) در زمینه (Context) اجرا میشود. اگر متد tools/call هرگز به این مسیر برسد، تابع محاسبه هزینه (Charging function) هیچ کاربری را پیدا نمیکند و درخواست را رد میکند. بدین ترتیب، گیت از هر دو جهت بسته میماند. این رویکرد در کنار ابزارهای نظارتی مانند سیستم mcp-fabric-toolmesh که برای جلوگیری از دسترسیهای غیرکنترلشده عاملها طراحی شده، امنیت سرور را دوچندان میکند.
تایید پیادهسازی
توسعهدهندگان باید با تست کدهای پاسخ خاص، صحت تنظیمات را بررسی کنند تا مطمئن شوند اکتشاف OAuth را از دست ندادهاند. یک تنظیم موفق باید این نتایج را بدهد:
initialize(بدون احراز) $\rightarrow$ ۲۰۰tools/list(بدون احراز) $\rightarrow$ ۲۰۰ (لیست کامل ابزارها)tools/call(بدون احراز) $\rightarrow$ ۴۰۱tools/list(توکن نامعتبر) $\rightarrow$ ۴۰۱- ترکیب دستهای
tools/list+tools/call$\rightarrow$ ۴۰۱ - GET (جریان SSE، بدون احراز) $\rightarrow$ ۴۰۱
نکته حیاتی این است که هر پاسخ ۴۰۱ باید شامل هدر WWW-Authenticate به همراه resource_metadata باشد تا کلاینتهای سازگار بدانند کجا باید احراز هویت کنند. میتوان این مورد را با دستور curl زیر تایید کرد:
curl -s -D - -o /dev/null -X POST https://your-server/api/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"x","arguments":{}}}' \ | grep -i 'www-authenticate'
خروجی مورد انتظار عبارت است از: www-authenticate: Bearer resource_metadata="https://your-server/api/auth/.well-known/oauth-protected-resource"
باگ پنهان در هویت سرور
تیم FrameThrower هنگام رفع مشکل احراز هویت، متوجه خطای رایج دومی در مورد هویت سرور شد. تابع createMcpHandler در کتابخانه mcp-handler، اگر مقدار serverInfo بهطور صریح تنظیم نشود، آن را بهصورت پیشفرض روی "mcp-typescript server on vercel" نسخه 0.1.0 قرار میدهد.
قبل از اصلاح، لاگ Smithery نشان میداد: [scan] Server info retrieved. name: mcp-typescript server on vercel, version: 0.1.0. این بدان معناست که بسیاری از سرورها خود را به کلاینتهایی مانند Claude Desktop و Cursor با یک نام پیشفرض و کلی معرفی میکنند، نه با نام برند واقعی خود. این نقص در شناسایی، مستقیماً بر معیارهای ارزیابی کیفیت اثر میگذارد؛ برای مثال ابزار mcpscore میتواند چنین نقصاتی در استانداردهای پیادهسازی سرور را شناسایی و نمرهگذاری کند.
این یک اصلاح تکخطی در شیء تنظیمات است: serverInfo: { name: 'FrameThrower', version: '1.0.0' }. این رشته متنی همان چیزی است که هر کلاینتی نمایش میدهد و عدم تنظیم آن منجر به تجربه کاربری ضعیف میشود. پس از اعمال هر دو اصلاح، اسکنر بدون نیاز به هیچ مرحلهای در مرورگر، بهطور تمیز اجرا شد: [scan] Server info retrieved. name: FrameThrower, version: 1.0.0 و [scan] Capabilities found: 4 tools.
برای توسعهدهندگان، این بدان معناست که لایهی اکتشاف تنها به کیفیت «دستدادن» وابسته است. اگر یک سرور MCP راه دور اجرا میکنید، باید صفحات دایرکتوری را چک کنید — نه لاگهای خود را — تا مطمئن شوید ابزارهایتان واقعاً برای عموم قابل مشاهده است. چه در حال ساخت یک کتابخانه سینماتوگرافی مانند FrameThrower (موجود در github.com/framethrower-ai/framethrower-mcp) باشید و چه یک ابزار داده سازمانی، قانون یکسان است: توصیف یک منبع عمومی است، اما استفاده از آن خیر.
گام بعدی شما
- اگر سرور MCP راه دور دارید، همین حالا صفحه دایرکتوری خود را چک کنید تا مطمئن شوید ابزارهایتان برای عموم قابل مشاهده است.
- متدهای
initializeوtools/listرا از لایهی احراز هویت OAuth خارج کنید. - مقدار
serverInfoرا در تنظیمات هندلر از حالت پیشفرض خارج کرده و نام برند خود را جایگزین کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو