@markdownai/mcp
v0.0.13
Published
MCP (Model Context Protocol) server for MarkdownAI. Bridges AI assistants and live documents - intercepts file reads, resolves directives, and serves phase-aware content through 8 tools AI tools can call directly.
Readme
@markdownai/mcp
MCP (Model Context Protocol) server for MarkdownAI. Bridges AI assistants and live documents - intercepts file reads, resolves directives, and serves phase-aware content through 8 tools AI tools can call directly.
All packages: @markdownai/core · @markdownai/engine · @markdownai/parser · @markdownai/renderer · @markdownai/mcp · @markdownai
What it does
@markdownai/mcp solves the core problem of AI tools reading live documents: without it, an AI opens a .md file and sees raw source - directives, macro definitions, unexpanded expressions. With the MCP server configured, the AI receives fully rendered, up-to-date content every time it reads a MarkdownAI document.
The server implements the Model Context Protocol over stdio. It exposes 8 tools AI assistants can call to interact with documents, navigate phases, invoke macros, and manage cache state.
Key capability - lazy phase loading. For multi-phase documents (workflows, runbooks, multi-step guides), the server loads only the currently active phase into AI context. A 20-phase document doesn't flood the AI with everything at once - the AI works through phases in sequence, requesting each one as needed.
Installation
npm install -g @markdownai/coreThis installs the mai CLI and the mai-serve binary, which is what your AI client will use as the MCP server process.
Setup with Claude Code
You don't start the server manually. Claude Code starts it automatically when you open a session, based on the configuration you add once.
The easiest way is mai init, which writes the config for you:
mai initOr add it manually to your project's .claude/settings.json (or your global Claude Code settings):
{
"mcpServers": {
"markdownai": {
"command": "mai-serve",
"args": []
}
}
}That's it. Claude Code reads this config on startup, launches mai-serve as a subprocess, and connects to it over stdio. From that point on, every .md file with a @markdownai header that Claude reads is automatically rendered before Claude sees it - no manual steps needed per session.
To scope the server to a specific project directory, add --cwd:
{
"mcpServers": {
"markdownai": {
"command": "mai-serve",
"args": ["--cwd", "/path/to/your/project"]
}
}
}The 8 MCP tools
Once the server is configured, Claude can call these tools directly during a session.
read_file
Reads and renders a MarkdownAI document, resolving all directives. Returns the same output you'd get from mai render.
{
"name": "read_file",
"arguments": {
"path": "./docs/status.md"
}
}Optional budget parameter (in tokens) enables AI-format compression - drops low-priority sections to fit within the limit.
{
"name": "read_file",
"arguments": {
"path": "./docs/status.md",
"budget": 4000
}
}list_phases
Lists all phases defined in a document and their transitions.
{
"name": "list_phases",
"arguments": {
"file": "./runbooks/deploy.md"
}
}Returns an array like:
[
{ "name": "preflight", "transitions": ["deploy"] },
{ "name": "deploy", "transitions": ["verify", "@call notify_team"] },
{ "name": "verify", "transitions": [] }
]resolve_phase
Renders the content of a specific named phase, running all its directives.
{
"name": "resolve_phase",
"arguments": {
"file": "./runbooks/deploy.md",
"phase": "preflight"
}
}next_phase
Returns the name of the phase that follows the given phase, or null if it's the last one. Used to walk through a multi-phase document step by step.
{
"name": "next_phase",
"arguments": {
"file": "./runbooks/deploy.md",
"phase": "preflight"
}
}
// returns: "deploy"call_macro
Invokes a named macro defined in a document, with optional arguments.
{
"name": "call_macro",
"arguments": {
"file": "./report-template.md",
"macro": "summary-block",
"args": { "period": "Q1 2026", "format": "concise" }
}
}get_env
Retrieves an environment variable value, with an optional fallback.
{
"name": "get_env",
"arguments": {
"key": "DATABASE_URL",
"fallback": "postgres://localhost:5432/dev"
}
}Keys matching credential patterns (PASSWORD, SECRET, TOKEN, API_KEY, etc.) are blocked - the tool returns an error rather than exposing sensitive values.
execute_directive
Runs a single MarkdownAI directive and returns its output. Useful for ad-hoc queries during a session.
{
"name": "execute_directive",
"arguments": {
"directive": "@query \"git log --oneline -5\""
}
}Security rules apply exactly as they do during a full render - the directive must pass the shell allowlist, HTTP domain allowlist, or database jail before it executes.
invalidate_cache
Clears cached rendered output for a specific file, or for all files if no path is given.
{
"name": "invalidate_cache",
"arguments": {
"file": "./docs/live-metrics.md"
}
}{
"name": "invalidate_cache",
"arguments": {}
}Security at the MCP boundary
The server enforces the same security rules as the CLI:
- File access - only files within the project's document root can be read. Path traversal attempts (
../../../etc/passwd) are rejected. - Environment variables - keys matching credential patterns are blocked in
get_envresponses. - Directives -
execute_directivecalls run through the full security jail. Shell-injection sequences are blocked. - Server resilience - a bad request never crashes the server. The server recovers and keeps running.
API Reference (library usage)
If you're building your own MCP server or tooling on top of this package:
startServer(options?): Promise<void>
Starts the MCP stdio server. Blocks until the client disconnects.
import { startServer } from '@markdownai/mcp'
await startServer({
cwd: process.cwd(),
})Individual tools
Each tool is exported as a standalone function:
import {
readFile,
listPhases,
resolvePhase,
nextPhase,
callMacro,
getEnv,
executeDirective,
invalidateCache,
} from '@markdownai/mcp'
const rendered = await readFile({ path: './docs/status.md' })
const phases = await listPhases({ file: './runbook.md' })Connection registry
The server maintains a session-level connection registry so database connections are established once and reused across all tool calls.
import { registerConnection, getConnection, listConnections, clearConnections } from '@markdownai/mcp'
registerConnection('reports', { type: 'mongodb', uri: process.env.MONGODB_URI })
const conn = getConnection('reports')
clearConnections() // closes all connectionsTypeScript
import type { ServerOptions } from '@markdownai/mcp'Part of the MarkdownAI toolchain
- Parse documents - use
@markdownai/parser - Execute directives - use
@markdownai/engine - Format output - use
@markdownai/renderer - Run from the CLI - install
@markdownai/coreglobally
License
MIT - GitHub
