Phase 13: Tools & Protocols

عقود وسائل MCP ومحتوى

أداة آمنة للتلقية فقط عندما يوافق اكتشاف، والحجج، والنتائج، والصفحات، ونقل البيانات المعدنية على عقد واحد.

Type: Build

Languages: Python

Prerequisites: Phase 13, Lessons 07, 09, and 10

Time: ~120 minutes

أهداف التعلم

  • تعريف مدخلات وأخراجات الأدوات مع مخطط JSON 2020-12.
  • تأكيد النتائج المهيكلة دون افتراض أنها كائنات JSON.
  • اختر بين النص، الصورة، الصوت، روابط الموارد، والموارد المدمجة.
  • رفض غير آمنx-mcp-headerالتعريفات قبل أن تصل أداة إلى النموذج.
  • قم بتشفير قيم المعايير-القب و التحقق من التساوي الدقيق من الرأس إلى الجسم.
  • تعريف صفحات المؤشر عبر دون تفسير قيم المؤشر.
  • إلتزام و إصدارcompletion/completeاقتراحات

المشكلة

استدعاء وظيفة Python سهل استدعاء قدرة بعيدة عن طريق مضيف الذكاء الاصطناعي مشكلة عقد

يقوم الخادم بنشر وصف. يقوم العميل بتحويل هذا المصف إلى سياق النموذج واجهة المستخدم. يقوم النموذج بإنشاء حجج. يمكن لبرة توجيه الطلب من رؤوس المرآة. يقوم الخادم بتنفيذ الأداة. ثم يقرر العميل ما إذا كانت النتيجة آمنة وموثوقة بما فيه الكفاية للعودة إلى النموذج.

الحدود الضعيفة واحدة تفسد السلسلة بأكملها

فلننظر إلى خمسة أخطاء:

  • المصطلح يقول أن النتيجة هي كائن، ولكن الخادم يعيد صف.
  • يوقف العميل عن البحث عندماnextCursorهو سلسلة فارغة.
  • يتم إضفاء عبارة عن مبرمجة رمزية في عنوان HTTP ويصبح مرئيًا للمساومين.
  • يتم إرسال قيمة توجيه يونيكود كقائمة خامة، ثم تفسير البوابة والمصدر بعبارات مختلفة.
  • نقطة نهاية الانتهاء تشير إلى بيئة إنتاج للمتصلين الذين لا يستطيعون الوصول إليها.

لا يمكن إصلاح أي من هذه الفشلات من خلال تحذير أفضل.

خط الأنابيب العقد

تعامل كل مكالمة أداة كخمسة بوابات:

  1. Discover.اقرأ قائمة أدوات محددة ومصفحة
  2. Admit.تأكيد كل وصف وتطبيق سياسة الأمن المحلية.
  3. Invoke.تأكيد الحجج ووضع بيانات نقل.
  4. Execute.إشغلي المدير وتصنيف الفشل بشكل صحيح.
  5. Consume.تأكيد كتلة المحتوى والمخرجات المهيكلة قبل استخدام النموذج.

المضيف يمتلك بوابات القبول والاستهلاك. لا يمكن لخادم أن يجبر العميل على الثقة في ملاحظاته أو مخططاته أو نتائجها.

مخطط JSON هو حدود وقت التشغيل

في MCP 2026-07-28،inputSchemaوoutputSchemaاستخدم نظام JSON. متى $schemaغائب، اللغة الافتراضية هي 2020-12.

يجب أن يكون مخطط الإدخال كائن مخطط. يجب أن تقول أداة بدون حجج ما يقبل به بالضبط:

json{
  "type": "object",
  "additionalProperties": false
}

هذا أكثر صرامة من{ "type": "object" }، والتي تقبل خصائص تعسفية.

مخطط الخروج اختياري. بمجرد نشر الخادم واحد، كل أداة كاملة

الالتزام بالعودة متوافقةstructuredContent، بما في ذلك النتائج

معisError: true. علامة الخطأ تصنف نتيجة التنفيذ؛ لا

التخلي عن عقد الإنتاج المنشورة. يجب على العملاء تحديد النتيجة بدلاً من ذلك

من الثقة بالوصف

المحتوى المهيكلي هو أي قيمة JSON

