markdown-vault-mcp

markdown-vault-mcp

Agent-controlled markdown files with frontmatter-based permissions, enabling read, append, or edit access for AI agents.

Category
访问服务器

README

markdown-vault-mcp

Agent-controlled markdown files with frontmatter-based permissions.

Works with Obsidian vaults, static site generators (Hugo, Jekyll, Astro), note-taking apps (Bear, Typora, iA Writer), or any folder of .md files.

Note: if you want a simple, read-only version with no file-write capabilities, see the 1.0 Release.

Quick Start

# 1. Clone and install
git clone https://github.com/mikesusz/markdown-vault-mcp
cd markdown-vault-mcp
python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -e .

# 2. Set your markdown folder path
cp .env.example .env
# Edit .env and set VAULT_PATH=/path/to/your/markdown/files

# 3. Add to your MCP client configuration

MCP Client Config (Claude Desktop, etc.):

{
	"mcpServers": {
		"markdown-vault": {
			"command": "/path/to/markdown-vault-mcp/.venv/bin/python",
			"args": ["-m", "markdown_vault_mcp.server"],
			"env": {
				"VAULT_PATH": "/path/to/your/markdown/files"
			}
		}
	}
}

Use the full path to the Python binary inside your virtualenv. The VAULT_PATH can be set in .env or passed via the client config (client takes precedence).

Requirements

  • Python 3.10+
  • A folder containing .md files

Works With

  • Obsidian — point VAULT_PATH at your vault folder
  • Hugo / Jekyll / Astro — point at your content/ folder
  • Bear / Typora / iA Writer — point at your notes folder
  • Plain markdown — point at any folder of .md files

The server doesn't care about your note-taking app — it just needs markdown files with optional YAML frontmatter.


Permission System

Control agent access with a single frontmatter field. Notes without frontmatter default to append — no configuration files, no hardcoded lists.

Permission Hierarchy

Value Agent can…
hidden Nothing — file is completely invisible
read View but not modify
append Add content only (default if no frontmatter)
edit Full read + write access

Usage

Add agent_access to any note's frontmatter:

---
agent_access: edit # or: append, read, hidden
---

Tool Requirements

Tools Minimum Permission
Read operations read
append_to_note append
Edit operations edit

Read operations: get_note, search_notes, list_notes Edit operations: update_note, replace_in_note, update_section

Notes with insufficient permissions return a clear error:

Error: Insufficient permissions. This note has agent_access: 'append',
but this operation requires: 'edit'. Add agent_access: 'edit' to the
note's frontmatter to enable this operation.

Available Tools

search_notes

Search markdown notes by title or content (case-insensitive). Returns up to 10 results with a short excerpt around each match.

Input:

  • query (string, required) — term to search for

Example result:

[
	{
		"title": "Meeting Notes",
		"path": "work/Meeting Notes.md",
		"snippet": "…discussed the new project architecture…",
		"relevance_score": 7.0
	}
]

get_note

Retrieve the full content and metadata of a specific note.

Input:

  • note_path (string, required) — path relative to vault root, e.g. "folder/My Note.md"

Example result:

{
	"title": "My Note",
	"path": "folder/My Note.md",
	"content": "# My Note\n\nBody text here…",
	"frontmatter": { "tags": ["idea"], "created": "2024-01-01" },
	"modified": "2024-06-15T10:30:00",
	"size": 1024
}

list_notes

List all markdown notes in the vault (or a subfolder).

Input:

  • folder (string, optional) — subfolder path relative to vault root

Example result:

[
	{ "title": "Home", "path": "Home.md", "size": 512, "modified": "2024-06-01T09:00:00" },
	{
		"title": "Daily Note",
		"path": "Daily/2024-01-15.md",
		"size": 256,
		"modified": "2024-01-15T08:00:00"
	}
]

list_writable_notes

Show all notes that agents can append to — those with agent_access: append or edit, plus notes without any frontmatter (which default to append).

Input: none

Example result:

