npm install -g @markdownai/core

MarkdownAI

documentation that cannot lie.

MarkdownAI hero image - documentation that cannot lie

The Problem With Documentation

Here is something that happens at every company: someone writes great documentation. It is accurate. It is detailed. It is helpful.

Then the code changes.

The database schema gets a new field. The API endpoint moves. The environment variable gets renamed. The service goes from port 3000 to 8080. But the docs stay exactly as they were - frozen at the moment someone last had time to update them. Within weeks, the documentation is wrong. Within months, it actively misleads. People stop trusting it. Eventually, they stop reading it.

This is not a process failure. This is physics. Static text cannot track a moving system.

Add @markdownai to the first line of any .md file and it becomes a live document. Instead of writing values that will go stale, you write directives that fetch the current value every time the document is rendered. Your database record count is queried when someone reads the doc, not when you wrote it. Your API response is real. Your environment variables are live. Your file tree reflects what is actually on disk right now.

Run mai render. The document executes. The output is clean, standard Markdown with everything resolved. The result is documentation that cannot lie - because it does not store facts, it fetches them.

How It Works

One line changes everything. Add @markdownai to the top of any .md file and the mai tool treats it as a live document. Everything after that line is standard Markdown, extended with directives. Directives become their results. Conditions are evaluated. Macros expand. Data sources are queried.

754
Tests passing
78
Features documented
11
Output formats
6
Packages

The Six Packages

MarkdownAI is a six-package monorepo. Each layer has a single, well-defined job:

Package Name Role
parser/ @markdownai/parser Converts .md files to an AST - inert, no execution
engine/ @markdownai/engine Walks the AST, runs directives, assembles output
renderer/ @markdownai/renderer Formats data into 11 output styles (table, bar chart, flow, etc.)
mcp/ @markdownai/mcp MCP server - serves live document execution to Claude and other AI tools
core/ @markdownai/core The mai binary and all CLI commands
vscode/ markdownai (VS Code) Language detection, syntax highlighting, snippets, completions, hover, diagnostics

The parser is intentionally inert - it never executes anything. Security enforcement lives in the engine, not in individual directives. This means security gates apply regardless of which directive triggers the operation.

Quick Start

1

Install

npm install -g @markdownai/core
2

Create a live document

Save this as status.md:

@markdownai

# Project Status

**Branch:** {{ @query git branch --show-current }}
**Tests:** {{ @query pnpm test 2>&1 | tail -1 }}
**Last commit:** {{ @query git log --oneline -1 }}

## Source Files

@list ./src/ match="**/*.ts" | sort | @render type="table" columns="name,size"

## Environment

@if env.NODE_ENV == "production"
Running in **production** mode.
@else
Running in **{{ env.NODE_ENV }}** mode.
@endif
3

Render it

mai render status.md

Every directive runs. The output is clean Markdown with real data from your system - not values someone typed in last month.

Language Features

MarkdownAI extends standard Markdown with a small set of directives. They compose naturally - you can pipe data through transforms, embed expressions in prose, define reusable blocks, and conditionally show sections based on any runtime value.

{{ }}

Inline Interpolation

Embed live values directly in prose. Works in paragraphs, headings, and table cells. Use {{ env.VAR }}, {{ @query git branch --show-current }}, or {{ @date format="YYYY-MM-DD" }} anywhere in your document. Unset variables evaluate to empty string rather than an error.

@env

Environment Variables

Declare the variables your document depends on. required causes mai validate to fail if the variable is unset. masked prevents the value from appearing in rendered output. fallback=value provides a default when the variable is absent.

@define

Macros

Define reusable blocks once with @define and call them anywhere with @call. Macros can accept named parameters, contain any directives, and be shared across documents by importing them from a shared file.

@include

Includes and Imports

Pull in file content with @include - supports a line range for embedding source code in docs. Use @import to import only definitions (macros, connections, env defaults) from a shared file without rendering its content.

@if

Conditionals

Full conditional system. Check env vars, file existence, string comparisons, and compound logic with && and ||. The same expression operators work in @if conditions, data query where filters, and {{ }} interpolations.

| @render

Pipe and Render