لا تكتبوا شفرة صلبةstructuredContentكقسم. يمكن أن يكون:

  • كائن؛
  • المجموعة
  • حبل
  • رقم؛
  • (بوليان)
  • null. . .

هذه الأداة تعيد صف:

json{
  "name": "tag_catalog",
  "inputSchema": {
    "type": "object",
    "additionalProperties": false
  },
  "outputSchema": {
    "type": "array",
    "items": {"type": "string"}
  }
}

النتيجة الناجحة لها صالحة:

json{
  "resultType": "complete",
  "content": [
    {
      "type": "text",
      "text": "[\"contracts\", \"mcp\", \"stateless\"]"
    }
  ],
  "structuredContent": ["contracts", "mcp", "stateless"],
  "isError": false
}

من أجل التوافق، يجب أن تتضمن النتائج المهيكلة أيضا JSON المتسلسلة في كتلة نص. النص ليس مصدر التحقق. structuredContent-أجل

مؤكد صغير لا يزال يعلم الحدود

تستخدم الدروس مجموعة فرعية من JSON Schema متعمدة لأنه يبقى داخل مكتبة Python القياسية. فإنها تحقق من الآليات المستخدمة من قبل أدوات العينات:

  • النماذج الكائنة، والصف، والسلسلة، والعدد الكامل، والرقم، والبوليان، والصفر؛
  • الخصائص المطلوبة
  • additionalProperties: false(إنه)
  • عناصر الترتيب
  • قيم enum؛
  • الحد الأدنى من طول السلسلة

هذا ليس بديلاً عن مؤكدة الإنتاج الكاملة، الدروس القابلة لإعادة الاستخدام هي حيث يحدث التحقق من التحقق من التحقق من التوصيفات، قبل تنفيذ الحجج، وقبل الاستهلاك للنتائج المهيكلة.

تكلفة المكونات مختلفة

  • نعمcontentيمكن أن يجمع المجموعة بين عدة أنواع المحتوى.
TypeUse it forMain boundary
textHuman and model-readable summariesTreat text as untrusted output
imageVisual evidence encoded as base64Validate media type and size
audioSpoken or recorded output encoded as base64Validate media type and duration limits
resource_linkA URI the client may fetch laterReauthorize the later resource read
resourceData embedded directly in the resultEnforce payload and content limits now

لا يثبت رابط للموارد أن الموارد تظهر في resources/list. هو مرجع يعود بهذه الدعوة الأداة. لا يزال العميل يطبق سياسة الموارد الخاصة به عندما يتبع URI.

يجنب الموارد المضمنة رحلة ذهابًا وإيابًا أخرى ولكنها تزيد من حجم الاستجابة الحالية. استخدم الروابط للأشياء الكبيرة أو المتغيرة بشكل مستقل. استخدم الموارد المضمنة للأدلة الصغيرة التي يجب أن تسافر بشكل ذري مع النتيجة.

الدرس هوevidence_bundleالنتيجة تشمل جميع الأنواع الخمسة. يقوم العميل بتصديق كل كتلة قبل قبول النتيجة.

x-mcp-headerهل توجيه البيانات المعدنية

ملكية داخلinputSchemaقد يعلنx-mcp-headerعبر HTTP المباشر، يعكس العميل هذه الحجة في Mcp-Param-{name}. . .

json{
  "region": {
    "type": "string",
    "x-mcp-header": "Region"
  }
}

معregion: "eu-west"، يمكن أن تنتج النقل:

httpMcp-Param-Region: eu-west

وتوجد الملاحظة بحيث يمكن للموازنة الحمولة أو البوابة أو محرك السياسة توجيه دون تحليل جسم JSON. ليس مكانًا لوضع الإثبات.

البروتوكول يفرض القيود على التعليق:

  • اسم العنوان غير فارغ ويتبع نحو رمز HTTP لسم الحقل
  • أسماء العناوين فريدة من نوعها بغض النظر عن الحالة.
  • نوع العقار هو سلسلة أو عدد كامل أو بولي
  • numberلا يُسمح بذلك.
  • الملاحظة تظهر فقط على عضو مباشر في inputSchema.properties(إنه)
  • قيم الأعداد الكاملة تبقى داخل -9007199254740991من خلال9007199254740991. . .

قاعدة الموقع هي نحوي وتغلق الفشل.

