
Write HTML. Render video. Built for agents.
MDMA extends Markdown with a typed component format inside fenced `mdma` blocks. The ecosystem parses those blocks, validates them, runs document state, and renders them in React, Vue, or a CLI flow. It also ships prompt packs for model-specific authoring and an MCP server so assistants can inspect the spec, build prompts, and validate output. That makes Markdown documents act more like interactive app screens than static text.
Builders who want their agents to generate, validate, and render interactive Markdown documents.
You can turn Markdown conversations and docs into structured flows with forms, approvals, and webhooks instead of manual follow-up text.
Uses fenced `mdma` blocks to define forms, buttons, task lists, tables, charts, callouts, approval gates, webhooks, thinking blocks, and custom components.
`@mobile-reality/mdma-parser` turns Markdown into an MDMA AST and validates the YAML inside component blocks.
`@mobile-reality/mdma-runtime` manages bindings, actions, policies, and an audit log with PII redaction.
Render MDMA documents with `@mobile-reality/mdma-renderer-react` or `@mobile-reality/mdma-renderer-vue`, including theme support and custom component overrides.
`@mobile-reality/mdma-prompt-pack` provides model-tuned prompts for generating valid MDMA documents from LLMs.
`@mobile-reality/mdma-validator` checks schema, IDs, bindings, sensitive fields, and flow order, and can auto-fix common issues.
`@mobile-reality/mdma-cli` opens a local prompt builder and also validates MDMA documents from the command line.
`@mobile-reality/mdma-mcp` exposes the spec, prompt variants, and validation tools to MCP clients.
pnpm install pnpm build pnpm test
Markdown Document with Mounted Applications
Interactive documents from Markdown. Built for next gen-apps
๐ Website ย ย ยทย ย ๐ Live Demo ย ย ยทย ย ๐ Docs ย ย ยทย ย ๐ฌ Discord ย ย ยทย ย ๐ค Model
AI 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.
No custom UI per use case. No parsing free-form text. The AI generates structured, validated components and your frontend renders them instantly.
MDMA extends Markdown with interactive components defined in fenced mdma code blocks. A regular Markdown file becomes an interactive application:
# Patient Intake
```mdma
type: form
id: intake-form
fields:
- name: patient-name
type: text
label: "Full Name"
required: true
sensitive: true
- name: email
type: email
label: "Email"
required: true
sensitive: true
- name: reason
type: textarea
label: "Reason for Visit"
required: true
onSubmit: submit-intake
```
```mdma
type: button
id: submit-btn
text: "Submit Intake Form"
variant: primary
onAction: submit
```
Same scenario, two models. GPT-5.5 and our own hosted MDMA-IL model.
Our model is available on Hugging Face: MobileReality/mdma-gemma4-26b-dsl-unsloth-v1
| GPT-5.5 | Our own hosted model |
|---|---|
![]() | ![]() |
Each cell shows the pass rate of the model-specialized MDMA_AUTHOR prompt variant on the listed eval suite.
โ 100% on the suite.
๐ก Scoring between 80โ99% on the suite.
๐ด Scoring below 80% on the suite.
| Variant | one-shot | one-shot with custom prompt | conversation | specific flow of conversation |
|---|---|---|---|---|
| OpenAI | ||||
gpt-5.6-sol | โ | โ | โ | โ |
gpt-5.6-terra | โ | โ | โ | โ |
gpt-5.6-luna | โ | โ | โ | โ |
gpt-5.5 | โ | โ | โ | โ |
gpt-5.4 | โ | โ โ | โ โ | โ โ |
gpt-5.4-mini | โ | โ | โ * | โ * |
gpt-5.4-nano | โ | โ | โ * | โ * |
gpt-5.2 | โ | โ | โ | โ |
gpt-5.1 | โ | โ | โ | โ |
gpt-5 [i] | โ | โ | โ | โ |
gpt-5-mini [i] | โ | โ | โ * | โ * |
gpt-5-nano [i] | โ | โ | ๐ก * | ๐ก * |
gpt-4.1 | โ | โ | โ | โ |
gpt-4.1-mini | โ | โ | โ * | โ * |
gpt-4.1-nano | โ | โ | โ * | ๐ก * |
| Anthropic | ||||
claude-opus-4.8 | โ | โ | โ | โ |
claude-opus-4.7 | โ | โ | โ | โ |
claude-opus-4.6 | โ | โ | โ | โ |
claude-sonnet-4.6 | โ | โ | โ | โ |
claude-haiku-4.5 | โ | โ | โ * | โ * |
claude-fable-5 | โ | โ | โ | โ |
gemini-3.5-flash | โ | โ | โ | โ |
gemini-3.1-pro-preview | โ | โ | โ | ๐ก โก |
gemini-3.1-pro-preview-customtools | โ | โ | โ | โ |
gemini-3.1-flash-lite-preview | โ | โ | โ * | โ * |
gemini-3-flash-preview | โ | โ | โ * | โ * |
gemini-2.5-pro | โ | โ | โ | โ |
gemini-2.5-flash | โ | โ | โ * | โ * |
gemini-2.5-flash-lite | โ | โ | โ * | โ * |
| xAI | ||||
grok-4.3 [i] | ๐ก | ๐ด | ๐ด | ๐ด |
grok-4.20 | โ | โ | โ | โ |
grok-4.5 | โ | โ | โ | โ |
| Zhipu (z.ai) | ||||
glm-4-plus | TBD | TBD | TBD | TBD |
| Moonshot | ||||
kimi-k2 | TBD | TBD | TBD | TBD |
| Alibaba | ||||
qwen3-max | TBD | TBD | TBD | TBD |
| MiniMax | ||||
minimax-m1 | TBD | TBD | TBD | TBD |
| Other | ||||
| model | โ | โ | โ | โ |
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.
โ 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 for details. If this affects your use case, prefer gpt-5.5 or gpt-5.2.
โก 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.
* 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.
[i] Noticeably slow response times โ single-turn responses commonly take tens of seconds and full eval runs measure in minutes.
Each 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.).
โ 100% on the single-block fixer eval (16/16).
| Variant | single-block fixer | notes for testing |
|---|---|---|
| OpenAI | ||
gpt-5.6-sol | โ | |
gpt-5.6-terra | โ | |
gpt-5.6-luna | โ | |
gpt-5.5 | โ | |
gpt-5.4 | โ | |
gpt-5.4-mini | โ | |
gpt-5.4-nano | โ | |
gpt-5.2 | โ | |
gpt-5.1 | โ | |
gpt-5 | โ | |
gpt-5-mini | โ | |
gpt-5-nano | โ | |
gpt-4.1 | โ | |
gpt-4.1-mini | โ | |
gpt-4.1-nano | โ | |
| Anthropic | ||
claude-opus-4.8 | โ | |
claude-opus-4.7 | โ | |
claude-opus-4.6 | โ | |
claude-sonnet | โ | catch-all variant โ matches claude-sonnet-4-5, claude-sonnet-4-6, etc. |
claude-haiku | โ | |
claude-fable-5 | โ โก | requires reasoning.exclude: true (wired in evals/promptfooconfig.fixer.js) |
gemini-3.5-flash | โ โก | requires reasoning.exclude: true; adds an inline no-leading-separator guard |
gemini-3.1-pro-preview | โ โก | requires OpenRouter reasoning.exclude: true (already wired in evals/promptfooconfig.fixer.js) |
gemini-3.1-pro-preview-customtools | โ โก | same reasoning.exclude requirement |
gemini-3.1-flash-lite-preview | โ | |
gemini-3-flash-preview | โ | |
gemini-2.5-pro | โ โก | same reasoning.exclude requirement |
gemini-2.5-flash | โ | |
gemini-2.5-flash-lite | โ | |
| xAI | ||
grok-4.3 | โ โก | minimal prompt + reasoning.exclude: true โ extra framing regresses Grok 4.3 |
grok-4.20 | โ | |
grok-4.5 | โ โก | requires reasoning.exclude: true (hidden-reasoning model) |
โก 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.
10 built-in component types, all rendered out of the box by @mobile-reality/mdma-renderer-react:
| Component | Type key | Description |
|---|---|---|
| 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. |
| Button | button | Action buttons with primary, secondary, and danger variants. |
| Tasklist | tasklist | Interactive checkbox task items with labels. |
| Table | table | Data tables with typed columns and row data. |
| 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 below). |
| Callout | callout | Alert banners with info, warning, error, and success variants. Supports optional title and dismiss button. |
| Approval Gate | approval-gate | Approve/deny workflow gates with pending, approved, and denied states. |
| Webhook | webhook | Webhook triggers with idle, executing, success, and error status indicators. |
| Thinking | thinking | Collapsible thinking/reasoning blocks that show the AI's chain of thought. |
| 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. |
Additionally, standard Markdown content (headings, paragraphs, lists, code blocks, images, links, tables, etc.) is rendered inline between components.
The built-in chart renderer intentionally renders data as a plain table so the library stays lightweight. To get actual charts, register a custom renderer:
import { MdmaDocument } from '@mobile-reality/mdma-renderer-react';
import { MyRechartsRenderer } from './MyRechartsRenderer';
function App({ ast, store }) {
return (
<MdmaDocument
ast={ast}
store={store}
customizations={{
components: {
chart: MyRechartsRenderer,
},
}}
/>
);
}
This pattern works for overriding any built-in component โ pass a custom React component under customizations.components.<type>.
# Core โ parse and run MDMA documents
npm install @mobile-reality/mdma-parser @mobile-reality/mdma-runtime
# React rendering
npm install @mobile-reality/mdma-renderer-react
# AI authoring โ system prompts for LLM-based generation
npm install @mobile-reality/mdma-prompt-pack
# Validation โ static analysis for MDMA documents
npm install @mobile-reality/mdma-validator
# CLI โ interactive prompt builder + document validation
npx @mobile-reality/mdma-cli
All packages are published under the @mobile-reality npm org.
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkGfm from 'remark-gfm';
import { remarkMdma } from '@mobile-reality/mdma-parser';
import { createDocumentStore } from '@mobile-reality/mdma-runtime';
import type { MdmaRoot } from '@mobile-reality/mdma-spec';
// 1. Parse markdown into AST
const processor = unified().use(remarkParse).use(remarkGfm).use(remarkMdma);
const tree = processor.parse(markdown);
const ast = (await processor.run(tree)) as MdmaRoot;
// 2. Create a reactive document store
const store = createDocumentStore(ast, {
documentId: 'my-doc',
sessionId: crypto.randomUUID(),
});
// 3. Subscribe to state changes
store.subscribe((state) => {
console.log('Bindings:', state.bindings);
});
// 4. Dispatch user actions
store.dispatch({
type: 'FIELD_CHANGED',
componentId: 'intake-form',
field: 'patient-name',
value: 'Jane Doe',
});
import { buildSystemPrompt, getAuthorPromptVariant } from '@mobile-reality/mdma-prompt-pack';
// Pick the prompt variant tuned for your model (falls back to default if unknown)
const { prompt: authorPrompt } = getAuthorPromptVariant('google/gemini-2.5-pro');
// Optionally layer a custom prompt on top for domain-specific generation
const systemPrompt = buildSystemPrompt({
authorPrompt,
customPrompt: `You are a bug tracking assistant. When a user reports a bug,
always generate a single form component matching this exact structure:
\`\`\`mdma
type: form
id: bug-report
fields:
- name: title
type: text
label: "Bug Title"
required: true
- name: severity
type: select
label: "Severity"
options:
- { label: Critical, value: critical }
- { label: High, value: high }
- { label: Medium, value: medium }
- { label: Low, value: low }
- name: steps
type: textarea
label: "Steps to Reproduce"
required: true
- name: expected
type: textarea
label: "Expected Behavior"
- name: actual
type: textarea
label: "Actual Behavior"
onSubmit: submit-bug-report
\`\`\``,
});
// Send to any OpenAI-compatible API
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` },
body: JSON.stringify({
model: 'gemini-2.5-pro',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: 'The login page crashes after entering my password.' },
],
}),
});
// The LLM responds with regular markdown containing ```mdma blocks
// Parse it into an AST + store as shown above
import { MdmaDocument } from '@mobile-reality/mdma-renderer-react';
import '@mobile-reality/mdma-renderer-react/styles.css'; // default styles
function App({ ast, store }) {
return <MdmaDocument ast={ast} store={store} />;
}
Note: The
styles.cssimport 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.
Both 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:
<MdmaDocument ast={ast} store={store} theme="dark" /> // built-in dark palette
<MdmaDocument ast={ast} store={store} theme="auto" /> // follows OS light/dark
<MdmaDocument ast={ast} store={store} theme={myTheme} /> // custom MdmaTheme tokens
The 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 for the full token reference.
| Package | Description |
|---|---|
@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. |
@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. |
@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. |
@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. |
@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. |
@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 ComputedRefs. Ships the same styles.css, so a theme is portable between the two web renderers. |
@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. |
@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. |
@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. |
@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. |
@mobile-reality/mdma-spec Format specification + Zod schemas
โโโ @mobile-reality/mdma-parser Markdown โ MDMA AST (remark plugin)
โโโ @mobile-reality/mdma-prompt-pack AI authoring prompts
โโโ @mobile-reality/mdma-validator Document validation
โโโ @mobile-reality/mdma-runtime State / events / policy engine
โโโ @mobile-reality/mdma-attachables-core Component handlers
โโโ @mobile-reality/mdma-renderer-react React components
โโโ @mobile-reality/mdma-renderer-vue Vue 3 components
@mobile-reality/mdma-cli CLI prompt builder + validation
@mobile-reality/mdma-mcp MCP server for AI assistants
pnpm install
pnpm build
pnpm test
Interactive prompt builder for creating custom MDMA prompts.
# Run the prompt builder โ opens a web app in your browser
npx @mobile-reality/mdma-cli
# Validate MDMA documents
npx @mobile-reality/mdma-cli validate "docs/**/*.md"
npx @mobile-reality/mdma-cli validate "docs/**/*.md" --fix # auto-fix issues
npx @mobile-reality/mdma-cli validate "docs/**/*.md" --json # JSON output
The prompt builder walks you through:
customPrompt based on your configurationimport { buildSystemPrompt } from '@mobile-reality/mdma-prompt-pack';
const systemPrompt = buildSystemPrompt({
customPrompt: '<paste generated prompt here>',
});
Static analysis engine for MDMA documents. Validates structure, catches common LLM mistakes, and auto-fixes what it can.
import { validate } from '@mobile-reality/mdma-validator';
const result = validate(markdown);
// result.ok โ true if no unfixed errors
// result.issues โ all issues found
// result.output โ auto-fixed markdown
// result.fixCount โ number of issues auto-fixed
Every rule can be individually disabled via the exclude option:
const result = validate(markdown, {
exclude: ['thinking-block', 'placeholder-content'],
});
| Rule | Severity | Auto-fix | Description |
|---|---|---|---|
yaml-correctness | error | -- | YAML parses successfully. Detects and auto-splits multi-component blocks, strips --- separators LLMs insert. |
field-name-typos | warning | -- | Common field name mistakes: roles -> allowedRoles, onClick -> onAction, submit -> onSubmit. |
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. |
duplicate-ids | error | yes | All component IDs are unique. Auto-fix appends -1, -2 suffixes. |
id-format | warning | yes | IDs follow kebab-case (my-component-id). Auto-fix converts camelCase, snake_case, PascalCase and updates all references. |
binding-syntax | error/warning | yes | {{binding}} expressions are well-formed. Catches empty {{ }}, extra whitespace {{ path }}, and single-brace {path}. |
form-submit-action | error | -- | Every type: form component declares a non-empty onSubmit action. |
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. |
required-markers | info | -- | Suggests required: true for fields named name, email, title, summary. |
thinking-block | warning/info | -- | If a thinking block is present, it should be the first component and only one should exist. |
table-data-keys | warning | -- | Data row keys match defined column keys. Flags extra keys and columns with no matching data. |
select-options | warning | -- | type: select fields have options defined as [{label, value}] objects. |
chart-validation | warning | -- | Chart CSV data has headers + data rows. xAxis/yAxis reference actual CSV column headers. |
placeholder-content | info | -- | Catches TODO, TBD, FIXME, ..., lorem ipsum in content fields. |
flow-ordering | warning | -- | Forward-only action references (targ |
Sign in to join the discussion.
No comments yet. Be the first to say what this is good for.

Write HTML. Render video. Built for agents.
Ultra-lightweight, open-source, self-hosted personal AI agent framework in Python with WebUI, tools, memory, MCP, multi-agent workflows, automation, and chat apps
SkillOpt is a text-space optimizer that trains reusable natural-language skills for frozen LLM agents through trajectory-driven edits, validation-gated updates, and deployable best_skill.md artifacts.

Omnigent is an open-source AI agent framework and meta-harness: orchestrate Claude Code, Codex, Cursor, Pi, and custom agents โ swap harnesses without rewriting, enforce policies and sandboxing, and collaborate in real time from any device.
A theoretical reconstruction of the Claude Mythos architecture, built from first principles using the available research literature.
๐ท๏ธ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!