Chain transforms before rendering: sort, grep, head, tail, uniq, wc -l. All built-in transforms are cross-platform pure Node.js. Feed the result into any of 11 output formats via @render.

Data Sources

MarkdownAI can pull from the filesystem, structured files, databases, HTTP APIs, and shell commands. Every data source is jailed by default - see the Security section for how the gates work.

@list

@list

List filesystem entries, or pull structured data from JSON, YAML, and CSV files. Filter with glob patterns, where clauses, and depth limits. Specify columns, sort order, and output format inline.

@list ./src/ match="**/*.ts"
@list ./config.yaml path=$.services
@list ./data.csv where="status == active"
@read

@read

Extract a specific value from a structured file using dot-notation paths. Supports JSON, YAML, TOML, and CSV. Useful for pulling a single config value, a version number, or a record field into prose.

@read ./package.json path=$.version
@read ./config.yaml path=$.server.port
@read ./data.csv column=email where="active == true"
@query

@query

Run a shell command and use its output. Supports a label= option to store the result for reuse in conditions and interpolations. Disabled by default - requires an explicit allowlist in your security config.

@query git branch --show-current
@query bash -c "find src -name '*.ts' | wc -l"
@query bash -c "git status --porcelain" label=dirty
@http

@http

Make HTTP requests and embed the response. Assert status codes with expected=, pass headers and bodies, and cache responses to avoid hammering external APIs. All @http calls are blocked by default until you add domains to the allowlist.

@http GET https://api.example.com/status
@http GET {{ env.API_ENDPOINT }}/health expected=200
@http GET https://api.example.com/data @cache persist ttl=3600
@db

@db and @connect

Register a database connection with @connect, then query it using MarkdownAI's database-agnostic query language. A document querying Postgres looks identical to one querying MongoDB. Supports SQLite, PostgreSQL, MySQL, MSSQL, and MongoDB.

@connect primary type="postgres" uri=env.POSTGRES_URI
@db using="primary" find="users" where="active==true"
@db using="primary" aggregate="sales" group="region" sum="revenue" | @render type="bar"
@phase

@phase and @graph

Structure a document as a sequential workflow with named phases and @on complete transitions. The MCP server exposes list_phases, resolve_phase, and next_phase tools so AI tools can navigate the workflow programmatically. Use @graph to build a dependency graph from document relationships.

Security

Security is a first-class concern in MarkdownAI, not an afterthought. Every external operation is jailed by default and must be explicitly enabled. The security layer lives in the engine - it applies regardless of which directive triggers the operation.

📁 Filesystem Confinement

All file access (@include, @read, @list, @tree) is confined to the document root - by default, the directory containing the document being rendered. No directive can read files above this root using path traversal. Content masking runs before any output is written, so sensitive values matching your configured patterns are replaced with [MASKED].

🔒 Shell Execution Jail

@query shell execution is disabled by default (allowShell: false). Enable it with an explicit allowlist: mai security shell add "git *". All executions are subject to deny patterns, jailRoot confinement, and optional audit logging. Commands not matching the allowlist are blocked even with the master switch on.

🗃 Database Query Jail

Database access is read-only by default. Write operations, DDL statements, and full-table scans on large collections are blocked unless explicitly permitted. Configure allowed operations, blocked SQL keywords, and maximum result sizes via mai security db commands.

🌐 HTTP Request Jail

All @http calls are blocked by default. Enable HTTP and add specific domains to the allowlist: mai security http enable, then mai security http add-domain api.example.com. Wildcard subdomains are supported. Requests to non-allowlisted domains fail regardless of configuration.

Immutable built-in rules. Some rules are hardcoded and cannot be disabled by any configuration - not by security policy, not by environment variables, not by document directives. Cloud metadata endpoints are always blocked (169.254.169.254, metadata.google.internal, and all similar credential-theft endpoints). Path traversal sequences are always blocked in jailed contexts. Content masking always runs before caching - credentials can never be stored in plain text. These rules are implemented as frozen, readonly arrays in the engine code.

Caching

