markdown-lsp

A Claude Code plugin providing Markdown language server support via Marksman, plus validation hooks for code blocks, links, frontmatter, and Claude Code syntax.
Features
LSP Operations
| Operation | Description |
|---|
documentSymbol | Navigate all headings with proper nesting |
goToDefinition | Jump from [link](#heading) to the heading |
findReferences | Find all links pointing to a heading |
hover | Preview section content |
workspaceSymbol | Search headings across all markdown files |
Validation Hooks
| Hook | Trigger | Validates |
|---|
validate-code-blocks | onSave | Code blocks have language identifiers |
validate-links | onSave | Internal anchors and file links exist |
validate-frontmatter | onSave | YAML frontmatter structure |
validate-claude-code | onSave | LSP operations, slash commands |
Dependencies
- Marksman - Markdown language server
- Node.js 18+ - For validation hooks
- Claude Code - With
ENABLE_LSP_TOOL=1
Installation
1. Install Marksman
# macOS
brew install marksman
# Linux (x86_64)
curl -L https://github.com/artempyanykh/marksman/releases/latest/download/marksman-linux-x64 -o marksman
chmod +x marksman && sudo mv marksman /usr/local/bin/
# Windows
scoop install marksman
# or
winget install artempyanykh.marksman
Verify installation:
marksman --version
2. Install the Plugin
Option A: From release tarball
tar -xzf marksman-lsp.tar.gz
/plugin install ./marksman-lsp --scope user
Option B: From repository
git clone https://github.com/zircote/markdown-lsp.git
/plugin install ./markdown-lsp --scope user
Option C: Local development (plugin-dir mode)
cd /path/to/markdown-lsp
# Claude Code auto-detects .claude-plugin/plugin.json
3. Enable LSP Tool
export ENABLE_LSP_TOOL=1
Or add to your shell profile (~/.zshrc, ~/.bashrc):
echo 'export ENABLE_LSP_TOOL=1' >> ~/.zshrc
Usage
LSP Operations
Use the LSP tool in Claude Code on any .md file:
# Get document outline
LSP documentSymbol README.md line=1 char=1
# Jump to heading from link
LSP goToDefinition README.md line=15 char=10
# Find all references to a heading
LSP findReferences README.md line=20 char=4
# Preview section content
LSP hover README.md line=15 char=10
# Search across workspace
LSP workspaceSymbol README.md line=1 char=1
Validation Hooks
Hooks run automatically on save. Diagnostics appear in Claude Code's system reminders.
Example output:
lsp-test.md:
[Line 186:1] Code block missing language identifier (warning)
[Line 198:26] Broken anchor link: #does-not-exist (warning)
Project Structure
marksman-lsp/
├── .claude-plugin/
│ └── plugin.json # Plugin metadata
├── .lsp.json # LSP server configuration
├── hooks/
│ ├── hooks.json # Hook definitions
│ ├── validate-code-blocks.js
│ ├── validate-links.js
│ ├── validate-frontmatter.js
│ └── validate-claude-code.js
├── tests/
│ └── lsp-test.md # Test file for all features
├── src/
│ └── index.ts # MCP server (optional)
├── CLAUDE.md # Project context for Claude
└── README.md
Configuration
LSP Configuration (.lsp.json)
{
"markdown": {
"command": "marksman",
"args": ["server"],
"extensionToLanguage": {
".md": "markdown"
},
"transport": "stdio"
}
}
Hook Configuration (hooks/hooks.json)
{
"markdownDiagnostics": [
{
"name": "validateCodeBlocks",
"trigger": "onSave",
"filePattern": "**/*.md",
"command": "$CLAUDE_PROJECT_DIR/hooks/validate-code-blocks.js"
}
]
}
Development
Testing Hooks Locally
# Test individual hooks
node hooks/validate-code-blocks.js tests/lsp-test.md
node hooks/validate-links.js tests/lsp-test.md
node hooks/validate-frontmatter.js tests/lsp-test.md
node hooks/validate-claude-code.js tests/lsp-test.md
Adding New Hooks