{
	"writable_notes": [
		{ "path": "__INBOX.md", "access_level": "append" },
		{ "path": "projects/status.md", "access_level": "edit" }
	]
}

append_to_note

Append content to the end of a note. Works on any note with agent_access: append or edit, or any note without frontmatter. Never overwrites existing content. Creates the file if it doesn't exist yet.

Input:

  • note_path (string, required) — path relative to vault root, e.g. "inbox.md" or "folder/notes.md"
  • content (string, required) — text to append
  • add_timestamp (boolean, optional, default: true) — if true, inserts a ## YYYY-MM-DD HH:MM heading before the content

Example result:

{
	"success": true,
	"note_path": "inbox.md",
	"appended_content": "\n## 2026-03-24 10:30\nOrder furnace filters\n",
	"message": "Successfully appended to 'inbox.md'."
}

Example usage:

  • append_to_note("inbox.md", "Order furnace filters") — timestamped capture
  • append_to_note("books.md", "- The Expanse", false) — add a list item without a heading
  • append_to_note("new-note.md", "Some content", false) — creates the file if it doesn't exist

list_templates

List all .md files in your vault's templates/ directory.

Input: none

Example result:

{
	"templates": [
		{
			"name": "PROJECT",
			"path": "templates/PROJECT.md",
			"size": 100,
			"description": "General project template"
		},
		{
			"name": "JOURNAL",
			"path": "templates/JOURNAL.md",
			"size": 96,
			"description": "Daily journal entry"
		}
	]
}

create_note_from_template

Create a new note from an existing template. Automatically replaces placeholders like {{TODAY}} and {{AGENT_ACCESS}}, and pre-fills frontmatter or heading-based fields.

Input:

  • template_name (string, required) — name of the template (e.g. "PROJECT", "New Book"). Must match a file in templates/.
  • note_suffix (string, optional) — appended to the filename, e.g. "deck replacement" → "PROJECT deck replacement.md". Letters, numbers, spaces, hyphens, underscores only.
  • field_values (object, optional) — pre-fill template fields. Keys match YAML frontmatter field names (e.g. {"title": "My Book", "authors": "Jane Smith"}). Keys not found in frontmatter will be matched against # KEY: headings in the body instead.
  • agent_access (string, optional) — permission level for agent access after creation: "edit", "append" (default), "read", or "hidden". Overrides any value already in the template's agent_access frontmatter field. Infer from the user's phrasing (see Templates section below).

Example result:

{
	"success": true,
	"file_path": "PROJECT deck replacement.md",
	"message": "Created new note from template",
	"template_used": "templates/PROJECT.md",
	"fields_applied": { "title": "deck replacement" }
}

update_note

Replace the entire body of a note with new content. Frontmatter (including agent_access) is always preserved. Requires agent_access: "edit" in the note.

Input:

  • note_path (string, required) — path relative to vault root, e.g. "My Note.md"
  • new_content (string, required) — full replacement body text

replace_in_note

Find and replace a specific piece of text in a note body. Exact match, case-sensitive. Only operates on body content — frontmatter is never touched. Requires agent_access: "edit".

Input:

  • note_path (string, required) — path relative to vault root
  • old_text (string, required) — exact text to find
  • new_text (string, required) — replacement text

Errors:

  • "Text not found in note: '...'" — if old_text doesn't appear in the body

update_section

Replace the content beneath a specific heading, preserving the heading line itself. Content is replaced up to (but not including) the next heading, or end of file. Requires agent_access: "edit".

Input:

  • note_path (string, required) — path relative to vault root
  • heading (string, required) — exact heading text, e.g. "## Next Steps" or "SYNOPSIS:"
  • new_content (string, required) — replacement content for that section

Errors:

  • "Heading '...' not found in note" — if the heading doesn't exist

Templates

Adding templates

Drop any .md file into your vault's templates/ directory — no configuration needed:

templates/
├── PROJECT.md
├── New Book.md
├── JOURNAL.md
└── RECIPE.md