Add @cache to any directive to avoid redundant fetches on repeated renders. Cache modes range from in-memory session caching to persistent disk storage with expiry. The mock mode is particularly useful for testing - your document runs with real directives but against predictable fixture data.

Mode Behavior
@cache session Store in memory for the current mai run. Gone when the process exits.
@cache session ttl=N Session cache that expires after N seconds within the run.
@cache persist Write to disk. Survives process restarts and re-renders.
@cache persist ttl=N Disk cache with expiry. Stale entries are refetched automatically.
@cache mock=./fixture.json Always return data from a local file - never call the live source. For testing.

Cache Management

mai cache clear              # clear all cached entries
mai cache clear my-doc.md    # clear for one document
mai cache show               # inspect what is currently cached
mai cache seed my-doc.md     # pre-populate by running all fetches ahead of time

Content masking always runs before caching. Sensitive values that match your configured secret patterns are replaced with [MASKED] before anything is written to the cache. Credentials can never be stored in plain text, even in session memory.

Output Formats

The renderer supports 11 output formats. All are plain ASCII that renders correctly everywhere - in terminals, GitHub, VS Code preview, and AI context windows.

Format Output
listUnordered bullet list
numberedOrdered numbered list
linksClickable markdown links
tableGrid table with headers
codeFenced code block
inlineEmbedded scalar value
barHorizontal ASCII bar chart
flowASCII flow diagram with arrows
treeIndented ASCII tree
timelineLeft-to-right ASCII timeline
jsonPretty-printed JSON

Pipe Chains

Formats become most useful when combined with the pipe operator. A data fetch, a few transforms, and a render sink - all on one line:

@list ./src/ | sort | @render type="tree"
@query bash -c "df -h" | @render type="table"
@db using="primary" aggregate="sales" group="name" sum="revenue" | @render type="bar"
@query bash -c "git log --oneline -20" | grep "feat" | @render type="numbered"

AI Integration

MarkdownAI was designed with AI-native workflows in mind. Every feature is built to serve both humans reading rendered output and AI tools consuming live document context. There are three distinct integration points.

💻 MCP Server (9 tools)

Run mai serve to start an MCP server. Claude, Cursor, and any MCP-compatible tool can query documents, execute directives, navigate phases, and get live data - without reading raw source files. The 9 exposed tools cover the full document lifecycle: read_file, list_phases, resolve_phase, next_phase, call_macro, get_env, execute_directive, get_constraints, and invalidate_cache.

🔌 PreToolUse Hook

Run mai init to install a PreToolUse hook into your AI client. When the AI reads a .md file starting with @markdownai, the hook intercepts the read and routes it through mai render first. The AI always receives the rendered, executed output - live data, not the last time someone manually updated the file.

🤖 AI-Native Features

@prompt embeds instructions for AI consumers that never appear in human output. @constraint expresses rules as structured, machine-readable blocks. @consumer=ai targets sections to AI readers only - token-efficient format mode strips narrative prose, reducing consumption by up to 35% compared to the same document in human mode.

Skill Context Variables

When a MarkdownAI document is used as a Claude Code skill file, the full slash command invocation context is available inside the document. This enables genuine engine-evaluated dispatch - the engine routes to the correct section before Claude even sees the file.

Variable Description
ARGUMENTS / argsFull raw $ARGUMENTS string
argsListPositional args, shell-style parsed
arg0 arg1 arg2 arg3Shorthand positionals
CLAUDE_EFFORTlow, medium, high, xhigh, or max
CLAUDE_SESSION_IDCurrent Claude Code session ID
CLAUDE_SKILL_DIRDirectory containing the skill file
@markdownai

@if ARGUMENTS.startsWith("audit")
  @include ./audit-mode.md
@elseif ARGUMENTS.startsWith("build")
  @include ./build-mode.md
@elseif ARGUMENTS.startsWith("plan")
  @include ./plan-mode.md
@endif

CLI Reference

All commands share these universal flags:

Flag Description
--env <file>Load a .env file for environment variables
--cwd <path>Run as if in a different directory
--verboseShow warnings in output
--strictTreat warnings as errors
--silentSuppress all output except fatal errors and security alerts

Commands

