خادم MCP
خادم MCP
استخدم FourA من أي عميل Model Context Protocol (مثل Claude Desktop، Claude Code، Cursor، Windsurf، VS Code) كأربع أدوات أصلية وست مطالبات لمسارات العمل. بدون كود تكامل، وبدون عميل HTTP مخصص.
مفتوح المصدر على GitHub؛ وعلى npm باسم @fouradata/mcp. الإصدار الحالي: 0.5.0.
البدء السريع: stdio المحلي (موصى به لـ Claude Desktop)
احصل على مفتاح من foura.ai/dashboard#api-keys (بنقرة واحدة، يُعرض مرة واحدة عند الإنشاء، بالتنسيق pk_live_...). أضف هذا إلى إعدادات عميل MCP الخاص بك:
{
"mcpServers": {
"foura": {
"command": "npx",
"args": ["-y", "@fouradata/mcp"],
"env": { "FOURA_API_KEY": "pk_live_..." }
}
}
}
تنبيه حول Claude Desktop: تأكد من إغلاق Claude Desktop بالكامل (
Cmd+Qفي نظام macOS) قبل تعديل ملف التكوين. إذا كان التطبيق لا يزال قيد التشغيل، فسيقوم بالكتابة فوق تعديلاتك باستخدام التكوين الموجود في الذاكرة عند الإغلاق.
يقوم الأمر npx بتنزيل @fouradata/mcp عند التشغيل الأول ويقوم بتشغيله كعملية فرعية لعميل MCP الخاص بك. لا حاجة إلى تثبيت عام.
| العميل | موقع ملف التكوين |
|---|---|
| Claude Desktop (macOS) | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Claude Desktop (Windows) | %APPDATA%\Claude\claude_desktop_config.json |
| Claude Code | claude mcp add foura -- npx -y @fouradata/mcp (قم بتعيين FOURA_API_KEY في متغيرات البيئة أولا) |
| Cursor | ~/.cursor/mcp.json |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
| VS Code (إضافة MCP) | .vscode/mcp.json |
أعد تشغيل العميل. ستظهر الأدوات (foura_auto، foura_single، foura_proxy، foura_browser) والست مطالبات في قائمة الأدوات الخاصة بك.
البدء السريع: مستضاف (Streamable HTTP)
للعملاء الذين يدعمون النقل عبر Streamable HTTP (مثل Cursor، وWindsurf، وVS Code، وClaude Code مع --transport http)، قم بتوجيههم إلى الـ endpoint المستضاف بدلا من تشغيل عملية فرعية محلية:
{
"mcpServers": {
"foura": {
"url": "https://mcp.foura.ai/mcp",
"headers": {
"Authorization": "Bearer pk_live_..."
}
}
}
}
بالنسبة لـ Claude Desktop، استخدم تكوين stdio أعلاه أو قم بتوصيل endpoint المستضاف عبر mcp-remote:
{
"mcpServers": {
"foura": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.foura.ai/mcp", "--header", "Authorization: Bearer pk_live_..."]
}
}
}
مرجع الـ endpoint المستضاف
| الخاصية | القيمة |
|---|---|
| URL | https://mcp.foura.ai/mcp |
| النقل | Streamable HTTP (POST /mcp, SSE responses) |
| المصادقة | Authorization: Bearer pk_live_... لكل request |
| إصدار بروتوكول MCP | لكل @modelcontextprotocol/sdk (حاليًا 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07) |
| تحدي 401 | WWW-Authenticate: Bearer realm="foura-mcp", resource_metadata="https://foura.ai/docs/mcp/server#auth" |
الخادم المستضاف لا يحفظ الحالة (stateless). كل request يحمل مفتاحه الخاص، والذي يوجهه الخادم إلى FourA API كـ X-API-Key. مفتاح واحد يفتح جميع الأدوات الأربعة.
للحماية من إعادة ربط DNS (CVE-2025-66414)، يتحقق الخادم من صحة header Host (يجب أن يكون mcp.foura.ai أو localhost) و header Origin عند وجوده (المسموح بها: mcp.foura.ai, claude.ai, app.cursor.sh, app.cursor.com). المتصلون من خادم إلى خادم (curl, عملاء MCP في وضع stdio bridge) لا يرسلون Origin ويمرون.
الأدوات
جميع الأدوات الأربعة مشروحة بـ readOnlyHint: true و openWorldHint: true وفقًا لـ مواصفات MCP 2025-06-18. العملاء الذين يوافقون تلقائيًا على الأدوات الموثوقة للقراءة فقط يستدعونها بدون نافذة تأكيد لكل request.
foura_auto هو الخيار الافتراضي الذكي: أعطه URL وسيعيد المحتوى، مختارًا طريقة الجلب لك. الثلاثة الآخرون هم العناصر الأساسية الأقل مستوى التي يديرها؛ استخدمهم عندما تريد تحكمًا صريحًا.
foura_auto
أعطه URL عندما تريد من FourA اختيار طريقة الـ request. يقوم بمحاولات محدودة عبر مسارات HTTP، و proxy، والمتصفح المتاحة. مرر validate على الأهداف المحمية بحيث يجب أن يحتوي الـ response على محتوى يحدد الصفحة الحقيقية. إذا لم تنجح أي محاولة في التحقق، ترجع الأداة خطأ بدلاً من تقديم صفحة تحدي كنجاح.
يتضمن الـ response تفاصيل الاكتمال في meta، وافتراضيًا، session قابل لإعادة الاستخدام مع proxy، cookies، و userAgent. لمتابعة عادية، استدعِ foura_single مع session.proxy كـ proxy، قم بتسلسل الـ cookies كـ header Cookie، وأرسل session.userAgent كـ header User-Agent. لتصيير JavaScript، مرر قيم الجلسة إلى حقول foura_browser المطابقة.
foura_single
request HTTP واحد، response راجع. يعكس POST /api/single/ واحد لواحد.
استخدمه للصفحات الثابتة، و JSON APIs، و HTML المصيّر من الخادم.
اختيار المتصفح الذي تقدمه
يقدم الـ request أحدث إصدار من Google Chrome افتراضيًا. عندما يقبل هدف متصفحًا ويرفض آخر، قم بتعيين browser (Chrome, Edge, Safari, Firefox, أو Tor)، os (Windows, macOS, Android, أو iOS)، أو version، أو مرر معرف profile دقيق:
{
"method": "GET",
"url": "https://example.com",
"browser": "Firefox",
"os": "Windows"
}
تفوز أحدث نسخة عندما تتطابق عدة ملفات تعريف. تُرجع المجموعة غير الموجودة خطأ يسرد ما هو متاح، لذلك لا يتم إرسال request أبدا كمتصفح لم تختره. يحتاج التحديد إلى unblocker، وهو قيد التشغيل افتراضيا. يتم نشر الكتالوج في GET /api/profiles ولا يحتاج إلى مفتاح API.
توجد نفس الحقول الأربعة داخل كائن request الخاص بـ foura_proxy.
foura_proxy
توجيه HTTP request واحد عبر proxies متناوبة مع إعادة المحاولة التلقائية. استخدمه عندما يتم حظر foura_single أو عندما يتطلب الهدف بلد خروج محدد.
قم بتعيين exitCountries إلى قائمة سماح صارمة لرموز البلدان المكونة من حرفين والمرئية للهدف والتي يوفرها المستخدم أو متطلبات الهدف:
{
"maxTries": 5,
"exitCountries": ["CZ", "GB"],
"request": {
"method": "GET",
"url": "https://example.com/pricing",
"browser": "Chrome",
"os": "Windows"
}
}
يتم تقليم القيم، وتحويلها إلى أحرف كبيرة، وإزالة التكرار منها. تُستثنى الـ proxies ذات المخارج غير المعروفة، ولا يتراجع الـ request أبدا إلى دولة لم يتم طلبها. يعتمد الاختيار على أحدث بيانات وصفية متاحة للدولة المرئية للهدف، والتي تُحَدَّث عادة خلال عشر دقائق؛ وهو ليس بحثا مباشرا عن الموقع الجغرافي أثناء الـ request. لا تستنتج دولة الخدمة من عنوان مضيف الـ proxy.
يُرجع النجاح المحدد النطاق exitCountry ومعرف proxy القابل لإعادة الاستخدام. تحقق من أن exitCountry ينتمي إلى قائمة السماح المطلوبة. إذا لم يكن هناك تطابق في التجمع الحالي، تُرجع الأداة code: "no_eligible_proxy" مع النطاق الموحد في details.exitCountries. احتفظ بهذا النطاق وأعد المحاولة لاحقا. قم بتغييره أو توسيعه فقط عندما يغير المستخدم المتطلب صراحة.
إذا احتاجت الصفحة المحددة لاحقا إلى JavaScript، فمرر معرف proxy المُرجع إلى foura_browser.proxy حتى يعيد المتصفح استخدام نفس المخرج.
foura_browser
جلسة متصفح كاملة. يتم تشغيل JavaScript، وعرض الـ DOM، وتعود الـ cookies. يطابق POST /api/browser/.
يُستخدم لتطبيقات الصفحة الواحدة، أو المحتوى المُحمّل بتأخير (lazy-loaded)، أو الصفحات المحمية بتحديات مكافحة البوتات التي تحتاج إلى متصفح حقيقي لتجاوزها.
بالنسبة لأشكال الإدخال، والقيم الافتراضية، وقواعد التحقق لكل أداة، راجع مرجع الـ endpoint لـ REST. تتطابق مخططات الأداة مع الـ API الخاصة بـ REST حقلا بحقل، بالإضافة إلى خيار offload_large الخاص بـ MCP فقط (انظر أدناه).
عندما يجري الهدف فحصا للبوتات
يُرجع foura_single و foura_proxy قيمة defense عندما يجري الهدف فحصا للبوتات في الطريق إلى الـ body. تعني defense.solved: true أنه تم اجتياز الفحص وأن data هي الصفحة الحقيقية؛ بينما تعني false أن الـ body قد يكون صفحة تحدٍ. أعد المحاولة باستخدام متصفح، أو نظام تشغيل، أو إصدار مختلف، أو انتقل إلى foura_proxy أو foura_browser، بدلا من التعامل مع صفحة التحدي كمحتوى.
استجابات محددة النوع
تتضمن كل استجابة أداة كلا من content (ملخص نصي قابل للقراءة البشرية) و structuredContent (JSON محدد النوع تم التحقق من صحته مقابل outputSchema الخاص بالأداة). لكل أداة شكلها الفريد:
foura_auto: شكل أحادي لـ{ status, headers, data }بالإضافة إلىmeta({ rung, solved, attempts, credits }، موجودة دائما، حيثrungهي إحدى القيمcache، أوprobe، أوproxy، أوbrowser، أوfail) و، افتراضيا،session({ proxy, cookies, userAgent }) لإعادة التشغيل عبر الأدوات ذات المستوى الأدنى. لا يوجدtotal_time.foura_single:{ status, headers, data, total_time, ... }(الـ headers عبارة عن مصفوفة، إدخال واحد لكل قفزة إعادة توجيه)foura_proxy: نفس الشكل الأحادي بالإضافة إلى{ proxy, total }؛ يتضمن النجاح المحدد النطاق أيضاexitCountryfoura_browser: شكل مميز{ status, headers: object, body, cookies, userAgent }(ملاحظة: قد يكونbodyعبارة عن سلسلة نصية أو كائن بناءً على الـ content-type)
يمكن للعملاء الذين يدعمون structuredContent تمرير الكائن محدد النوع مباشرة إلى الـ LLM بدلا من مطالبته بتحليل الـ JSON من النص.
الـ headers للاستجابة متعددة القيم
الـ headers التي تظهر عدة مرات (Set-Cookie، Link، WWW-Authenticate) تعود في شكل مصفوفات:
{
"headers": [
{
"result": { "version": "HTTP/2", "code": 200, "reason": "" },
"content-type": "text/html",
"set-cookie": ["a=1; Path=/", "b=2; Path=/"]
}
]
}
هذا الأمر مهم للمواقع التي تقوم بتعيين ملفات cookies للـ session والتتبع والموافقة في response واحد (معظم مواقع التجارة الإلكترونية).
الـ responses الكبيرة: offload_large (الافتراضي: inline)
بشكل افتراضي (بدءا من الإصدار v0.2.0)، يتم إرجاع أجسام الـ response بالكامل بشكل inline في structuredContent بغض النظر عن الحجم. هذا يعمل في كل عميل MCP مباشرة دون إعدادات إضافية.
إذا كان عميلك يدعم MCP resources/read وكنت ترغب في توفير الـ token في الصفحات الكبيرة، قم بتمرير offload_large: true لكل استدعاء للأداة. بعد ذلك، يتم كتابة الـ responses التي بحجم >= 50 كيلوبايت على القرص، وتُرجع كـ resource_link، ويقوم عميلك بجلب الـ body فقط عندما يحتاج إليه فعليا. تنتهي صلاحية الـ payloads المخزنة مؤقتا بعد 1 ساعة.
{
"method": "GET",
"url": "https://en.wikipedia.org/wiki/Web_scraping",
"offload_large": true
}
| العميل | offload_large: true |
|---|---|
| Claude Desktop | ليس بعد، اترك الافتراضي false |
| Claude Code, Cursor, Windsurf | مدعوم |
| إضافة VS Code MCP | مدعوم |
معزول حسب المستأجر: يحصل كل مفتاح API على مساحة أسماء خاصة به (sha256(apiKey)[:16]). المفتاح الذي قام بتخزين الحمولة هو الوحيد الذي يمكنه قراءتها. تُرجع عمليات القراءة عبر المستأجرين Payload not found دون تسريب وجود البيانات.
المطالبات المضمنة
تظهر ستة قوالب لسير العمل تحت /prompts في أي عميل MCP. يأخذ كل منها وسيطات مسماة ويُرجع رسالة مستخدم كقالب تنسق أداة واحدة أو أكثر.
| المطالبة | الوسيطات | ماذا تفعل |
|---|---|---|
smart_fetch |
url، اختياري must_contain، extract |
جلب تلقائي (يختار الطريقة، ويعالج حماية الروبوتات)، ثم يُرجع أو يستخرج المحتوى |
scrape_product_page |
url |
جلب عبر المتصفح، ثم استخراج عنوان المنتج والسعر والصورة والمخزون وSKU بتنسيق JSON |
extract_article |
url |
طلب فردي مع خيار التراجع إلى proxy، ثم إزالة التنقل/الإعلانات وإرجاع مقال نظيف بتنسيق JSON |
monitor_pricing |
url، اختياري target_price |
جلب عبر proxy، استخراج السعر الحالي، ومقارنته بالهدف |
check_endpoint_health |
url، اختياري expected_text |
طلب فردي مع تحقق صارم، إرجاع إمكانية الوصول والتوقيت |
bulk_fetch_urls |
urls (مفصولة بفواصل) |
طلب فردي متوازي، خيار تراجع تلقائي إلى proxy لكل URL، إرجاع البيانات الوصفية فقط |
لا تكلف المطالبات أي token في وضع الخمول. المطالبات المستدعاة فقط هي التي تدخل في سياق LLM.
النص الكامل بالإضافة إلى مطالبات التراجع اليدوية: وصفات MCP.
غلاف الخطأ
يحمل كل خطأ (isError: true) غلاف structuredContent. الحقول الدنيا في كل خطأ:
{
"service": "auto | single | proxy | browser",
"code": "rate_limited",
"error": "Rate limit exceeded"
}
في أخطاء upstream ذات حالة HTTP، يكون status موجوداً أيضاً. في أخطاء rate-limit والسعة، يضيف upstream envelope retryAfter وcurrent.{concurrency, rpm} وlimits.{maxConcurrency, maxRpm}. راجع API Errors لمعرفة هيكل REST الأساسي.
قيم code المستقرة:
| الرمز | HTTP | المعنى | هل إعادة المحاولة آمنة؟ |
|---|---|---|---|
ssrf_blocked |
غير متوفر | عنوان IP الهدف في نطاق خاص أو محجوز (RFC 5735 و6598 وIPv6 المحجوزة) | لا، غيّر الـ URL |
upstream_non_json |
يختلف | أرجع upstream هيكل body غير صالح | ربما، تحقّق |
output_validation_failed |
غير متوفر | رفض outputSchema لخادم MCP استجابة upstream (خطأ في الخادم أو هيكل upstream غير متوقع) |
ربما، أبلغ عنه |
bad_request |
400 | رُفض هيكل الإدخال | لا، أصلح الوسائط |
auth_failed |
401 | المفتاح مفقود أو غير صالح أو معطل | لا، أصلح المفتاح |
forbidden |
403 | تمت المصادقة ولكنه غير مسموح | لا، أو انتقل إلى foura_proxy |
not_found |
404 | الهدف أو הـ endpoint مفقود | لا |
rate_limited |
429 | تم الوصول إلى حد RPM | نعم، انتظر retryAfter |
at_capacity |
503 | تم الوصول إلى حد التزامن | نعم، انتظر retryAfter |
service_disabled |
503 | نافذة الصيانة أو أن خطتك لا تتضمن هذه الأداة | تواصل مع الدعم |
service_unavailable |
503 | 503 عام | نعم، تراجع قصير |
upstream_error |
500+ | Upstream 5xx | نعم، تراجع أسي |
upstream_client_error |
4xx | 4xx آخر | عادة لا |
upstream_unknown |
آخر | دفاعي، يجب ألا يحدث عملياً | تحقّق |
no_eligible_proxy |
غير متوفر | لا يوجد proxy يطابق نطاق exitCountries الصارم |
أعد المحاولة لاحقاً؛ غيّر النطاق بشكل صريح فقط |
يمكن لوكلاء LLM قراءة code مباشرة لمنطق إعادة المحاولة دون تحليل النص. دليل المصادقة: Authentication.
Limits
- الـ body المضمن افتراضياً. مع
offload_large: true، الاستجابات >= 50 كيلوبايت تنتقل إلى القرص +resource_link(لكل مستأجر، مدة بقاء TTL ساعة واحدة). - تُرفض الأهداف الخاصة (RFC 5735 وRFC 6598 وكتل IPv6 المحجوزة) في طبقة MCP. يتم توجيه المضيفين العامين فقط.
- حد الـ request body يبلغ 256 كيلوبايت في طلبات
/mcpالواردة (تكون الـ payloads الحقيقية لـ MCP < 4 كيلوبايت). - يفرض FourA API الـ rate limits لكل خدمة. راجع Rate Limits.
Self-Hosting
يتوفر المصدر الكامل للخادم بشكل عام على GitHub ضمن @fouradata/mcp. قم بعمل نسخة (clone) للمستودع، npm install، npm run build، وشغّل node dist/http.js لإنشاء نسختك الخاصة. يعمل بدون حالة (stateless) في حاوية واحدة خلف أي load balancer.
بيئة قابلة للتكوين:
| المتغير | الافتراضي | الغرض |
|---|---|---|
PORT |
3076 |
منفذ استماع HTTP |
FOURA_API_BASE |
https://api.foura.ai/api |
عنوان URL الأساسي لـ FourA REST للتيار الصاعد |
FOURA_MCP_PAYLOADS_DIR |
/data/payloads |
حيث يتم تخزين الـ responses التي حجمها >= 50 كيلوبايت مؤقتا على القرص (مع offload_large: true) |
FOURA_MCP_ALLOWED_HOSTS |
mcp.foura.ai,localhost,127.0.0.1,[::1] |
قائمة السماح لأسماء المضيفين للـ header Host (دفاع ضد DNS-rebinding) |
FOURA_MCP_ALLOWED_ORIGINS |
https://mcp.foura.ai,https://claude.ai,https://app.cursor.sh,https://app.cursor.com |
قائمة السماح للمصادر (Origin) للمتصلين عبر المتصفح |
FOURA_MCP_RESOURCE_METADATA_URL |
https://foura.ai/docs/mcp/server#auth |
عنوان URL المُرجع في WWW-Authenticate عند 401 |
تعمل الحاوية الرسمية كـ uid 1001 (غير root). يجب أن يكون ربط المضيف (bind mount) /data/payloads قابلا للكتابة بواسطة معرف المستخدم (uid) ذلك.
يمكن التوسع أفقيا خلف أي موازن أحمال (load balancer). يقدم العملاء مفتاحهم في كل request، لذلك لا توجد جلسة لاصقة (sticky session).