ليس فقط الخصائص التي يدركها مؤكّدك

التعليق تحت كائن مستقر properties، أoneOfالفرعitems، أ

التعريف الذي تم التوصل إليه من قبل $ref، أو أي مخطط خروجي. حل مرجع لا

لا تحويل العقدة المرجعية إلى خاصية مباشرة من المستوى الأعلى.

هذا الدروس يضيف سياسة تنفيذ: رفض وصفات تعكس أسماء مثل password،secret،token،api_keyأوauthorizationالمواصفات الرسمية تنصح مؤلفي الخادمين بعدم انعكاس المعايير الحساسة. يمكن للعميل تحويل هذه النصيحة إلى قاعدة صعبة.

قم بمراجعة اسم العنوان وليس قيمته.Mcp-Param-Regionبينما تمسكeu-westخارج حدث التدقيق.

قيم تشفير قبل بناء عناوين HTTP

قيمة المعايير يمكن أن تسير كنص بسيط فقط عندما يكون سلسلة غير فارغة

من أرقام ASCII مرئية من !من خلال~ولا يشبه

كل شيء آخر يستخدم هذا الشكل بالضبط:

text=?base64?{Base64UTF8}?=

Base64UTF8هو القاعدة القياسية 64 على البايتات UTF-8 بالضبط. لا تقص،

تعاديل أو استبدال القيمة أولاً.

علامات التبويب، وشخصيات التحكم، CR أو LF، مساحة بيضاء متقدمة أو متأخرة، وأي

قيمة تبدأ ب =?base64?تشفير قيمة تبدو وكأنها حارس مرة أخرى

ما الذي يسمح للمستلم باسترداد النص الأصلي حرفيا بدلا من فك الشفرة

و هو كـ "نحو النقل"

الكلمات البولية تُرجع بأحرف صغيرةtrueأوfalse. الأعداد الصحيحة التي تم إعطائها في القاعدة 10 و

يجب أن تبقى داخل نطاق الأعداد الكاملة الآمنة في JavaScript. القيم خارج هذا النطاق

يتم رفضها بدلاً من تجويبها بواسطة وسيط.

الخادم يفتش النسخة المرئية

إن توليد الرأس هو نصف العميل فقط. عند حدود HTTP المباشرة،

يجب على الخادم:

  1. أجد معترف بهاMcp-Param-*الأسماء دون النظر في حالة اسم العنوان
  2. فك شفرة النموذج الدقيق للقائدة Base64 عند وجودها
  3. مقارنة النص المفكّر مع الحجة الجسدية JSON المقابلة بدقة.
  4. رفض المفقود أو المكرر أو غير المتوقع أو غير المتكامل أو غير المتناسب

رأس معروف قبل الإرسال

الرفض هو HTTP 400مع رمز الخطأ JSON-RPC -32020ولا

قيمة الجسم ولا شكل رأسها المشفر ينتمي إلى سجل المراجعة.

اسم العنوان المعترف به و فئة الرفض فقط.

code/main.pyيُمثّل هذا الحدّ مباشرةً. Lesson 09

تغطي النظام الأساسي للتحقق من التحقق من HTTP، بما في ذلك الطريقة وال

بروتوكول نسخة الموازاة.

السلطات المكرمة لا تظهر

عمليات MCP القائمة تستخدم صفحات المرشح. يختار الخادم حجم الصفحة ومصطلح المرشح. يحصل العميل على قرار واحد:

pythonif result.get("nextCursor") is None:
    break
cursor = result["nextCursor"]

لا تكتب هذا

pythonif not result.get("nextCursor"):
    break

السلسلة الفارغة هي مؤشر صالح، الحقيقة ستنتهي مبكراً جداً

يجب على العملاء عدم فك رمزية الوسيط ، أو زيادة ذلك ، أو مقارنته مع السيطرة السابقة للطلب ، أو استنباط رقم الصفحة. يمكن للخادم توقيع السيطرة ، أو ربطها بإصدار الكتالوج ، أو خريطه إلى حالة خاصة. هذا هو تفاصيل تنفيذ الخادم.

الخادم العينية يعود عمدا ""بعد الصفحة الأولى يجب على العميل إرسال هذا القيمة الدقيقة في الطلب الثاني.

text<first request with no cursor>
<second request with cursor "">