Command Description
mai render <file>Execute and print rendered markdown to stdout
mai render <file> -o <path>Execute and write result to a file
mai validate <file>Check for errors and warnings; exits 1 on error
mai parse <file>Output the document AST as JSON
mai eval "<expression>"Evaluate a single expression against the environment
mai strip <file>Remove all directives; output plain Markdown
mai serveStart the MCP server
mai initInstall the PreToolUse hook into your AI client
mai build <file> -o <output>Render and write to disk (alias for render -o)
mai watch <file> -o <output>Watch for changes and re-render automatically
mai cache clear [file]Clear cache for one document or all
mai cache show [file]Inspect cache entries
mai cache seed <file>Pre-populate cache by running all fetches
mai security initCreate or import a security policy
mai security showDisplay the active security policy
mai security shell enableEnable shell command execution
mai security shell add <pattern>Add a command pattern to the allowlist
mai security http enableEnable HTTP requests
mai security http add-domain <domain>Add a domain to the allowlist
mai security db add <connection>Register a database connection
mai security db test "<query>"Test whether a query would be permitted

Directive Reference

Every directive supported by MarkdownAI, grouped by category.

Header and Activation

DirectiveDescription
@markdownaiActivate MarkdownAI processing. Must be on line 1 (or first line after YAML frontmatter).
@markdownai v1.0Pin to a specific specification version.
@markdownai shell-inline="passthrough"Let Claude Code's !`cmd` syntax pass through unintercepted.

Environment and Variables

DirectiveDescription
@env VAR requiredDeclare a required variable. mai validate fails if unset.
@env VAR required maskedRequired and suppress from output.
@env VAR fallback=valueDeclare a default when variable is not set.
{{ env.VAR }}Inline interpolation of any environment variable.
{{ var ?? "default" }}Interpolation with explicit fallback.

Macros and Composition

DirectiveDescription
@define name ... @endDefine a reusable macro block.
@call nameExpand a macro at this location.
@call name param=valueExpand a macro with named parameters.
@include ./path.mdInclude another file's content verbatim.
@include ./file.ts lines=N-MInclude specific line range.
@import ./path.mdImport definitions (macros, connections, env defaults) from another file.
@import ./path.md @cache sessionImport with session caching.

Conditionals

DirectiveDescription
@if <expr>Include content if expression is true.
@elseif <expr>Alternate branch.
@elseFallback branch.
@endifClose conditional block.

Operators: ==, !=, <, >, <=, >=, &&, ||, !, startsWith, endsWith, includes, file.exists, file.isFile, file.isDir

Data Sources

DirectiveDescription
@list <path>List filesystem entries or structured data from JSON/YAML/CSV.
@read <path>Read and extract a value from a structured file.
@tree <path>Render a directory tree.
@date format="..."Current date/time in any format.
@count <path> "<pattern>"Count files matching a pattern.
@connect <name> type="<db>" uri=env.VARRegister a named database connection.
@db using="<name>" find="<collection>"Query a collection or table.
@db using="<name>" raw="<query>"Run a native query.
@query <command>Execute a shell command and use its stdout.
@query <command> label=nameExecute and store result in named label.
@http <METHOD> <url>Make an HTTP request and use the response body.
@http <METHOD> <url> expected=<code>Assert response status code.

Pipeline and Rendering

DirectiveDescription
| sort / sort -n / sort -rSort alphabetically, numerically, or in reverse.
| grep <pattern> / grep -v / grep -iInclude, exclude, or case-insensitive match.
| head -N / tail -NKeep first or last N lines.
| uniqDeduplicate consecutive lines.
| wc -lCount lines.
@render type="<format>"Render accumulated pipeline result in the specified format.

Phases and Graphs

DirectiveDescription
@phase <name>Open a named phase block.
@on complete ... @endActions to take when a phase completes.
@graphBuild a dependency graph from document relationships.

Caching

DirectiveDescription
@cache sessionCache in memory for the current run.
@cache session ttl=NSession cache, expires after N seconds.
@cache persistCache to disk across runs.
@cache persist ttl=NDisk cache with expiry.
@cache mock=./file.jsonAlways use fixture file, never call live source.