Add an optional description comment at the top to make it show up nicely in list_templates:

%% My recipe template %%

---

date: "{{TODAY}}"
source: ""

---

# TITLE:

# SOURCE:

## Ingredients

## Steps

The templates/examples/ directory in this repo contains ready-to-use templates you can copy into your vault:

  • JOURNAL.md — Daily journal with date auto-filled (agent_access: "append")
  • RECIPE.md — Recipe with source and ingredients
  • MEETING_NOTES.md — Meeting notes with attendees array (agent_access: "edit")
  • REFERENCE_DOC.md — Reference document with dynamic agent_access via {{AGENT_ACCESS}}

Agent access in templates

Templates can include an agent_access frontmatter field to declare how freely an agent should edit the note after creation.

Hardcoded in template (e.g. JOURNAL.md always appends):

---
date: '{{TODAY}}'
agent_access: 'append'
---

Dynamic via placeholder (e.g. REFERENCE_DOC.md — set at creation time):

---
title: '{{TITLE}}'
agent_access: '{{AGENT_ACCESS}}'
---

When using the dynamic placeholder, pass agent_access to create_note_from_template and the value is inferred from the user's phrasing:

User says… Inferred value
"you can edit/update/modify" or "fully editable" edit
"you can add to" or "append-only" append
No specific instruction append
"read-only", "I'll edit this myself", "just create it" read
"this is private", "keep this hidden", "don't show this" hidden

If agent_access is passed explicitly to create_note_from_template, it overrides whatever value the template has (hardcoded or placeholder).

Template placeholders

The following {{PLACEHOLDER}} tokens are automatically replaced when a note is created:

Placeholder Example output
{{TODAY}} 2026-03-24
{{DATE}} 2026-03-24 (alias for TODAY)
{{NOW}} 2026-03-24 15:30:45
{{TIME}} 15:30:45
{{TIMESTAMP}} 1742820645 (Unix timestamp)
{{YEAR}} 2026
{{MONTH}} 03
{{DAY}} 24
{{AGENT_ACCESS}} "append" (resolved from the agent_access parameter, default "append")

Placeholders are expanded after field_values are applied, so caller-supplied values always take precedence.

Array fields

If a field_values value for an array-typed frontmatter field contains and, it's automatically split into a list:

"William Gibson and Bruce Sterling" → ["William Gibson", "Bruce Sterling"]

Testing

Run the end-to-end test harness (requires a real vault):

python test_server.py --vault /path/to/your/vault

Options:

  • --vault — path to vault (overrides VAULT_PATH env)
  • --note — specific note path to test get_note with
  • --query — search term for search_notes (default: "the")

Troubleshooting

VAULT_PATH environment variable is not set Copy .env.example to .env and set the path, or pass VAULT_PATH in your MCP client config.

Templates directory not found in vault Create a templates/ folder in your vault root, or copy templates from templates/examples/ in this repo.

Insufficient permissions The note has agent_access: read or hidden. Add agent_access: append or agent_access: edit to its frontmatter.

Changes to server code aren't taking effect Your MCP client keeps the server process alive. Restart the client (e.g. quit and relaunch Claude Desktop) to pick up code changes.

field_values not updating frontmatter Check that the key names exactly match the YAML frontmatter keys in the template (case-sensitive). Use fields_applied in the response to confirm what was actually written.


Running the server manually

python -m markdown_vault_mcp.server
# or
markdown-vault-mcp

The server communicates over stdio and is normally launched automatically by your MCP client.


Migrating from obsidian-mcp v2

See MIGRATION.md for upgrade steps.


License

GNU Affero General Public License v3.0 — see LICENSE for the full text.

You are free to use, modify, and distribute this software under the AGPL v3 terms. If you run a modified version as a network service, you must make the modified source available to users of that service.


Feedback

This project is a work in progress, and may have bugs. You can submit a Github Issue if you encounter any problems, and I will probably fix it! Because I don't want to have that problem, either.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选