@nyuchi/mzizi-mcp 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/generated/registry-pin.d.ts +1 -1
- package/dist/generated/registry-pin.js +1 -1
- package/dist/generated/registry.d.ts +2 -2
- package/dist/generated/registry.js +7 -7
- package/dist/generated/registry.js.map +1 -1
- package/dist/generated/skills.d.ts +1 -1
- package/dist/generated/skills.d.ts.map +1 -1
- package/dist/generated/skills.js +20 -44
- package/dist/generated/skills.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/package.json +1 -1
package/dist/generated/skills.js
CHANGED
|
@@ -8,60 +8,36 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export const BUNDLED_SKILLS = [
|
|
10
10
|
{
|
|
11
|
-
"name": "
|
|
12
|
-
"description": "
|
|
13
|
-
"body_mdx": "# Simplify — code that reads like the system\n\n**The simplest change is the one you don't write.** Reuse before you build, compose\nbefore you implement, and let one source own each decision. Use this before adding a\ncomponent, a style, or a page — and during any refactor.\n\n> This skill is the narrative form of the Mzizi MCP tool `get_simplify_guidance`, which\n> returns the same ruleset as structured data. Pull it live when you want the machine-readable\n> checklist; read this when you want the reasoning.\n\n## The rules\n\n1. **Reuse before build.** Search the registry first — `search_components` /\n `list_components` over the MCP, or `GET https://mzizi.dev/api/v1/ui`. Install via the\n shadcn CLI; never reimplement a primitive that already exists.\n2. **Styles are tokens.** Never hardcode a colour, radius, spacing, or motion value —\n consume `var(--…)`. N1 is the only layer allowed to define CSS values, so a rebrand is\n one edit. New tokens go in `:root` + `.dark` + the `@theme` block.\n3. **Compose, don't implement.** A page (N6) is a composition of N2 primitives and N3 brand\n components — never inline a `<button>`, a card, or an SVG. Pull spinners, skeletons, and\n badges from N2.\n4. **Variants over copies.** Collapse near-duplicate components into one CVA variants block.\n One `cva()` per primitive; express differences as `variant` / `size` props, not new files.\n5. **One composition path.** Compose class names through `cn()`; extract shared logic into\n `lib/` or a hook. No string-concatenated classes, no logic duplicated across files.\n6. **Right altitude.** Keep each component self-contained, importing only from the layer\n below on its strand. A primitive that reaches up into page code can't be installed on its\n own.\n7. **Delete first.** Prefer removing over adding — dead code, unused props, redundant\n wrappers, commented-out blocks. Less code is less to break, read, and keep true.\n8. **Live data, not copies.** Read counts, tokens, and doctrine live (`/api/v1/stats`, the\n generated palette, the MCP). A baked-in number or duplicated token guarantees drift.\n\n## Checklist\n\n- [ ] Searched the registry before building?\n- [ ] All styles are tokens (`var(--…)`), zero hardcoded values outside N1?\n- [ ] Page is pure composition of N2/N3, no inlined primitives?\n- [ ] Variation expressed as CVA variants, not near-duplicate files?\n- [ ] All class names through `cn()`, shared logic extracted once?\n- [ ] Each file self-contained and installable, importing only downward?\n- [ ] Deleted more than you added where you could?\n- [ ] Counts, tokens, and doctrine read live, not hardcoded?\n\n## See also\n\n- MCP tool `get_simplify_guidance` — the same ruleset as structured data.\n- `get_ubuntu_doctrine` — the philosophy the rules serve.\n- The `scaffold-component` skill — how to add a component the right way when reuse genuinely\n isn't possible.",
|
|
14
|
-
"source": "mzizi-tools/mzizi-skills/skills/
|
|
11
|
+
"name": "mzizi-language",
|
|
12
|
+
"description": "Write and check Mzizi language source (.mz): the component syntax (enum tables, props, the view tree, when / for each, records, use motion), the mz check --agent loop and its NDJSON diagnostics, mz fix for every exact fix, mz contract for the contract … end block (RFC-0006, RFC-0010), the diagnostic codes you will meet (MZ0101, MZ0106, MZ0312, MZ0313, MZ0406–MZ0409, MZ06xx, MZ07xx), and what is not built yet: lowering to Rust, a runtime, handlers, a release.",
|
|
13
|
+
"body_mdx": "# Writing and checking Mzizi\n\nMzizi is a language whose syntax, type system and compiler feedback loop are designed for\nmachine authorship. The language lives in [`mzizi-dev/mzizi`](https://github.com/mzizi-dev/mzizi)\n(public). It is **not** the component registry (`mzizi-dev/mzizi-registry`); that is the\ncompanion `mzizi-roots` skill.\n\nThis skill describes `main` at `e9e9233` (2026-09-29). Where this skill and the code\ndisagree, **the code is the fact** (RFC-0003 §7.1). Every example below is copied from a file\nin the repository that passes `mz check` and `mz contract` there.\n\n## What exists, and what does not\n\n**Built and tested:** the lexer, the recovering parser, the name and type resolver, the agent\ndiagnostic protocol (`mz check --agent`), `mz fix`, the content-addressed IR, `mz outline`,\ncontract evaluation (`mz contract`), nine primitives written in Mzizi (`primitives/*.mz`), two\nexamples (`examples/*.mz`) and the Phase 0 benchmark harness and runner. All of it is gated in\nCI.\n\n**Not built:** lowering to Rust, code generation, a runtime, rendering, a release and a\npublished binary. A `.mz` file today is checked and its contract evaluated; **nothing runs\nit.** Do not tell anyone Mzizi \"compiles to Rust\" or is \"production ready\".\n\n**In progress, as design only:**\n\n- **Handlers and the backend slice.** RFC-0011 (handlers, the backend measurement slice) is a\n draft on a branch, not on `main`. RFC-0007 G2.2 (handler declarations) and G1.2 (function\n parameters and bodies) are not implemented. There is no `route`, `service` or HTTP syntax\n you can check today.\n- **Contracts everywhere.** RFC-0010 (draft) extends contracts to functions, handlers,\n services and records, with codes `MZ0606`–`MZ0613`. **None of those codes exist in the\n compiler yet.** Only component contracts (RFC-0006) are evaluated.\n- **Modules.** There are no imports. Components are referenced by bare name\n (`primitives/confirm_bar.mz` uses `button` and `alert` with no import line). A reference to\n another component's enum (`button_variant.ghost`) is warning `MZ0502`, because it cannot be\n checked until modules exist.\n\n**Results, as they fell.** Two Phase 0 pilots ran on 2026-09-27 and neither showed an\nadvantage for Mzizi. On the frontier model the Mzizi and Dioxus arms tied, with Mzizi about 8%\nfewer transcript tokens. On the ~7B open-weight model, the design target, Mzizi did worse on\nall three metrics. Neither pilot is the kill-criterion run (RFC-0009 §6), and that run has not\nhappened. \"Designed for agents\" is accurate; \"faster\" or \"better\" is not.\n\n## Getting `mz`\n\nThere is no release. Build it from source; the compiler crate has zero dependencies:\n\n```bash\ngit clone https://github.com/mzizi-dev/mzizi.git && cd mzizi/compiler\ncargo build --bin mz # ../target/debug/mz\ncargo run --bin mz -- check ../primitives/button.mz\n```\n\n**Never `cargo install mz`.** The `mz` crate on crates.io is an unrelated project, and the\ncompiler crate (`mzizi-lang-compiler`) is `publish = false`.\n\n## The commands\n\n`mz` takes exactly one file per call. `mz check ../primitives/*.mz` is a usage error (exit 2).\nLoop over files instead.\n\n| Command | Does |\n| --------------------------------- | -------------------------------------------------------------------------------- |\n| `mz check <file>` | human-readable diagnostics |\n| `mz check --agent <file>` | NDJSON: one diagnostic per line, then one summary line. **Target this.** |\n| `mz fix <file>` | applies every `exact` fix in place, in one pass, then re-checks the file |\n| `mz contract <file>` | evaluates the file's `contract` block (only on a file that checks clean) |\n| `mz contract --agent <file>` | the same, as NDJSON; the summary adds `contract_clauses` and `contract_failures` |\n| `mz outline <file>` | the interface only (name, enums, props, contract subjects), as valid Mzizi |\n| `mz hash <file>` / `mz ir <file>` | the content-addressed root hash / every IR node with its hash and path |\n\nExit status: **0** no errors (warnings do not fail), **1** errors or a failed contract\nassertion, **2** a usage or I/O problem. Branch on the status; parse the NDJSON for detail.\n\n`check` and `contract` are deliberately separate: a compile error is not a defect, and a\ncontract failure is. Your file is done when **both** `mz check --agent` and\n`mz contract --agent` exit 0.\n\n## The loop\n\n1. Write the file.\n2. `mz check --agent file.mz`. If it exits 0, go to 5.\n3. `mz fix file.mz` to apply every `exact` fix. Read each remaining diagnostic's `say`; apply\n `guess` fixes only after checking them.\n4. Fix everything reported, then go to 2. Errors are independent, so fix them all before\n re-running; do not fix one and re-run to \"see the next\".\n5. `mz contract --agent file.mz`. `MZ0603` means an assertion is false (a defect in the\n component or the contract); `MZ0605` means it could not be evaluated, which **counts as a\n failure**.\n\n## Reading the NDJSON\n\nThis file is wrong on purpose. It is the example in `benchmarks/prompts/mzizi-guide.md`, which\n`benchmarks/prompts/verify-guide.sh` checks against the real compiler.\n\n```mz\n## WRONG ON PURPOSE: three mistakes.\ncomponent tag\n\n enum tag_size\n snug class \"h-13 px-3\"\n end\n\n prop onPick: event(none)\n prop size: tag_size = snug\n\n view\n row\n slot = \"tag\"\n class = \"rounded-full {size.class}\"\n tap = on_pick\n end\n end\n\n contract\n slot is \"tag\"\n tag_size.snug height 52\n end\n\nend\n```\n\n`mz check --agent tag.mz` exits 1 and prints exactly:\n\n```text\n{\"code\":\"MZ0101\",\"severity\":\"error\",\"file\":\"tag.mz\",\"span\":[8,8,8,14],\"say\":\"`onPick` is not snake_case — Mzizi names are snake_case, so write `on_pick`\",\"fix\":{\"span\":[8,8,8,14],\"replace\":\"on_pick\",\"confidence\":\"exact\"}}\n{\"code\":\"MZ0602\",\"severity\":\"error\",\"file\":\"tag.mz\",\"span\":[21,26,21,28],\"say\":\"a contract clause needs a predicate (is / in / contains / not_empty / at_least / uses / min_height / shows), found `52`\",\"fix\":{\"span\":[21,26,21,26],\"replace\":\"is \",\"confidence\":\"exact\"}}\n{\"code\":\"MZ0208\",\"severity\":\"error\",\"file\":\"tag.mz\",\"span\":[24,1,24,4],\"say\":\"top-level blocks close with their name: write `end component tag`\",\"fix\":{\"span\":[24,1,24,4],\"replace\":\"end component tag\",\"confidence\":\"exact\"}}\n{\"summary\":true,\"errors\":3,\"warnings\":0,\"exact_fixable\":3,\"ms\":0}\n```\n\n`mz fix tag.mz` then prints `mz: applied 3 exact fixes; 0 errors (0 exact-fixable) remain`, and\n`mz contract --agent tag.mz` reports `\"contract_clauses\":2,\"contract_failures\":0`.\n\n- `code`, `severity` (`error` or `warning`), `file` (the path you passed), `span`\n `[line, col, end_line, end_col]` (1-indexed, end exclusive) and `say` are always present.\n- `say` quotes the offending source, so you need not re-read the file to understand it.\n- `fix` is optional: put `replace` at `fix.span`. An empty `replace` is a deletion. `exact`\n is safe to apply blind; `guess` has the right location and an inferred replacement.\n- The last line is always the summary.\n\n## Syntax essentials\n\nOne file holds one component. There is one form per intent: if a construct is not shown here or\nin the RFCs, assume it does not exist. The order inside a component is doc lines, `use`, `enum`\nand `record`, `prop`, `view`, `fn`, `contract`.\n\nA complete primitive, `primitives/badge.mz`:\n\n```mz\n## A small status label. Not interactive, so no touch floor applies.\n## Corpus reference: n2-primitives/badge.tsx.\ncomponent badge\n\n enum badge_variant\n default class \"bg-primary text-primary-foreground\"\n secondary class \"bg-secondary text-secondary-foreground\"\n destructive class \"bg-destructive/10 text-destructive dark:bg-destructive/20\"\n outline class \"border-border text-foreground bg-input/30\"\n ghost class \"hover:bg-muted hover:text-muted-foreground\"\n link class \"text-primary underline-offset-4 hover:underline\"\n end\n\n prop variant: badge_variant = default\n prop label: text\n\n view\n mark\n slot = \"badge\"\n portal = \"https://mzizi.dev/components/badge\"\n variant = variant\n class = \"inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-md border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all {variant.class}\"\n text = label\n end\n end\n\n contract\n every badge_variant class not_empty\n badge_variant.default class contains \"bg-primary\"\n slot is \"badge\"\n end\n\nend component badge\n```\n\n- **Shape.** `component <name>` opens the file and `end component <name>` closes it, repeating\n the name. Every other block (`enum`, `record`, `view`, `contract`, each element, each `when`,\n each `for each`, each `fn`) closes with a bare `end`. Never `end enum` or `end view`.\n- **Comments.** `##` to end of line is the only comment form. `//` and `/* */` are errors.\n- **Names** are `snake_case`. `onRemove` is `MZ0101`, with the snake form as its `exact` fix.\n- **Enums are variant tables.** One row per variant: its name, then `column value` pairs. Every\n variant has every column (`MZ0303` otherwise). A column can carry meaning beyond styling, as in\n `primitives/alert.mz`, where `announce` ties the ARIA role to the severity:\n\n ```mz\n enum alert_variant\n default class \"bg-card text-card-foreground\" announce \"status\"\n destructive class \"bg-card text-destructive\" announce \"alert\"\n warning class \"bg-card text-[var(--status-warning,#FFD740)]\" announce \"alert\"\n info class \"bg-card text-[var(--status-info,#00B0FF)]\" announce \"status\"\n end\n ```\n\n- **Heights come from the class.** A variant's `height` is the pixels its `h-N` or `size-N`\n class renders (`N × 4`). You may leave the `height` column out. A written `height` that\n disagrees with the class is `MZ0313`, and its `exact` fix is the rendered number.\n- **Props.** `prop <name>: <type>`, optionally `= <default>`. The types are an enum name\n (`= default`), `text`, `bool`, `int`, `event(none)`, `event(<type>)`, `list(<type>)`\n (omitted means empty), `option(<type>)` (omitted means none) and a record name. Never\n `option(list(T))` (`MZ0703`).\n- **The view** holds exactly one element tree. An element is a bare word on its own line,\n closed by `end`. Inside it, each line is an attribute `name = value` (the `=` is required),\n a child element, a `when` block, a `for each`, or `nothing`. Strings interpolate with\n `{...}`; there is no `+`.\n- **Conditionals.** `when x` … `end`, `when not x`, `when v is <variant>`, and for an option or\n a list `when x is none` … `else` … `end`. There is no `if` (`MZ0407`, `exact` fix `when`)\n and no `match`. From `primitives/avatar.mz`:\n\n ```mz\n when image is none\n row\n slot = \"avatar-fallback\"\n class = \"flex size-full items-center justify-center rounded-full bg-muted text-muted-foreground {size.text_class}\"\n text = initials\n end\n else\n picture\n slot = \"avatar-image\"\n source = image\n alt = alt\n class = \"aspect-square size-full rounded-full object-cover\"\n end\n end\n ```\n\n- **Lists** render with `for each item in items`, whose first line is its `key = item.<field>`.\n- **Capabilities.** `use motion` if the component animates (`primitives/spinner.mz`). Nothing\n else needs `use`.\n- **Events.** Bind with `tap = on_tap` / `change = on_change`. A parameterless `fn` can `emit`\n an event prop, as in `examples/connectivity_bar.mz`:\n\n ```mz\n fn retry\n emit on_state_change(syncing)\n end\n ```\n\n- **No React carry-overs.** `...props` spreads are `MZ0106` (`exact` fix deletes the line).\n An `as_child` prop is warning `MZ0312`: delete it, and keep what the `as_child = false`\n branch renders. `className` pass-through has no equivalent.\n\n`benchmarks/prompts/mzizi-guide.md` is the complete one-file guide the benchmark gives a model,\nincluding records, `for each` and a React-to-Mzizi table. Read it before writing a component.\n\n## Contracts\n\nEvery component carries a `contract … end` block: one assertion per line,\n`<subject> <predicate>`, no colon. A file with no contract is warning `MZ0501`. It is checked\nagainst the file's own enums, view attributes and prop defaults (RFC-0006). From\n`primitives/button.mz`:\n\n```mz\n contract\n every button_size height at_least 48\n button_size.default height is 56\n button_size.sm height is 48\n button_variant.default class contains \"bg-primary\"\n slot is \"button\"\n end\n```\n\n| Subject | Means |\n| --------------------------- | -------------------------------------------------- |\n| `every <enum> <column>` | that column in every variant |\n| `<enum>.<variant> <column>` | one cell |\n| `<attribute>` | the root view element's attribute (`slot`, `role`) |\n| `<prop>` | the prop's default (only a prop that has one) |\n| `<element> \"<text>\"` | the element carrying that text |\n| `when <name>` | the `when` branch that mentions that name |\n\nPredicates: `is <value>` (exactly as written: `height is \"52\"` fails against `height 52`),\n`contains \"<s>\"`, `not_empty`, `at_least <n>`, `in \"<a>\" \"<b>\"`, `uses \"--token\"`,\n`uses <component>` (no subject), `shows <element> \"<t>\"` (after `when <name>`) and\n`min_height <n>`. `examples/connectivity_bar.mz` uses the last two:\n\n```mz\n when offline shows button \"Retry\"\n button \"Retry\" min_height 48\n```\n\nAssert what the source guarantees: every interactive size clears 48px, the `slot`, and the key\ncells. An assertion on a prop with no default is `MZ0605` (unevaluable), which fails.\n\n`mz contract` checks nothing rendered. The rendered half is `benchmarks/harness`, which diffs a\n`.mz` component's variants, defaults, touch heights and `data-slot` set against the Rust\nreference. Mzizi Roots components carry the same clause grammar as `pub const CONTRACT` and\ncheck it against their server-rendered markup (see the `mzizi-roots` skill).\n\nThe direction (RFC-0010, draft) is that **everything built carries a contract**: components,\nlanguage functions and backend handlers. Only the component form is evaluated today.\n\n## Diagnostic codes you will meet\n\nThe ranges are `MZ01xx` lexing and names, `MZ02xx` blocks and closers, `MZ03xx` declarations,\n`MZ04xx` the view, `MZ05xx` lints, `MZ06xx` contracts and `MZ07xx` types and resolution. The\nfull tables are RFC-0001 §4.7, RFC-0006 §8 and RFC-0008 (types).\n\n| Code | Severity | Written | Repair |\n| ------------------- | -------- | ----------------------------------------------------------- | ---------------------------------------------- |\n| `MZ0101` | error | a name that is not `snake_case` | `exact`: the snake form |\n| `MZ0105` | error | `list<T>`, `T[]`, `[T]` | `exact`: `list(T)` |\n| `MZ0106` | error | `...props`, `{...props}`, `..attributes` | `exact`: delete (the whole line when alone) |\n| `MZ0204` | error | a block never closed | `guess`: insert the `end`s |\n| `MZ0206`–`MZ0208` | error | a wrong closer (`end view`, a bare `end` for `component`) | `exact`: rewrite the whole closer line |\n| `MZ0303` | error | a variant missing a column another variant has | add the column |\n| `MZ0312` | warning | `prop as_child: bool` (React `asChild`) | `exact` when unread: delete the prop |\n| `MZ0313` | error | `icon class \"size-14\" height 48` | `exact`: the rendered px (`56`) |\n| `MZ0406` | error | `class \"flex\"` (attribute without `=`) | insert `=` (`exact` for known attribute words) |\n| `MZ0407` | error | `if open` | `exact`: `when open` |\n| `MZ0408` | error | an attribute directly under `view` | move it inside the root element |\n| `MZ0409` | error | `span class = \"x\"` (more after an element word) | put the element word on its own line |\n| `MZ0501` | warning | no `contract` block | write one |\n| `MZ0502` | warning | `<other>_enum.variant` from another component | none until modules |\n| `MZ0602` | error | a clause with no predicate (`height 52`) | `exact`: insert `is` (needs an operand) |\n| `MZ0603` | error | **an assertion that does not hold** | fix the component or the clause |\n| `MZ0605` | error | an assertion that cannot be evaluated | assert something the file defines |\n| `MZ0701` | error | an unknown type (`strng`) | alias or nearest-name fix |\n| `MZ0706` | error | a default that does not fit its type | varies |\n| `MZ0707` / `MZ0708` | error | an unknown name / variant, column or field | nearest-name fix |\n| `MZ0710` | error | an option used without `when … is none … else` | narrow it first |\n| `MZ0711` / `MZ0712` | error | a wrong kind of value / a `when` that does not fit its type | see `say` |\n\nTwo real outputs, from pilot-2 candidates written by the 7B model and re-checked on `main`\n(`benchmarks/results/2026-09-27-pilot-2/raw/scored/…/mzizi/{button,badge}/seed-1/iter-01/candidate.mz`):\n\n```text\n{\"code\":\"MZ0313\",\"severity\":\"error\",\"file\":\"candidate.mz\",\"span\":[16,35,16,37],\"say\":\"`icon` declares `height 48` but its class `size-14` renders 56px (N × 4) — write `height 56`, or drop the column and let the class say it once\",\"fix\":{\"span\":[16,35,16,37],\"replace\":\"56\",\"confidence\":\"exact\"}}\n{\"code\":\"MZ0106\",\"severity\":\"error\",\"file\":\"candidate.mz\",\"span\":[15,8,15,16],\"say\":\"`...props` is a spread, and Mzizi has none: a component names each prop it reads and each attribute it sets. `props` has no equivalent — delete the line\",\"fix\":{\"span\":[15,1,16,1],\"replace\":\"\",\"confidence\":\"exact\"}}\n```\n\n## Contributing to the language\n\n- A language change needs an RFC in `design/`. Take the next unused number (RFC-0005 is\n reserved; RFC-0011 is on a branch).\n- CI runs `cargo fmt -- --check`, `cargo clippy --all-targets -- -D warnings`, `cargo test`,\n and `mz check` and `mz contract` over every file in `examples/` and `primitives/`, one file\n per call. `AGENTS.md` in the repo has the exact script.\n- Pull requests are rebase-merged. State what you measured in the commit message.\n- The repo consumes nothing else in the org: no build step, test or script may reach into\n `mzizi-registry` or `agent-tools`.\n\n## See also\n\n- `CHARTER.md` (v0.3), `design/RFC-0001` (syntax and the agent protocol), `RFC-0006`\n (contracts), `RFC-0008` (types, collections, records), `RFC-0009` (the comparison benchmark\n and the kill criterion), `RFC-0010` (contracts everywhere, draft) and\n `benchmarks/READINESS.md`.\n- The `mzizi-roots` skill: the Rust components the language is benchmarked against, and where a\n lowering would target.",
|
|
14
|
+
"source": "mzizi-dev/agent-tools/mzizi-skills/skills/mzizi-language"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
|
-
"name": "
|
|
18
|
-
"description": "
|
|
19
|
-
"body_mdx": "# Discoverability — SEO + AIO for the Mzizi ecosystem\n\n> **Covenant:** _If the machine can't see it, it doesn't exist._\n\nDiscoverability is the machine-facing surface of every mzizi-ecosystem site — the\npart crawlers, social/messaging scrapers, and AI agents read. It is **cross-cutting**,\nnot a component you install: it is a genetic-code **convention** (\"every surface ships\na title, a description, a dynamic preview image, structured data, and an\nagent-readable index\") base-paired to the components that satisfy it (the OG template,\n`llms.txt`, JSON-LD helpers). A surface a machine can't parse may as well not exist —\nand a **missing social preview is the single biggest discoverability no-go**, because\nit's the first thing a human sees when a link is shared.\n\nAll values here are the current doctrine. The source of truth is the repo —\n`mzizi-dev/mzizi-registry` for doctrine and tokens, this package for the skills —\nserved read-only over the API at <https://api.mzizi.dev/v1> and the MCP at\n`mcp.mzizi.dev/mcp`. Supabase is not it: `mzizi-registry/docs/db-contents-rule.md`\n(2026-08-04) limits the database to version history, logging and the issue log.\nBrand colour specifically is `palette.canonical.json` in this package — 21 families,\ngenerated and gated, never retyped.\n\n## The number-one failure: a missing OG image\n\nThe most common — and most damaging — bug is a link that unfurls with **no preview\nimage at all**. The canonical cause is metadata that points at an image file that\ndoesn't exist:\n\n```ts\n// ❌ WRONG — points at a static file that isn't in /public, so every scraper 404s\nopenGraph: { images: [\"/og-image.png\"] }\ntwitter: { images: [\"/og-image.png\"] }\n```\n\nWorse, an explicit `images` array **overrides** a framework's dynamic OG route, so\nthe good image never gets a chance. **Rule: never hardcode `openGraph.images`.** Let\nthe framework's file-convention / endpoint serve a _generated_ image, and set an\nabsolute base URL so scrapers get an absolute URL.\n\n## The Mzizi \"hive\" OG card\n\nOne template, every route, every brand. The card is:\n\n- **Deep-night ground** (`#1B1A17`) with a faint flat-top **honeycomb hex lattice**\n (white at ~5% — texture, not noise) and a few solid **mineral accent cells**\n (cobalt / gold lead, plus tanzanite / malachite / copper for warmth).\n- The **site icon on top, above the title** (brand recognition first), then the\n title, an optional eyebrow, the SEO description, and the domain.\n- **Left-aligned** (reads better than centred at thumbnail size).\n- **Brand-agnostic:** the icon is a parameter, so the same template serves nyuchi\n (the bee), mukoko (the swarm), mzizi, shamwari — each with its own mark + domain.\n It picks up the _site's own icon_, never a hardcoded one.\n\n### Colours are APCA-checked\n\nOG text is read at thumbnail size, so contrast matters. Verified against the\n`#1B1A17` ground (APCA reverse polarity; design-system floor is **Lc 75 large /\nLc 90 body**):\n\n| Role | Hex | APCA Lc | Verdict |\n| -------------------------------- | --------- | ------- | ------- |\n| Title | `#F5F5F4` | 92 | ✓ body |\n| Secondary (eyebrow, description) | `#E5E3DE` | 81 | ✓ large |\n| Domain (gold) | `#FFD740` | 76 | ✓ large |\n\nDo **not** use dim greys like `#B2AFA8` (only Lc 50) for OG text — it looks fine on a\nmonitor and vanishes in a messaging thumbnail. Re-run APCA when changing any pair.\n\n## Implementation — Next.js (App Router)\n\n> Astro is the standard for new work — see the section below it, and the\n> `documentation-architecture-framework` doctrine. This section stays because\n> mzizi.dev still runs Next.js until that migration lands. Do not start a new\n> surface here.\n\nThe portal ships the reference template at `lib/og/mzizi-og.tsx`\n(`renderMziziOg({ title, eyebrow, description, iconPath, domain })`). Wire it via the\nfile convention so metadata stays dynamic:\n\n```tsx\n// app/opengraph-image.tsx (root) — and app/<section>/[slug]/opengraph-image.tsx\nimport { renderMziziOg, OG_SIZE, OG_CONTENT_TYPE } from \"@/lib/og/mzizi-og\"\nexport const runtime = \"nodejs\" // reads the icon PNG off disk\nexport const size = OG_SIZE\nexport const contentType = OG_CONTENT_TYPE\nexport default function Image() {\n return renderMziziOg({ title: \"mzizi\" }) // per-page: pass that page's title + description\n}\n```\n\n```ts\n// app/layout.tsx — set metadataBase, DO NOT set openGraph.images / twitter.images\nexport const metadata = {\n metadataBase: new URL(\"https://mzizi.dev\"),\n openGraph: { type: \"website\", siteName: \"Mzizi\", title: \"Mzizi\", description: \"…\" },\n twitter: { card: \"summary_large_image\", title: \"Mzizi\", description: \"…\" },\n // No `images` — the opengraph-image.tsx route supplies an absolute, hashed URL,\n // and the twitter card falls back to it.\n}\n```\n\nFor dynamic routes, add `opengraph-image.tsx` alongside `page.tsx` and pull the\nentity's title + description (e.g. from the registry) so the shared link unfurls with\n_that_ page's identity.\n\n## Implementation — Astro / Starlight (the standard)\n\nA statically-built site (deployed as static assets) generates OG images **at build\ntime**:\n\n1. A build-time image endpoint, `src/pages/og/[...slug].png.ts`, with\n `getStaticPaths()` over the docs collection; render each page's title +\n description with **satori** → SVG → rasterise with **sharp** → PNG.\n2. Override Starlight's `Head` component (`components.Head` in `astro.config`) to add\n `<meta property=\"og:image\" content={new URL(`/og/${slug}.png`, site)} >` per page.\n3. Use the same hive design: honeycomb + the site's icon + the brand mineral\n (nyuchi docs → **gold**).\n\nKeep the site's runtime dependency-light; satori + sharp are build-only.\n\n## Beyond the image — the rest of the convention\n\n- **Title + meta description** on every route (unique, page-specific).\n- **Structured data**: schema.org JSON-LD for the page's primary entity.\n- **`llms.txt`** at the site root — the machine-readable index for LLMs.\n- **`robots.txt` + `sitemap.xml`** — generated, not hand-maintained.\n- **Canonical URLs** absolute (a set `metadataBase` / `site`).\n- **Agent-readiness**: an MCP server-card at `/.well-known/mcp/server-card.json`\n where the surface exposes an MCP endpoint.\n\n## Pre-ship checklist\n\n- [ ] Sharing the URL in Slack / WhatsApp / iMessage / X renders a preview **image**.\n- [ ] The OG image is **generated** (dynamic), carries the page's title + description + the site icon, and its URL is **absolute**.\n- [ ] No hardcoded `openGraph.images` / `twitter.images`.\n- [ ] OG text colours pass APCA (Lc ≥ 75 large / ≥ 90 body).\n- [ ] `metadataBase` / `site` is set; canonical URL is absolute.\n- [ ] Title + meta description are unique per route.\n- [ ] `llms.txt`, `robots.txt`, `sitemap.xml` present; JSON-LD on primary entities.\n- [ ] Validate the card in a scraper (e.g. opengraph.xyz) after deploy.\n\n_The whole helix is Mzizi — and discoverability is the convention that makes each\nsurface legible to the machines that carry it. Ndiri nekuti tiri._",
|
|
20
|
-
"source": "mzizi-tools/mzizi-skills/skills/
|
|
17
|
+
"name": "mzizi-roots",
|
|
18
|
+
"description": "Everything about Mzizi's components: Mzizi Roots, the Rust build (Dioxus UI and host-agnostic server components). cargo add mzizi-roots / mzizi-roots-server, what mzizi_get_component returns (Rust first: lead, rust, react), the node-to-crate map, pub const CONTRACT checked on dioxus-ssr markup, the two frontend paths (Astro with Roots, or pure Rust) and app setup, reuse before build, and adding a component to the registry (the node, the .rs and .tsx, registry.json, per-node checklists, the 48px floor, pnpm check and the cargo gates). The React/TSX build is the deprioritised secondary. Replaces scaffold-component, simplify and half of ecosystem-app-setup.",
|
|
19
|
+
"body_mdx": "# Mzizi Roots — the Rust build of Mzizi's components\n\n**Mzizi Roots** is Mzizi's own components in Rust: UI components in Dioxus 0.7, and server\ncomponents as host-agnostic Rust. They live in\n[`mzizi-dev/mzizi-registry`](https://github.com/mzizi-dev/mzizi-registry) and are compiled by\nthe crates in its `mzizi-rs/` workspace. The plan is `docs/roots/RFC-roots.md` there.\n\n**Rust first.** Where a Rust implementation exists, present it and use it first. The React/TSX\ncomponents are **the React build**: they keep working for existing React apps, and they are\ndeprioritised, so no new work goes into them.\n\nThis skill describes registry `main` at `6663cb5` (2026-09-29), the crates published to\ncrates.io at **0.1.0** on 2026-09-29, and `mzizi-mcp` 0.11.0. It replaces three 0.7.0 skills:\n`scaffold-component` (now \"Contributing a component\"), `simplify` (now \"Reuse before build\")\nand the frontend half of `ecosystem-app-setup` (now \"Starting an app\"). Tokens, brand and the\nUbuntu constraints are in the `mzizi-design` skill.\n\n## Install\n\nTwo umbrella crates with no code of their own re-export the node crates behind features:\n\n```sh\ncargo add mzizi-roots # UI: tokens + ui + brand + shell (all default features)\ncargo add mzizi-roots-server # server: assurance + fundi + docs + discovery\n```\n\nTake only what you use:\n\n```toml\n[dependencies]\nmzizi-roots = { version = \"0.1\", default-features = false, features = [\"brand\"] }\n```\n\nTo follow `main`, or pin a commit, install from git. Cargo finds a crate by name anywhere in the\nrepository's workspace:\n\n```sh\ncargo add mzizi-roots --git https://github.com/mzizi-dev/mzizi-registry\n```\n\n```toml\nmzizi-brand = { git = \"https://github.com/mzizi-dev/mzizi-registry\", rev = \"<commit>\" }\n```\n\nA single node crate works too (`cargo add mzizi-ui`). The crates depend on `dioxus` with\n`macro`, `signals`, `hooks` and `html` only, so the app that mounts them chooses web, desktop,\nmobile or server rendering.\n\n`mzizi add <name> --target rust` (`@nyuchi/mzizi-cli`) records the crate and prints the\n`cargo add` rather than copying a `.rs` file into your project: a copied component is a fork\nof a crate module.\n\n## What the MCP returns\n\n`mzizi_get_component` on `https://mcp.mzizi.dev/mcp` (free, no account; `mzizi-mcp` 0.11.0) is\nRust first. It returns `{name, renamedFrom?, lead, note, rust, react, docs?, versions?}`:\n\n- `lead` is `\"rust\"` when the component has a Roots implementation, else `\"react\"`; `note`\n says which build to use.\n- `rust` is the registry's `/v1/rs/<name>` answer with the install first: `crate`\n (`{name, registry, git}`), `install.cargo` (`cargo add <crate>`), `install.pinned`\n (`cargo add <crate> --git https://github.com/mzizi-dev/mzizi-registry --rev <pin>`, which\n builds exactly the source shown), `module` (e.g. `mzizi_brand`), `contract` (the\n `contract … end` text, or `null`), and `files[]` with the `.rs` source inline. With no Rust\n it is `null`.\n- `react` is the React build: `build: \"react\"`, `status: \"deprioritised\"`, `install` (the\n `npx shadcn@latest add https://api.mzizi.dev/v1/ui/<name>` line), then the `/v1/ui/<name>`\n body. It is no longer under a `component` key (that was 0.10.x).\n\n`mzizi_list_components` rows carry `rustCrate` when a component has Rust, the response carries\n`withRust`, and `rust: true` lists only those. `mzizi_search` hits carry `rustCrate` too. For\n`mzizi-hero-stat` the answer is `lead: \"rust\"`, crate `mzizi-brand`, and the four-clause\ncontract shown under \"Contracts\" below.\n\n## The crates\n\nOne crate per registry node that has Rust. The map is `mzizi-rs/crate-for-node.json`, and\n`pnpm rust:generate` refuses a node with Rust in it that no crate claims.\n\n| Node directory | Crate | Umbrella | What it holds |\n| ------------------- | ----------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `n1-tokens` | `mzizi-tokens` | `mzizi-roots` (always on) | the 21-family palette and scales as Rust consts, generated from `lib/tokens/palette.source.ts` |\n| `n2-primitives` | `mzizi-ui` | `mzizi-roots` (`ui`) | avatar, badge, button, card, chart, input, label, progress, separator |\n| `n3-brand` | `mzizi-brand` | `mzizi-roots` (`brand`) | alert banner, avatar stack, cover header, empty state, escalation card, gauge card, hero stat, meta tile, stats row, success screen, suitability card, user card (all `mzizi-*`) |\n| `n7-shell` | `mzizi-shell` | `mzizi-roots` (`shell`) | bottom nav, command palette, connectivity bar, deep-link handler, footer, mini-app runtime, notification center, persistent player, route guard, theme provider, toast provider, update prompt |\n| `n8-assurance` | `mzizi-assurance` | `mzizi-roots-server` (`assurance`) | probes, telemetry, the OTLP exporter, a11y and RTL conformity checks, alert and incident engines |\n| `n9-fundi` | `mzizi-fundi` | `mzizi-roots-server` (`fundi`) | the Fundi reporter and learning loop (client side) |\n| `n10-documentation` | `mzizi-docs` | `mzizi-roots-server` (`docs`) | the docs API and routing, AI context, the changelog and docs renderers (brings in `dioxus`) |\n| `n11-discovery` | `mzizi-discovery` | `mzizi-roots-server` (`discovery`) | page metadata and Schema.org JSON-LD |\n\nN4 safety, N5 resilience and N6 pages have no crate yet. When they do (`mzizi-safety`,\n`mzizi-resilience`, `mzizi-pages`), they join `mzizi-roots` as features. A Workers adapter\n(`mzizi-worker`) is planned for `mzizi-roots-server` and has not landed.\n\nThe registry has 577 items and 55 `.rs` files. Cite `GET https://api.mzizi.dev/v1/ui` or\n`/v1/stats` for a current count rather than repeating these. To find a component's crate, read\n`rust.crate` from `mzizi_get_component`, or take its node directory and read the table above.\n`https://api.mzizi.dev/v1/rs/<name>` also serves a `crate` field, derived from the same map on\nregistry `main`; the API serves the registry at a pinned commit, and until the gateway's pin\nmoves past the map it names `mzizi-ui` for every component. Trust the map, or the MCP.\n\n## Using a UI component\n\nThis compiles against `mzizi-roots` 0.1.0 from crates.io and renders the component to a static\nHTML fragment with `dioxus-ssr`:\n\n```toml\n[dependencies]\nmzizi-roots = { version = \"0.1\", default-features = false, features = [\"brand\"] }\ndioxus = { version = \"0.7\", default-features = false, features = [\"macro\", \"signals\", \"hooks\", \"html\"] }\ndioxus-ssr = \"0.7\"\n```\n\n```rust\nuse dioxus::prelude::*;\nuse mzizi_roots::brand::{AlertSeverity, MziziAlertBanner};\n\nfn banner() -> Element {\n rsx! {\n MziziAlertBanner {\n r#type: \"Thunderstorm\",\n severity: AlertSeverity::Severe,\n headline: \"Severe storms this afternoon\",\n }\n }\n}\n\nfn main() {\n let mut dom = VirtualDom::new(banner);\n dom.rebuild_in_place();\n let html = dioxus_ssr::render(&dom); // a static HTML fragment for Astro to place\n println!(\"{html}\");\n}\n```\n\nThe output is a `<div data-slot=\"mzizi-alert-banner\" data-portal=\"https://mzizi.dev/components/mzizi-alert-banner\" role=\"alert\" …>`\nwith the same Tailwind classes and CSS custom properties as the React build, so one stylesheet\nstyles both.\n\nWhat a Roots UI component looks like:\n\n- **Same contract as the React build:** the same registry name, `data-slot`, `data-portal`,\n variant names and Tailwind classes over the same `var(--…)` tokens. No colour literals.\n- **Variants are enums**, and a `match` replaces `class-variance-authority`\n (`n2-primitives/badge.rs`, `button.rs`).\n- **Browser facilities are props.** Where the `.tsx` reads `useMziziHarness`, the Rust takes the\n one bit it branches on, usually `prefers_reduced_motion: bool`. Logging is the host's.\n- **Icons are slots:** an `Element` the host renders, where the `.tsx` takes a Lucide component.\n- **Each file is self-contained.** No helpers shared between components.\n\n## The two frontend paths\n\nThe frontend is either **Astro with Roots underneath** or **pure Rust end to end**.\n\n- **Pure Rust.** A Dioxus app depends on the crates and mounts the components on web (wasm),\n desktop or mobile. Every component supports this today.\n- **Astro, static.** Render with `dioxus-ssr` at build time or in a Worker, and let Astro place\n the fragment, as in the example above. It ships no JavaScript and suits display components.\n The brand crate's contract suite does this for every component on every run.\n- **Astro, interactive islands.** Compile to wasm with Dioxus's web renderer and wrap it as a\n custom element. **Not packaged yet**: the wrapper crate (`mzizi-islands`) is proposed for\n batch 2 of the RFC.\n\nAstro has no `.astro` components to install. `mzizi add` refuses an Astro target and names the\ngap. There is no React, Svelte or Vue layer under Astro.\n\n## Starting an app\n\nFor a new Mzizi-ecosystem app, or any app that consumes the design system. Pick the path\nfirst (above); these steps are the Astro one, with the pure-Rust differences noted.\n\n### 1. Create the project (Astro)\n\n```bash\npnpm create astro@latest my-app -- --template minimal --typescript strict\ncd my-app\npnpm astro add tailwind cloudflare\n```\n\nAstro renders HTML by default, which is what makes a new app indexable by N11 and paintable on\na low-end device without opting in to either. For a pure-Rust app, start from\n`cargo new my-app` and a Dioxus 0.7 app instead, and skip to step 3.\n\n### 2. Wire the tokens\n\nCopy the generated stylesheet rather than retyping hexes: `skills/mzizi-design/tokens/palette.css`\nin `@nyuchi/mzizi-skills` (all 21 families, light and dark, generated from\n`palette.canonical.json` at the package root). Import it from your global stylesheet (in Astro,\n`src/styles/global.css`) and register the properties in the Tailwind 4 `@theme` block so\nutilities generate. In Rust the same palette is the `mzizi-tokens` crate, always on in\n`mzizi-roots`. How the 21 families are meant to be used, the radius scale and the 48px floor\nare in the `mzizi-design` skill.\n\nPick the app's accent through `--brand-accent` rather than hardcoding a mineral hex: nyuchi =\ngold, mukoko = tanzanite.\n\n### 3. Add the Roots components\n\n```bash\ncargo add mzizi-roots # tokens, primitives, brand components, app shell\n```\n\nFor Astro, a small Rust crate (a build step, or a `workers-rs` Worker for request-time\nrendering) renders each component to HTML with `dioxus-ssr`, and Astro places the fragment.\nThe fragment carries the same Tailwind classes as the React build, so the stylesheet from step 2\nstyles it, and it ships no JavaScript. Interactive islands (Roots compiled to wasm and wrapped\nas custom elements) are proposed and not packaged yet. \"Using a UI component\" above has a\ncompiling example.\n\nNever copy component source between apps. Depend on the crate, so every app inherits fixes.\n\n### 4. Give the app's AI assistants the doctrine\n\nInstall the published skills bundle so any assistant working in the repo has the design system,\nthe language and the components on hand:\n\n```bash\nnpm install -D @nyuchi/mzizi-skills\nnpx skills experimental_sync # links the bundled skills into .agents/skills/\n```\n\n`npx skills add @nyuchi/mzizi-skills` does **not** work: the `skills` CLI reads that argument\nas a GitHub repository and fails to clone it. `experimental_sync` is the CLI's route for skills\nthat ship in `node_modules`, and it is marked experimental. The same skills are served with no\ninstall at all by the MCP's `mzizi_get_skills`.\n\nThe public Claude Code plugin installs these skills and the MCP in one step:\n\n```\n/plugin marketplace add mzizi-dev/mzizi-registry\n/plugin install mzizi@mzizi\n```\n\nTo wire the MCP directly instead, add it to `.mcp.json` or your client config. It is free with\nno account; only the Fundi tools need sign-in:\n\n```json\n{\n \"mcpServers\": {\n \"mzizi\": { \"type\": \"http\", \"url\": \"https://mcp.mzizi.dev/mcp\" }\n }\n}\n```\n\n### 5. Let fundi read the project\n\n`@nyuchi/mzizi-cli` ships the **fundi** agent, which reads the project off disk and plans the\nwiring against the registry:\n\n```bash\npnpm add -D @nyuchi/mzizi-cli\npnpm exec fundi explore # offline — snapshot the project\npnpm exec fundi plan \"wire up Mzizi tokens\"\n```\n\nRun the bins from the installed package (`pnpm exec`, or `npx` inside the project). A bare\n`npx fundi` outside it fetches an unrelated npm package called `fundi`. `explore` runs without\ncredentials; `plan` and `chat` need `ANTHROPIC_API_KEY`. A plan is\ninert until you apply it, so read it before letting it write.\n\n### 6. Verify\n\n```bash\npnpm dev\n```\n\nConfirm the tokens resolve in light and dark, a rendered Roots fragment picks up the styles,\nand the page is readable with JavaScript off.\n\n### 7. An existing React app\n\nIf the app is already React, the React build installs with the CLI or shadcn, by absolute URL:\n\n```bash\npnpm add -D @nyuchi/mzizi-cli && pnpm exec mzizi add button # infers the tsx target\nnpx shadcn@latest add https://api.mzizi.dev/v1/ui/mzizi-theme-provider\n```\n\nPoint `components.json` `registries.mzizi.url` at `https://api.mzizi.dev/v1/ui`. A bare name\nresolves against shadcn's own registry and installs a different component. Do not start a new\napp this way.\n\n## Server components\n\nA Roots server component is a **sans-IO core**: typed request in, typed response out, every\nexternal facility (a store, a clock, an outbound fetch, a secret) behind a trait. The host owns\nI/O and credentials. `n10-documentation/mzizi-docs-api.rs` is the model: it parses a path into a\ntyped `Route`, asks a `DocsStore` trait for data and returns an `ApiResponse`. `mzizi-assurance`,\n`mzizi-fundi` and `mzizi-discovery` have no dependencies at all. The RFC records a passing\n`wasm32-unknown-unknown` `cargo check` for those three and `mzizi-docs`; CI does not run a\nwasm32 job yet.\n\nA host adapter mounts the core: a `workers-rs` adapter for Cloudflare Workers, an axum adapter\nfor containers. Neither adapter crate exists yet. Until then the host writes the few lines that\nturn its request into the core's request (see the `mzizi-backend` skill). Dioxus\n`#[server]` functions are one more possible adapter, not the building block.\n\n## Contracts\n\nEvery Roots component carries a contract, in two parts:\n\n1. **The registry contract:** one registry name, description, dependencies and variants shared\n by both builds, and the same `data-slot`, `data-portal` and tokens. Each crate's\n `tests/contract.rs` reads the `.tsx` sibling and asserts that the slot, the portal and every\n variant value the Rust emits appear there too. A deliberate divergence, such as a touch\n target raised to the 48px floor, is asserted from both sides, so the test fails when the\n React build is fixed.\n2. **Checkable clauses** in the Mzizi language's `contract … end` grammar (RFC-0006; the\n `mzizi-language` skill), exported as `pub const CONTRACT: &str`. From\n `n3-brand/mzizi-hero-stat.rs`:\n\n ```rust\n pub const CONTRACT: &str = r#\"contract\n slot is \"mzizi-hero-stat\"\n role is \"region\"\n label not_empty\n button \"Share\" min_height 48\n end\"#;\n ```\n\n `mzizi-brand`'s contract suite renders each component with `dioxus-ssr` in named states and\n evaluates every clause against the markup it emits. **A clause it cannot evaluate fails.**\n `mzizi_roots::brand::CONTRACTS` lists every brand component with its contract.\n\nToday the twelve `mzizi-brand` components export `CONTRACT`. The other crates check the\nregistry contract against the `.tsx`, and their `CONTRACT` clauses arrive batch by batch.\nServer-component clauses (status per route, CORS and cache headers, no credential in the core)\nwait on the language's contracts-everywhere RFC. Until then their tests assert those properties\nagainst an in-memory implementation of the traits. When the language lowers to Rust, `mz\ncontract` reads the same text; it does not yet.\n\n## Reuse before build\n\n**The simplest change is the one you don't write.** Reuse before you build, compose before you\nimplement, and let one source own each decision. Use this before adding a component, a style or\na page, and during any refactor.\n\n1. **Reuse before build.** Search the registry first: `mzizi_search` or\n `mzizi_list_components` over the MCP (`https://mcp.mzizi.dev/mcp`, free), or\n `GET https://api.mzizi.dev/v1/ui`. Where a Rust implementation exists, depend on its crate\n (`cargo add mzizi-roots`). For the React build, install with\n `mzizi add <name>` or `npx shadcn@latest add https://api.mzizi.dev/v1/ui/<name>`. Never\n reimplement a primitive that already exists.\n2. **Styles are tokens.** Never hardcode a colour, radius, spacing, or motion value —\n consume `var(--…)` (or the `mzizi-tokens` consts in Rust). N1 is the only layer allowed to\n define values, so a rebrand is one edit. A new colour goes in the registry's\n `lib/tokens/palette.source.ts`, and `pnpm tokens:sync` regenerates `globals.css`, the Rust\n tokens and the other emitters from it.\n3. **Compose, don't implement.** A page (N6) is a composition of N2 primitives and N3 brand\n components — never inline a `<button>`, a card, or an SVG. Pull spinners, skeletons, and\n badges from N2.\n4. **Variants over copies.** Collapse near-duplicate components into one variant table: an\n enum with a `match` in Rust, an `enum` table in Mzizi source, one `cva()` in the React\n build. Express differences as `variant` / `size` props, not new files.\n5. **One composition path.** In the React build, compose class names through `cn()` and\n extract shared logic into `lib/` or a hook. In Rust, a pure function beside the component.\n No logic duplicated across files.\n6. **Right altitude.** Keep each component self-contained, importing only from the layer\n below on its strand. A primitive that reaches up into page code can't be installed on its\n own.\n7. **Delete first.** Prefer removing over adding — dead code, unused props, redundant\n wrappers, commented-out blocks. Less code is less to break, read, and keep true.\n8. **Live data, not copies.** Read counts, tokens and doctrine from their source\n (`https://api.mzizi.dev/v1/stats`, `palette.canonical.json`, `mzizi_get_tokens`,\n `mzizi_get_doctrine`). A baked-in number or duplicated token guarantees drift.\n\n### Checklist\n\n- [ ] Searched the registry before building, and used the Rust build where one exists?\n- [ ] All styles are tokens (`var(--…)`), zero hardcoded values outside N1?\n- [ ] Page is pure composition of N2/N3, no inlined primitives?\n- [ ] Variation expressed as one variant table, not near-duplicate files?\n- [ ] One composition path, shared logic extracted once?\n- [ ] Each file self-contained and installable, importing only downward?\n- [ ] Deleted more than you added where you could?\n- [ ] Counts, tokens, and doctrine read live, not hardcoded?\n\n## Contributing a component\n\n**A component is a real file.** `components/registry/n<N>-<label>/<name>.rs` (Rust, compiled by\nthe node's crate in `mzizi-rs/`) and, for the React build, `<name>.tsx` beside it, which the\nNext.js build compiles and typechecks. An error in either fails CI.\n\n| File | What it is |\n| --------------------------------------------- | ----------------------------------------------------------------- |\n| `components/registry/n<N>-<label>/<name>.rs` | the Roots component (Dioxus UI, or a sans-IO server core) |\n| `components/registry/n<N>-<label>/<name>.tsx` | the React build of the same contract |\n| `registry.json` | the manifest `/v1/ui/<name>` and `npx shadcn add` resolve against |\n\n`registry.json` carries the install contract (description, dependencies,\n`registryDependencies`, target paths) and **never the source**. The node is derived from the\ndirectory the component lives in, so it cannot disagree with where the file is.\n\n**There is no database.** The registry's files are its data layer (mzizi-registry PR #368), and\na test fails the build if a Supabase import or a `SUPABASE_*` read comes back. Nothing about a\ncomponent, including its source, status or history, is written to a table. If you find\ninstructions anywhere telling you to `INSERT` a component or read a `component_documents` row,\nthey are stale.\n\n### Step 1 — Decide the node\n\nThe architecture is the **Mzizi DNA double helix**: eight nodes (N1–N8) on the engineering\nbackbone, four rungs (N9–N12) that bridge both backbones, and six strands. The N-numbers are\n**labels, not a sequence**.\n\nThe node decides the **directory**, the directory decides the **crate** that compiles the Rust\n(`mzizi-rs/crate-for-node.json`), and the directory is what the build reads. Choosing the node\nand choosing where the file goes are the same decision.\n\n| Node | Directory | Crate | What belongs there |\n| ----------------- | -------------------- | ----------------- | --------------------------------------------------------------------------- |\n| N1 tokens | `n1-tokens/` | `mzizi-tokens` | A token value or emitter (colour, spacing, motion, radius), not a component |\n| N2 primitive | `n2-primitives/` | `mzizi-ui` | Generic UI: button, card, input, dialog, toolbar, bento-grid |\n| N3 brand | `n3-brand/` | `mzizi-brand` | A branded composition with the mineral palette |\n| N4 safety | `n4-safety/` | none yet | A conditional-rendering gate: permission, geo, rate limit |\n| N5 resilience | `n5-resilience/` | none yet | Error boundary, skeleton, offline banner, fallback chain |\n| N6 pages | `n6-pages/` | none yet | Full-screen layout composition |\n| N7 shell | `n7-shell/` | `mzizi-shell` | App container: navigation, routing, lifecycle |\n| N8 assurance | `n8-assurance/` | `mzizi-assurance` | Instrumentation, a11y audit, RTL conformity, probes |\n| N9 fundi | `n9-fundi/` | `mzizi-fundi` | Automated healing (a rung) |\n| N10 documentation | `n10-documentation/` | `mzizi-docs` | Docs, AI instructions, docs infrastructure (a rung) |\n| N11 discovery | `n11-discovery/` | `mzizi-discovery` | SEO / AIO surfaces: if the machine can't see it, it doesn't exist (a rung) |\n| N12 skills | none in the registry | none | Agent skills (a rung), authored in `mzizi-dev/agent-tools/mzizi-skills/` |\n\nA node with no crate yet (N4, N5, N6) takes a `.tsx` today. Adding its first `.rs` means adding\nits crate and its `crate-for-node.json` entry in the same pull request; `pnpm rust:generate`\nrefuses Rust that no crate claims.\n\nIf you are unsure where something belongs, ask the MCP (`https://mcp.mzizi.dev/mcp`, free, no\naccount): `mzizi_list_components` with `node: N` shows what already lives in a node, and\n`mzizi_get_architecture` returns the node, rung and strand model with live counts. Never\nhardcode counts.\n\n### Step 2 — Write the file\n\nCreate it in the directory the node names:\n\n```\ncomponents/registry/n3-brand/mzizi-your-component.rs # the Roots component\ncomponents/registry/n3-brand/mzizi-your-component.tsx # the React build, same contract\n```\n\nThe Rust file carries `pub const CONTRACT: &str` and follows the rules in \"Using a UI component\" above: variants as enums, tokens by reference, browser facilities as props, icons as slots,\nno helpers shared between components. `.ts` is for a non-rendering module.\n\nThat is the authoring step. There is no row to insert and no registration to perform before\nwriting code: the file _is_ the component, and `cargo test` (for the `.rs`), `pnpm typecheck`\nand `pnpm build` (for the `.tsx`) are what tell you it is real.\n\nNaming: N3 brand components take a `mzizi-` prefix. (Components formerly named `nyuchi-*` are\n`mzizi-*`, and the old names redirect.) Everything else is the bare name, matching what a\nconsumer will type in `mzizi add` or `npx shadcn add`.\n\n### Wiring the Rust into its crate\n\n`pnpm rust:generate` writes the committed copy to `mzizi-rs/crates/<crate>/src/generated/`. Add\nthe `#[path = \"generated/<name>.rs\"] pub mod <name_snake>;` line and the `pub use` to the\ncrate's `src/lib.rs` by hand, and add the component to the crate's `tests/contract.rs`. Edit\nthe registry file, never the copy. The workspace lints forbid `unsafe_code` and warn on\n`missing_docs`; clippy turns the warning into a failure. Cap parallel builds on a small machine\n(`CARGO_BUILD_JOBS=2`).\n\nA release is a version bump in `mzizi-rs/Cargo.toml`. On merge to `main`,\n`.github/workflows/publish-crates.yml` re-runs the Rust gate and publishes each crate in\ndependency order, skipping any version already on crates.io. A published crate name is\npermanent. Say in the PR body that components changed, so the site, docs and skills are\nupdated to match.\n\n### Step 3 — Declare it in `registry.json`\n\nOne item, carrying the install contract. `meta` is the part you author; four other fields are\n**derived from it** and must not be hand-written.\n\n```jsonc\n{\n \"name\": \"your-component-name\",\n \"type\": \"registry:ui\",\n \"description\": \"Short description of what it does.\",\n \"dependencies\": [],\n \"registryDependencies\": [],\n \"files\": [\n {\n \"path\": \"components/registry/n2-primitives/your-component-name.tsx\",\n \"target\": \"components/ui/your-component-name.tsx\",\n \"type\": \"registry:ui\"\n }\n ],\n \"meta\": {\n \"collection\": \"primitives\",\n \"owner\": \"mzizi\",\n \"useCases\": [\"Use case 1\", \"Use case 2\"],\n \"variants\": [\"default\", \"sm\", \"lg\"],\n \"sizes\": [\"default\", \"sm\", \"lg\"],\n \"features\": [\"cn() for className composition\", \"data-slot attribute\"],\n \"a11y\": [\"ARIA attributes\", \"Keyboard accessible\"],\n \"hasDemo\": true\n }\n}\n```\n\n`files[]` lists the `.tsx`: it is the shadcn install contract for the React build. The `.rs`\nbeside it is not listed; the node's crate compiles it, and `/v1/rs/<name>` serves it with the\ncrate's name.\n\n**Do not write `title`, `categories`, `docs` or `author`.** `pnpm registry:metadata` derives all\nfour from `meta` and the repo. `docs` in particular is what the shadcn CLI prints _after_\ninstalling, so it is generated from your `useCases` / `variants` / `sizes` / `features` / `a11y`\nrather than duplicated by hand — and an item with no `meta` deliberately gets no `docs`, because\na stub reads as documentation and says nothing.\n\nThen run the generators and let the gates check you:\n\n```bash\npnpm registry:paths # fill file paths from what is on disk\npnpm registry:metadata # derive title, categories, docs, author\npnpm registry:normalize # canonical ordering\npnpm registry:validate # every item resolves on disk; every dependency addressable\npnpm registry:verify # the CI drift gate\npnpm typecheck && pnpm build\n```\n\nFollow the criteria for the chosen node.\n\n#### Every node: the Rust file\n\n- `pub const CONTRACT: &str` with the clauses the rendered markup must meet (slot, role, touch\n floors, variant tables), evaluated by the crate's `tests/contract.rs` on `dioxus-ssr` output\n- The same `data-slot`, `data-portal` and variant values as the `.tsx`, asserted by the same\n suite; a deliberate divergence is asserted from both sides\n- `prefers_reduced_motion: bool` as a prop where the `.tsx` reads the harness's motion\n- Icons as `Element` slots, not an icon crate\n- Doc comments on every public item (`missing_docs` is a warning, and clippy runs with\n `-D warnings`); no `unsafe`\n\n#### N2 primitive checklist\n\n- No `useMziziHarness` import in the `.tsx`\n- `data-slot` attribute on the root element\n- `data-portal` attribute pointing at `https://mzizi.dev/components/<name>`\n- `cn()` for className composition in the `.tsx`\n- No raw Tailwind colours: use semantic tokens (`bg-primary`, `text-foreground`, …)\n- Icons from `@/lib/icons` in the `.tsx`, never `lucide-react` directly\n- **Touch targets never go below 48px.** Buttons and inputs are `h-14` (56px) default and\n `h-12` (48px) small, as `button.tsx`, `button.rs` and `primitives/button.mz` all state, and\n `/v1/brand` `.componentSpecs` serves (`minTouchTarget: 48`). A smaller visual box, such as a\n badge, is not a touch target; if it becomes one, give it `min-h-[48px]`.\n- Pill-shaped categories (button, input, avatar, badge, toggle) use `rounded-full`\n\n#### N3 brand checklist\n\n- `mzizi-` prefix on the name\n- `useMziziHarness(\"<name>\")` in the `.tsx` for the motion preference (and `log` or\n `LiveRegion` where the component uses them); `prefers_reduced_motion` as a prop in the `.rs`\n- The entrance animation off when motion is reduced\n- An ARIA role or `aria-label`, and a region that is named in every state, the loading\n skeleton included\n- `data-slot` and `data-portal` attributes, in every state\n- `focus-visible` ring on interactive elements, and interactive elements at the 48px floor\n- I18N via `Intl` formatters for dates, numbers and currency\n- Semantic tokens for status-bearing colours\n\n#### N6 page checklist\n\n- Pure composition: no inline buttons, cards or SVGs\n- Accept children/slots for content\n- Semantic CSS vars only (`bg-card`, `text-foreground`, `bg-primary`)\n- A loading-state prop\n- `role=\"main\"` and an `aria-label`\n\n#### N8 assurance checklist\n\n- A sans-IO Rust core (typed input, typed result, every facility behind a trait) in\n `mzizi-assurance`, with the `.ts` module as the React build\n- A banner comment identifying the node and purpose\n- A configurable rules array\n- Optional `onViolation` / `onComplete` callbacks\n\n### Step 4 — Docs and demos\n\nThere is nothing extra to write. Docs come from the `meta` you already authored in Step 3\n(`useCases`, `variants`, `sizes`, `features`, `a11y`), and `pnpm registry:metadata` turns them\ninto the `docs` string the CLI prints after install. Set `meta.hasDemo` when a demo exists.\n\nIf a prop table is expected, `pnpm props:extract` derives it from the component's own types and\n`pnpm props:verify` is the gate. Props are read from the code, never restated beside it.\n\n### Step 5 — Versions and history\n\nThere is no status row and no database. A component's version history is the registry's\nchangelog (`content/changelog/`, generated, checked by `pnpm changelog:generate:check`) and the\nrename map. `mzizi_get_component` with `include: [\"versions\"]` returns it. Read what a component\nis through the MCP or `https://api.mzizi.dev/v1/ui/<name>` rather than assuming.\n\n### Step 6 — Validate accessibility\n\nIf your component introduces a new colour pair, validate it before shipping. The MCP tool is\ncomputed locally and needs no account:\n\n```\nmzizi_check_accessibility(foreground: \"#FFFFFF\", background: \"#0047AB\")\n```\n\nIt returns the WCAG 2.1 contrast ratio with AA/AAA verdicts and whether the pair clears the\nMzizi floor (APCA and AAA).\n\n### Step 7 — Before you open the PR\n\n`pnpm check` runs every registry gate CI runs, in order, including `registry:validate`,\n`registry:verify`, `tokens:verify`, `changelog:generate:check`, `rust:generate:check`, the\ntests and the build. Then the Rust workspace gate:\n\n```bash\npnpm check\ncargo fmt --manifest-path mzizi-rs/Cargo.toml --all -- --check\ncargo clippy --manifest-path mzizi-rs/Cargo.toml --workspace --all-targets -- -D warnings\ncargo test --manifest-path mzizi-rs/Cargo.toml --workspace\ncargo package --manifest-path mzizi-rs/Cargo.toml --workspace\n```\n\nIf you touched `files[]` or `meta`, run `pnpm registry:paths`, `pnpm registry:metadata` and\n`pnpm registry:normalize` first; their `--check` twins fail otherwise.\n\nA green run means the item resolves to real files, every dependency is addressable, the derived\nfields match `meta`, the Rust compiles, and its contract holds on rendered markup. Say in the PR\nbody that a component changed, so the site, the docs and the skills are updated to match.\n\n### What to avoid\n\n- **Never put component source in a database row, a JSON document, or any second place.** The\n file under `components/registry/` is the component; everything else is a representation of\n it, and a representation can drift from the code while still looking correct. That is not\n hypothetical here: a `source_code` column once served stale copies for 571 components.\n- Never edit `mzizi-rs/crates/*/src/generated/`. It is overwritten by `pnpm rust:generate`.\n- Never hand-write `title`, `categories`, `docs` or `author` in `registry.json`. They are\n derived, and a hand-written value is a second source that the next generator run silently\n overwrites.\n- Never hardcode hex colour values (except documented third-party brands like Ethereum,\n Google, EcoCash).\n- Never use `lucide-react` directly in a `.tsx`; go through `@/lib/icons`.\n- Never use `margin-left` / `padding-right` / `left`; use logical properties\n (`margin-inline-start`, …) for RTL support.\n- Never start new work in the React build alone. If a component has a Rust implementation,\n change the Rust and keep the `.tsx` in step.\n- Never bump a major version on your own initiative. That is a maintainers' call.\n\n## The React build\n\n`npx shadcn@latest add https://api.mzizi.dev/v1/ui/<name>` installs the `.tsx` for an existing\nReact app, for all 577 items. Always pass the absolute URL: a bare name resolves against\nshadcn's own registry and installs a different component. `mzizi add <name>` installs the React\nbuild too. Neither is where new work goes.\n\n## See also\n\n- `docs/roots/RFC-roots.md` in the registry: the inventory, the conversion batches and what\n the API and site still need for Rust-first serving (`/v1/roots` is proposed, not live).\n- The `mzizi-language` skill for the clause grammar and `mz contract`.\n- The `mzizi-design` skill for the palette, radius, type and the Ubuntu constraints.\n- The `mzizi-backend` skill for mounting a server core on Workers.",
|
|
20
|
+
"source": "mzizi-dev/agent-tools/mzizi-skills/skills/mzizi-roots"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"name": "mzizi-design",
|
|
24
|
-
"description": "
|
|
25
|
-
"body_mdx": "# Mzizi — _The Ecosystem_\n\n**Mzizi** is the parent identity at the ecosystem level — the framework, the design language, the\ncomponent registry and the open doctrine every brand builds on. Everything revenue-generating (the\nMzizi console, fundi, paid plans, billing) is Nyuchi's. Three pillars: **mukoko** (consumer super app), **nyuchi** (infrastructure &\nenterprise), and the **sister brands** (specialist verticals) — one identity, one design system, one\nopen data commons.\n\nDoctrine **v4.1.0**. **The repo is the source of truth, not the database.** Colour comes from\n`palette.canonical.json` at the root of this package — 21 families, vendored from\n`mzizi-dev/mzizi-registry → lib/tokens/palette.source.ts` and gated against it. The ecosystem\nconstellation and the Ubuntu pillars/principles come from `mzizi-registry → content/doctrine/`,\nserved at <https://api.mzizi.dev/v1/brand> (`.ecosystem`, `.philosophy`). Supabase holds no brand\nor doctrine data: `mzizi-registry/docs/db-contents-rule.md` (2026-08-04) moved all of it into the\nrepo, so the three Supabase collections an earlier revision of this skill cited — for the\necosystem, the pillars and the principles — no longer exist. (Two notes survive the move:\nprinciples were renamed in v4.x, so use the set below and not older Shona-named lists; and the\nregistry still names its ecosystem-layer entry `bundu` (URL **bundu.org**) — that is registry data,\nrenamed in mzizi-registry, not here.)\n\nThe **five** pillars and **five** principles below are a count of Ubuntu doctrine, not of the\npalette, and they are correct — `/v1/brand` `.philosophy.ubuntuPillars` serves exactly these five.\nDo not \"fix\" them to seven to match the colour families.\n\n## The ecosystem-level identity\n\n- **Mineral:** `copper` — `#BF5A36` light / `#FF8A65` dark (`--color-copper`) · connection, foundation, stewardship\n- **Registry entry:** `bundu` in `/v1/brand` `.ecosystem` (meaning _wilderness_, Shona) · **URL:** <https://bundu.org>\n- **Voice:** visionary, grounded, inclusive\n- **Role:** the ecosystem — the layer that holds the whole, the commons, governance, philosophy\n- **Wordmark:** `mzizi`, lowercase always, Noto Serif 600 (per ecosystem rule)\n- **No standalone ecosystem-level logo mark is registered in the design DB yet** — that's an open identity\n item. Until one exists, ecosystem-level materials use the wordmark in copper, optionally with\n Mukoko's Swarm mark when the subject is the product family. Don't invent an ecosystem mark ad hoc.\n\n## The brand constellation (verified, v4.1.0)\n\nEvery brand owns a mineral and a voice. Theme each brand's surfaces and copy in **its own** mineral\nand register — never default everything to tanzanite.\n\n| Brand | Role | Mineral | Meaning | Voice |\n| ------------ | --------------------------- | ---------- | ---------- | -------------------------------------- |\n| **bundu** | The ecosystem | copper | Wilderness | visionary, grounded, inclusive |\n| **nyuchi** | Infrastructure & enterprise | gold | Bee | technical, reliable, industrious |\n| **mukoko** | Africa's super app | tanzanite | Beehive | welcoming, structured, protective |\n| **shamwari** | Sovereign AI companion | sodalite | Friend | helpful, warm, intelligent |\n| nhimbe | Events & gatherings | malachite | Gathering | celebratory, communal, vibrant |\n| bushtrade | Marketplace | gold | Bush trade | practical, trustworthy, local |\n| lingo | Language learning | cobalt | Language | encouraging, cultural, playful |\n| campfire | Platform messaging anchor | malachite | Campfire | direct, warm, always present |\n| bytes | Short-form creator video | tanzanite | Bytes | energetic, creative, youthful |\n| novels | Publishing platform | malachite | Novels | literary, thoughtful, immersive |\n| places | Geographic knowledge graph | gold | Places | authoritative, helpful, discoverable |\n| transport | Public transit & booking | gold | Transport | efficient, reliable, practical |\n| planner | Productivity hub | cobalt | Planner | organised, clear, supportive |\n| wallet | Payments & tokens | gold | Wallet | trustworthy, precise, secure |\n| pulse | Feed & Mukoko Home | tanzanite | Pulse | personal, adaptive, ambient |\n| health | Wellness & telemedicine | malachite | Health | caring, accurate, private |\n| circles | Community messaging | terracotta | Circles | inclusive, moderated, community-driven |\n\nThe `bundu` row is the ecosystem layer as `/v1/brand` serves it today; the table mirrors the\nregistry, so it changes when mzizi-registry renames the entry.\n\nThe narrative spine: **nyuchi is the bee, mukoko is the hive, mzizi is the root everything\ngrows from.** Use this when explaining the ecosystem — it is the one-sentence architecture.\n\n## Ubuntu as identity (live doctrine)\n\nMzizi carries the philosophy. _Ndiri nekuti tiri_ — I am because we are.\n\n**Five Pillars** (spheres of belonging): **family** (the household and kin, the primary web of\nbelonging) → **community** (neighbours, peers, chosen kin) → **society** (the broader human web —\nstrangers, institutions, the polity) → **environment** (the natural world that holds all\nrelationships; Ubuntu is ecological before it is social) → **spirituality** (the vertical\ndimension — meaning, purpose, ancestry, the sacred).\n\n**Five Principles** (operating rules): **survival** (collective endurance — no one survives alone) ·\n**solidarity** (firm, persevering commitment to the common good) · **compassion** (active care shown\nthrough helping, sharing, welcoming) · **respect** (unprejudiced consideration of another's\nprivileges, beliefs, norms — includes elder-respect) · **dignity** (inherent worth by virtue of\nexistence, irrespective of status or background).\n\nIn ecosystem materials, philosophy is **structural, not decorative**: name a pillar/principle only\nwhen the feature or message genuinely embodies it.\n\n## Rules for ecosystem-level work\n\n- **Lowercase wordmarks, always** — `mzizi`, `nyuchi`, `mukoko`, `shamwari`, and every sister brand.\n- **Copper accents** signal \"this is the ecosystem speaking\" (parent-level pages, governance,\n commons, manifesto-register material).\n- **Cross-brand layouts:** lead with the brand that owns the message; bring in the constellation\n table's minerals when showing the family together (each brand in its own mineral — this is what\n the Swarm mark's full palette expresses at logo scale).\n- **Voice shifts with the speaker.** An ecosystem-level page is visionary/grounded/inclusive; the same content\n on mukoko.com becomes welcoming/structured/protective. Don't copy-paste tone across brands.\n- Cultural specificity is non-negotiable: Shona vocabulary and Zimbabwean grounding are structural.\n- All UI tokens (radius, type, spacing, surfaces, status colors) come from **nyuchi-design**;\n Mukoko logo/assets come from **mukoko-design**. This skill governs _which brand speaks and how_.\n- **Every brand owns a mineral, but the palette is not only minerals.** A cross-brand piece has 21\n families to draw on — seven minerals, seven Heritage tones, the Experimental Seven. Use the\n minerals to say _who is speaking_; use Heritage for atmosphere and surface, and the Experimental\n Seven for categorical sets that would otherwise stretch a mineral past its meaning.\n\n## If invoked with no other guidance\n\nAsk whether the work is (a) parent-brand material (ecosystem-level page, manifesto, investor/ecosystem\ndeck), (b) a cross-brand piece (family overview, comparison, announcement spanning brands), or\n(c) a voice/mineral decision for a specific brand — then apply the constellation table and the\nvoice rules, pulling tokens and assets from the sibling skills.\n\n_Mzizi — the root everything grows from._",
|
|
26
|
-
"source": "mzizi-tools/mzizi-skills/skills/mzizi-design"
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"name": "nyuchi-design",
|
|
30
|
-
"description": "UI + tokens for the nyuchi / mzizi / mukoko ecosystem: the canonical 21-family Mzizi palette (seven African Minerals + seven Heritage tones + the Experimental Seven), the named radius scale, Noto Sans/Serif + JetBrains Mono type, pill-button + dense control-scale rules, and a super-app UI kit. Use to generate well-branded interfaces.",
|
|
31
|
-
"body_mdx": "> **Doctrine v4.1.0. The canonical palette ships with this package.**\n> `palette.canonical.json` at the root of `@nyuchi/mzizi-skills` states all **21 colour\n> families** — seven African Minerals, seven Heritage tones, the Experimental Seven — and\n> every token file in this bundle is generated from it and gated against it. Read it, or the\n> stylesheets generated from it (`tokens-update.css` here, `tokens/palette.css` in\n> `mukoko-design`). Do not retype hex values out of this prose into a project.\n>\n> Canon upstream is `mzizi-dev/mzizi-registry → lib/tokens/palette.source.ts`, served publicly\n> at <https://api.mzizi.dev/v1/brand>. **Supabase is not the source of truth for any token or\n> brand value** — `mzizi-registry/docs/db-contents-rule.md` (2026-08-04) removed brand data from\n> the database, and the three Supabase collections that once held the palette are gone. Earlier\n> revisions of this skill told agents to pull them live; that instruction was wrong and is\n> withdrawn.\n>\n> **Colour is derived and gated. Everything else on this page is hand-maintained** —\n> typography, spacing, radius, motion, shadow, z-index and the semantic set were not\n> re-derived, so confirm them against the registry if precision matters.\n\nRead the `README.md` file within this skill, and explore the other available files. Key entry points:\n\n- `README.md` — brand context, content fundamentals, visual foundations, iconography.\n- `tokens-update.css` — **all 21 families**, generated from `palette.canonical.json`, light + dark + `.dark` class + `prefers-color-scheme`. Merge this into `colors_and_type.css`, or `@import` it directly. Never hard-code a hex.\n- `colors_and_type.css` — the older local mirror. It predates the heritage seven and the Experimental Seven; where the two disagree, `tokens-update.css` wins, because only it is generated from canon.\n- `assets/` — brand marks. **nyuchi** = the bee (worker / infrastructure). **mukoko** = the hive. Mukoko's current mark is **the Swarm** (seven-mineral Seed-of-Life cluster) — see the dedicated `mukoko-design` skill. Use the light mark on dark surfaces and vice versa.\n- `preview/` — reference cards for type, colors, spacing, components, brand.\n- `ui_kits/mukoko/` — React/JSX super-app UI kit with a working click-through prototype.\n\n## Rules that are non-negotiable\n\n- **Lowercase wordmarks** — `mukoko`, `nyuchi`, `bundu`, `shamwari`, `nhimbe`, `bushtrade` — always.\n- **The palette is 21 families: seven Minerals + seven Heritage + the Experimental Seven.** Any interface that uses only the minerals is using a third of the system. Do not describe it as \"the seven-colour palette\".\n- **Seven African Minerals** (geological, the core palette). The custom properties are `--color-<mineral>`, `--color-<mineral>-container`, `--color-<mineral>-on-container` — those are the names the design system defines. (`--container-<mineral>` / `--on-container-<mineral>` appeared in earlier revisions of this skill and are defined nowhere upstream; `tokens-update.css` now ships them as aliases so existing work keeps rendering, but do not reach for them in new code.)\n - `cobalt` — primary blue, links, CTAs · digital future, trust\n - `tanzanite` — **mukoko's brand mineral**, social features · premium, connection\n - `malachite` — success states, positive actions · growth\n - `gold` — achievements, rewards, highlights · honey, warmth\n - `terracotta` — community features · earth, grounding\n - `sodalite` — **AI / Shamwari surfaces, deep-reasoning states** · intelligence, depth _(added v4.1.0)_\n - `copper` — **Bundu ecosystem identity, the commons** · connection, stewardship _(added v4.1.0)_\n- **Seven Heritage tones** (atmospheric anchors — mini-app surfaces, backgrounds, moods; no family or role): `indigo` (dusk, the dyer's craft) · `savanna` (sun-dried grass, the dry season) · `baobab` (the tree of life, bark, shelter) · `sunset` (day's end, the gathering hour) · `river` (flow, the journey) · `hematite` (the neutral anchor, the substrate) · `kalahari` (the light anchor, openness). Reach for them via `--color-<name>` (aliasing `--heritage-<name>`); values are in `palette.canonical.json`. _(Two earlier framings are withdrawn: the ten-colour one from before the palette settled, and the two-anchor one that survived into v0.5.1 of this bundle. **There are seven.** A skill that named two produced two-tone output in projects that had never seen each other.)_\n- **The Experimental Seven** — a computed heptagon (hues offset 17°, prime saturations, foregrounds solved to P7), each tone carrying a `heptagonIndex` 0–6 that fixes its place on the wheel: `ember` · `acacia` · `fern` · `lagoon` · `storm` · `dusk` · `protea`. Four tiers each: `--exp-<name>`, `--exp-<name>-container`, `--exp-<name>-on`, `--exp-<name>-ui`. Use them for generated/wheel-derived surfaces and categorical sets where the minerals would have to be stretched past their meanings.\n- **Each brand owns a mineral** (the ecosystem map, verified v4.1.0 and served at `/v1/brand` as `.ecosystem`): bundu→**copper**, nyuchi→**gold**, mukoko→**tanzanite**, shamwari→**sodalite**, nhimbe/campfire/novels/health→malachite, lingo/planner→cobalt, bushtrade/places/transport/wallet→gold, bytes/pulse→tanzanite, circles→terracotta. Theme a surface in its brand's mineral — never default everything to tanzanite.\n- **Buttons are ALWAYS pill** (`rounded-full` → `--radius-full`, 9999px). Inputs match buttons visually.\n- **Controls are dense by default:** `h-8` (32px) small · `h-9` (36px) default · `h-10` (40px) large. **56px (`h-14`) is reserved for specific prominent surfaces — hero CTAs and the like — it is not the default.** An earlier revision of this skill said 56px default / 48px minimum; the shipped primitives never did that, so the rule described a system that did not exist. Because the default sits below the 44pt (Apple HIG) and 48dp (Material) guidance, a dense control that is a **touch** target must earn its hit area another way — surrounding spacing, or padding the interactive area beyond the visual box. Badges are 20px tall.\n- **Radius scale is named** (px): `none` 0 · `xs` 4 · `sm` 7 · `md` 12 · `lg` 14 (**DEFAULT** cards/panels) · `xl` 17 · `2xl` 24 · `full` 9999 (pills). Checkboxes use `sm`(7), inputs/small cards `md`(12), cards/panels `lg`(14), modals/sheets/dialogs/tabs `xl`(17), hero cards `2xl`(24). Reference via `--radius-<name>`.\n- **H1–H3 are Noto Serif. H4+ and body are Noto Sans. Code is JetBrains Mono.**\n- **Rings over shadows.** Cards get `ring-1` of `--border`, not a drop shadow.\n- **No gradients, no glassmorphism** except sticky chrome (`backdrop-blur`).\n- **Emoji only as mini-app identifiers**, never as chrome or body decoration.\n- **Status has its own names: `--status-success` / `--status-warning` / `--status-error` / `--status-info` / `--status-neutral`.** Use those names and do not hardcode a hex. Upstream they alias `--success` / `--warning` / `--destructive` / `--info` / `--neutral`, so there is one decision per status — which means the resolved values are mineral-derived in the shipped theme (success reads as malachite, info as cobalt), not the standalone #22C55E/#F59E0B/#EF4444/#3B82F6 set an earlier revision of this skill listed. Those hexes are the per-component `var(--token, #hex)` fallback for an app that has not defined the properties; they are not the theme. Severity scale (`--severity-low/moderate/high/severe/extreme/cold`) and connection (`--connection-online/syncing/cached/offline`) follow the same logic. Verification tiers are the only place minerals carry status meaning (community→malachite, otp→cobalt, government→tanzanite, licensed→gold).\n- **Surfaces (April 2026 AAA refresh):** light mode — `--background` warm paper, `--card` pure white, `--muted` cream, `--overlay` white. Dark mode — `--background` warm stone L10%, `--card` L6% (darker than bg), `--muted` L2% (deepest), `--overlay` L14% (lighter than bg, scrim creates pop).\n- **Mukoko's mark is the Swarm** — a seven-mineral Seed-of-Life cluster (tanzanite core, six minerals ringed around it). Full-palette at ≥32px; mono single-mineral version below that. Full kit + rules live in the `mukoko-design` skill.\n- **Lucide React** for UI icons (or the Lucide CDN JS build for HTML prototypes).\n- **APCA 3.0 AAA contrast** on both primary and secondary text on every surface.\n\n## When creating visual artifacts\n\nCopy assets out of this skill and create static HTML files for the user to view. `@import` `colors_and_type.css` at the top of every HTML file. For phone prototypes copy the structure from `ui_kits/mukoko/index.html`. For slide decks use the MineralStrip accent and serif display type.\n\n## When working on production code\n\nInstall upstream components with the shadcn CLI — never hand-roll copies:\n\n```bash\nnpx shadcn@latest add https://mzizi.dev/api/v1/ui/<name>\n```\n\nRead the rules in `README.md` to become an expert in designing with this brand. Take CSS variables from `tokens-update.css` in this skill — it is generated from `palette.canonical.json` and carries all 21 families. The upstream equivalent is `mzizi-registry/app/globals.css`, which is generated from the same canon. There is no live database to refresh from.\n\n## If invoked with no other guidance\n\nAsk what the user wants to build or design (marketing page? a new mini-app for mukoko? a deck? a component?), ask what surface (mobile / desktop / print / slide), ask which brand voice (mzizi / nyuchi / mukoko / shamwari / nhimbe), then act as an expert designer who outputs HTML artifacts _or_ production code, depending on the need.",
|
|
32
|
-
"source": "mzizi-tools/mzizi-skills/skills/nyuchi-design"
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"name": "mukoko-design",
|
|
36
|
-
"description": "Mukoko brand identity: the seven-mineral Seed-of-Life Swarm mark, ready-made SVG/PNG assets (logos, app icons, favicons, lockups, honeycomb backgrounds), and design tokens generated from the canonical 21-family palette — seven minerals for the mark, plus the seven Heritage tones and the Experimental Seven for everything else. Use when creating or exporting Mukoko visual identity.",
|
|
37
|
-
"body_mdx": "# Mukoko — _The Swarm_\n\nThe visual identity for **Mukoko**, the hive at the centre of the Nyuchi ecosystem. Every asset\nhere is drawn from the canonical palette (doctrine **v4.1.0**), which this package states in\n`palette.canonical.json` at its root — 21 colour families, vendored from\n`mzizi-dev/mzizi-registry → lib/tokens/palette.source.ts` and served at\n<https://api.mzizi.dev/v1/brand>. The token files in `tokens/` are **generated** from it and\ngated against it (`node scripts/generate-tokens.mjs --check`), so they cannot quietly drift.\n\n**Supabase is not the source of truth** for any of this. The Supabase collections that once held\nthe palette were removed in 2026-08 (`mzizi-registry/docs/db-contents-rule.md`); an earlier\nrevision of this skill told agents to re-pull from them, and that instruction is withdrawn.\n\n## The idea (the meaning, say it right)\n\nThe mark is the **Seed of Life** — one centre cell ringed by six, the first complete ring of the\nhoneycomb — rendered in the **seven African Minerals**:\n\n> **Tanzanite at the core** (Mukoko, the brand), ringed by the six minerals of the ecosystem.\n> One hive holding the whole. **Ndiri nekuti tiri** — _I am because we are._\n\nIt is a _known_ geometric figure (like the Audi or Olympic rings), made ours through the mineral\npalette and the meaning. Symmetry of form; distinctiveness through colour and story. **nyuchi is the\nbee; mukoko is the hive** — so Mukoko's hero mark is the hive/cluster, never a bee (that's Nyuchi's).\n\n## The seven minerals\n\n| Mineral | Light | Dark | Meaning | Role |\n| ------------- | --------- | --------- | ----------------------- | --------------------- |\n| cobalt | `#0047AB` | `#00B0FF` | digital future, trust | CTAs, links |\n| **tanzanite** | `#4B0082` | `#B388FF` | premium, connection | **brand / core cell** |\n| malachite | `#004D40` | `#64FFDA` | growth, success | positive actions |\n| gold | `#5D4037` | `#FFD740` | honey, rewards | achievements |\n| terracotta | `#A0522D` | `#E1B07E` | earth, community | community |\n| sodalite | `#283593` | `#3D5AFE` | intelligence, depth | AI / Shamwari |\n| copper | `#BF5A36` | `#FF8A65` | connection, stewardship | Bundu / commons |\n\nUse `light` on light surfaces, `dark` on dark. Full machine-readable values (containers +\non-containers) are in `tokens/minerals.json`; drop-in CSS custom properties are in\n`tokens/minerals.css`. The custom properties are `--color-<mineral>`,\n`--color-<mineral>-container`, `--color-<mineral>-on-container`.\n\n**The seven minerals are the MARK, not the palette.** The Swarm is seven cells, so this skill is\nabout seven colours — but the Mzizi palette is **21 families**: these seven, plus seven Heritage\ntones (indigo, savanna, baobab, sunset, river, hematite, kalahari) and the Experimental Seven\n(ember, acacia, fern, lagoon, storm, dusk, protea). For anything beyond the mark — a page, a\ndeck, a mini-app surface — use `tokens/palette.css` / `tokens/palette.json` here, or the\n`nyuchi-design` skill. Building an interface from the mineral seven alone leaves two thirds of\nthe system on the floor.\n\n**Ring order** (clockwise from top): `cobalt → gold → malachite → copper → sodalite → terracotta`,\n**tanzanite** in the centre. This alternates cool/warm for visual balance — a design decision, not a\nlaw. If positions are ever assigned meaning, regenerate from the geometry script.\n\n## Assets in this skill\n\n```\nlogo/mark/ full-palette mark (light/dark, SVG + PNG) + mono/ (7 minerals × light|dark SVG)\nlogo/lockup/ mark + lowercase `mukoko` serif wordmark (light/dark, SVG + PNG)\napp-icon/ primary (full palette on deep tanzanite), paper, mono-tanzanite knockout (SVG + 1024 PNG)\nfavicon/ MONO tanzanite mark, legible small (SVG + 16/32/48/180 PNG)\nbackgrounds/ honeycomb grain (paper), tanzanite header band, stone splash 1080×1920, full-mineral pattern\ntokens/ palette.css + palette.json (ALL 21 families) and minerals.css + minerals.json\n (the mineral seven, for the mark). All four are generated from\n palette.canonical.json — never hand-edit them.\nBRAND-GUIDE.md the full written guide (meaning, manifest, usage, do/don't)\n```\n\n## Rules for using the mark\n\n- **Default to the full-palette mark at ≥32px** (headers, About screens, decks, profile images, merch).\n- **Below 32px use the mono favicon** — the seven hues fuse small; the single-mineral version stays crisp. This is why the favicon ships mono tanzanite.\n- **Mono variants** let one app/surface/sub-brand wear a single mineral (Shamwari→sodalite, community→terracotta, success→malachite). Shape constant; only colour changes.\n- **Clear space** = one cell-radius on all sides. **Min size:** 24px full mark, 16px mono favicon.\n- **App icon default** = `app-icon/mukoko-appicon-primary` (full palette on deep tanzanite `#1A0033`).\n- **Wordmark:** Noto Serif 600, **lowercase always** (`mukoko`, never `Mukoko`/`MUKOKO`). UI = Noto Sans; code/labels = JetBrains Mono.\n\n## Do / don't\n\n**Do** — use the supplied SVGs; flip light/dark mineral values to the surface; use mono variants to signal context; give the mark room.\n**Don't** — add gradients/shadows/glows/outlines; recolour petals off-palette or reorder the ring arbitrarily; rotate, stretch or rearrange cells; capitalise the wordmark; use the full-palette mark below 32px.\n\n## Regenerating\n\nAll assets are deterministic from the minerals tokens. Take the values from `tokens/minerals.json`\n(regenerate it with `node scripts/generate-tokens.mjs` if canon has moved) and rebuild the cluster (core + six petals at edge-normal angles `30 + 60·i`, distance `√3·r + gap`,\nsharing true edges) so geometry, ring order and exports stay reproducible.\n\n## If invoked with no other guidance\n\nAsk what's needed (logo export? app icon? social/launch asset? splash? a new surface wearing a\nmineral?), confirm light vs dark surface, then produce the asset using the files here — or regenerate\nfrom tokens for a size/format not already exported.\n\n_Mukoko — the hive that holds the whole._",
|
|
38
|
-
"source": "mzizi-tools/mzizi-skills/skills/mukoko-design"
|
|
24
|
+
"description": "The Mzizi design system and brand: the canonical 21-family palette (seven African Minerals + seven Heritage tones + the Experimental Seven) with generated stylesheets (tokens/palette.css, tokens/minerals.css, tokens-update.css), the radius scale, Noto Sans/Serif + JetBrains Mono type, pill buttons and the 48px touch floor, status and surface tokens, the verified brand constellation, the ecosystem identity (copper), the five Ubuntu pillars + five principles, what belongs in the ecosystem, and the wordmarks. Replaces nyuchi-design and half of ecosystem-app-setup, and carries the tokens that were in mukoko-design.",
|
|
25
|
+
"body_mdx": "# Mzizi design — the system and the ecosystem\n\nThis skill replaces three 0.7.0 skills: `nyuchi-design` (UI and tokens, now \"The design\nsystem\" below), the Ubuntu and ownership half of `ecosystem-app-setup` (now \"What belongs in\nthe ecosystem\"), and the tokens of `mukoko-design`, which moved here. The mukoko brand assets\n(the Swarm mark, logos, icons) are no longer in this package; they move to a Nyuchi-owned\nbundle.\n\n## Files this skill ships\n\nAll generated from `palette.canonical.json` at the package root by\n`scripts/generate-tokens.mjs`, and gated against it. Never hand-edit them; never retype a hex.\n\n| File | Contents |\n| ---------------------- | ---------------------------------------------------------------------------------------------------- |\n| `tokens/palette.css` | all 21 families, light + dark, as CSS custom properties |\n| `tokens/palette.json` | all 21 families, machine-readable |\n| `tokens/minerals.css` | the seven minerals only |\n| `tokens/minerals.json` | the seven minerals only, machine-readable |\n| `tokens-update.css` | all 21 families (light, dark, `.dark`, `prefers-color-scheme`) plus the radius scale, as one drop-in |\n\n## The ecosystem\n\n**Mzizi** is the parent identity at the ecosystem level — the framework, the design language, the\ncomponent registry and the open doctrine every brand builds on. Everything revenue-generating (the\nMzizi console, fundi, paid plans, billing) is Nyuchi's. Three pillars: **mukoko** (consumer super app), **nyuchi** (infrastructure &\nenterprise), and the **sister brands** (specialist verticals) — one identity, one design system, one\nopen data commons.\n\nDoctrine **v4.1.0**. **The repo is the source of truth, and there is no database.** Colour\ncomes from `palette.canonical.json` at the root of this package — 21 families, vendored from\n`mzizi-dev/mzizi-registry → lib/tokens/palette.source.ts` and gated against it. The ecosystem\nconstellation and the Ubuntu pillars and principles come from `mzizi-registry →\ncontent/doctrine/`, served at <https://api.mzizi.dev/v1/brand> (`.ecosystem`, `.philosophy`) and\nby the MCP as `mzizi_get_tokens` (`family: \"ecosystem\"`) and `mzizi_get_doctrine`. Two notes:\nprinciples were renamed in v4.x, so use the set below and not older Shona-named lists; and the\nregistry still names its ecosystem-layer entry `bundu` (URL **bundu.org**). That is registry\ndata, and it changes in mzizi-registry, not here.\n\nThe **five** pillars and **five** principles below are a count of Ubuntu doctrine, not of the\npalette, and they are correct — `/v1/brand` `.philosophy.ubuntuPillars` serves exactly these five.\nDo not \"fix\" them to seven to match the colour families.\n\n### The ecosystem-level identity\n\n- **Mineral:** `copper` — `#BF5A36` light / `#FF8A65` dark (`--color-copper`) · connection, foundation, stewardship\n- **Registry entry:** `bundu` in `/v1/brand` `.ecosystem` (meaning _wilderness_, Shona) · **URL:** <https://bundu.org>\n- **Voice:** visionary, grounded, inclusive\n- **Role:** the ecosystem — the layer that holds the whole, the commons, governance, philosophy\n- **Wordmark:** `Mzizi`, capitalised, Noto Serif 600. (The other wordmarks stay lowercase.)\n- **No standalone ecosystem-level logo mark is registered in the registry yet** — that's an open identity\n item. Until one exists, ecosystem-level materials use the wordmark in copper, optionally with\n Mukoko's Swarm mark (a Nyuchi asset, not in this package) when the subject is the product family. Don't invent an ecosystem mark ad hoc.\n\n### The brand constellation (verified, v4.1.0)\n\nEvery brand owns a mineral and a voice. Theme each brand's surfaces and copy in **its own** mineral\nand register — never default everything to tanzanite.\n\n| Brand | Role | Mineral | Meaning | Voice |\n| ------------ | --------------------------- | ---------- | ---------- | -------------------------------------- |\n| **bundu** | The ecosystem | copper | Wilderness | visionary, grounded, inclusive |\n| **nyuchi** | Infrastructure & enterprise | gold | Bee | technical, reliable, industrious |\n| **mukoko** | Africa's super app | tanzanite | Beehive | welcoming, structured, protective |\n| **shamwari** | Sovereign AI companion | sodalite | Friend | helpful, warm, intelligent |\n| nhimbe | Events & gatherings | malachite | Gathering | celebratory, communal, vibrant |\n| bushtrade | Marketplace | gold | Bush trade | practical, trustworthy, local |\n| lingo | Language learning | cobalt | Language | encouraging, cultural, playful |\n| campfire | Platform messaging anchor | malachite | Campfire | direct, warm, always present |\n| bytes | Short-form creator video | tanzanite | Bytes | energetic, creative, youthful |\n| novels | Publishing platform | malachite | Novels | literary, thoughtful, immersive |\n| places | Geographic knowledge graph | gold | Places | authoritative, helpful, discoverable |\n| transport | Public transit & booking | gold | Transport | efficient, reliable, practical |\n| planner | Productivity hub | cobalt | Planner | organised, clear, supportive |\n| wallet | Payments & tokens | gold | Wallet | trustworthy, precise, secure |\n| pulse | Feed & Mukoko Home | tanzanite | Pulse | personal, adaptive, ambient |\n| health | Wellness & telemedicine | malachite | Health | caring, accurate, private |\n| circles | Community messaging | terracotta | Circles | inclusive, moderated, community-driven |\n\nThe `bundu` row is the ecosystem layer as `/v1/brand` serves it today; the table mirrors the\nregistry, so it changes when mzizi-registry renames the entry.\n\nThe narrative spine: **nyuchi is the bee, mukoko is the hive, Mzizi is the root everything\ngrows from.** Use this when explaining the ecosystem — it is the one-sentence architecture.\n\n### Ubuntu as identity (live doctrine)\n\nMzizi carries the philosophy. _Ndiri nekuti tiri_ — I am because we are.\n\n**Five Pillars** (spheres of belonging): **family** (the household and kin, the primary web of\nbelonging) → **community** (neighbours, peers, chosen kin) → **society** (the broader human web —\nstrangers, institutions, the polity) → **environment** (the natural world that holds all\nrelationships; Ubuntu is ecological before it is social) → **spirituality** (the vertical\ndimension — meaning, purpose, ancestry, the sacred).\n\n**Five Principles** (operating rules): **survival** (collective endurance — no one survives alone) ·\n**solidarity** (firm, persevering commitment to the common good) · **compassion** (active care shown\nthrough helping, sharing, welcoming) · **respect** (unprejudiced consideration of another's\nprivileges, beliefs, norms — includes elder-respect) · **dignity** (inherent worth by virtue of\nexistence, irrespective of status or background).\n\nIn ecosystem materials, philosophy is **structural, not decorative**: name a pillar/principle only\nwhen the feature or message genuinely embodies it.\n\n### Rules for ecosystem-level work\n\n- **Wordmarks:** `Mzizi` is capitalised. Every other wordmark is lowercase: `nyuchi`, `mukoko`,\n `shamwari`, `bundu`, `nhimbe` and every sister brand.\n- **Copper accents** signal \"this is the ecosystem speaking\" (parent-level pages, governance,\n commons, manifesto-register material).\n- **Cross-brand layouts:** lead with the brand that owns the message; bring in the constellation\n table's minerals when showing the family together (each brand in its own mineral).\n- **Voice shifts with the speaker.** An ecosystem-level page is visionary/grounded/inclusive; the same content\n on mukoko.com becomes welcoming/structured/protective. Don't copy-paste tone across brands.\n- Cultural specificity is non-negotiable: Shona vocabulary and Zimbabwean grounding are structural.\n- All UI tokens (radius, type, spacing, surfaces, status colours) are under \"The design system\"\n below. Mukoko's logo and assets are Nyuchi's and are not in this package.\n- **Every brand owns a mineral, but the palette is not only minerals.** A cross-brand piece has 21\n families to draw on — seven minerals, seven Heritage tones, the Experimental Seven. Use the\n minerals to say _who is speaking_; use Heritage for atmosphere and surface, and the Experimental\n Seven for categorical sets that would otherwise stretch a mineral past its meaning.\n\n## The design system\n\n> **Doctrine v4.1.0. The canonical palette ships with this package.**\n> `palette.canonical.json` at the root of `@nyuchi/mzizi-skills` states all **21 colour\n> families** — seven African Minerals, seven Heritage tones, the Experimental Seven — and\n> every token file in this bundle is generated from it and gated against it. Read it, or the\n> stylesheets generated from it (`tokens-update.css` and `tokens/palette.css` here). Do not retype hex values out of this prose into a project.\n>\n> Canon upstream is `mzizi-dev/mzizi-registry → lib/tokens/palette.source.ts`, served publicly\n> at <https://api.mzizi.dev/v1/brand> and by the MCP as `mzizi_get_tokens`. There is no\n> database: the registry's files are the data layer.\n>\n> **Colour is derived and gated. Everything else on this page is hand-maintained** —\n> typography, spacing, radius, sizes, motion, shadow, z-index and the semantic set. The\n> radius scale and control sizes were checked against `mzizi-registry` `main` for 0.8.0;\n> confirm the rest with `mzizi_get_tokens` (`typography`, `spacing`, `radii`, `semantic`,\n> `componentSpecs`) if precision matters.\n\n### Rules that are non-negotiable\n\n- **Wordmarks** — `Mzizi` is capitalised. `mukoko`, `nyuchi`, `bundu`, `shamwari`, `nhimbe`, `bushtrade` are lowercase, always.\n- **The palette is 21 families: seven Minerals + seven Heritage + the Experimental Seven.** Any interface that uses only the minerals is using a third of the system. Do not describe it as \"the seven-colour palette\".\n- **Seven African Minerals** (geological, the core palette). The custom properties are `--color-<mineral>`, `--color-<mineral>-container`, `--color-<mineral>-on-container` — those are the names the design system defines. (`--container-<mineral>` / `--on-container-<mineral>` appeared in earlier revisions of this skill and are defined nowhere upstream; `tokens-update.css` now ships them as aliases so existing work keeps rendering, but do not reach for them in new code.)\n - `cobalt` — primary blue, links, CTAs · digital future, trust\n - `tanzanite` — **mukoko's brand mineral**, social features · premium, connection\n - `malachite` — success states, positive actions · growth\n - `gold` — achievements, rewards, highlights · honey, warmth\n - `terracotta` — community features · earth, grounding\n - `sodalite` — **AI / Shamwari surfaces, deep-reasoning states** · intelligence, depth _(added v4.1.0)_\n - `copper` — **Bundu ecosystem identity, the commons** · connection, stewardship _(added v4.1.0)_\n- **Seven Heritage tones** (atmospheric anchors — mini-app surfaces, backgrounds, moods; no family or role): `indigo` (dusk, the dyer's craft) · `savanna` (sun-dried grass, the dry season) · `baobab` (the tree of life, bark, shelter) · `sunset` (day's end, the gathering hour) · `river` (flow, the journey) · `hematite` (the neutral anchor, the substrate) · `kalahari` (the light anchor, openness). Reach for them via `--color-<name>` (aliasing `--heritage-<name>`); values are in `palette.canonical.json`. _(Two earlier framings are withdrawn: the ten-colour one from before the palette settled, and the two-anchor one that survived into v0.5.1 of this bundle. **There are seven.** A skill that named two produced two-tone output in projects that had never seen each other.)_\n- **The Experimental Seven** — a computed heptagon (hues offset 17°, prime saturations, foregrounds solved to P7), each tone carrying a `heptagonIndex` 0–6 that fixes its place on the wheel: `ember` · `acacia` · `fern` · `lagoon` · `storm` · `dusk` · `protea`. Four tiers each: `--exp-<name>`, `--exp-<name>-container`, `--exp-<name>-on`, `--exp-<name>-ui`. Use them for generated/wheel-derived surfaces and categorical sets where the minerals would have to be stretched past their meanings.\n- **Each brand owns a mineral** (the ecosystem map, verified v4.1.0 and served at `/v1/brand` as `.ecosystem`): bundu→**copper**, nyuchi→**gold**, mukoko→**tanzanite**, shamwari→**sodalite**, nhimbe/campfire/novels/health→malachite, lingo/planner→cobalt, bushtrade/places/transport/wallet→gold, bytes/pulse→tanzanite, circles→terracotta. Theme a surface in its brand's mineral — never default everything to tanzanite.\n- **Buttons are ALWAYS pill** (`rounded-full` → `--radius-full`, 9999px). Inputs match buttons visually.\n- **Nothing tappable goes below 48px.** Buttons are `h-14` (56px) default and large, `h-12` (48px) small; icon buttons `size-14` / `size-12`. Inputs are 56px default, 48px small. That is what `button.tsx`, the Roots `button.rs` and the language's `primitives/button.mz` all ship, and what `/v1/brand` `.componentSpecs` serves (`minTouchTarget: 48`). (0.7.0 of this skill said controls were dense, 32/36/40px, with 56px only for hero CTAs. The shipped primitives do not do that, so that rule described a system that does not exist.) Badges are 20px tall and are not touch targets; a small element that becomes one gets `min-h-[48px]`.\n- **Radius scale** (px), every step from a 7px unit: `sm` 7 · `md` 12 · `lg` 14 (**DEFAULT** cards/panels) · `xl` 17 · `2xl` 17 · `full` 9999 (pills). That is `mzizi-registry/app/globals.css`, the Rust `mzizi-tokens` `Radius`, and `/v1/brand` `.radii` (\"ecosystem numbers: 7, 12, 14, 17\"). Checkboxes use `sm`, inputs and small cards `md`, cards and panels `lg`, modals, sheets, dialogs and tabs `xl`. Reference via `--radius-<name>`. (Until 0.8.0 this skill also listed `none` 0, `xs` 4 and `2xl` 24; none of those is defined upstream.)\n- **H1–H3 are Noto Serif. H4+ and body are Noto Sans. Code is JetBrains Mono.**\n- **Rings over shadows.** Cards get `ring-1` of `--border`, not a drop shadow.\n- **No gradients, no glassmorphism** except sticky chrome (`backdrop-blur`).\n- **Emoji only as mini-app identifiers**, never as chrome or body decoration.\n- **Status has its own names: `--status-success` / `--status-warning` / `--status-error` / `--status-info` / `--status-neutral`.** Use those names and do not hardcode a hex. Upstream they alias `--success` / `--warning` / `--destructive` / `--info` / `--neutral`, so there is one decision per status — which means the resolved values are mineral-derived in the shipped theme (success reads as malachite, info as cobalt), not the standalone #22C55E/#F59E0B/#EF4444/#3B82F6 set an earlier revision of this skill listed. Those hexes are the per-component `var(--token, #hex)` fallback for an app that has not defined the properties; they are not the theme. Severity scale (`--severity-low/moderate/high/severe/extreme/cold`) and connection (`--connection-online/syncing/cached/offline`) follow the same logic. Verification tiers are the only place minerals carry status meaning (community→malachite, otp→cobalt, government→tanzanite, licensed→gold).\n- **Surfaces (April 2026 AAA refresh):** light mode — `--background` warm paper, `--card` pure white, `--muted` cream, `--overlay` white. Dark mode — `--background` warm stone L10%, `--card` L6% (darker than bg), `--muted` L2% (deepest), `--overlay` L14% (lighter than bg, scrim creates pop).\n- **Icons:** in the React build, Lucide through `@/lib/icons`, never `lucide-react` directly; in Mzizi Roots, an icon is an `Element` slot the host renders. For an HTML prototype, the Lucide CDN build is fine.\n- **APCA 3.0 AAA contrast** on both primary and secondary text on every surface.\n\n### When creating visual artifacts\n\nCreate static HTML files for the user to view. `@import` `tokens-update.css` at the top of every HTML file (or inline it). For slide decks use a mineral accent strip and serif display type.\n\n### When working on production code\n\nUse the registry's components, never hand-rolled copies. **Rust first:** Mzizi Roots is the lead build (`cargo add mzizi-roots`; see the `mzizi-roots` skill). The React build installs by absolute URL, for existing React apps:\n\n```bash\nnpx shadcn@latest add https://api.mzizi.dev/v1/ui/<name>\n```\n\nTake CSS variables from `tokens-update.css` or `tokens/palette.css` in this skill — it is generated from `palette.canonical.json` and carries all 21 families. The upstream equivalent is `mzizi-registry/app/globals.css`, which is generated from the same canon. There is no database to refresh from.\n\n## What belongs in the ecosystem\n\n### Ubuntu alignment\n\nThe ecosystem is built on Ubuntu — \"I am because we are.\" When you architect a new app:\n\n- Prefer open source over proprietary\n- Build for community, not extraction\n- Respect data sovereignty — African data stays on African infrastructure where possible\n- Accessibility is not an afterthought; it is the starting point\n- Every feature should contribute to collective wellbeing, not just individual efficiency\n\nThese aren't marketing claims, they're architectural constraints. If you find yourself\ndesigning something that only benefits a small subset of users at others' expense, question\nwhether it belongs in the ecosystem.\n\n### What is and isn't an ecosystem app\n\nThe ecosystem has two layers. **Mzizi** owns and operates the framework, the language, the\nregistry, the design system, the docs and the API, and sits under every app. **Nyuchi Africa**\noperates the Mzizi console and everything revenue-generating. Inside Nyuchi Africa\nsit three pillars and the platform:\n\n- **mukoko** — the consumer pillar: the super-app (social, commerce, payments, identity)\n- **nyuchi** — the commercial/infrastructure pillar and the platform (web services, developer\n tools, storage)\n- **shamwari ai** — the intelligence pillar, sovereign by nature (on-device inference,\n pod-resident context, no third-party AI vendor)\n- **nhimbe** — cooperative economic features\n\nMzizi is not an app and not a layer in a stack; it is the open, non-revenue foundation the\necosystem builds on and the doctrine that governs it. mukoko and shamwari ai are pillars inside Nyuchi Africa, not separate companies.\n\nNot ecosystem apps:\n\n- Any app that extracts value without giving back\n- Any app that requires surveillance for its business model\n- Any app that centralises what should be federated\n\nIf the app you're building doesn't fit, that's fine — consume the Mzizi design system, but\ndon't brand it as ecosystem.\n\n## If invoked with no other guidance\n\nAsk what the work is: (a) an interface or prototype (which surface: mobile, desktop, print,\nslide; which brand voice: Mzizi, nyuchi, mukoko, shamwari, nhimbe), (b) parent-brand material\n(ecosystem page, manifesto, deck), (c) a cross-brand piece, or (d) a voice or mineral decision\nfor one brand. Then apply the constellation table, the voice rules and the design system, and\noutput HTML artifacts or production code as the need requires.\n\n_Mzizi — the root everything grows from._",
|
|
26
|
+
"source": "mzizi-dev/agent-tools/mzizi-skills/skills/mzizi-design"
|
|
39
27
|
},
|
|
40
28
|
{
|
|
41
|
-
"name": "
|
|
42
|
-
"description": "
|
|
43
|
-
"body_mdx": "# Scaffolding a Mzizi component\n\nFollow this workflow whenever you are creating a new component in the Mzizi design system.\n\n**A component is a real file.** `components/registry/n<N>-<label>/<name>.<ext>`, in the\n`nyuchi/mzizi` repo, which the build compiles and typechecks. That is the whole point: an\nerror in a component fails `pnpm build`.\n\nTwo files, and only two:\n\n| File | What it is |\n| ----------------------------------------------- | ------------------------------------------------------------------ |\n| `components/registry/n<N>-<label>/<name>.<ext>` | the component itself |\n| `registry.json` | the shadcn manifest a consumer's `npx shadcn add` resolves against |\n\n`registry.json` carries the install contract — description, dependencies,\n`registryDependencies`, target paths — and **never the source**. The node is derived from the\ndirectory the component lives in, so it cannot disagree with where the file actually is.\n\n> **This skill previously told you to author source into a `component_documents` row and\n> called the system \"DB-first\". That is retired.** A database row and a JSON document are\n> _representations_ of a component rather than the component, and both can drift from the code\n> while still looking correct — which is exactly what happened: a `source_code` column served\n> stale copies for 571 components until it was cleared and the fallback deleted. Rows still\n> exist for lifecycle metadata; they do not hold source, and nothing reads source from them.\n> If you find instructions anywhere telling you to `INSERT` a component, they are stale.\n\n## Step 1 — Decide the node\n\nThe architecture is the **Mzizi DNA double helix**: nodes sit on one of two backbones\n(engineering / meaning), and rungs bridge both. The N-numbers are **labels, not a sequence**.\n\nThe node decides the **directory**, and the directory is what the build reads — so choosing\nthe node and choosing where the file goes are the same decision.\n\n| Node | Directory | What belongs there |\n| ----------------- | -------------------- | ----------------------------------------------------------------------------------------- |\n| N1 tokens | `n1-tokens/` | A CSS value — colour, spacing, motion, radius (a `styling-*` collection, not a component) |\n| N2 primitive | `n2-primitives/` | Generic UI — button, card, input, dialog, toolbar, bento-grid |\n| N3 brand | `n3-brand/` | A branded composition with the mineral palette and harness integration |\n| N4 safety | `n4-safety/` | A conditional-rendering gate — permission, geo, rate limit |\n| N5 resilience | `n5-resilience/` | Error boundary, skeleton, offline banner, fallback chain |\n| N6 pages | `n6-pages/` | Full-screen layout composition |\n| N7 shell | `n7-shell/` | App container — navigation, routing, lifecycle |\n| N8 assurance | `n8-assurance/` | Instrumentation, a11y audit, RTL conformity, probes |\n| N9 fundi | `n9-fundi/` | Automated healing (a rung — owned by `mzizi-tools`) |\n| N10 documentation | `n10-documentation/` | Docs, AI instructions, docs infrastructure (a rung) |\n| N11 discovery | `n11-discovery/` | SEO / AIO surfaces — if the machine can't see it, it doesn't exist (a rung) |\n\nIf you are unsure, query the MCP: `get_node_categories(N)` shows what already lives in each\nnode, and `get_node_documents` returns the live node/strand model. Never hardcode counts.\n\n## Step 2 — Write the file\n\nCreate it in the directory the node names. The extension follows the language: `.tsx` for a\nReact component, `.ts` for a non-rendering module, `.rs` for a Rust sibling.\n\n```\ncomponents/registry/n2-primitives/your-component-name.tsx\n```\n\nThat is the authoring step. There is no row to insert and no registration to perform before\nwriting code — the file _is_ the component, and `pnpm typecheck` and `pnpm build` are what tell\nyou it is real.\n\nNaming: N3 brand components take a `nyuchi-` prefix. Everything else is the bare name, matching\nwhat a consumer will type in `npx shadcn add`.\n\n## Step 3 — Declare it in `registry.json`\n\nOne item, carrying the install contract. `meta` is the part you author; four other fields are\n**derived from it** and must not be hand-written.\n\n```jsonc\n{\n \"name\": \"your-component-name\",\n \"type\": \"registry:ui\",\n \"description\": \"Short description of what it does.\",\n \"dependencies\": [],\n \"registryDependencies\": [],\n \"files\": [\n {\n \"path\": \"components/registry/n2-primitives/your-component-name.tsx\",\n \"target\": \"components/ui/your-component-name.tsx\",\n \"type\": \"registry:ui\"\n }\n ],\n \"meta\": {\n \"collection\": \"primitives\",\n \"owner\": \"mzizi\",\n \"useCases\": [\"Use case 1\", \"Use case 2\"],\n \"variants\": [\"default\", \"sm\", \"lg\"],\n \"sizes\": [\"default\", \"sm\", \"lg\"],\n \"features\": [\"cn() for className composition\", \"data-slot attribute\"],\n \"a11y\": [\"ARIA attributes\", \"Keyboard accessible\"],\n \"hasDemo\": true\n }\n}\n```\n\n**Do not write `title`, `categories`, `docs` or `author`.** `pnpm registry:metadata` derives all\nfour from `meta` and the repo. `docs` in particular is what the shadcn CLI prints _after_\ninstalling, so it is generated from your `useCases` / `variants` / `sizes` / `features` / `a11y`\nrather than duplicated by hand — and an item with no `meta` deliberately gets no `docs`, because\na stub reads as documentation and says nothing.\n\nThen run the generators and let the gates check you:\n\n```bash\npnpm registry:paths # fill file paths from what is on disk\npnpm registry:metadata # derive title, categories, docs, author\npnpm registry:normalize # canonical ordering\npnpm registry:validate # every item resolves on disk; every dependency addressable\npnpm registry:verify # the CI drift gate\npnpm typecheck && pnpm build\n```\n\nFollow the criteria for the chosen node.\n\n### N2 primitive checklist\n\n- No `useNyuchiHarness` import\n- `data-slot` attribute on the root element\n- `data-portal` attribute pointing at `mzizi.dev/components/{name}`\n- `cn()` for className composition\n- No raw Tailwind colours — use semantic tokens (`bg-primary`, `text-foreground`, …)\n- Icons from `@/lib/icons`, never `lucide-react` directly\n- **Control height and touch target are two different measurements — don't collapse them.** The visual box follows the dense scale (`h-8` 32px small · `h-9` 36px default · `h-10` 40px large); the hit area follows `styling-touch-targets` (`comfortable` 47 · `accessible` 43 · `minimum` 41, with spacing). A 36px-tall button reaches a 43–47px target through padding, not by growing. `generous` (56) is **hero CTAs only** — the collection says so in its own `usage` field\n- Pill-shaped categories (button, input, avatar, badge, toggle) use `rounded-full` (9999)\n\n### N3 brand checklist\n\n- `nyuchi-` prefix on the name\n- `useNyuchiHarness` with the full destructure `{ log, motion, LiveRegion }`\n- `animStyle` guarded by a `motion.prefersReduced` check\n- An ARIA role or `aria-label`\n- `data-slot` and `data-portal` attributes\n- `focus-visible` ring on interactive elements\n- A dense control that is a **touch** target earns its hit area through surrounding spacing or padding beyond the visual box — the 36px default sits below the 44pt/48dp guidance, so the spacing is what makes it tappable\n- I18N via `Intl` formatters for dates, numbers, currency\n- Semantic tokens for status-bearing colours\n\n### N6 page checklist\n\n- Pure composition — no inline buttons, cards, or SVGs\n- Accept children/slots for content\n- Semantic CSS vars only (`bg-card`, `text-foreground`, `bg-primary`)\n- A loading-state prop\n- `role=\"main\"` and an `aria-label`\n\n### N8 assurance checklist\n\n- A TypeScript module exporting typed functions and React hooks\n- A banner comment identifying the node and purpose\n- A configurable rules array\n- Optional `onViolation` / `onComplete` callbacks\n- A React hook variant for continuous monitoring\n\n## Step 4 — Docs and demos\n\nThere is nothing extra to write. Docs come from the `meta` you already authored in Step 3 —\n`useCases`, `variants`, `sizes`, `features`, `a11y` — and `pnpm registry:metadata` turns them\ninto the `docs` string the CLI prints after install. Set `meta.hasDemo` when a demo exists.\n\nIf a prop table is expected, `pnpm props:extract` derives it from the component's own types and\n`pnpm props:verify` is the gate. Props are read from the code, never restated beside it.\n\n## Step 5 — Lifecycle\n\nStatus and version history live in `component_documents` — that row still exists and is still\nthe right place for lifecycle metadata. It does **not** hold source, and nothing reads source\nfrom it.\n\nRead the current status through the MCP (`mzizi_get_component`) rather than assuming it. Promote\nalpha to stable only once the component has been dogfooded somewhere real; a status that\nadvances on a schedule rather than on evidence is worth nothing to the consumer reading it.\n\n## Step 6 — Validate accessibility\n\nIf your component introduces a new colour pair, validate before shipping. The MCP tool is the\ncheapest path and needs no database access:\n\n```\nmzizi_check_accessibility(foreground: \"#FFFFFF\", background: \"#0047AB\")\n```\n\nIt returns the WCAG 2.1 contrast ratio with AA/AAA verdicts and whether the pair clears the\nMzizi floor. For systematic pair tracking see `styling-accessibility-checks` — critical pairs\nhave a row with a computed ratio and colour-blindness safety flags.\n\n## Step 7 — Before you open the PR\n\nEvery gate that guards the registry, in the order CI runs them:\n\n```bash\npnpm registry:paths:check\npnpm registry:metadata:check\npnpm registry:verify\npnpm registry:validate\npnpm props:verify\npnpm typecheck\npnpm test\npnpm build\n```\n\nA green run means the item resolves to a real file, every dependency is addressable, and the\nderived fields match what `meta` says. That is the whole contract.\n\n## What to avoid\n\n- **Never put component source in a database row, a JSON document, or any second place.** The\n file under `components/registry/` is the component; everything else is a representation of\n it, and a representation can drift from the code while still looking correct. That is not\n hypothetical here — a `source_code` column served stale copies for 571 components before it\n was cleared.\n- Never hand-write `title`, `categories`, `docs` or `author` in `registry.json` — they are\n derived, and a hand-written value is a second source that the next generator run silently\n overwrites.\n- Never hardcode hex colour values (except documented third-party brands like Ethereum,\n Google, EcoCash).\n- Never use `lucide-react` directly — go through `@/lib/icons`.\n- Never use `margin-left` / `padding-right` / `left` — use logical properties\n (`margin-inline-start`, …) for RTL support.\n- Never bump a major version on your own initiative. The portal's code SemVer and the\n doctrine/IP line are independent tracks, and a major on either is a maintainers' call.",
|
|
44
|
-
"source": "mzizi-tools/mzizi-skills/skills/
|
|
29
|
+
"name": "mzizi-backend",
|
|
30
|
+
"description": "A Mzizi backend service on Cloudflare Workers: where it stands today (every live Mzizi Worker is TypeScript; the language has no handlers yet), the live endpoint map (api.mzizi.dev, mcp.mzizi.dev, docs.mzizi.dev/mcp), a Rust Worker as a sans-IO core plus thin host (workers-rs, wrangler, cron, queues, bindings, no database client), an MCP server (Streamable HTTP, one factory with many entrypoints, a code-defined catalogue over data bundled at a pinned commit, federation, naming and annotations, npm + MCP Registry publish), the free-except-Fundi access rule, logging and deploy. Replaces cloudflare-worker-rust and mcp-server-cloudflare.",
|
|
31
|
+
"body_mdx": "# Mzizi backend on Cloudflare Workers\n\nThis skill replaces two 0.7.0 skills: `cloudflare-worker-rust` (now \"A Rust Worker\") and\n`mcp-server-cloudflare` (now \"An MCP server\"). The rules they shared (access, logging, deploy)\nare stated once, at the end.\n\n## Where the backend stands today\n\n**Direction: Rust first, TypeScript second** (`mzizi-registry` →\n`content/doctrine/documentation-architecture-framework/`), and Mzizi's backend is meant, in\nthe end, to be built in the Mzizi language itself (CHARTER v0.3). Neither is the state today,\nand a skill that implied otherwise would mislead you:\n\n- **Every Worker Mzizi runs is TypeScript.** `api.mzizi.dev` is `mzizi-dev/mzizi-api-gateway`,\n a Hono Worker. `mzizi-dev/agent-tools` holds `mzizi-mcp` (mcp.mzizi.dev), `mzizi-fundi` and\n `bushtrade-mcp`, all TypeScript, because the MCP SDK, `hono` and `agents` are TypeScript.\n- **The Rust building blocks exist.** Mzizi Roots server components (the `mzizi-roots-server`\n crate: `mzizi-assurance`, `mzizi-fundi`, `mzizi-docs`, `mzizi-discovery`) are sans-IO cores\n meant to be mounted in a Worker. The `workers-rs` adapter crate (`mzizi-worker`) is planned\n and has not landed, so today the Worker writes its own few lines of adapter.\n- **The Mzizi language has no handlers yet.** RFC-0011 (handlers) is a draft; nothing lowers to\n Rust. See the `mzizi-language` skill.\n\nSo: write a new service's logic in Rust as a sans-IO core. Use TypeScript where the work leans\non the MCP SDK or `agents` (see \"An MCP server\" below). Port nothing that works\nwithout a reason; when a service is ported, its existing tests and parity script are the\ncontract the port must meet.\n\n## The live endpoint map\n\nRead this before inventing a hostname. Each MCP has its own host, and the old umbrella\n`mcp.nyuchi.dev/{service}` path scheme is **not** how these are deployed:\n\n| Endpoint | Worker / source | Serves |\n| -------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------- |\n| `api.mzizi.dev` | `mzizi-api-gateway` (Hono, TypeScript) | The registry API: the registry's files at a pinned commit; no origin, no database |\n| `mcp.mzizi.dev/mcp` | `mzizi-mcp` (`mzizi-dev/agent-tools`) | The Mzizi registry, tokens, architecture, doctrine and skills (`mzizi_*`), plus the docs (`docs_*`) |\n| `mcp.mzizi.dev/mcp/signin` | the same Worker | The same MCP, answering 401 until you sign in: the URL for the Fundi tools |\n| `docs.mzizi.dev/mcp` | Mintlify (`mzizi-dev/mzizi-docs`) | The Mzizi docs; `mzizi-mcp` federates these as `docs_*` |\n| (Worker, no public route) | `bushtrade-mcp` (`mzizi-dev/agent-tools`) | Agentic commerce: task-driven UCP/AP2 verbs (Nyuchi) |\n| `docs.nyuchi.com/mcp` | `nyuchi-docs-mcp` (`nyuchi/nyuchi-docs`) | Nyuchi's own docs corpus, not Mzizi's |\n\n`design.nyuchi.com` is a 308 permanent redirect to `mzizi.dev`. Never reference it in new code\nor docs. `mzizi.dev/mcp` 308-redirects to `mcp.mzizi.dev/mcp` (method and body preserved), so\npoint new clients at `mcp.mzizi.dev/mcp` directly.\n\n**One MCP per surface.** Two endpoints over one corpus is two tool catalogues to keep in step,\ntwo access stories to explain, and a silent choice for every client author. That is why the\ndocs are federated into `mzizi-mcp` rather than listed as a second Mzizi MCP.\n\n## A Rust Worker\n\n### The shape: a sans-IO core and a thin host\n\nThe core owns routing and response shaping. The host owns I/O, bindings and secrets. This is\nthe shape of `n10-documentation/mzizi-docs-api.rs` in the registry, and it is what lets the\nsame core run in a Worker, in a container behind axum, and in a native unit test.\n\n```\nservice-name/\n├── src/\n│ ├── core.rs # routes, types, a Store trait — no `worker` import\n│ └── lib.rs # the Worker: reads bindings, calls the core, writes the response\n├── wrangler.toml\n└── Cargo.toml\n```\n\nThis example compiles against `worker` 0.8.7, natively and for `wasm32-unknown-unknown`:\n\n```toml\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nworker = \"0.8\"\n```\n\n```rust\n// src/core.rs\n//! The sans-IO core: typed request in, typed response out. No `worker` import,\n//! so it builds and tests natively and for wasm32 alike.\n\n/// Every route this service answers. An unhandled one is a compile error in `respond`.\n#[derive(Debug, PartialEq, Eq)]\npub enum Route {\n Health,\n Item(String),\n NotFound,\n}\n\n/// Parse a method and path into a route.\npub fn route(method: &str, path: &str) -> Route {\n match (method, path.trim_end_matches('/')) {\n (\"GET\", \"/health\") => Route::Health,\n (\"GET\", p) if p.starts_with(\"/v1/items/\") => Route::Item(p[\"/v1/items/\".len()..].to_string()),\n _ => Route::NotFound,\n }\n}\n\n/// What the core needs from the host, behind a trait so a test can pass an in-memory one.\npub trait Store {\n fn item(&self, id: &str) -> Option<String>;\n}\n\n/// Shape the answer: a status and a body. The host owns I/O, bindings and secrets.\npub fn respond(route: &Route, store: &dyn Store) -> (u16, String) {\n match route {\n Route::Health => (200, r#\"{\"ok\":true}\"#.to_string()),\n Route::Item(id) => match store.item(id) {\n Some(body) => (200, body),\n None => (404, r#\"{\"error\":\"not found\"}\"#.to_string()),\n },\n Route::NotFound => (404, r#\"{\"error\":\"no such route\"}\"#.to_string()),\n }\n}\n```\n\n```rust\n// src/lib.rs\nuse worker::*;\n\nmod core;\n\n/// A `Store` over data the Worker has already read from its bindings (KV, R2, D1).\nstruct Loaded(Option<String>);\n\nimpl core::Store for Loaded {\n fn item(&self, _id: &str) -> Option<String> {\n self.0.clone()\n }\n}\n\n#[event(fetch)]\nasync fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> {\n let route = core::route(req.method().as_ref(), &req.path());\n // The host does the I/O the core asked for, then hands the result over.\n let loaded = match &route {\n core::Route::Item(id) => env.kv(\"ITEMS\")?.get(id).text().await?,\n _ => None,\n };\n let (status, body) = core::respond(&route, &Loaded(loaded));\n let headers = Headers::new();\n headers.set(\"content-type\", \"application/json\")?;\n Ok(Response::ok(body)?.with_status(status).with_headers(headers))\n}\n\n#[event(scheduled)]\nasync fn scheduled(_event: ScheduledEvent, _env: Env, _ctx: ScheduleContext) {\n console_log!(\"[example] scheduled run\");\n}\n```\n\nTest the core natively with an in-memory `Store`, and assert status codes, headers and bodies\nper route there. That is the contract a server component carries until the language's handler\ncontracts (RFC-0010, RFC-0011) are implemented.\n\n### Project setup\n\n```bash\nnpm install -g wrangler\ncargo install worker-build\ncargo generate cloudflare/workers-rs\n```\n\n### wrangler.toml\n\n```toml\nname = \"mzizi-example-service\"\nmain = \"build/worker/shim.mjs\"\ncompatibility_date = \"2026-09-01\"\n\n[build]\ncommand = \"cargo install -q worker-build && worker-build --release\"\n\n[[kv_namespaces]]\nbinding = \"ITEMS\"\nid = \"<namespace id>\"\n\n[[triggers.crons]]\ncron = \"*/5 * * * *\"\n\n[[queues.consumers]]\nqueue = \"mzizi-example-signals\"\nmax_batch_size = 10\n```\n\nKeep `compatibility_date` current; a stale date silently pins you to old runtime behaviour.\nSecrets are set out of band with `wrangler secret put <NAME>` and never committed.\n\n### Storage\n\nA Mzizi service holds **no database client**. Its data is files bundled at build time (the way\n`api.mzizi.dev` and `mcp.mzizi.dev` serve the registry at a pinned commit) or Workers bindings:\nKV, R2, D1, Durable Objects. Put each behind a trait in the core so the core never imports\n`worker`. Only the Mzizi console (`app.mzizi.dev`, operated by Nyuchi) uses Supabase, and\nnothing else in the estate should talk to it.\n\n### Queue consumers\n\nA queue consumer needs the crate's `queue` feature:\n`worker = { version = \"0.8\", features = [\"queue\"] }`.\n\n```rust\n#[event(queue)]\npub async fn queue(batch: MessageBatch<String>, _env: Env, _ctx: Context) -> Result<()> {\n for message in batch.messages()? {\n // hand message.body() to the core, then acknowledge\n message.ack();\n }\n Ok(())\n}\n```\n\n## An MCP server\n\n### Transport\n\nMCP over HTTP is **Streamable HTTP**, JSON-RPC 2.0:\n\n- `POST /mcp` — JSON-RPC requests\n- `GET /mcp` — SSE stream\n- `DELETE /mcp` — session cleanup\n- `OPTIONS /mcp` — CORS preflight\n\nA server exposes `tools/list` and `tools/call`, and optionally `resources/list` and\n`prompts/list`.\n\nUse `@modelcontextprotocol/sdk`. TypeScript is the right choice for an MCP server today,\nbecause the SDK, `hono` and `agents` are TypeScript-first; put any heavy logic in a Rust core\n(\"A Rust Worker\" above). Pin the SDK version deliberately: `agents` hard-pins\n`@modelcontextprotocol/sdk`, and letting your own declaration drift produces two incompatible\n`McpServer` classes in the type graph and a private-field mismatch at typecheck.\n\n### One factory, many entrypoints\n\nKeep the server definition free of any runtime binding, then wrap it per surface. This is what\nlets the same catalogue ship as a Worker, a generic fetch handler and a local stdio bin\n(`mzizi-mcp/src/`):\n\n```\nsrc/\n├── server.ts # createXMcpServer(deps) → McpServer ← the only place tools are defined\n├── http.ts # createXHttpHandler() — any fetch runtime\n├── worker.ts # Cloudflare Worker entry, bound to env\n└── stdio.ts # bin for local clients / the MCP Registry\n```\n\n### The catalogue is code; the data is bundled\n\n`mzizi-mcp` defines its tools in code (`src/tools.ts`) and reads its data from files bundled at\nbuild time: `scripts/generate-registry.mjs` fetches the registry at the exact commit in\n`registry.pin.json`, and `scripts/generate-skills.mjs` inlines `mzizi-skills/`. There is **no\ndatabase client, dependency, env var or secret** behind a read tool. To move the MCP to newer\nregistry content, change the pinned commit in a pull request; the diff is the review.\n\nThe gates are tests, not a live drift check:\n\n1. **A tool-surface suite** connects a real MCP `Client` to the factory over an in-memory\n transport and asserts the contract: the tool names and prefix, annotations, input and output\n schemas, and which tools need sign-in (`test/tool-surface.test.ts`, `free-tier.test.ts`).\n2. **A bundle suite** calls every tool against the real bundled data with the network disabled,\n and asserts that what a tool serves is byte-identical to the file it came from\n (`registry-bundle.test.ts`, `rust-first.test.ts`, `skills-bundle.test.ts`, `no-database.test.ts`).\n\n**Federating another MCP.** `mzizi-mcp` lists the docs MCP's tools under a `docs_` prefix and\nforwards each call, with a cached tool list and a clear tool error when the upstream fails\n(`src/docs-federation.ts`). Prefer that to copying another server's content.\n\n### Tool naming and annotations\n\nTools are `snake_case`, prefixed with the server (`mzizi_`), and named for the action:\n`mzizi_search`, `mzizi_get_component`, `mzizi_list_components`, `mzizi_get_tokens`,\n`mzizi_get_architecture`, `mzizi_get_doctrine`, `mzizi_get_skills`,\n`mzizi_check_accessibility`, `mzizi_report_issue`, `mzizi_fundi`, `mzizi_mcp_describe`. Read the\nlive list with `tools/list` rather than trusting a copy, this one included. Inputs take plain\nnames (`{ \"name\": \"button\" }`); the old `p_`-prefixed database parameters are gone.\n\nEvery tool carries a `title`, an `outputSchema`, and honest behaviour hints:\n\n- `readOnlyHint` — true only if the tool cannot mutate anything. An action that runs an audit\n or files a report is **not** read-only.\n- `destructiveHint` / `idempotentHint` / `openWorldHint` — set them deliberately; clients use\n them to decide what to auto-approve. A tool that reads only bundled data is\n `openWorldHint: false`.\n\nReturn `structuredContent` matching the declared `outputSchema`.\n\n### Static assets\n\nIf the Worker also serves `llms.txt`, `robots.txt` or `/.well-known/*` cards, put them behind an\n`[assets]` block so they resolve ahead of the OAuth handler; everything else falls through to\nthe server.\n\n### Instrumentation\n\nIf you report tool errors to an observability sink, do it fire-and-forget via\n`ctx.waitUntil`, so reporting never blocks or fails the response.\n\n### Publishing to npm and the MCP Registry\n\n`server.json` next to `package.json` is the source of truth, and `package.json#mcpName`\n**must** equal `server.json#name` or the registry refuses the publish. Registry names are\n`io.github.mzizi-dev/<server>`: the namespace is the GitHub owner of the repository the publish\nruns from, because `mcp-publisher login github-oidc` only authorises `io.github.<owner>/*`. The\nold `io.github.nyuchi/mzizi-mcp` entry is stale and no longer updated.\n\nRelease checklist:\n\n1. Bump the version everywhere it is stated: the constant the server reports in `serverInfo`\n (`MZIZI_MCP_VERSION` in `src/server.ts`), `package.json#version`, `server.json#version`,\n and `server.json#packages[0].version`. A mismatch here is the most common failed publish.\n2. Merge to `main`. `.github/workflows/publish-mzizi-mcp.yml` publishes to npm (skipped if the\n version is already there) and then runs `mcp-publisher login github-oidc` and\n `mcp-publisher publish`, treating \"this version already exists\" as success. OIDC means no\n registry secret. A merge that does not change the version is a no-op.\n\n## Access: free, except what acts as a person (every service)\n\nOwner decision, 2026-09-29: **the MCP server and the CLI are free, with no gate.** An anonymous\nsession can `initialize`, `tools/list` and call every tool except the Fundi ones. Gating starts\nat **Fundi**: `mzizi_fundi` and `mzizi_report_issue` act as a Mzizi console user.\n\nHow `mzizi-mcp` does it (`src/worker.ts`, `src/access.ts`):\n\n- A tokenless request to `/mcp` is routed around the OAuth provider, rate-limited per IP, and\n served with no user.\n- Any request with an `Authorization` header, and `/mcp/signin`, goes through\n `@cloudflare/workers-oauth-provider`, which validates the token and puts the WorkOS identity\n on `ctx.props`. A bad or expired token still gets the 401 that makes a client refresh.\n- The gated tools stay **listed** to an anonymous caller and answer with a tool error that\n starts `Sign-in required:` and says how to sign in. `access.ts` is the one list.\n\nWhen a person signs in, it is WorkOS Connect OAuth with PKCE (a public client, no secret). A\nmachine calling an internal service uses WorkOS M2M; only the Worker holds that secret, and it\nmints the token and proxies the call so a CLI never holds it. Auth happens **inline in the\nclient**, never by punting the user to a browser tab mid-task.\n\nValidate an inbound token against the WorkOS JWKS before any privileged work, and cache the\nJWKS; refetching per request is the classic latency bug. Return `401` for a missing or invalid\ntoken and `403` for a valid token without the required scope. Never collapse the two, or\ncallers cannot tell \"sign in\" from \"you can't do this\". Cron, queue and health routes are not\nuser-triggered and bypass the gate by design.\n\n## Logging (every service)\n\nPrefix every line with the service in brackets, as `mzizi-fundi` does with `[fundi]`, so it is\ngreppable across services:\n\n```rust\nconsole_log!(\"[example] cycle complete: {} records processed\", count);\nconsole_error!(\"[example] upstream error: {:?}\", e);\n```\n\nNever log a token, a secret, or a full request body.\n\n## Deploy (every service)\n\n```bash\nwrangler dev # local\nwrangler deploy # manual only; normally CI deploys\n```\n\nWire deploys to CI rather than a laptop, so what is live is what is merged. `mzizi-mcp` is\ndeployed by Cloudflare Workers Builds on every push to `main`, including its bundled registry\npin and skills. Run the test suites before you merge, and a live smoke pass of every read tool\n(`mzizi-mcp/scripts/integration.mjs`) after.",
|
|
32
|
+
"source": "mzizi-dev/agent-tools/mzizi-skills/skills/mzizi-backend"
|
|
45
33
|
},
|
|
46
34
|
{
|
|
47
|
-
"name": "
|
|
48
|
-
"description": "
|
|
49
|
-
"body_mdx": "#
|
|
50
|
-
"source": "mzizi-tools/mzizi-skills/skills/
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
"name": "cloudflare-worker-rust",
|
|
54
|
-
"description": "Build a nyuchi Cloudflare Worker in Rust with workers-rs: project layout, wrangler config, cron triggers and queue consumers, the Supabase access pattern, WorkOS token validation by audience, logging conventions, and deploy. Covers when Rust is the right choice versus the TypeScript worker stack.",
|
|
55
|
-
"body_mdx": "# Cloudflare Worker in Rust — the nyuchi pattern\n\n`workers-rs` compiles Rust to WebAssembly and runs it on the Cloudflare Worker runtime. This\nskill is the house pattern for nyuchi services written that way.\n\n## Pick the language before you pick the pattern\n\nBoth stacks are in production, and the choice is not stylistic:\n\n- **Rust (`workers-rs`)** — CPU-bound work, strict correctness requirements, or a service that\n already shares Rust crates with its siblings. The Nyuchi Console (`nyuchi/mukoko-platform`)\n runs Rust workers.\n- **TypeScript** — anything that leans on the MCP SDK, `hono`, `agents`, Durable Objects, or\n the Supabase JS clients. Every Worker in `mzizi-dev/agent-tools` (`mzizi-mcp`, `fundi-tester`,\n `bushtrade-mcp`) is TypeScript for exactly this reason: the ecosystem's MCP and agent\n libraries are TS-first.\n\nIf you are adding an MCP server or an agent surface, you almost certainly want TypeScript —\nsee the `mcp-server-cloudflare` skill. Reach for Rust when the workload, not the taste,\njustifies it.\n\n## Project setup\n\n```bash\nnpm install -g wrangler\ncargo generate cloudflare/workers-rs\n```\n\n```\nservice-name/\n├── src/\n│ ├── lib.rs # Worker entry points\n│ ├── router.rs # Request routing\n│ ├── supabase.rs # Supabase access\n│ ├── auth.rs # WorkOS token validation\n│ └── handlers/ # One file per route\n├── wrangler.toml\n└── Cargo.toml\n```\n\n## wrangler.toml\n\n```toml\nname = \"nyuchi-example-service\"\nmain = \"build/worker/shim.mjs\"\ncompatibility_date = \"2026-01-01\"\n\n[build]\ncommand = \"cargo install -q worker-build && worker-build --release\"\n\n# Secrets — set out-of-band with `wrangler secret put <NAME>`, never committed:\n# SUPABASE_URL\n# SUPABASE_SECRET_KEY\n# WORKOS_M2M_CLIENT_ID\n# WORKOS_M2M_CLIENT_SECRET\n\n[[triggers.crons]]\ncron = \"*/5 * * * *\"\n\n[[queues.consumers]]\nqueue = \"nyuchi-example-signals\"\nmax_batch_size = 10\n```\n\nKeep `compatibility_date` current; a stale date silently pins you to old runtime behaviour.\n\n## Entry points\n\n```rust\n// src/lib.rs\nuse worker::*;\nmod router;\nmod supabase;\nmod auth;\n\n#[event(fetch)]\npub async fn main(req: Request, env: Env, _ctx: Context) -> Result<Response> {\n router::handle(req, env).await\n}\n\n// Cron trigger\n#[event(scheduled)]\npub async fn scheduled(_event: ScheduledEvent, env: Env, _ctx: ScheduleContext) {\n if let Err(e) = jobs::run_cycle(&env).await {\n console_error!(\"[nyuchi:example] cycle error: {:?}\", e);\n }\n}\n\n// Queue consumer\n#[event(queue)]\npub async fn queue(batch: MessageBatch<String>, env: Env, _ctx: Context) -> Result<()> {\n jobs::process_signals(batch, &env).await\n}\n```\n\n## Supabase access\n\nTwo routes, and the choice matters for latency:\n\n- **PostgREST over `fetch`** — no extra infrastructure, works from any Worker, and is what the\n snippet below does. Fine for a handful of reads per request.\n- **Hyperdrive** — globally-pooled native Postgres, so no TCP handshake per request and the\n full SQL function surface is available. Worth it once a service is chatty or needs RPCs.\n\n```rust\n// src/supabase.rs\npub struct SupabaseClient {\n url: String,\n secret_key: String,\n}\n\nimpl SupabaseClient {\n pub fn from_env(env: &Env) -> Result<Self> {\n Ok(Self {\n url: env.secret(\"SUPABASE_URL\")?.to_string(),\n secret_key: env.secret(\"SUPABASE_SECRET_KEY\")?.to_string(),\n })\n }\n\n pub async fn get(&self, table: &str, query: &str) -> Result<String> {\n let url = format!(\"{}/rest/v1/{}?{}\", self.url, table, query);\n let mut headers = Headers::new();\n headers.set(\"apikey\", &self.secret_key)?;\n headers.set(\"Authorization\", &format!(\"Bearer {}\", self.secret_key))?;\n headers.set(\"Content-Type\", \"application/json\")?;\n\n let req = Request::new_with_init(&url, RequestInit::new().with_headers(headers))?;\n let mut resp = Fetch::Request(req).send().await?;\n resp.text().await\n }\n}\n```\n\nA service-role key bypasses RLS. Only reach for it on a server-side write path; anything\nserving public reads should use the anon key and let RLS do the gating.\n\n## Auth — decide by audience\n\nAuthentication follows the ecosystem's auth-by-audience doctrine:\n\n- **Public / customer surfaces** → WorkOS **Connect OAuth PKCE** (public client, no secret).\n Entitlement is a commerce subscription, never a per-user tag.\n- **Internal staff / machine surfaces** → WorkOS **M2M** (client-credentials). Only the Worker\n holds the secret; CLIs and browsers never do. Per-route scopes come from the token claims.\n\nValidate the inbound token against the WorkOS JWKS before doing any privileged work, and cache\nthe JWKS — refetching per request is the classic latency bug:\n\n```rust\n// src/auth.rs\npub struct Claims { pub sub: String, pub scopes: Vec<String> }\n\npub async fn validate_token(req: &Request, env: &Env) -> Result<Option<Claims>> {\n let Some(header) = req.headers().get(\"Authorization\")? else {\n return Ok(None); // no token — the caller decides whether that is allowed\n };\n let token = header.strip_prefix(\"Bearer \").unwrap_or(&header);\n\n // 1. Fetch (and cache) the JWKS for the WorkOS environment.\n // 2. Verify signature, `exp`, `iss`, and `aud`.\n // 3. Map the verified claims onto `Claims`.\n verify_against_jwks(token, env).await\n}\n```\n\nReturn `401` on an invalid token and `403` on a valid token missing the required scope — never\ncollapse the two, or callers can't tell \"log in\" from \"you can't do this\".\n\nRoutes that are not user-triggered — cron, queue consumers, health — bypass the gate by design.\n\n## Logging convention\n\nEvery log line carries a `[nyuchi:service-name]` prefix so it is greppable across services:\n\n```rust\nconsole_log!(\"[nyuchi:example] cycle complete: {} records processed\", count);\nconsole_error!(\"[nyuchi:example] upstream error: {:?}\", e);\n```\n\nNever log a token, a secret, or a full request body.\n\n## Deploy\n\n```bash\nwrangler dev # local\nwrangler deploy # production\n\nwrangler secret put SUPABASE_URL\nwrangler secret put SUPABASE_SECRET_KEY\nwrangler secret put WORKOS_M2M_CLIENT_ID\nwrangler secret put WORKOS_M2M_CLIENT_SECRET\n```\n\nPrefer wiring deploys to CI (a workflow on push to `main`, or Cloudflare Workers Builds) over\ndeploying from a laptop, so what is live always matches what is merged.",
|
|
56
|
-
"source": "mzizi-tools/mzizi-skills/skills/cloudflare-worker-rust"
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"name": "mcp-server-cloudflare",
|
|
60
|
-
"description": "Add or change an MCP server running as a Cloudflare Worker in the Mzizi ecosystem: the live endpoint map, one-factory/many-entrypoints layout, Streamable HTTP transport, registry-driven tool catalogues with drift gates, tool naming and annotations, the auth-by-audience gate (Connect OAuth PKCE vs M2M), and publishing to the MCP registry.",
|
|
61
|
-
"body_mdx": "# MCP server on Cloudflare Workers — the ecosystem pattern\n\n## The live endpoint map\n\nRead this before inventing a hostname — each MCP has its own host, and the umbrella\n`mcp.nyuchi.dev/{service}` path scheme is **not** how these are deployed:\n\n| Endpoint | Worker | Serves |\n| --------------------- | ----------------- | --------------------------------------------------------------- |\n| `mcp.mzizi.dev/mcp` | `mzizi-mcp` | The Mzizi registry — components, tokens, architecture, doctrine |\n| `docs.nyuchi.com/mcp` | `nyuchi-docs-mcp` | The docs corpus — search / ask / read / feedback |\n| (Worker) | `bushtrade-mcp` | Agentic commerce — task-driven UCP/AP2 verbs |\n\n`design.nyuchi.com` is a 308 permanent redirect to `mzizi.dev`. Never reference it in new code\nor docs.\n\n**One MCP per surface.** Mzizi previously ran a second, unauthenticated MCP at\n`mzizi.dev/mcp` inside the portal, serving a subset of the same registry. Two\nendpoints over one corpus is two tool catalogues to keep in step, two auth\nstories to explain, and a silent choice for every client author. `mzizi.dev/mcp`\nnow 308-redirects to `mcp.mzizi.dev/mcp` — method and body preserved, so an\nin-flight `POST` survives the hop. Point new clients at `mcp.mzizi.dev/mcp`\ndirectly. It is free with no account; only the Fundi tools (`mzizi_fundi`,\n`mzizi_report_issue`) need a free WorkOS sign-in, at `mcp.mzizi.dev/mcp/signin`.\n\n## Transport\n\nMCP over HTTP is **Streamable HTTP**, JSON-RPC 2.0:\n\n- `POST /mcp` — JSON-RPC requests\n- `GET /mcp` — SSE stream\n- `DELETE /mcp` — session cleanup\n- `OPTIONS /mcp` — CORS preflight\n\nA server exposes `tools/list` + `tools/call`, and optionally `resources/list` and\n`prompts/list`.\n\nUse `@modelcontextprotocol/sdk` — TypeScript is the right choice here, because the SDK, `hono`,\nand `agents` are all TS-first. Pin the SDK version deliberately: `agents` hard-pins\n`@modelcontextprotocol/sdk`, and letting your own declaration drift produces two incompatible\n`McpServer` classes in the type graph and a private-field mismatch at typecheck.\n\n## One factory, many entrypoints\n\nKeep the server definition free of any runtime binding, then wrap it per surface. This is what\nlets the same catalogue ship as a Worker, a generic fetch handler, and a local stdio bin:\n\n```\nsrc/\n├── server.ts # createXMcpServer(deps) → McpServer ← the only place tools are defined\n├── http.ts # createXHttpHandler() — any fetch runtime\n├── worker.ts # Cloudflare Worker entry, bound to env\n└── stdio.ts # bin for local clients / the MCP registry\n```\n\n## Prefer a registry-driven tool catalogue\n\nHardcoding a tool list means every new tool is a code change and a deploy. Build the catalogue\nat startup from a table instead, and dispatch by the row's `kind`:\n\n- `sql_function` → `supabase.rpc(...)`\n- `edge_function` → `supabase.functions.invoke(...)`\n- `source_table` → `supabase.from(...).select(...)`\n\nKeep only genuinely code-shaped tools (health, collection listing) in source.\n\n**Registry-driven surfaces need two gates**, because a table edit changes what the server\nexposes with no code change:\n\n1. An **offline suite** that connects a real MCP `Client` to the factory over an in-memory\n transport and asserts the contract — the tool prefix, annotations, filtering, output schemas,\n and dispatch routing.\n2. A **live drift gate** that reconstructs the public surface from the live registry and diffs\n it against a committed manifest, failing when a tool was added, edited, or removed without\n updating it. Run it before deploy.\n\n## Tool naming and annotations\n\nTools are `snake_case` and named for the action: `list_components`, `get_component`,\n`search_components`, `get_node_documents`, `get_brand_tokens`, `get_skill`.\n\nEvery tool carries a `title`, an `outputSchema`, and honest behaviour hints:\n\n- `readOnlyHint` — true only if the tool cannot mutate anything. An action-kind tool that runs\n an audit is **not** read-only.\n- `destructiveHint` / `idempotentHint` / `openWorldHint` — set them deliberately; clients use\n them to decide what to auto-approve.\n\nReturn `structuredContent` matching the declared `outputSchema` (array results wrapped under\n`rows`, scalars under `result`).\n\nNote the RPC parameter convention: registry functions take `p_`-prefixed params. Call\n`get_component` with `{ p_name }`, `search_components` with `{ p_query, p_limit }`. Getting the\nprefix wrong surfaces as \"Could not find function public.get_component(name)\".\n\n## Auth — decide by audience\n\n- **Public / customer MCPs** (`mzizi-mcp`, `bushtrade-mcp`) → **WorkOS Connect OAuth PKCE**,\n a public client with no secret, each MCP with its own Connect app. Entitlement for paid\n surfaces is a commerce subscription, never a per-user tag.\n- **Internal staff / machine surfaces** → **WorkOS M2M** (client-credentials). Only the Worker\n holds the secret. A Worker that fronts an internal service mints the M2M token itself and\n proxies the call, so a CLI never has to hold it.\n\nWrite-kind tools stay off the anonymous surface. If a public write is genuinely needed, keep it\nto an append-only sink on an explicit allow-list, running `SECURITY DEFINER` or self-gating —\nand enumerate those exceptions in one place in the source.\n\nAuth happens **inline in the client**, never by punting the user to a browser tab.\n\n## Static assets\n\nIf the Worker also needs to serve `llms.txt` or `robots.txt`, put them behind an `[assets]`\nblock so they resolve ahead of the OAuth handler; everything else falls through to the server.\n\n## Instrumentation\n\nWrap tool dispatch and emit an observability event on any tool error, fire-and-forget via\n`ctx.waitUntil` so it never blocks the response. Errors feed the fundi self-healing loop;\ndeliberately injected faults go to the chaos stream instead.\n\n## Publishing to the MCP registry\n\n`server.json` next to `package.json` is the source of truth, and\n`package.json#mcpName` **must** equal `server.json#name` or the registry refuses the publish.\nRegistry names look like `io.github.mzizi-dev/<server>`: the namespace is the GitHub owner of the\nrepository the publish runs from, because `mcp-publisher login github-oidc` only authorises\n`io.github.<owner>/*`. (`io.github.nyuchi/*` stopped working when the repos moved to mzizi-dev.)\n\nRelease checklist:\n\n1. Bump the version everywhere it is stated — the version constant the server reports in\n `serverInfo`, `package.json#version`, `server.json#version`, and\n `server.json#packages[0].version`. A mismatch here is the most common failed publish.\n2. Tag and push. CI publishes to npm with provenance, then runs\n `mcp-publisher login github-oidc` followed by `mcp-publisher publish` to reach the registry.\n The login uses OIDC, so no registry secret is needed.\n\n## Deploy\n\n```bash\nwrangler dev\nwrangler deploy\nwrangler secret put SUPABASE_URL\nwrangler secret put SUPABASE_PUBLISHABLE_KEY\n```\n\nWire the deploy to CI on push to `main` so what is live matches what is merged, and run the\ndrift gate + a live smoke pass of every read tool before `wrangler deploy`.",
|
|
62
|
-
"source": "mzizi-tools/mzizi-skills/skills/mcp-server-cloudflare"
|
|
35
|
+
"name": "discoverability",
|
|
36
|
+
"description": "SEO + AIO for the Mzizi ecosystem: how to guarantee machines can find and render a site — social/messaging link previews (OpenGraph), search engines, and AI agents. Covers the 'if the machine can't see it, it doesn't exist' convention, the brand-agnostic Mzizi hive OG-image template (dynamic per page: title + description + site icon), build-time OG images on static Astro (the standard), the Next.js reference template in mzizi-registry, APCA-checked text colours, and a pre-ship checklist. Use when a shared link shows no preview or when adding a new route/site.",
|
|
37
|
+
"body_mdx": "# Discoverability — SEO + AIO for the Mzizi ecosystem\n\n> **Covenant:** _If the machine can't see it, it doesn't exist._\n\nDiscoverability is the machine-facing surface of every Mzizi-ecosystem site — the\npart crawlers, social/messaging scrapers, and AI agents read. It is **cross-cutting**,\nnot a component you install: it is a genetic-code **convention** (\"every surface ships\na title, a description, a dynamic preview image, structured data, and an\nagent-readable index\") base-paired to the components that satisfy it (the OG template,\n`llms.txt`, JSON-LD helpers). A surface a machine can't parse may as well not exist —\nand a **missing social preview is the single biggest discoverability no-go**, because\nit's the first thing a human sees when a link is shared.\n\nAll values here are the current doctrine. The source of truth is the repo:\n`mzizi-dev/mzizi-registry` for doctrine and tokens, this package for the skills, served\nread-only at <https://api.mzizi.dev/v1> and by the MCP at `https://mcp.mzizi.dev/mcp`. There is\nno database behind any of it. Brand colour specifically is `palette.canonical.json` in this\npackage: 21 families, generated and gated, never retyped.\n\n## The number-one failure: a missing OG image\n\nThe most common — and most damaging — bug is a link that unfurls with **no preview\nimage at all**. The canonical cause is metadata that points at an image file that\ndoesn't exist:\n\n```ts\n// ❌ WRONG — points at a static file that isn't in /public, so every scraper 404s\nopenGraph: { images: [\"/og-image.png\"] }\ntwitter: { images: [\"/og-image.png\"] }\n```\n\nWorse, an explicit `images` array **overrides** a framework's dynamic OG route, so\nthe good image never gets a chance. **Rule: never hardcode `openGraph.images`.** Let\nthe framework's file-convention / endpoint serve a _generated_ image, and set an\nabsolute base URL so scrapers get an absolute URL.\n\n## The Mzizi \"hive\" OG card\n\nOne template, every route, every brand. The card is:\n\n- **Deep-night ground** (`#1B1A17`) with a faint flat-top **honeycomb hex lattice**\n (white at ~5% — texture, not noise) and a few solid **mineral accent cells**\n (cobalt / gold lead, plus tanzanite / malachite / copper for warmth).\n- The **site icon on top, above the title** (brand recognition first), then the\n title, an optional eyebrow, the SEO description, and the domain.\n- **Left-aligned** (reads better than centred at thumbnail size).\n- **Brand-agnostic:** the icon is a parameter, so the same template serves nyuchi\n (the bee), mukoko (the swarm), mzizi, shamwari — each with its own mark + domain.\n It picks up the _site's own icon_, never a hardcoded one.\n\n### Colours are APCA-checked\n\nOG text is read at thumbnail size, so contrast matters. Verified against the\n`#1B1A17` ground (APCA reverse polarity; design-system floor is **Lc 75 large /\nLc 90 body**):\n\n| Role | Hex | APCA Lc | Verdict |\n| -------------------------------- | --------- | ------- | ------- |\n| Title | `#F5F5F4` | 92 | ✓ body |\n| Secondary (eyebrow, description) | `#E5E3DE` | 81 | ✓ large |\n| Domain (gold) | `#FFD740` | 76 | ✓ large |\n\nDo **not** use dim greys like `#B2AFA8` (only Lc 50) for OG text — it looks fine on a\nmonitor and vanishes in a messaging thumbnail. Re-run APCA when changing any pair.\n\n## Implementation — Astro, static (the standard)\n\nNew surfaces are static Astro: mzizi.dev (`mzizi-dev/mzizi-site`) is one. A statically built\nsite generates its OG images **at build time**:\n\n1. A build-time image endpoint, `src/pages/og/[...slug].png.ts`, with `getStaticPaths()` over\n the site's pages or content collection. Render each page's title and description with\n **satori** to SVG, then rasterise with **sharp** to PNG.\n2. In the shared layout's `<head>`, set\n `<meta property=\"og:image\" content={new URL(`/og/${slug}.png`, Astro.site)} />` per page,\n plus `twitter:card` = `summary_large_image`. Set `site` in `astro.config.mjs` so every URL is\n absolute.\n3. Use the hive design: honeycomb, the site's icon, and the brand mineral.\n\nKeep the site's runtime dependency-light; satori and sharp are build-only. If the site renders\nMzizi Roots components, they arrive as static HTML from `dioxus-ssr` and change nothing here.\n\n**Docs on Mintlify** (docs.mzizi.dev) get their preview images from Mintlify. Check them with a\nscraper like any other surface; do not hand-wire `og:image` there.\n\n## Reference template — Next.js (App Router)\n\n`mzizi-registry` is a Next.js app and still carries the reference template at\n`lib/og/mzizi-og.tsx` (`renderMziziOg({ title, eyebrow, description, iconPath, domain })`),\nwired through `app/opengraph-image.tsx`. It no longer serves mzizi.dev. Do not start a new\nsurface on Next.js; use this only for a surface that is already on it:\n\n```tsx\n// app/opengraph-image.tsx (root) — and app/<section>/[slug]/opengraph-image.tsx\nimport { renderMziziOg, OG_SIZE, OG_CONTENT_TYPE } from \"@/lib/og/mzizi-og\"\nexport const runtime = \"nodejs\" // reads the icon PNG off disk\nexport const size = OG_SIZE\nexport const contentType = OG_CONTENT_TYPE\nexport default function Image() {\n return renderMziziOg({ title: \"Mzizi\" }) // per page: pass that page's title + description\n}\n```\n\n```ts\n// app/layout.tsx — set metadataBase, DO NOT set openGraph.images / twitter.images\nexport const metadata = {\n metadataBase: new URL(\"https://example.org\"),\n openGraph: { type: \"website\", siteName: \"Mzizi\", title: \"Mzizi\", description: \"…\" },\n twitter: { card: \"summary_large_image\", title: \"Mzizi\", description: \"…\" },\n // No `images` — the opengraph-image.tsx route supplies an absolute, hashed URL,\n // and the twitter card falls back to it.\n}\n```\n\n## Beyond the image — the rest of the convention\n\n- **Title + meta description** on every route (unique, page-specific).\n- **Structured data**: schema.org JSON-LD for the page's primary entity.\n- **`llms.txt`** at the site root — the machine-readable index for LLMs.\n- **`robots.txt` + `sitemap.xml`** — generated, not hand-maintained.\n- **Canonical URLs** absolute (a set `metadataBase` / `site`).\n- **Agent-readiness**: an MCP discovery card at `/.well-known/mcp.json` pointing at the MCP\n endpoint (mzizi.dev ships one for `https://mcp.mzizi.dev/mcp`).\n\n## Pre-ship checklist\n\n- [ ] Sharing the URL in Slack / WhatsApp / iMessage / X renders a preview **image**.\n- [ ] The OG image is **generated** (dynamic), carries the page's title + description + the site icon, and its URL is **absolute**.\n- [ ] No hardcoded `openGraph.images` / `twitter.images`.\n- [ ] OG text colours pass APCA (Lc ≥ 75 large / ≥ 90 body).\n- [ ] `metadataBase` / `site` is set; canonical URL is absolute.\n- [ ] Title + meta description are unique per route.\n- [ ] `llms.txt`, `robots.txt`, `sitemap.xml` present; JSON-LD on primary entities.\n- [ ] Validate the card in a scraper (e.g. opengraph.xyz) after deploy.\n\n_The whole helix is Mzizi — and discoverability is the convention that makes each\nsurface legible to the machines that carry it. Ndiri nekuti tiri._",
|
|
38
|
+
"source": "mzizi-dev/agent-tools/mzizi-skills/skills/discoverability"
|
|
63
39
|
}
|
|
64
40
|
];
|
|
65
41
|
/** Bundle version, from mzizi-skills/index.json. */
|
|
66
|
-
export const SKILLS_VERSION = "0.
|
|
42
|
+
export const SKILLS_VERSION = "0.8.1";
|
|
67
43
|
//# sourceMappingURL=skills.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/generated/skills.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,MAAM,CAAC,MAAM,cAAc,GAA4B;IACrD;QACE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/generated/skills.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,MAAM,CAAC,MAAM,cAAc,GAA4B;IACrD;QACE,MAAM,EAAE,gBAAgB;QACxB,aAAa,EAAE,gdAAgd;QAC/d,UAAU,EAAE,60oBAA60oB;QACz1oB,QAAQ,EAAE,0DAA0D;KACrE;IACD;QACE,MAAM,EAAE,aAAa;QACrB,aAAa,EAAE,wpBAAwpB;QACvqB,UAAU,EAAE,+wmCAA+wmC;QAC3xmC,QAAQ,EAAE,uDAAuD;KAClE;IACD;QACE,MAAM,EAAE,cAAc;QACtB,aAAa,EAAE,4mBAA4mB;QAC3nB,UAAU,EAAE,shnBAAshnB;QAClinB,QAAQ,EAAE,wDAAwD;KACnE;IACD;QACE,MAAM,EAAE,eAAe;QACvB,aAAa,EAAE,moBAAmoB;QAClpB,UAAU,EAAE,q1gBAAq1gB;QACj2gB,QAAQ,EAAE,yDAAyD;KACpE;IACD;QACE,MAAM,EAAE,iBAAiB;QACzB,aAAa,EAAE,ujBAAujB;QACtkB,UAAU,EAAE,kmOAAkmO;QAC9mO,QAAQ,EAAE,2DAA2D;KACtE;CACO,CAAA;AAEV,oDAAoD;AACpD,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAA"}
|
package/dist/server.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type { Registry } from "./registry.js";
|
|
|
16
16
|
import { type DocsFederationOptions } from "./docs-federation.js";
|
|
17
17
|
import type { FundiToolContext } from "./fundi-client.js";
|
|
18
18
|
import { type IssueDeskContext } from "./tools.js";
|
|
19
|
-
export declare const MZIZI_MCP_VERSION = "0.11.
|
|
19
|
+
export declare const MZIZI_MCP_VERSION = "0.11.2";
|
|
20
20
|
export type CreateServerOptions = {
|
|
21
21
|
fundi?: FundiToolContext;
|
|
22
22
|
/** The signed-in user's issue desk. See `tools.ts` — absent off the Worker. */
|
package/dist/server.js
CHANGED
|
@@ -17,7 +17,7 @@ import { registerDocsTools, DOCS_MCP_URL } from "./docs-federation.js";
|
|
|
17
17
|
import { registerMziziTools, TOOL_COVERAGE, TOOL_PREFIX } from "./tools.js";
|
|
18
18
|
import { jsonContent } from "./tool-result.js";
|
|
19
19
|
import { accessFor, accessSummary } from "./access.js";
|
|
20
|
-
export const MZIZI_MCP_VERSION = "0.11.
|
|
20
|
+
export const MZIZI_MCP_VERSION = "0.11.2";
|
|
21
21
|
/**
|
|
22
22
|
* What this server exposes, computed from what was actually registered.
|
|
23
23
|
*
|