AI-Native

DirectiveDescription
@consumer=aiTag a block for AI consumers only.
@consumer=humanTag a block for human readers only.
@prompt ... @endEmbed instructions for AI consumers (not in human output).
@constraint <name> ... @endDeclare a machine-readable rule.
@note ... @endHuman-readable source comment, always stripped. Add visible to render as a blockquote.

VS Code Extension

The MarkdownAI VS Code extension provides full IDE support for .md files that begin with @markdownai. Open the Extensions panel (Ctrl+Shift+X / Cmd+Shift+X), search for MarkdownAI, and click Install. Or install from the VS Code Marketplace.

👁

Live Preview

Open any MarkdownAI file and click the preview icon in the editor title bar, or run MarkdownAI: Open MarkdownAI Preview from the Command Palette. The panel opens to the side, runs the engine on the saved file, and shows rendered output. It refreshes automatically every time you save.

🏢

Language Detection

Any .md file whose first line is @markdownai is automatically re-tagged as the markdownai language type, activating all extension features. Files with YAML frontmatter before @markdownai are also detected.

🌟

Syntax Highlighting

TextMate grammar covers all MarkdownAI directives: @markdownai, @define, @phase, @if, @include, @import, @env, @call, @list, @read, @db, @http, @query, @render, @prompt, @constraint, @cache, and {{ }} interpolations.

Snippets and Completion

15+ tab-triggered snippets for all directives. Type @def and expand to a complete @define/@end block, @if to a full conditional, @phase to a phase skeleton. As you type @, completions show all valid directives with descriptions.

🔍

Navigation

Hover over any @define name or @call to see the macro definition inline. Press F12 on any @call name to jump to the @define that declares it, even across files linked by @import. Right-click any @define name for Find All References.

🔴

Diagnostics

The extension checks documents as you type and reports: @call to undefined macros (error), @include/@import pointing to missing files (error), @env variables without a fallback (warning, configurable), and undefined macros in @call positions (warning, configurable).

Architecture

MarkdownAI is a six-package npm workspaces monorepo, all TypeScript with strict mode throughout. Each package has a single, well-bounded responsibility.

Package Role
@markdownai/parser AST production only. Parses .md files to a typed node tree. Intentionally inert - no execution, no I/O.
@markdownai/engine Walks the AST, runs directives, resolves env variables, manages the pipe chain, applies caching, and enforces security gates.
@markdownai/renderer Formats data into 11 output styles. Accepts structured data from the engine and produces ASCII-safe Markdown.
@markdownai/mcp MCP server with 9 tools. Serves live document execution, phase navigation, and constraint access to AI tools.
@markdownai/core The mai binary. Wires the other packages together and exposes all CLI commands.
markdownai (VS Code) Language detection, TextMate grammar, snippets, completions, hover, go-to-definition, find references, diagnostics, live preview.

The separation between parser and engine is intentional. The parser never executes anything - you can safely parse any document in any environment. Security enforcement sits in the engine, between the directive runner and every external system. 754 tests across all packages, including unit tests for every directive, E2E CLI pipeline tests, MCP protocol conformance tests, and a dedicated AI-native feature test suite.

Installation

MarkdownAI installs globally via npm. One command puts mai on your path. Node.js 18 or higher required.

1

Install the CLI

npm install -g @markdownai/core
mai --version
2

Install the VS Code extension

Open Extensions (Ctrl+Shift+X), search MarkdownAI, click Install. Gives you syntax highlighting, snippets, completions, hover, diagnostics, and live preview.

3

Install the PreToolUse hook

Lets your AI client (Claude Code, Cursor) automatically render MarkdownAI documents before reading them. Claude always sees live data, not stale source files.

mai init
4

Initialize a security policy

Before using data sources that touch external systems, set up a security policy for your project. This creates .markdownai/security.json and walks you through enabling the gates you actually need.

cd your-project
mai security init

Platform note: Works on macOS, Linux, and Windows. WSL is recommended on Windows for the shell command features (@query with shell execution enabled). Built-in pipe transforms (sort, grep, head, tail, uniq, wc -l) are cross-platform pure Node.js - no shell required.