Phase 13: Tools & Protocols

زنجیره تامین MCP Registry: پذیرش، حرکت و بازگشت

ثبت ثبت نام به شما می گوید که یک ناشر چه اعلامی کرده است. ثبت تولید اثبات می کند که شما چه چیزی را به دست آورده اید، آنچه را مشاهده کرده اید، آنچه را که شما تایید کرده اید و آنچه را که می توانید به طور ایمن بازگرداند.

Type: Build

Languages: Python

Prerequisites: Phase 13 · 17 (gateways and registries), Phase 13 · 18 (production authentication)

Time: ~90 minutes

اهداف یادگیری

  • انتشار جداگانه از ثبت، منبع بسته، کشف زمان اجرا و تأیید محلی.
  • یک فضای نام سرور MCP را بدون اعتماد به نام در داخل رکورد خود تأیید کنید.
  • چاپ نامتعدی، منبع اجرا، اصل و شواهد توصیف کننده زنده.
  • تشخیص تغییرات وضعیت ثبت و انحراف زمان اجرا پس از پذیرش.
  • بازگردانید مسیر به نسخه ای که قبلاً پذیرفته شده بود بدون نوشتن مجدد تاریخچه.
  • يه دفترچه ي قبولي که هر تصميم رو شرح ميده رو نگه دار

مشکل

تو پیدا کردیcom.example/inventoryدر يک سجل، وصفش درست به نظر مياد، بسته اش وجود داره، سرور جواب ميدهserver/discover. .

این یک واقعیت نیست بلکه یک زنجیره واقعیت از مقامات مختلف است:

  1. یک ناشر که برای یک فضای نامها معتبر شده است یک رکورد را ارسال کرد.
  2. یک ثبت بسته به یک اثر با هویت و هضم خاص خدمت می کرد.
  3. یک نقطه پایان در حال اجرا یک نسخه پروتکل، قابلیت ها، ابزارها و اطلاعات سرور تشخیصی را گزارش می دهد.
  4. سازمان شما تصميم گرفت که اين ترکیب درست رو مجاز باشه

در این صورت، یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه از یک نسخه

.ملاحظه کننده یه کنترل کننده پذیرش با شواهد در هر مرز

ثبت نامه یک شاخص است نه سیستم تأیید شما

دفتر ثبت رسمی MCP متاداتا سرور را ذخیره می کند.server.jsonثبت نام یک نسخه سرور و اعلام یک یا چند بسته یا نقاط پایان از راه دور. قوانین انتشار اضافه کردن هویت فضای نام، بررسی مالکیت بسته، قوانین ثبت نام محدود و یک مکان متاداتا ناشر باریک.

این کنترل ها به سوالات انتشار پاسخ می دهند. سیاست تولید شما هنوز به سوالات انتشار پاسخ می دهد:

BoundaryQuestionEvidence owner
NamespaceWas the publisher allowed to use this name?Registry authentication plus your verified namespace input
RecordWhat did the publisher declare for this version?Immutable server.json digest
Execution sourceWhich package or remote endpoint will execute?Declared source fields, verified ownership result, transport, and trusted digest
RuntimeWhat does the endpoint expose now?server/discover and tool descriptors
AdmissionDid your policy approve this exact set?Local pin and ledger entry
OperationsIs it still safe, and what can replace it?Drift checks, status sync, health, and rollback route

نسخه اسکیم ثبت و نسخه پروتکل MCP مستقل هستند. یک رکورد ممکن است از نسخه منتشر شده استفاده کند 2025-12-11برنامه سرور در حالی که سرور زنده از MCP پشتیبانی می کند 2026-07-28هرگز از هم جدا نشو

هفت کنترل در یک تصمیم پذیرش

1. تأیید فضای نام

