{
  "markdown": "<p align=\"center\">\n  <img src=\"assets/logo.svg\" alt=\"MDMA Logo\" width=\"160\" />\n</p>\n\n<h1 align=\"center\">MDMA</h1>\n<p align=\"center\">Markdown Document with Mounted Applications</p>\n<p align=\"center\">Interactive documents from Markdown. Built for next gen-apps</p>\n<p align=\"center\">\n  <a href=\"https://mdma.software\"><b>🌐 Website</b></a>\n  &nbsp;&nbsp;·&nbsp;&nbsp;\n  <a href=\"https://mobilereality.github.io/mdma/\"><b>🚀 Live Demo</b></a>\n  &nbsp;&nbsp;·&nbsp;&nbsp;\n  <a href=\"https://mobilereality.github.io/mdma/#/docs\"><b>📖 Docs</b></a>\n  &nbsp;&nbsp;·&nbsp;&nbsp;\n  <a href=\"https://discord.gg/etGSuCuR7B\"><b>💬 Discord</b></a>\n  &nbsp;&nbsp;·&nbsp;&nbsp;\n  <a href=\"https://huggingface.co/MobileReality/mdma-gemma4-26b-dsl-unsloth-v1\"><b>🤗 Model</b></a>\n</p>\n\n\n\n\n## Why MDMA?\n\nAI conversations today are plain text — the user reads a response and manually acts on it. MDMA changes that. When an LLM knows the MDMA spec, it can respond with interactive components (forms, tables, approval gates) instead of just text. The conversation becomes actionable: the user fills out a form, approves a step, or reviews structured data — all inline, with a predictable schema that your app already knows how to render and process.\n\nNo custom UI per use case. No parsing free-form text. The AI generates structured, validated components and your frontend renders them instantly.\n\n<p align=\"center\">\n  <img src=\"assets/mdma-3.gif\" alt=\"MDMA Demo\" width=\"800\" />\n</p>\n\n## What is MDMA?\n\nMDMA extends Markdown with interactive components defined in fenced `mdma` code blocks. A regular Markdown file becomes an interactive application:\n\n````markdown\n# Patient Intake\n\n```mdma\ntype: form\nid: intake-form\nfields:\n  - name: patient-name\n    type: text\n    label: \"Full Name\"\n    required: true\n    sensitive: true\n  - name: email\n    type: email\n    label: \"Email\"\n    required: true\n    sensitive: true\n  - name: reason\n    type: textarea\n    label: \"Reason for Visit\"\n    required: true\nonSubmit: submit-intake\n```\n\n```mdma\ntype: button\nid: submit-btn\ntext: \"Submit Intake Form\"\nvariant: primary\nonAction: submit\n```\n````\n\n\n## Speed comparison\n\nSame scenario, two models. GPT-5.5 and our own hosted MDMA-IL model.\n\nOur model is available on Hugging Face: [MobileReality/mdma-gemma4-26b-dsl-unsloth-v1](https://huggingface.co/MobileReality/mdma-gemma4-26b-dsl-unsloth-v1)\n\n| GPT-5.5 | Our own hosted model |\n| :---: | :---: |\n| <img src=\"assets/gpt-5.5.gif\" width=\"100%\"> | <img src=\"assets/own-model.gif\" width=\"100%\"> |\n\n\n## MDMA_AUTHOR prompt matrix\n\nEach cell shows the pass rate of the model-specialized MDMA_AUTHOR prompt variant on the listed eval suite.\n\n✅ 100% on the suite.\n\n🟡 Scoring between 80–99% on the suite.\n\n🔴 Scoring below 80% on the suite.\n\n\n| Variant | one-shot | one-shot with custom prompt | conversation | specific flow of conversation |\n| :--- | :---: | :---: | :---: | :---: |\n| **OpenAI** | | | | |\n| `gpt-5.6-sol` | ✅ | ✅ | ✅ | ✅ |\n| `gpt-5.6-terra` | ✅ | ✅ | ✅ | ✅ |\n| `gpt-5.6-luna` | ✅ | ✅ | ✅ | ✅ |\n| `gpt-5.5` | ✅ | ✅ | ✅ | ✅ |\n| `gpt-5.4` | ✅ | ✅ † | ✅ † | ✅ † |\n| `gpt-5.4-mini` | ✅ | ✅ | ✅ \\* | ✅ \\* |\n| `gpt-5.4-nano` | ✅ | ✅ | ✅ \\* | ✅ \\* |\n| `gpt-5.2` | ✅ | ✅ | ✅ | ✅ |\n| `gpt-5.1` | ✅ | ✅ | ✅ | ✅ |\n| `gpt-5` \\[i] | ✅ | ✅ | ✅ | ✅ |\n| `gpt-5-mini` \\[i] | ✅ | ✅ | ✅ \\* | ✅ \\* |\n| `gpt-5-nano` \\[i] | ✅ | ✅ | 🟡 \\* | 🟡 \\* |\n| `gpt-4.1` | ✅ | ✅ | ✅ | ✅ |\n| `gpt-4.1-mini` | ✅ | ✅ | ✅ \\* | ✅ \\* |\n| `gpt-4.1-nano` | ✅ | ✅ | ✅ \\* | 🟡 \\* |\n| **Anthropic** | | | | |\n| `claude-opus-4.8` | ✅ | ✅ | ✅ | ✅ |\n| `claude-opus-4.7` | ✅ | ✅ | ✅ | ✅ |\n| `claude-opus-4.6` | ✅ | ✅ | ✅ | ✅ |\n| `claude-sonnet-4.6` | ✅ | ✅ | ✅ | ✅ |\n| `claude-haiku-4.5` | ✅ | ✅ | ✅ \\* | ✅ \\* |\n| `claude-fable-5` | ✅ | ✅ | ✅ | ✅ |\n| **Google** | | | | |\n| `gemini-3.5-flash` | ✅ | ✅ | ✅ | ✅ |\n| `gemini-3.1-pro-preview` | ✅ | ✅ | ✅ | 🟡 ‡ |\n| `gemini-3.1-pro-preview-customtools` | ✅ | ✅ | ✅ | ✅ |\n| `gemini-3.1-flash-lite-preview` | ✅ | ✅ | ✅ \\* | ✅ \\* |\n| `gemini-3-flash-preview` | ✅ | ✅ | ✅ \\* | ✅ \\* |\n| `gemini-2.5-pro` | ✅ | ✅ | ✅ | ✅ |\n| `gemini-2.5-flash` | ✅ | ✅ | ✅ \\* | ✅ \\* |\n| `gemini-2.5-flash-lite` | ✅ | ✅ | ✅ \\* | ✅ \\* |\n| **xAI** | | | | |\n| `grok-4.3` \\[i] | 🟡 | 🔴 | 🔴 | 🔴 |\n| `grok-4.20` | ✅ | ✅ | ✅ | ✅ |\n| `grok-4.5` | ✅ | ✅ | ✅ | ✅ |\n| **Zhipu (z.ai)** | | | | |\n| `glm-4-plus` | TBD | TBD | TBD | TBD |\n| **Moonshot** | | | | |\n| `kimi-k2` | TBD | TBD | TBD | TBD |\n| **Alibaba** | | | | |\n| `qwen3-max` | TBD | TBD | TBD | TBD |\n| **MiniMax** | | | | |\n| `minimax-m1` | TBD | TBD | TBD | TBD |\n| **Other** | | | | |\n| *model* | — | — | — | — |\n\n\n> **Don't see your model?** Add a prompt variant under `packages/prompt-pack/src/prompts/mdma-author/<vendor>/` and open a PR — we'll run the eval suite and add it to this table.\n\n† **gpt-5.4 intermittent duplication bug** — `gpt-5.4` passes one-shot evals reliably but shows a non-deterministic output duplication in multi-turn, custom-prompt, and flow evals (~7–15% of runs). The model generates a complete, correct response and then immediately re-emits the entire output verbatim, causing `[duplicate-ids]` validation errors. This is a known model-level issue unrelated to the prompt variant. See the [OpenAI community thread](https://community.openai.com/t/seeing-intermittent-duplicate-strings-in-gpt-5-4-responses/1376651) for details. If this affects your use case, prefer `gpt-5.5` or `gpt-5.2`.\n\n‡ **gemini-3.1-pro-preview stochastic preamble loop** — on ~7–15% of flow-eval runs, the model emits a chain-of-thought as visible Markdown prose (e.g. `**Investigating Production Errors**` repeated 3–5 times) instead of opening a ```` ```mdma ```` block, producing either `[yaml-correctness: outside fenced block]` or `[duplicate-ids]` errors. Per Google's official Gemini 3 prompting guide, this is a model-level behavior driven by temperature/sampling — prompt-level fixes shift which test loops rather than eliminating the loops. If deterministic flow output matters, prefer `gemini-2.5-pro` for production multi-step flows.\n\n\\* Smaller / lower-tier models from any lab (OpenAI mini · nano, Anthropic Haiku, Google Gemini Flash, etc.) pass our eval suites, which exercise short, structured test cases. In longer real-world conversations they tend to hallucinate, forget earlier turns, or drift from the spec. For production use that involves multi-turn dialogue or stateful flows, prefer the flagship-tier model from the same family.\n\n\\[i] Noticeably slow response times — single-turn responses commonly take tens of seconds and full eval runs measure in minutes.\n\n\n## MDMA_FIXER prompt matrix\n\nEach cell shows the pass rate of the model-specialized MDMA_FIXER prompt variant on the single-block fixer eval (16 tests covering structural fixes, bindings, PII, forms, tables/charts, approvals, and custom-component extraction). The fixer is what powers automatic repair of LLM output that fails `validate()` — every supported model lands at ✅ via model-tailored inline guards (no-leading-separator, preserve-input-structure, table-key-direction, replace-all-placeholders, fix-all-listed-errors, etc.).\n\n✅ 100% on the single-block fixer eval (16/16).\n\n\n| Variant | single-block fixer | notes for testing |\n| :--- | :---: | :--- |\n| **OpenAI** | | |\n| `gpt-5.6-sol` | ✅ | |\n| `gpt-5.6-terra` | ✅ | |\n| `gpt-5.6-luna` | ✅ | |\n| `gpt-5.5` | ✅ | |\n| `gpt-5.4` | ✅ | |\n| `gpt-5.4-mini` | ✅ | |\n| `gpt-5.4-nano` | ✅ | |\n| `gpt-5.2` | ✅ | |\n| `gpt-5.1` | ✅ | |\n| `gpt-5` | ✅ | |\n| `gpt-5-mini` | ✅ | |\n| `gpt-5-nano` | ✅ | |\n| `gpt-4.1` | ✅ | |\n| `gpt-4.1-mini` | ✅ | |\n| `gpt-4.1-nano` | ✅ | |\n| **Anthropic** | | |\n| `claude-opus-4.8` | ✅ | |\n| `claude-opus-4.7` | ✅ | |\n| `claude-opus-4.6` | ✅ | |\n| `claude-sonnet` | ✅ | catch-all variant — matches `claude-sonnet-4-5`, `claude-sonnet-4-6`, etc. |\n| `claude-haiku` | ✅ | |\n| `claude-fable-5` | ✅ ‡ | requires `reasoning.exclude: true` (wired in `evals/promptfooconfig.fixer.js`) |\n| **Google** | | |\n| `gemini-3.5-flash` | ✅ ‡ | requires `reasoning.exclude: true`; adds an inline no-leading-separator guard |\n| `gemini-3.1-pro-preview` | ✅ ‡ | requires OpenRouter `reasoning.exclude: true` (already wired in `evals/promptfooconfig.fixer.js`) |\n| `gemini-3.1-pro-preview-customtools` | ✅ ‡ | same `reasoning.exclude` requirement |\n| `gemini-3.1-flash-lite-preview` | ✅ | |\n| `gemini-3-flash-preview` | ✅ | |\n| `gemini-2.5-pro` | ✅ ‡ | same `reasoning.exclude` requirement |\n| `gemini-2.5-flash` | ✅ | |\n| `gemini-2.5-flash-lite` | ✅ | |\n| **xAI** | | |\n| `grok-4.3` | ✅ ‡ | minimal prompt + `reasoning.exclude: true` — extra framing regresses Grok 4.3 |\n| `grok-4.20` | ✅ | |\n| `grok-4.5` | ✅ ‡ | requires `reasoning.exclude: true` (hidden-reasoning model) |\n\n‡ Reasoning-token leak suppression — for reasoning-flavoured Gemini Pro variants and Grok 4.3, the fixer would otherwise see visible \"Thinking: **Topic**\" prose prepended to every response. The eval config sets `passthrough.reasoning.exclude: true` (and the demo's `usePreviewValidation` does the same per-provider) to strip reasoning tokens from the response body at the API layer rather than at the prompt layer.\n\n\n\n## Components\n\n10 built-in component types, all rendered out of the box by `@mobile-reality/mdma-renderer-react`:\n\n| Component | Type key | Description |\n|-----------|----------|-------------|\n| **Form** | `form` | Multi-field forms with text, number, email, date, select, checkbox, textarea, and file fields. Supports validation, required fields, default values, and sensitive (PII) flags. |\n| **Button** | `button` | Action buttons with `primary`, `secondary`, and `danger` variants. |\n| **Tasklist** | `tasklist` | Interactive checkbox task items with labels. |\n| **Table** | `table` | Data tables with typed columns and row data. |\n| **Chart** | `chart` | **Table fallback by default** — renders chart data as a simple HTML table to avoid forcing a charting dependency (~400KB). Override with your own renderer (e.g. recharts) via `customizations.components.chart` (see [Custom Chart Renderer](#custom-chart-renderer) below). |\n| **Callout** | `callout` | Alert banners with `info`, `warning`, `error`, and `success` variants. Supports optional title and dismiss button. |\n| **Approval Gate** | `approval-gate` | Approve/deny workflow gates with pending, approved, and denied states. |\n| **Webhook** | `webhook` | Webhook triggers with idle, executing, success, and error status indicators. |\n| **Thinking** | `thinking` | Collapsible thinking/reasoning blocks that show the AI's chain of thought. |\n| **Custom** | `custom` | Host-extensible escape hatch — a stable envelope (`name` + open `props` + `actions`) that dispatches to a host-registered variant renderer. Register a variant's schema/behavior with `registerCustomComponent` and its renderer via `customizations.customVariants`. Use only when no built-in type fits. |\n\nAdditionally, standard **Markdown** content (headings, paragraphs, lists, code blocks, images, links, tables, etc.) is rendered inline between components.\n\n### Custom Chart Renderer\n\nThe built-in chart renderer intentionally renders data as a plain table so the library stays lightweight. To get actual charts, register a custom renderer:\n\n```tsx\nimport { MdmaDocument } from '@mobile-reality/mdma-renderer-react';\nimport { MyRechartsRenderer } from './MyRechartsRenderer';\n\nfunction App({ ast, store }) {\n  return (\n    <MdmaDocument\n      ast={ast}\n      store={store}\n      customizations={{\n        components: {\n          chart: MyRechartsRenderer,\n        },\n      }}\n    />\n  );\n}\n```\n\nThis pattern works for overriding any built-in component — pass a custom React component under `customizations.components.<type>`.\n\n## Installation\n\n```bash\n# Core — parse and run MDMA documents\nnpm install @mobile-reality/mdma-parser @mobile-reality/mdma-runtime\n\n# React rendering\nnpm install @mobile-reality/mdma-renderer-react\n\n# AI authoring — system prompts for LLM-based generation\nnpm install @mobile-reality/mdma-prompt-pack\n\n# Validation — static analysis for MDMA documents\nnpm install @mobile-reality/mdma-validator\n\n# CLI — interactive prompt builder + document validation\nnpx @mobile-reality/mdma-cli\n```\n\nAll packages are published under the [`@mobile-reality`](https://www.npmjs.com/org/mobile-reality) npm org.\n\n## Usage\n\n```typescript\nimport { unified } from 'unified';\nimport remarkParse from 'remark-parse';\nimport remarkGfm from 'remark-gfm';\nimport { remarkMdma } from '@mobile-reality/mdma-parser';\nimport { createDocumentStore } from '@mobile-reality/mdma-runtime';\nimport type { MdmaRoot } from '@mobile-reality/mdma-spec';\n\n// 1. Parse markdown into AST\nconst processor = unified().use(remarkParse).use(remarkGfm).use(remarkMdma);\nconst tree = processor.parse(markdown);\nconst ast = (await processor.run(tree)) as MdmaRoot;\n\n// 2. Create a reactive document store\nconst store = createDocumentStore(ast, {\n  documentId: 'my-doc',\n  sessionId: crypto.randomUUID(),\n});\n\n// 3. Subscribe to state changes\nstore.subscribe((state) => {\n  console.log('Bindings:', state.bindings);\n});\n\n// 4. Dispatch user actions\nstore.dispatch({\n  type: 'FIELD_CHANGED',\n  componentId: 'intake-form',\n  field: 'patient-name',\n  value: 'Jane Doe',\n});\n```\n\n### In a Chat\n\n```typescript\nimport { buildSystemPrompt, getAuthorPromptVariant } from '@mobile-reality/mdma-prompt-pack';\n\n// Pick the prompt variant tuned for your model (falls back to default if unknown)\nconst { prompt: authorPrompt } = getAuthorPromptVariant('google/gemini-2.5-pro');\n\n// Optionally layer a custom prompt on top for domain-specific generation\nconst systemPrompt = buildSystemPrompt({\n  authorPrompt,\n  customPrompt: `You are a bug tracking assistant. When a user reports a bug,\nalways generate a single form component matching this exact structure:\n\n\\`\\`\\`mdma\ntype: form\nid: bug-report\nfields:\n  - name: title\n    type: text\n    label: \"Bug Title\"\n    required: true\n  - name: severity\n    type: select\n    label: \"Severity\"\n    options:\n      - { label: Critical, value: critical }\n      - { label: High, value: high }\n      - { label: Medium, value: medium }\n      - { label: Low, value: low }\n  - name: steps\n    type: textarea\n    label: \"Steps to Reproduce\"\n    required: true\n  - name: expected\n    type: textarea\n    label: \"Expected Behavior\"\n  - name: actual\n    type: textarea\n    label: \"Actual Behavior\"\nonSubmit: submit-bug-report\n\\`\\`\\``,\n});\n\n// Send to any OpenAI-compatible API\nconst response = await fetch('https://api.openai.com/v1/chat/completions', {\n  method: 'POST',\n  headers: { Authorization: `Bearer ${apiKey}` },\n  body: JSON.stringify({\n    model: 'gemini-2.5-pro',\n    messages: [\n      { role: 'system', content: systemPrompt },\n      { role: 'user', content: 'The login page crashes after entering my password.' },\n    ],\n  }),\n});\n\n// The LLM responds with regular markdown containing ```mdma blocks\n// Parse it into an AST + store as shown above\n```\n\n### React\n\n```tsx\nimport { MdmaDocument } from '@mobile-reality/mdma-renderer-react';\nimport '@mobile-reality/mdma-renderer-react/styles.css'; // default styles\n\nfunction App({ ast, store }) {\n  return <MdmaDocument ast={ast} store={store} />;\n}\n```\n\n> **Note:** The `styles.css` import provides default styling for all MDMA components (forms, tables, callouts, animations, etc.). It's optional — you can write your own styles targeting the `.mdma-*` CSS classes instead.\n\n### Theming\n\nBoth renderers accept a `theme` prop on `MdmaDocument`, so theming is entirely opt-in — omit it and you get the default light look. Pass a built-in palette, follow the OS preference, or hand over a full custom token object:\n\n```tsx\n<MdmaDocument ast={ast} store={store} theme=\"dark\" />   // built-in dark palette\n<MdmaDocument ast={ast} store={store} theme=\"auto\" />   // follows OS light/dark\n<MdmaDocument ast={ast} store={store} theme={myTheme} /> // custom MdmaTheme tokens\n```\n\nThe web renderers (`renderer-react`, `renderer-vue`) and the native one (`renderer-react-native`) share the same `MdmaTheme` token shape, so a theme object is portable between them. On the web, tokens are applied as `--mdma-*` CSS variables (still fully overridable in your own CSS); on native, renderers read them via `useMdmaTheme()`. See the [Theming guide](docs/guides/theming.md) for the full token reference.\n\n## Packages\n\n| Package | Description |\n|---------|-------------|\n| `@mobile-reality/mdma-spec` | The foundation of the MDMA ecosystem — Zod schemas, TypeScript types, and AST definitions for all 10 component types. Every other package depends on spec for validation and type safety. |\n| `@mobile-reality/mdma-parser` | A remark plugin that transforms standard Markdown into an MDMA-extended AST. Extracts `mdma` code blocks, validates YAML against component schemas, and builds a binding dependency graph. |\n| `@mobile-reality/mdma-runtime` | Headless state management engine for MDMA documents — like a mini state specialized for interactive documents. Manages reactive bindings, dispatches actions, enforces environment policies, and writes every event to a tamper-evident audit log with automatic PII redaction. |\n| `@mobile-reality/mdma-attachables-core` | Handlers for 7 of the 10 component types — the ones that manage state (form, button, tasklist, table, callout, approval-gate, webhook). Chart and thinking are display-only and rendered directly without state handlers. |\n| `@mobile-reality/mdma-renderer-react` | React rendering layer with components for all 10 MDMA types and hooks for state access. Provides `MdmaDocument` for full-document rendering and `useComponentState`/`useBinding` for fine-grained reactivity. |\n| `@mobile-reality/mdma-renderer-vue` | Vue 3 rendering layer — the same surface as the React renderer, in Vue idiom: `MdmaDocument`, `MdmaBlock`, and composables (`useComponentState`/`useBinding`) that return `ComputedRef`s. Ships the same `styles.css`, so a theme is portable between the two web renderers. |\n| `@mobile-reality/mdma-prompt-pack` | System prompts that teach LLMs how to author valid MDMA documents. Ships model-specialised variants for OpenAI, Anthropic, Google, and xAI — select one with `getAuthorPromptVariant(modelId)`. Exports `buildSystemPrompt()` to combine the variant with optional custom instructions for domain-specific generation. |\n| `@mobile-reality/mdma-validator` | Static analysis engine with 17 lint rules covering YAML correctness, schema conformance, ID uniqueness, binding syntax, action references, PII sensitivity, expected component verification, and flow ordering. Includes 6 auto-fix strategies and fuzzy type/ID suggestions. Powers programmatic validation in CI pipelines and custom tooling. |\n| `@mobile-reality/mdma-cli` | Interactive CLI tool for creating custom MDMA prompts. Opens a local web app where you visually select components, configure fields, set domain rules and trigger conditions, then an LLM generates a tailored `customPrompt` for use with `buildSystemPrompt()`. Also includes a `validate` command for static document analysis. |\n| `@mobile-reality/mdma-mcp` | MCP (Model Context Protocol) server that exposes MDMA spec, prompts, and tooling to AI assistants. Tools: `get-spec`, `get-prompt` (with optional `variantId` for model-optimised prompts), `list-prompt-variants`, `build-system-prompt`, `validate-prompt`, `list-packages`. Works with Claude Desktop, VS Code, Cursor, and any MCP-compatible client. |\n\n## Architecture\n\n```\n@mobile-reality/mdma-spec                  Format specification + Zod schemas\n  ├── @mobile-reality/mdma-parser          Markdown → MDMA AST (remark plugin)\n  ├── @mobile-reality/mdma-prompt-pack     AI authoring prompts\n  ├── @mobile-reality/mdma-validator       Document validation\n  └── @mobile-reality/mdma-runtime         State / events / policy engine\n        └── @mobile-reality/mdma-attachables-core   Component handlers\n              ├── @mobile-reality/mdma-renderer-react   React components\n              └── @mobile-reality/mdma-renderer-vue     Vue 3 components\n@mobile-reality/mdma-cli                   CLI prompt builder + validation\n@mobile-reality/mdma-mcp                   MCP server for AI assistants\n```\n\n## Getting Started\n\n```bash\npnpm install\npnpm build\npnpm test\n```\n\n## CLI\n\nInteractive prompt builder for creating custom MDMA prompts.\n\n```bash\n# Run the prompt builder — opens a web app in your browser\nnpx @mobile-reality/mdma-cli\n\n# Validate MDMA documents\nnpx @mobile-reality/mdma-cli validate \"docs/**/*.md\"\nnpx @mobile-reality/mdma-cli validate \"docs/**/*.md\" --fix  # auto-fix issues\nnpx @mobile-reality/mdma-cli validate \"docs/**/*.md\" --json # JSON output\n```\n\nThe prompt builder walks you through:\n1. **Pick components** — select from the 10 MDMA types (form, table, approval-gate, etc.)\n2. **Configure** — define fields, options, roles, sensitive flags, and business rules\n3. **Set triggers** — specify when the AI should generate MDMA components (keywords, contextual conditions)\n4. **Generate** — an LLM creates a tailored `customPrompt` based on your configuration\n5. **Export** — copy the result and use it in your app:\n\n```typescript\nimport { buildSystemPrompt } from '@mobile-reality/mdma-prompt-pack';\n\nconst systemPrompt = buildSystemPrompt({\n  customPrompt: '<paste generated prompt here>',\n});\n```\n\n## Validator\n\nStatic analysis engine for MDMA documents. Validates structure, catches common LLM mistakes, and auto-fixes what it can.\n\n```typescript\nimport { validate } from '@mobile-reality/mdma-validator';\n\nconst result = validate(markdown);\n// result.ok        — true if no unfixed errors\n// result.issues    — all issues found\n// result.output    — auto-fixed markdown\n// result.fixCount  — number of issues auto-fixed\n```\n\n### Rules\n\nEvery rule can be individually disabled via the `exclude` option:\n\n```typescript\nconst result = validate(markdown, {\n  exclude: ['thinking-block', 'placeholder-content'],\n});\n```\n\n| Rule | Severity | Auto-fix | Description |\n|------|----------|----------|-------------|\n| `yaml-correctness` | error | -- | YAML parses successfully. Detects and auto-splits multi-component blocks, strips `---` separators LLMs insert. |\n| `field-name-typos` | warning | -- | Common field name mistakes: `roles` -> `allowedRoles`, `onClick` -> `onAction`, `submit` -> `onSubmit`. |\n| `schema-conformance` | error | yes | Component type exists and data conforms to its Zod schema. Suggests closest type via fuzzy matching (e.g. `\"frm\"` -> `did you mean \"form\"?`) and lists all valid types. |\n| `duplicate-ids` | error | yes | All component IDs are unique. Auto-fix appends `-1`, `-2` suffixes. |\n| `id-format` | warning | yes | IDs follow kebab-case (`my-component-id`). Auto-fix converts camelCase, snake_case, PascalCase and updates all references. |\n| `binding-syntax` | error/warning | yes | `{{binding}}` expressions are well-formed. Catches empty `{{ }}`, extra whitespace `{{ path }}`, and single-brace `{path}`. |\n| `form-submit-action` | error | -- | Every `type: form` component declares a non-empty `onSubmit` action. |\n| `sensitive-flags` | warning | yes | Form fields and table columns with PII-like names (email, phone, ssn, address, etc.) have `sensitive: true`. Supports custom PII patterns. |\n| `required-markers` | info | -- | Suggests `required: true` for fields named `name`, `email`, `title`, `summary`. |\n| `thinking-block` | warning/info | -- | If a thinking block is present, it should be the first component and only one should exist. |\n| `table-data-keys` | warning | -- | Data row keys match defined column keys. Flags extra keys and columns with no matching data. |\n| `select-options` | warning | -- | `type: select` fields have `options` defined as `[{label, value}]` objects. |\n| `chart-validation` | warning | -- | Chart CSV data has headers + data rows. `xAxis`/`yAxis` reference actual CSV column headers. |\n| `placeholder-content` | info | -- | Catches `TODO`, `TBD`, `FIXME`, `...`, `lorem ipsum` in content fields. |\n| `flow-ordering` | warning | -- | Forward-only action references (targets defined later in the document), no circular refs, and multi-step flows flagged to be split across messages. |\n| `single-interactive-component` | warning | -- | At most one interactive component (form, button, webhook, approval-gate, tasklist) per message. |\n| `expected-components` | error | -- | Verifies that components present in the message match their expected types, form fields, and table columns. Components not in the message are silently skipped — useful for multi-turn flows where you pass all expected components upfront. |\n\n### Auto-fix Pipeline\n\nWhen `autoFix: true` (default), 6 fix strategies run in strict dependency order:\n\n1. **thinking-block** — merge stray thinking blocks into one and move it to the top\n2. **id-format** — normalize IDs to kebab-case, update all cross-references\n3. **duplicate-ids** — deduplicate after normalization\n4. **binding-syntax** — fix `{x}` -> `{{x}}`, strip whitespace\n5. **sensitive-flags** — add `sensitive: true` to PII fields\n6. **schema-conformance** — patch missing labels/headers/content, infer field types, wrap bare bindings, re-validate with Zod\n\n### Expected Components\n\nWhen you need to guarantee that the LLM generates specific critical components for the user (e.g. a form with required fields, a table with specific columns), pass their expected shapes to the validator. The rule only validates components that are actually present in the current message — components not found are silently skipped. This makes it safe to pass the full set of expected components across a multi-turn flow:\n\n```typescript\n// Define all expected components once (e.g. from a blueprint or flow definition)\nconst expectedComponents = {\n  'contact-form': {\n    type: 'form',\n    fields: ['email', 'phone', 'full-name'],\n  },\n  'approval-gate': { type: 'approval-gate' },\n  'submit-btn': { type: 'button' },\n};\n\n// Pass the same set to every message — the rule checks only what's present\nconst result = validate(message1, { expectedComponents });\n// Message 1 contains contact-form → validates type + fields\n// approval-gate and submit-btn not in this message → skipped\n\nconst result2 = validate(message2, { expectedComponents });\n// Message 2 contains approval-gate → validates type\n// contact-form and submit-btn not in this message → skipped\n```\n\nFor each component found in the message, the rule checks:\n- Is the type correct?\n- Are all expected form fields present? (lists available fields on mismatch)\n- Are all expected table columns present? (lists available columns on mismatch)\n\n### LLM Error Recovery\n\nThe parser handles three common LLM mistakes automatically during block extraction:\n\n- **Colon-space in values** — `label: Step 1: Enter info` auto-quoted to `label: \"Step 1: Enter info\"`\n- **YAML `---` separators** — stripped before parsing\n- **Multiple components in one block** — split at each root-level `type:` line into separate blocks\n\n## MCP Server\n\nMCP (Model Context Protocol) server that lets AI assistants understand and work with MDMA.\n\n### Setup\n\nAdd to your AI tool config (Claude Desktop, VS Code, Cursor, etc.):\n\n```json\n{\n  \"mcpServers\": {\n    \"mdma\": {\n      \"command\": \"npx\",\n      \"args\": [\"@mobile-reality/mdma-mcp\"]\n    }\n  }\n}\n```\n\n### Tools\n\n| Tool | Description |\n|------|-------------|\n| `get-spec` | Returns the full MDMA specification: component types, JSON schemas, binding syntax, and authoring rules |\n| `get-prompt` | Returns a named prompt (`mdma-author`, `mdma-reviewer`, or `mdma-fixer`). For `mdma-author`, accepts an optional `variantId` (e.g. `google/gemini-2.5-pro`) to return the model-optimised variant |\n| `list-prompt-variants` | Returns all available `MDMA_AUTHOR` prompt variants (id, label, description) — use the id with `get-prompt` to fetch the model-optimised prompt |\n| `build-system-prompt` | Generates a custom MDMA prompt from structured input (domain, components, fields, steps, business rules) |\n| `validate-prompt` | Validates a custom prompt against MDMA conventions — returns warnings, suggestions, and constraint reference |\n| `list-packages` | Returns all MDMA packages with purpose, install command, and usage example |\n| `list-docs` | Returns the catalog of MDMA documentation files (path, title, description) available for fetching from the public GitHub repo |\n| `get-doc` | Fetches the latest version of an MDMA documentation file from `raw.githubusercontent.com/MobileReality/mdma`. Supports optional `ref` (branch/tag/SHA, defaults to `main`) |\n\n### Example: Building a prompt with structured input\n\nAn AI agent calls `build-system-prompt` with:\n\n```json\n{\n  \"domain\": \"HR onboarding\",\n  \"components\": [\"form\", \"approval-gate\", \"webhook\"],\n  \"fields\": [\n    { \"name\": \"email\", \"type\": \"email\", \"sensitive\": true, \"required\": true },\n    { \"name\": \"department\", \"type\": \"select\", \"options\": [\"Engineering\", \"Marketing\"] }\n  ],\n  \"steps\": [\n    { \"label\": \"Registration\", \"description\": \"Employee fills in personal details\" },\n    { \"label\": \"Approval\", \"description\": \"Manager reviews and approves\" }\n  ],\n  \"businessRules\": \"All PII fields must be marked sensitive.\"\n}\n```\n\nThe tool returns a structured custom prompt ready to use with `buildSystemPrompt({ customPrompt })`.\n\n### Testing locally\n\n```bash\nnpx @modelcontextprotocol/inspector node packages/mcp/dist/bin/mdma-mcp.js\n```\n\n### MCP vs No-MCP: Agent Implementation Comparison\n\nWe tested building the same MDMA chat app with two AI agents — one with the MCP server enabled, one without. Here's what happened:\n\n| Aspect | With MCP | Without MCP |\n|--------|----------|-------------|\n| **Package discovery** | Agent called `list-packages` — got all 9 packages with install commands and usage in one step | Agent had to read README, explore repo, and piece together which packages exist |\n| **Spec knowledge** | Agent called `get-spec` — received all 10 component types with JSON schemas, binding syntax, and authoring rules | Agent had to read source files across multiple packages to understand component types |\n| **Prompt setup** | Agent called `get-prompt(\"mdma-author\")` — got the exact system prompt ready to use | Agent had to find `mdma-prompt-pack`, understand `buildSystemPrompt()`, and figure out how to use it |\n| **Time to working app** | Agent knew the right packages, APIs, and patterns from the start — fewer wrong turns | Agent spent significant time exploring, reading docs, and backtracking on wrong approaches |\n| **Code quality** | Focused implementation — agent used exactly the right APIs because MCP told it what exists | More verbose — agent implemented some things manually that packages already provided |\n\n**Key takeaway:** The MCP server eliminated the discovery phase entirely. Instead of the agent reading source code to understand MDMA, it called 3 tools (`list-packages` → `get-spec` → `get-prompt`) and had complete, structured knowledge of the ecosystem within seconds.\n\n\n## Skills\n\nAgent-authoring guidance packaged as a portable [Agent Skill](https://docs.claude.com/en/docs/claude-code/skills) (compatible with Claude Code, the Agent SDK, and any harness that consumes `SKILL.md`).\n\n| Skill | Path | Purpose |\n|-------|------|---------|\n| `mdma-integration` | [skills/mdma-integration/SKILL.md](skills/mdma-integration/SKILL.md) | Teaches agents how to integrate MDMA into an application — package selection, parse → store → render wiring, LLM streaming (with the `updateAst` reparse pattern), custom components, prompt authoring & maintenance, CI validation, and MCP exposure. |\n\nThe skill is intentionally portable: every code sample is inline and every reference uses `@mobile-reality/mdma-*` package names, so it works when dropped into a project that only installs the published packages. Drop the folder into `.claude/skills/` (Claude Code), your Agent SDK skills directory, or any compatible location.\n\nPaired with the MCP server, an agent gets both *how to think about the integration* (skill) and *live access to spec, prompts, and docs* (MCP tools) — the skill tells it to call `buildSystemPrompt` / `validate` / `updateAst`, and the MCP tools give it the actual spec and docs to do so correctly.\n\n\n## Evals\n\nLLM evaluation suite using [promptfoo](https://www.promptfoo.dev/) to verify MDMA generation quality.\n\n```bash\n# Run base eval suite (25 tests)\npnpm eval\n\n# Run custom system prompt tests (10 tests)\npnpm eval:custom\n\n# Run multi-turn conversation tests (25 turns across 11 conversations)\npnpm eval:conversation\n\n# Run prompt builder tests (25 tests)\npnpm eval:prompt-builder\n\n# Run all eval suites\npnpm eval:all\n\n# View results in browser\npnpm eval:view\n```\n\n## Key Features\n\n- **Deterministic parsing** — Markdown + YAML, no runtime JS in documents\n- **PII protection** — Automatic detection + redaction (hash, mask, omit)\n- **Audit trail** — Append-only event log with tamper-evident hash chaining\n- **Policy engine** — Allow/deny rules per action and environment\n- **AI authoring** — System prompts for AI-assisted document creation\n\n## Initial Roadmap\n\n### v0.2 — Developer Experience\n- [x] More examples (14 real-world use cases)\n- [x] CLI tool for prompt creation (MDMA flows)\n- [x] Improved validator\n- [x] Added MCP\n- [x] Added Skills for Agentic usage\n- [x] Improved error messages in parser\n- [x] File upload field type for forms\n\n### v0.3 — AI & Generation\n- [x] Multi-model eval coverage (Claude, GPT, Gemini, Grok)\n- [x] Prompt tuning toolkit — test and compare custom prompts\n- [x] Agent-friendly SDK — let AI agent generate your MDMA\n- [x] Validator tests & Fixer evals\n- [ ] Integrations\n- [ ] Webhook execution engine (real HTTP calls in production environments)\n\n### v1.0 — Production Ready\n- [ ] Stable API with semantic versioning guarantees\n- [ ] E2E test suite for full document workflows\n- [ ] Performance benchmarks and optimization\n- [ ] Migration guides between versions\n- [ ] Blueprints promoted from experimental to stable\n\n### Future\n- [ ] Collaborative editing (multiplayer document state)\n- [ ] Custom component marketplace\n- [ ] Audit trail dashboard UI\n- [ ] HIPAA / SOC 2 compliance documentation\n\n## Tech Stack\n\nTypeScript monorepo — pnpm workspaces, Turborepo, Zod, React, Vitest, remark\n\n## Built by Mobile Reality\n\nMDMA is built and maintained by [Mobile Reality](https://themobilereality.com/services/ai-automation-agency) — an AI automation agency specializing in AI agent development, custom software, and enterprise automation. We use MDMA in production across fintech and proptech projects.\n\n**Read more:**\n- [LLM Interface: The Missing Layer Between Your AI Model and Your Users](https://themobilereality.com/blog/ai/llm-interface)\n- [Structured LLM Output Without JSON Schemas](https://themobilereality.com/blog/ai/structured-output-llm)\n- [Generative UI: How AI Creates Dynamic User Interfaces](https://themobilereality.com/blog/ai/generative-ui-ai)\n\n## License\n\nMIT\n\n---\n\n<p align=\"center\">Made with ❤️ by <a href=\"https://themobilereality.com/\">Mobile Reality</a></p>\n",
  "bytes": 34959,
  "sha": "6f32b4863157ba8f242238fff31c606d40e4eb4d5b98871d8061caca6d548163",
  "repo_slug": "mobilereality/mdma",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_mobilereality_mdma_909074d6/readme"
}