mdma-integration — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited mdma-integration (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill guides integration of MDMA (Markdown Document with Mounted Applications) into real applications. MDMA is published as a set of composable npm packages under the @mobile-reality org — mdma-spec, mdma-parser, mdma-runtime, mdma-renderer-react, mdma-attachables-core, mdma-prompt-pack, mdma-validator, mdma-cli, mdma-mcp. A good integration picks the narrowest set of packages for the task, follows the parse → store → render contract, and uses the authoring and validation tooling rather than rolling its own. The goal is to make it obvious to a developer — or an agent — how to plug MDMA into their stack with minimum ceremony and maximum correctness.
The user provides an integration task: "add MDMA to this React chat app", "stream MDMA from the LLM and re-render as chunks arrive", "write a domain prompt for a clinical intake", "validate our MDMA docs in CI", "register a custom chart renderer", "expose MDMA to our agent via MCP", or "migrate our form rendering to MDMA". They may specify framework (React is first-class), LLM provider, validation rules, or PII constraints.
Before editing code, establish what "integrate MDMA" actually means for this request:
*.md files against the spec), custom prompt authoring (build a domain-specific customPrompt that feeds buildSystemPrompt), MCP exposure (let an agent call get-spec / build-system-prompt / validate-prompt), or custom component (register a Zod schema + renderer for a non-builtin type). Each has a different package footprint.mdma-parser + mdma-runtime + mdma-renderer-react + mdma-prompt-pack. A CI validator needs only mdma-validator. An agent harness may need only mdma-mcp. Don't install the full matrix "just in case" — the dependency graph is structured so you never have to.sensitive? Which environment policies apply (allow/deny actions, redaction mode)? Which LLM provider is streaming? Does the host app already own state management, or will createDocumentStore be the source of truth for document state?prompts/ directory, exported from a package, generated via the CLI prompt builder) and how it's versioned, before writing it.CRITICAL: Integrate with the smallest valid surface area. Prefer the provided MdmaDocument + createDocumentStore pairing over hand-rolled rendering. Prefer buildSystemPrompt({ customPrompt }) over concatenating strings yourself. Prefer the validator package over ad-hoc regex. The packages encode invariants (binding graph, PII redaction, audit log, policy) that are painful to re-derive.
Focus on:
spec → parser → runtime → attachables-core → renderer-react. prompt-pack, validator, cli, mcp are leaves that depend only on spec. Install the minimum footprint for the task: # Parse + run MDMA documents (headless)
npm install @mobile-reality/mdma-parser @mobile-reality/mdma-runtime
# Add React rendering
npm install @mobile-reality/mdma-renderer-react
# LLM authoring — domain system prompts
npm install @mobile-reality/mdma-prompt-pack
# CI / static analysis
npm install @mobile-reality/mdma-validatorNever reinstall peers that are already pulled transitively.
unified().use(remarkParse).use(remarkMdma) processor once (module-level singleton), feed it markdown, pass the AST to createDocumentStore, pass both to <MdmaDocument>: import { unified } from 'unified';
import remarkParse from 'remark-parse';
import { remarkMdma } from '@mobile-reality/mdma-parser';
import { createDocumentStore } from '@mobile-reality/mdma-runtime';
import { MdmaDocument } from '@mobile-reality/mdma-renderer-react';
import '@mobile-reality/mdma-renderer-react/styles.css';
import type { MdmaRoot } from '@mobile-reality/mdma-spec';
const processor = unified().use(remarkParse).use(remarkMdma); // singleton
const tree = processor.parse(markdown);
const ast = (await processor.run(tree)) as MdmaRoot;
const store = createDocumentStore(ast, {
documentId: 'my-doc',
sessionId: crypto.randomUUID(),
});
return <MdmaDocument ast={ast} store={store} />;createDocumentStore on every chunk (you'd wipe user state). The right pattern: throttle reparse (~150ms), and on each reparse call existingStore.updateAst(newAst) instead of building a fresh store. This preserves bindings, focus, and in-flight form edits across reparses. // On each ~150ms tick while streaming:
const tree = processor.parse(message.content);
const newAst = (await processor.run(tree)) as MdmaRoot;
if (existingStore) {
existingStore.updateAst(newAst); // preserves user state
} else {
existingStore = createDocumentStore(newAst, { documentId, sessionId });
}Copy this shape; do not invent a replacement.
buildSystemPrompt({ customPrompt }) from @mobile-reality/mdma-prompt-pack. It sandwiches your domain rules between the MDMA spec and a reinforcement tail — the ordering matters for model adherence. Your customPrompt should name the domain, allowed components, required fields, sensitive fields, trigger conditions, and business rules. A vague "help the user" prompt produces generic forms; a prompt that encodes the domain produces documents that fit the product. When the prompt grows non-trivial, use the CLI prompt builder (npx @mobile-reality/mdma-cli) to generate it structurally, then commit the result as a maintained file. import { buildSystemPrompt } from '@mobile-reality/mdma-prompt-pack';
const systemPrompt = buildSystemPrompt({
customPrompt: `Domain: clinical intake.
Allowed components: form, approval-gate, callout.
All PII fields (name, email, phone, DOB, MRN) MUST be sensitive: true.
Generate MDMA when the user describes a patient visit or symptom.`,
});prompts/clinical-intake.ts), not the author.validate(markdown, { autoFix: false }) from @mobile-reality/mdma-validator and fail the job on !result.ok. For local DX, wire npx @mobile-reality/mdma-cli validate "docs/**/*.md" --fix. Do not write custom YAML-regex linters — the validator already covers YAML correctness, schema conformance, ID uniqueness, binding resolution, and PII sensitivity. import { validate } from '@mobile-reality/mdma-validator';
const result = await validate(markdown, { autoFix: false });
if (!result.ok) {
for (const issue of result.issues) console.error(issue);
process.exit(1);
}customSchemas, and register a React renderer via customizations.components.<type> on MdmaDocument. The same slot overrides built-ins (e.g. swap the default table-fallback chart for a recharts renderer). import { z } from 'zod';
import { ComponentBaseSchema } from '@mobile-reality/mdma-spec';
const ProgressSchema = ComponentBaseSchema.extend({
type: z.literal('progress'),
value: z.number().min(0).max(100),
});
const processor = unified()
.use(remarkParse)
.use(remarkMdma, { customSchemas: new Map([['progress', ProgressSchema]]) });
<MdmaDocument
ast={ast}
store={store}
customizations={{
components: {
progress: ProgressRenderer,
chart: MyRechartsRenderer, // overrides the built-in table fallback
},
}}
/>;store.getEventBus().onAny(handler) to observe user actions without coupling to React state. Dispatch programmatically with store.dispatch({ type: 'FIELD_CHANGED' | 'ACTION_TRIGGERED' | ... }). Multi-step flows listen for ACTION_TRIGGERED and advance the conversation (e.g. inject the next assistant message when a button fires).@mobile-reality/mdma-mcp instead of bundling prompts into the agent. Its tools — get-spec, get-prompt, build-system-prompt, validate-prompt, list-packages — collapse the discovery phase so the agent gets the spec, packages, and ready-made prompts in a handful of tool calls. The integration is three lines of JSON in the agent's MCP config: {
"mcpServers": {
"mdma": { "command": "npx", "args": ["@mobile-reality/mdma-mcp"] }
}
}NEVER ship these integration anti-patterns:
createDocumentStore(ast) on every streamed chunk instead of calling store.updateAst(ast) — wipes user input mid-typingunified processor on every render — it's expensive; make it a module-level singletonbuildSystemPrompt — breaks the spec + reinforcement sandwich the model relies onvalidate() — misses binding-graph and schema-conformance issues the package already catches@mobile-reality/mdma-* package when the integration only needs two or threeMDMA_AUTHOR_PROMPT / calling buildSystemPrompt — the spec evolves with the package"All PII fields must be marked sensitive" to every domain prompt by defaultrenderer-react — use customizations.components.chart insteadmdma-cli as ephemeral — it's the domain spec; commit itInterpret pragmatically and pick the shallowest integration that satisfies the requirement. A one-page demo that renders a static MDMA file needs three imports and ten lines. A production chat with streaming, custom components, and CI validation needs the full stack. Don't converge on a default kitchen-sink wiring across integrations.
IMPORTANT: Match integration depth to the app's vision. A static document viewer wants parser + renderer-react and nothing else — no store events, no prompt pack, no validator. A domain chat platform wants the full sequence: streaming reparse, domain customPrompt committed to the repo, custom components with Zod + renderer, CI validation, and MCP exposure for the agent author. Elegance comes from executing the chosen depth well, not from using every package in every app.
Remember: MDMA's whole point is that the packages already solve the hard parts — binding graphs, audit logs, PII redaction, policy, prompt assembly, validation. A good integration trusts them and composes them; a bad integration reinvents them. Compose, don't reinvent.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.