obsidian-markdown-lint-mcp-server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@obsidian-markdown-lint-mcp-serverLint the markdown content of my current note"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
obsidian-markdown-lint-mcp-server
An MCP server that lints, validates, and renders your Obsidian vault markdown — all inside Docker.
Tools • Quick Start • Configuration • Schemas • Development
Run markdown linting, front matter validation, and Mermaid-to-SVG rendering as MCP tools callable from Claude Code. Docker isolates Chromium and Node.js from your host machine. The server is stateless — it processes content you send and returns results; Claude Code handles all disk I/O.
This is a stdio MCP server: Claude Code launches it as a subprocess (docker run -i --rm) and talks to it over stdin/stdout. There is no port and no long-running container — the container starts when a session opens and is removed when it ends.
Why Docker
Mermaid rendering requires a Chromium browser (via Puppeteer). Running that, plus dozens of Node.js dependencies, directly on your laptop is a valid security concern. This server packages everything inside a container. Your vault never mounts into it; content travels as strings.
Tools
Tool | What it does |
| Runs markdownlint on content, returns errors and a corrected version |
| Validates YAML front matter against a JSON Schema you pass in |
| Renders all |
| Reads a rendered SVG and returns the original Mermaid source as a code block, ready to edit |
The render/extract pair supports a full edit cycle: render → view SVG in Obsidian → extract → edit source → re-render.
Quick start
Prerequisites: Docker Desktop, Node.js ≥ 20, Claude Code.
git clone <repo>
cd obsidian-markdown-lint-mcp-server
npm install
npm run build # compile TypeScript → dist/
docker build -t obsidian-markdown-lint-mcp . # or: docker compose buildRegister the server with Claude Code. Claude Code runs docker run -i --rm itself, per session — you do not start a container yourself.
Per-project (recommended for a vault) — run this from your vault root; it writes a committable .mcp.json:
claude mcp add obsidian-markdown-lint -s project -- docker run -i --rm obsidian-markdown-lint-mcpThe resulting .mcp.json:
{
"mcpServers": {
"obsidian-markdown-lint": {
"command": "docker",
"args": ["run", "-i", "--rm", "obsidian-markdown-lint-mcp"]
}
}
}Global — make it available in every project (-s user writes to ~/.claude.json):
claude mcp add obsidian-markdown-lint -s user -- docker run -i --rm obsidian-markdown-lint-mcpVerify with claude mcp list (should show obsidian-markdown-lint → connected), then start a new Claude Code session — tools are discovered at session start. The four tools are now available.
The
-iflag is required: it keeps the container's stdin open for the JSON-RPC stream. Without it the container gets EOF and exits immediately. Do not usedocker compose upfor this server — it is a stdio subprocess, not a long-running HTTP service.
Hardening (optional)
The server needs no network, no writable root filesystem, and little memory, so you can lock the container down. --network none and --security-opt no-new-privileges are always safe (the image skips the Puppeteer download and renders offline):
claude mcp add obsidian-markdown-lint -s project -- docker run -i --rm --network none --security-opt no-new-privileges --memory 2g obsidian-markdown-lint-mcpFor stricter isolation, add a read-only root with a writable temp dir and drop all Linux capabilities. Chromium renders into /tmp because the server launches it with --disable-dev-shm-usage, so the --tmpfs /tmp is required. Verify a Mermaid render still succeeds under these flags before relying on them, since Chromium's writable paths vary by version:
docker run -i --rm --network none --security-opt no-new-privileges --read-only --tmpfs /tmp --cap-drop ALL --memory 2g obsidian-markdown-lint-mcpConfiguration
Vault layout
The server reads nothing from disk directly. Claude Code reads your vault files and passes content as strings. Place these config files at your vault root:
vault/
.markdownlint.json ← linting rules (optional; defaults to markdownlint defaults)
.schemas/
_shared.json ← shared field definitions (reference; not loaded at runtime)
article.json
how-to.json
technical.json
deep-research.json
strategy.json
meeting.json
brainstorming.jsonPre-built schemas for all seven note types are included in this repo's .schemas/ directory. Copy them to your vault root. The seven type schemas are self-contained, so validate_front_matter only needs the one matching the document's type; _shared.json is the source the type schemas are built from and is kept for maintenance.
CLAUDE.md for your vault
Add a CLAUDE.md to your vault project that tells Claude how to use the server. Minimum viable example:
## MCP: obsidian-markdown-lint-mcp-server
Vault attachments directory: attachments
Schemas directory: .schemas/
Linting config: .markdownlint.json
When asked to "process a vault file":
1. Read the file
2. Read .markdownlint.json and call lint_markdown
3. Read the type field from front matter, read .schemas/{type}.json, call validate_front_matter
4. Call render_mermaid_diagrams with attachments_dir="attachments" and the document title
5. Write the modified_content back to the file
6. Write each SVG from the svgs array to its path field (decoded from base64)
SVG files contain the original Mermaid source base64-encoded in <metadata>.
To edit a diagram: call extract_mermaid_from_svg with the SVG content,
edit the returned mermaid_block, then re-run render_mermaid_diagrams.Mermaid front matter options
Control rendering per-document:
mermaid_theme: dark # default | dark | neutral | forest (default: default)
mermaid_background: white # any CSS color or "transparent" (default: white)After rendering, the server adds mermaid_svg_source: base64-embedded to the front matter so Claude knows SVGs in this document contain extractable source.
Front matter schemas
Seven note types are supported. Each schema lives at .schemas/{type}.json.
Type | Extra required fields |
| core only |
| core only |
|
|
|
|
|
|
|
|
| core only |
Core required fields on every type: type, title, author, category, tags, description, summary, status, version, date_created, date_updated.
The type field is the discriminator. Claude reads it from the front matter to select the right schema file before calling validate_front_matter.
How SVG embedding works
Each rendered SVG contains the original Mermaid source, base64-encoded, inside the SVG's <metadata> element:
<metadata>
<mermaid:source xmlns:mermaid="http://mermaid.js.org/" encoding="base64">
Zmxvd2NoYXJ0IExSCiAgICBBIC0tPiBCCg==
</mermaid:source>
</metadata>Base64 avoids all whitespace normalization issues — Mermaid is whitespace-sensitive and attribute-value normalization in XML would corrupt indentation. The extract_mermaid_from_svg tool decodes this and returns a ready-to-use ```mermaid ``` block.
SVG output paths
Diagrams render to {attachments_dir}/{document-title-slug}/{diagram-type}-{n}.svg.
For a document titled "System Architecture" with attachments_dir = "attachments" and two flowchart blocks, you get:
attachments/system-architecture/flowchart-1.svg
attachments/system-architecture/flowchart-2.svgThe markdown is updated to:
Standard GitHub-flavored markdown — renders in Obsidian, GitHub, and any standard markdown viewer.
Testing
Three layers, all runnable from the repo without building the Docker image:
npm test # 52 Jest unit tests (ESM), enforced coverage thresholds
npm run eval # tool-correctness evals against the compiled functions
npm run snapshot # end-to-end fixture snapshots (requires a real Chromium)Unit tests (tests/unit/) call the tool functions directly. Coverage is gated (90% lines/functions/statements, 85% branches); src/server.ts and src/create-server.ts are excluded as pure wiring and are covered separately through an in-memory MCP transport. Mermaid rendering is tested with an injected browser/renderer, so no Chromium is needed here. Use npm run test:coverage for the lcov/HTML report.
Evals (tests/evals/) check tool correctness against the compiled functions (no Docker). Output is JSON with summary.passed, summary.failed, and a results array; exit code is 1 if any eval fails. The suite covers all four tools: lint detection, schema validation (pass and fail cases), Mermaid rendering with a mock browser, and SVG round-trip extraction. In Claude Code, say "run evals" or "evaluate the tools" and it runs npm run eval and reports the results.
Snapshots (tests/snapshot/) prove that each input under test-obsidian-vault/original/ reproduces its committed test-obsidian-vault/snap-shot/ output through the real tool pipeline. Unlike the evals, test-1 launches Puppeteer for an actual render, so this needs a real Chromium and is kept out of npm test. SVG geometry is not byte-compared (it varies by Mermaid, Chromium, and font version); the deterministic projection is, namely the modified markdown, the file layout, and the embedded Mermaid source. Output is JSON; exits non-zero on any failure.
JSON Schema format
Each file in .schemas/ is a self-contained JSON Schema draft-07 object. validate_front_matter parses the document's YAML front matter and validates the resulting object against the schema you pass in. A trimmed view of .schemas/article.json:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Article",
"type": "object",
"required": ["type","title","author","category","tags","description","summary","status","version","date_created","date_updated"],
"properties": {
"type": { "const": "article" },
"title": { "type": "string", "minLength": 1 },
"category": { "type": "string", "enum": ["Software Development", "Security", "DevOps", "..."] },
"tags": { "type": "array", "items": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_\\-/]*$" }, "minItems": 4, "maxItems": 12 },
"status": { "type": "string", "enum": ["published", "draft", "in-progress"] },
"version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" },
"date_created": { "type": "string", "format": "date" }
},
"additionalProperties": false
}See .schemas/article.json for the complete schema, including the optional fields it also permits (aliases, subtitle, source, sources, reviewers, signoff, mermaid_theme, mermaid_background, mermaid_svg_source). Every schema sets additionalProperties: false, so any front matter key not declared in the schema is a validation error; add a key to the schema before using it in notes.
Important: Date fields must be quoted strings in YAML (date_created: "2026-01-01") because YAML auto-converts unquoted YYYY-MM-DD values to JavaScript Date objects, which fail the type: string check.
The per-type required fields are listed in Front matter schemas above.
Development
npm install
npm run build # compile TypeScript → dist/TypeScript source is in src/. After changing code, rebuild the image so the next Claude Code session picks it up:
npm run build && docker build -t obsidian-markdown-lint-mcp . # or: docker compose buildThen start a new Claude Code session (stdio tools are loaded at session start).
To smoke-test the container by hand without Claude Code, drive it over stdio:
docker compose run --rm obsidian-markdown-lint-mcp
# then paste a JSON-RPC line, e.g. an initialize request, and read the replyProject structure
src/
server.ts stdio entry point (StdioServerTransport bootstrap)
create-server.ts builds the McpServer and registers the 4 tools
tools/
lint.ts lint_markdown implementation
validate.ts validate_front_matter implementation
mermaid.ts render_mermaid_diagrams + extract_mermaid_from_svg
lib/
frontmatter.ts gray-matter parse/update helpers
svg-metadata.ts base64 embed/extract helpers
.schemas/ JSON Schema files for all 7 note types
Dockerfile stdio server image (Chromium for Mermaid)
docker-compose.yml build/tag helper (not `up` — see comments)License
MIT © 2026 Philip Senger
Lint, validate, and render your Obsidian vault markdown without touching your host system.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/psenger/obsidian-markdown-lint-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server