المؤشرات غير صالحة تنتج JSON-RPC المعلمات غير صالحة، رمز -32602. . .

الانتهاء هو سطح تفويض

completion/completeيقدم اقتراحات للحجج السريعة وحجج نموذج الموارد. هو مفيد للشرائح التفاعلية، ولكنه يمكن أن تسرب الأسماء التي تحميها طرق القائمة العادية.

طلب الإكمال يذكر الإشارة والحجة التي يتم إكمالها:

json{
  "method": "completion/complete",
  "params": {
    "ref": {
      "type": "ref/prompt",
      "name": "deployment_review"
    },
    "argument": {
      "name": "environment",
      "value": "st"
    }
  }
}

النتيجة تعود إلى 100 قيم على الأكثر ويمكن تقديم تقرير totalبالإضافةhasMore. . .

تطبيق نفس حدود الإذن المستخدمة من قبل المرجع أو الموارد. يحصل محلل في العينة developmentوstagingفقط المُشغل يمكنه أن يتلقّىproduction. . .

إن الانتهاء من الإنتاج يحتاج أيضاً إلى:

  • التحقق من الصحة
  • التصفية المعرفة على المكالمة
  • طلب إزالة التكلفة في العميل
  • الحد من المعدلات في الخادم
  • عدد النتائج المحدودة
  • السجلات التي لا تعرض قيم الإشارة الحساسة.

الإكمال هو المساعدة، وليس الاكتشافات المتحول.

طبقتين من الأخطاء

حافظ على أخطاء البروتوكول منفصلة عن أخطاء تنفيذ الأداة.

استخدام خطأ JSON-RPC عندما لا يمكن إرسال طلب MCP بشكل صحيح:

  • اسم الأداة غير معروفة
  • شكل الطلب المخطط
  • البيانات المعدنية المفقودة للطلب
  • مؤشر غير صالح

استخدم النتيجة الكاملة من الأداة مع isError: trueعندما وصل الدعوة إلى الأداة وتبلغ الأداة عن فشل يمكن القيام به:

  • مصدر تقرير غير متوفر.
  • التاريخ خارج النطاق المدعوم
  • قاعدة تجارية ترفض العملية المطلوبة.

يمكن أن تقوم النماذج في كثير من الأحيان بإصلاح خطأ تنفيذ الأداة. لا يمكنها إصلاح خادم انتهك مخطط الخروج الخاص به.

إذا أعلنت الأداة مخطط الخروج، نموذج فشل قابل للتنفيذ داخل ذلك

النموذجroute_reportالفشل يعيد منطقته المطلوبة مع

accepted: false، إلى جانب نص خطأ يمكن قراءته من قبل الإنسان و isError: true. . .

بناءها

code/main.pyيُبني كلا الجانبين من الحدود مع مكتبة Python القياسية.

يقوم الخادم بتنفيذ:

  • التحقق من المصادقة مع البيانات المعدنية المعدنية المعدنية حسب الطلب؛
  • server/discoverمع أدوات وقدرات الإكمال؛
  • تحديديةtools/listالصفحة
  • أربعة وصفات للأدوات، بما في ذلك واحدة يجب رفضها.
  • الخروج المُهيّن للمصفوفة
  • كل نوع من كتلة محتوى الأداة الحالية
  • بوابة متساوية HTTP المباشرة التي تقوم بتشخيص عناوين المعلمات المعترف بها

يعود HTTP 400بالإضافة إلى JSON-RPC -32020في حالة عدم التطابق

  • الإكمال المسموح به ومحدود بالحد من المعدلات.

العميل ينفذ:

  • إدخال وصف؛
  • شجرة كاملةx-mcp-headerالتحقق من التحقق من التمويل والسياسة الحساسة في المجال؛
  • التشفير الدقيق لقيمة ASCII أو base64 UTF-8 الواضحة بشكل واضح.
  • حلقة مؤشر غير شفافة تتبع سلسلة فارغة.
  • الحجة والتحقق من النتيجة
  • التحقق من صحة كتلة المحتوى
  • أحداث تدقيق الرأس التي تحتوي على أسماء ولكن لا قيم.

وصف غير آمن عمدا هو بيانات تعليمية. فإنه يثبت أن أداة واحدة رفض لا يمنع أدوات صالحة من تحميل.