نام های رسمی ثبت نام از نام های معتبر استفاده می کنند. یک دامنه تایید شده می تواند به یک پیشگویی دامنه معکوس نقشه برداری کند. به عنوان مثال، کنترل example.comمی تونم ثابت کنمcom.example/*. .

یک چک پیشگویی رشته را قبول نکنید:

pythonserver_name.startswith("com.example")

که هم قبول ميکنهcom.exampleevil/tool. نام رو در قسمت دوم تقسیم کن/، نیاز به یک کلگ خالی نیست، و به طور دقیق بخش نام فضا را مقایسه کنید. مهمتر از همه، نام فضا تایید شده را از نتیجه تأیید هویت به پذیرش منتقل کنید. از رکورد غیر قابل اعتماد اعتماد اعتماد اعتماد نکنید.

نام های پشتیبانی شده توسط GitHub و نام های پشتیبانی شده توسط دامنه از مسیرهای تأیید هویت مختلف استفاده می کنند. هر یک از مسیرها را به یک ورودی پذیرش عادی کنید: رشته نام های دقیق تایید شده.

2. تعلق به اصل

برای یک بسته ثبت، اعلامیه و اثر دریافت شده باید در زمینه های صریح به هم پیوسته شوند:

  • نوع بسته ثبت
  • پیژندنده بسته
  • نسخه بسته
  • نتیجه مالکیت تایید شده
  • دانلود شده است

همچنین انتقال بسته اعلام شده را تأیید کنید. یک رکورد با فقط یک نقطه پایان از راه دور معتبر است و نباید به دلیل عدم وجود یک بسته رد شود. برای یک منبع از راه دور، URL و نوع حمل و نقل اعلام شده را به مالکیت نقطه پایان مستقل تأیید شده و یک هضم از اتصال قابل اعتماد یا شواهد انتشار پیوست.

کد درس هر دو نوع منبع را پشتیبانی می کند و منبع انتخاب شده را همراه با منبع ثبت، نام سرور، نسخه ثبت، ثبت ثبت و ثبت شواهد هش می کند. هش اصلی حاصل شده یک اشاره کننده کامپکت به مجموعه شواهد کامل است. این جایگزین حفظ شواهد نیست.

هرگز یک هضم را که فقط توسط اثر هنری که می خواهید تأیید کنید ارائه می شود، قبول نکنید. آن را در یک مرز قابل اعتماد برای دریافت دریافت محاسبه کنید یا از یک سرویس بسته بندی دریافت کنید که نتیجه تأیید آن را تأیید می کنید.

3. نه فقط نسخه

نسخه های ثبت نام شناسه های منحصر به فرد انتشار هستند. متاداتا منتشر شده تغییر ناپذیر است. یک رکورد تغییر یافته نیاز به نسخه جدید دارد. نسخه سازی معنوی توصیه می شود، اما ثبت نام آن را مورد نیاز نیست و محدوده نسخه ها را قبول نمی کند.

این یعنی^1.4یک پین قابل قبول نیست. نه آخرین. یک پین مفید حاوی:

json{
  "server": "com.example/inventory",
  "version": "1.0.0",
  "recordDigest": "...",
  "source": {"kind": "package", "registryType": "pypi"},
  "sourceDigest": "...",
  "toolsetDigest": "...",
  "provenanceDigest": "...",
  "registryStatus": "active"
}

پیوند چند لایه به شما اجازه می دهد تا مشخص کنید که کدام مرز تغییر کرده است. تغییر هضم رکورد در همان نسخه Registry یک شکست هضم یکپارچگی Registry است. تغییر هضم منبع در زیر هماهنگی بسته یا انتشار از راه دور یک شکست هضم یکپارچگی منبع اجرا است. تغییر هضم ابزار مجموعه ای drift runtime است.

4. تشخیص حرکت زنده

ورود باید سرور را که در واقع ترافیک دریافت می کند مشاهده کند. تماس بگیریدserver/discover، لیست یا به طور دیگری از طریق مسیر قابل اعتماد خود توصیف کننده های ابزار را بدست آورید و تایید کنید:

  • 2026-07-28در حال حاضرsupportedVersions
  • تمام قابلیت های مورد نیاز محلی موجود است
  • هر توصیف کننده ابزار دارای سطح هویت و طرح مورد نیاز است
  • هضم توصیفگر عادی شده با پین پذیرفته شده در چک های بعدی مطابقت دارد

نتیجه اختیاری_meta["io.modelcontextprotocol/serverInfo"]ارزش خود گزارش شده نمایش، ثبت و تنظیمات است. آن را به عنوان شواهد تشخیصی ثبت کنید، اما هرگز از آن برای تعیین نام فضای، مالکیت بسته، مالکیت نقطه پایان، پذیرش یا هر تصمیم امنیتی دیگر استفاده نکنید. یک مستقیم serverInfoاسم مستعار بیرون_metaاین موضوع در مورد این موضوع بحث می کند.

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

نمونه توصیفات نادرست و هر تغییر هضم توصیفات را به عنوان حرکت در نظر می گیرد، پن را قرنطینه می کند، مسیر فعال آن را حذف می کند و آن نسخه را به عنوان هدف بازپسین مسدود می کند. یک سیاست تولید ممکن است تغییر اداری را تنها از طریق بررسی جدید اجازه دهد، زیرا توضیحات بر انتخاب ابزار مدل تأثیر می گذارد.

5. وضعیت ثبت زنده است

API Registry یک سطح پاسخ را متصل می کند _metaموضوع کنار هر رکورد سرور._meta["io.modelcontextprotocol.registry/official"]. پاسخ رو بده_metaاعتراض به پذیرش و خواندن_meta["io.modelcontextprotocol.registry/official"].status. یک مستقیم_meta.statusارزش شکل رسمی سیم نیست. متاداتا پاسخ را با اطلاعات خود ثبت نشریه اشتباه نگیرید _metaوضعیت می تواند:

  • active: به صورت پیش فرض بازگردانده شده و واجد شرایط پذیرش محلی است
  • deprecated: هنوز با هشدار قابل کشف است، اما دیگر یک انتخاب خودکار امن نیست
  • deleted: به طور پیش فرض پنهان شده در حالی که سوابق تاریخی آن در طریق مشاهده های حذف شده یا افزون شده در دسترس باقی می ماند

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

متاداتا سفارشی که توسط ناشر ارائه می شود تنها به زیر تعلق دارد_meta.io.modelcontextprotocol.registry/publisher-providedدر یک رکورد انتشارات. متادای پاسخ های مدیریت شده توسط ثبت جداگانه است. اجازه ندهید که یک ناشر وضعیت رسمی خود را تعیین کند.

6. بازگشت به مسیر به معنی بازسازی مسیر است

یک نشریه ثابت در طول رولبک ویرایش نمی شود. رولبک یک پین قبلاً پذیرفته شده و در حال حاضر واجد شرایط را انتخاب می کند و مسیر فعال را تغییر می دهد.

هدف امن باید:

  1. سابقه پذیرش کامل داشته باش
  2. هنوز حالت فعالي ثبت نام رو تحت سيستم داريد
  3. به خاطر زمان اجرا و یا مدارک امنیتی قرنطینه نشوند.
  4. هنوز به بسته بسته و مجموعه توصيفي زنده حل ميکنه
  5. از چک هاي بهداشتي جاري عبور کنيد

نمونه بر سه شرط اول تمرکز می کند. یک سازنده واقعی باید بسته را دوباره جمع کند و قبل از فعال شدن دوباره نقطه پایانی زنده را بررسی کند.

7. یک دفترچه پذیرش را اضافه کنید

یک پایگاه داده پذیرش میگه چه چیزی فعاله و یک دفترچه توضیح میده چرا

هر ورودی نمونه شامل یک ردیابی، زمان، رویداد، سرور، نسخه، نتیجه، دلایل، شواهد، هش ورودی قبلی و هش خود است. تغییر یک نتیجه قدیمی تأیید این ورودی و هر لینک بعدی را متوقف می کند.

این آشکار است، نه جادوگرانه ثابت است. سرنامه های دوره ای لنگر در یک دامنه اعتماد جداگانه مانند متاداتا انتشار امضا شده یا ذخیره سازی یک بار نوشتن است. محدودیت کسانی که می توانند اضافه کنند. توکن های مجوز، اعتبارات بسته، استدلال ابزار و داده های انتهای خصوصی را از شواهد دور نگه دارید.

آن را بسازید

کنترل کننده قابل اجرا فعالهcode/main.pyفقط از کتابخانه استاندارد پایتون استفاده می کنه

با نمایش محدود شروع کن:

bashcd phases/13-tools-and-protocols/30-mcp-registry-supply-chain-and-drift
python3 code/main.py

این نمایش پنج عملیات را انجام می دهد:

  1. اعتراف کن1.0.0با نام های نام، منبع بسته، پروتکل، قابلیت ها و ابزارها.
  2. اعتراف کن1.1.0و آن را فعال کنید.
  3. یک ابزار حذف غیر منتظره را در زمان اجرا مشاهده کنید.
  4. وضعیت ثبت نام را مشاهده کنید1.1.0تبدیل شدنdeprecated. .
  5. راهبردي را به بازيابي بازگردانيد1.0.0. پين

شکل انتظار می رود:

json{
  "admitted": [true, true],
  "driftAllowed": false,
  "rollbackAllowed": true,
  "activeVersion": "1.0.0",
  "ledgerValid": true
}

اجرا را در این ترتیب بخوانید:

  1. namespace_for_domain()وnamespace_matches()تصميم گرفتن تصميم نامي دقیق
  2. digest()وnormalized_tools()شواهد تعیین کننده ای تولید می کنند.
  3. RegistryAdmissionController.admit()با انتشار، اصل، زمان اجرا و سیاست ها هم می پیوندد.
  4. check_live()با این که یک مشاهده جدید با این پن مقایسه می شود.
  5. observe_registry_status()نسخه های قرنطینه که وضعیت ثبت نام تغییر می کند.
  6. rollback()فقط یک هدف واجد شرایط که قبلاً پذیرفته شده است را فعال می کند.
  7. AdmissionLedger.verify()تغییرات در تاریخ ثبت شده را تشخیص می دهد.

ازش استفاده کن

کنترل کننده رو بين کشف و روت کردن قرار بده

textRegistry sync -> artifact verifier -> live discovery -> admission controller -> route table
                                               |                 |
                                               v                 v
                                          evidence store    admission ledger

برای این کار ها از هویت های جداگانه استفاده کنید. یک کارمند همگام سازی سجل نیاز به دسترسی به متاداتا دارد. یک تأیید کننده آرتیفکت نیاز به دسترسی به جمع آوری بسته دارد. یک سازنده مسیر نیاز به مجوز برای فعال کردن یک پین تأیید شده دارد. هیچ یک از آنها نیاز به هر اعتبارنامه ای ندارد.

بیان انتشار را صریح کنید. تأیید شده به معنای سیاست تایید شده است. فعال به معنای مسیر فعلی است که آن را انتخاب می کند. قرنطینه شده به معنای دریافت کار جدید نیست. اضافه شده به معنای نسخه دیگری است که قبول شده است فعال است. همه چهار معنی را در یک بول کد ندهید.

قبل از اينکه سرور رو در معرض قرار بدي، واردش رو اجرا کنtools/listدر غیر این صورت، مشتری می تواند در زمان شکاف بین انتشار و ارزیابی سیاست ها یک ابزار را کشف کند.

آزمایشگاه تعاملی

شما شاهد شکست یک مرز در یک زمان خواهید بود.

آزمایشگاه A: برخورد فضای نام

یک پوسته پایتون را از دایرکتوری کد باز کنید:

bashcd phases/13-tools-and-protocols/30-mcp-registry-supply-chain-and-drift/code
python3 -q

پس اجرا کن

pythonfrom main import namespace_matches
namespace_matches("com.example/inventory", "com.example")
namespace_matches("com.exampleevil/inventory", "com.example")

اولين نتيجه اينهTrue؛ دومين اينهFalse. مقايسه دقیق را با startswithدر محل و مشاهده کنید که چرا نام دوم از مرز عبور می کند. قبل از ادامه مقایسه دقیق را بازگرداند.

آزمایشگاه B: حرکت توصیف کننده

pythonfrom main import *
times = iter(f"2026-08-21T12:00:{n:02d}+00:00" for n in range(10))
c = RegistryAdmissionController(clock=lambda: next(times))
meta = {OFFICIAL_META_KEY: {"status": "active"}}
c.admit(sample_record("1.0.0"), meta, "com.example", evidence_for("1.0.0"), sample_live("1.0.0"))
c.check_live("com.example/inventory", "1.0.0", sample_live("1.0.0", True))

دلیل و وضعیت مسیر را بررسی کنید. ثبت بسته و ثبت ثبت تغییر نکرده است. سطح ابزار زمان اجرا تغییر کرده است، بنابراین کنترل کننده پن را قرنطینه کرده و غیرفعال کرده است. به همین دلیل کنترل زنجیره تامین باید پس از نصب ادامه یابد.

آزمایشگاه C: وضعیت و برگشت

اعتراف کن1.1.0، آن را با استفاده از زمان و سعی کنید هر دو هدف برگشت:

pythonc.admit(sample_record("1.1.0"), meta, "com.example", evidence_for("1.1.0"), sample_live("1.1.0"))
c.observe_registry_status("com.example/inventory", "1.1.0", "deprecated")
c.rollback("com.example/inventory", "1.1.0", "unsafe retry")
c.rollback("com.example/inventory", "1.0.0", "restore known release")
c.ledger.verify()

هدف قرنطينه رد شده است. پين فعال قبلی پذیرفته شده است. دفترچه معتبر باقی می ماند.

آزمایشگاه تمرین

کنترل کننده را با یک دروازه تایید دو نفر گسترش دهید.

الزامات:

  • مجوزها را به عنوان مرجع های امضا شده اثبات نگه دارید، نه نام های تغییر پذیر در پین.
  • برای یک مجموعه ابزار که شامل یک ابزار با destructiveHint: true. .
  • هویت های تکراری بازرس را رد کنید.
  • سعی اصلی پذیرش را در دفترچه نگهداری کنید وقتی تایید نامکمل است.
  • تست های صفر، یک، دوگانه و دو تایید جداگانه را اضافه کنید.
  • دستمزد ها، اعتبارنامه ها یا تمام بحث های ابزار خصوصی را ثبت نکنید.

موفقیت به این معنی است که یک ابزار تخریب کننده نمی تواند فعال شود تا زمانی که هر دو هویت، ضبط دقیق، بسته بندی و دستگاه های ابزار را تایید کنند.

آثار هنری ارسال شده

اين درس به ما ميگيرهoutputs/skill-mcp-registry-admission.md. آن را به عنوان یک کتابچه ثابت و قابل استفاده مجدد در هنگام بررسی نسخه جدید Registry یا بررسی drift استفاده کنید. این ورودی ها، قوانین رد، بسته شواهد، سازگاری وضعیت و اثبات برگشت بدون وابستگی به نام کلاس نمونه را تعریف می کند.

بررسی کنید

نمایش و مجموعه تعیین کننده را اجرا کنید:

bashcd phases/13-tools-and-protocols/30-mcp-registry-supply-chain-and-drift
python3 code/main.py
python3 -m unittest discover -s code/tests -v

بررسی باید ثابت کند:

  • مرز های دقیق فضای نامها پیش فرض های مشابه را رد می کنند
  • فقط وضعیت رسمی ثبت نام با فضای نام می تواند یک نسخه را واجد شرایط کند
  • بسته ای که تایید نشده یا مطابقت ندارد و شواهد از راه دور رد شده است
  • متادای ناشر نمی تواند متادای مدیریت شده توسط ثبت نام را تقلید کند
  • دستور ابزار بدون پنهان کردن تغییرات توصیفگر عادی می شود
  • بسته بندی و ساختارهای ابزار بد شکل به طور ایمن رد می شوند
  • serverInfoهنوز تشخیص داده شده و هیچ وقت به مقام پذیرش نمی رسد
  • قرنطینه های فراری، غیرفعال سازی و بازتاب به پین
  • وضعیت تغییر می کند پن های فعال قرنطینه
  • rollback نمی تواند نسخه قرنطینه شده یا ناشناخته ای را انتخاب کند
  • دستکاری در دفترچه ثبت شده

روش های شکست تولید

FailureWhy it happensRequired response
Name looks valid but namespace was never authenticatedPolicy trusted record textReject until a trusted namespace verifier supplies the exact prefix
Same package coordinate returns new bytesMutable upstream or compromised distributionStop activation, retain both digests, investigate the fetch boundary
“Latest” changes without reviewFloating selection escaped the pinResolve only exact admitted versions and digests
New tool appears after approvalRuntime drift or a different deploymentQuarantine the route and capture a fresh descriptor observation
Deprecated version remains activeStatus sync is missing or delayedReconcile status on a schedule and before activation
Deleted record disappears from default syncClient requested only active recordsUse incremental or deleted-aware reconciliation and preserve local history
Rollback target was never admittedRoute control and approval state are disconnectedRefuse rollback and run a new admission for the target
Ledger verifies locally after an attacker rewrites all entriesHash chain has no external anchorPublish signed ledger heads to a separate trust domain
Evidence contains bearer tokens or tool argumentsLogging copied whole requestsRedact at collection time and store only the minimum proof

قاعده عملیاتی

پاسخ های انتشار آیا این هویت می تواند این نام را منتشر کند؟ پاسخ های پذیرش آیا ما این اثر هنری را اجرا خواهیم کرد و این رفتار دقیق را افشا خواهیم کرد؟ این تصمیمات را جداگانه نگه دارید، هر پیوستن را وصل کنید و به دنبال شواهد را به جای حافظه انتخاب کنید.

خواندن بیشتر

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.