استخدمها

من جذور المخبأ:

bashcd phases/13-tools-and-protocols/28-mcp-tool-contracts-and-content/code
python3 main.py
python3 -m unittest discover tests -v

طباعة الاكتشافية أدوات معترف بها، وصف رفض، كلا الصفحات

الطلبات، محتوى الترتيب المهيكل، أنواع كتلة المحتوى، العنوان المرئي

الأسماء، سواء كانت القيمة المطلوبة للتشفير، وضع متساوية HTTP، و

قيم الإكمال المصفاة من المتصلين.

المختبر التفاعلي

افتحcode/main.pyو تحديد موقعهاTOOLS. . .

  1. التغييرtag_catalog.outputSchema.typeمنarrayإلىobject. . .
  2. أطلقي الظهور يجب أن يرفض العميل المصفوفة المرجعة
  3. استعادة النظام.
  4. حافظ على الصفحة الأولىnextCursorكـ""، ثم أعيد الصفحة الأخيرة

nextCursor: Noneبدلاً من إغفال الحقل

  1. اجري الاختبارات وقارن آثار المؤشر
  2. إضافةx-mcp-header: "Authorization"إلى خاصية سلسلة.
  3. تصريح التأكيد الوصف الاعتراف يرفض قبل الدعوة.
  4. حاولregionالقيم التي تحتوي على Unicode، خط جديد، المساحات المحيطة بها، و

النص المكتوب حرفياً=?base64?SGVsbG8=?=. فك كل عنوان إصدار و إثبات

القيمة الأصلية تبقى دقيقة

  1. تحريك الملاحظة تحت oneOf،itemsأو$refتعريف، تأكيد

يتم رفض كل وصف حتى لو لم تستخدم هذه الفرع من قبل الديمو.

  1. إزالة العنوان المعترف به أو تغيير قيمته المفكورة. تأكيد HTTP

حالة إرجاع الحدود 400و رمز JSON-RPC -32020. . .

النقطة ليست حفظ شكل JSON، بل مشاهدة كل بوابة تفشل في الحدود التي تمتلكها.

مختبر التدريب

تمديد مختبر العقد مع search_evidenceأداة

المتطلبات:

  1. مخطط إدخاله يقبلquery،limit، و خزينةregionحقل التوجيه
  2. مخططها الخارجي هو مجموعة من الأشياء مع uri،titleوscore. . .
  3. النتيجة تشمل نص التوافق ورابط الموارد لكل عنصر.
  4. الادعاءات ترفض الخصائص غير المعروفة.
  5. limitيتم تحديدها بموافقة الطلب.
  6. المُتصل دون إمكانية الوصول إلى URI واحد لا يرى ذلك URI من خلال الإكمال أو إصدار الأداة.
  7. تشمل الاختبارات نتيجة غير متوافقة، وتعليقًا غير صالحًا في العنوان، وقائمة من صفحتين.
  8. اختبارات القيمة العنوان تغطي ASCII المرئية، و Unicode، والخطوط التحكمية،

الفضاء الأبيض، النص الذي يبدو وكأنه حارس، وكلاهما حدود الأعداد الكاملة الآمنة لـ JavaScript.

  1. يقبل إطار HTTP أسماء العناوين غير الحساسة للحالة ولكنه يرفض غيابها

أو غير متطابقة القيم المعترف بها مع الوضع 400و الرمز-32020. . .

الأثاث المُرسل

outputs/skill-mcp-contract-reviewer.mdهو مهارة مراجعة مسطحة قابلة للاستعمال مرة أخرى. أعطها وصف الأداة ، نتائج العينات ، سلوك البحركات ، وسياسة الإكمال. فإنه يعيد قرار القبول ، خطة تأكيد النتائج ، سياسة العنوان ، واختبارات الفشل الملموسة.

تحقق من ذلك

الدرس يكمل عندما تكون هذه التصريحات صحيحة:

  • tools/listيعود نفس الترتيب المنطقي على المكالمات المتكررة.
  • العميل يقوم بطلب ثان عندماnextCursorهو"". . .
  • يتم استبعاد وصف الرأس الحساس غير الآمن بينما تبقى الأدوات الأخرى متاحة.
  • تعبر صف المخططات الخروج من صف.
  • إنّه لا يُمكن أن يُمكنك أن تُعطي نفس النظام.
  • لا يمكن أن تفرغ نتائج الخطأ أو تنتهك مخطط الخروج المنشورة.
  • يصدق النص والصورة والصوت والرابط الموارد والبلوكات الموارد المضمنة.
  • أحداث مراجعة العناوين تحتوي على أسماء ولا قيم.
  • ASCII واضحة مرئية ما زالت واضحة؛ يونيكود، التحكم، المغطاة، الفارغة، وال

القيم التي تبدو وكأنها خادمة العبثة، تتجول إلى الوراء عبر تشفير أوتف-8 الدقيق للقاعدة 64.

  • يتم رفض الأرقام الكاملة المتراجعة خارج نطاق الأمان JavaScript.
  • الإشارات تحت oneOf،items، الأشياء المربوطة ،$refالتعريفات، أو

يتم رفض مخططات الناتج أثناء القبول.

  • أسماء العناوين المعترف بها غير حساسة للقضية تم إدخالها فقط عندما يتم فك القيمة

يطابق تماما الجسم ؛ نسخة مفقودة أو غير متطابقة تنتج HTTP 400

و JSON-RPC -32020. . .

  • لا يُرجع التكمل المحللproduction. . .
  • إعاقة أداة تستخدمisError: true؛ مكالمة بروتوكول خاطئة تستخدم JSON-RPC error. . .

أساليب فشل الإنتاج

FailureWhat the learner seesCorrect response
Client assumes object outputValid arrays fail or are silently wrappedValidate against the published schema without object-only types
Empty cursor treated as falseFinal pages disappearContinue whenever nextCursor is present and non-null
Sensitive value mirroredSecret appears in proxy, WAF, or trace dataReject the descriptor and keep secrets in protected request data
Raw Unicode or whitespace mirroredGateway and origin disagree or the value is normalizedUse exact base64 UTF-8 sentinel encoding and compare after decoding
Annotation hidden in a schema branchA client misses routing metadata during admissionTraverse the entire schema tree and allow only direct top-level properties
Large integer mirroredJavaScript intermediary rounds the routing valueReject values outside the JavaScript safe integer range
Header and body disagreeGateway routes one target while the origin executes anotherReject before dispatch with HTTP 400 and JSON-RPC -32020
Output schema ignoredDownstream code consumes corrupt structureValidate before model or application use
Resource link trusted automaticallyCaller follows an unauthorized URIReauthorize every resource read
Completion shares global suggestionsHidden tenant names leakFilter by caller, reference, and authorization
Tool annotations treated as policyDestructive operation bypasses confirmationEnforce authorization and approval outside annotations
One malformed tool breaks discoveryEntire server becomes unavailableReject the bad descriptor and admit valid tools independently

اتصال كابستون

تحتاج الحجر النهائي للمرحلة 13 إلى بوابة يمكنها دمج الأدوات من عدة خوادم. هذه الدروس توفر جوهر القبول لها.

استخدم الأثاث لتصفية أربعة قطع من الأدلة:

  • اكتشافات تحديدية ومكتملة من صفحات
  • التحقق من المصادقة مع المصنف قبل التعرض للنموذج.
  • المخرجات المهيكلة المصدقة بالإضافة إلى كتلة المحتوى المحدودة
  • إكمال وتوجيه البيانات المعدنية التي تحافظ على حدود الترخيص.

لا تدعي ان التوافق بين البوابات من الناجحةtools/callفقط. التقاط وصف، تتبع الصفحة، مجموعة أدوات مقبولة، مجموعة أدوات رفض، ونتيجة واحدة معتمدة.

الشروط الرئيسية

TermMeaning
inputSchemaJSON Schema object defining accepted tool arguments
outputSchemaOptional JSON Schema defining structuredContent
structuredContentAny JSON value produced by a tool result
Content blockTyped text, image, audio, resource link, or embedded resource
x-mcp-headerSchema annotation that mirrors a primitive argument into Streamable HTTP metadata
Opaque cursorServer-issued pagination token whose value the client does not interpret
Completion referencePrompt name or resource URI/template whose argument is being completed
AdmissionClient decision to expose or reject a discovered descriptor

المزيد من القراءة

This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.

Browse the complete course catalog or open this lesson on GitHub.