frontmatter-mcp

kzmshx/frontmatter-mcp

3.2

If you are the rightful owner of frontmatter-mcp and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.

An MCP server for querying Markdown frontmatter with DuckDB SQL.

Tools
2
Resources
0
Prompts
0

frontmatter-mcp

An MCP server for querying Markdown frontmatter with DuckDB SQL.

Configuration

Basic Usage

{
  "mcpServers": {
    "frontmatter": {
      "command": "uvx",
      "args": ["frontmatter-mcp"],
      "env": {
        "FRONTMATTER_BASE_DIR": "/path/to/markdown/directory"
      }
    }
  }
}

With Semantic Search

Semantic search requires large dependencies (~1GB). Set MCP_TIMEOUT to extend installation timeout:

{
  "mcpServers": {
    "frontmatter": {
      "command": "uvx",
      "args": ["--from", "frontmatter-mcp[semantic]", "frontmatter-mcp"],
      "env": {
        "FRONTMATTER_BASE_DIR": "/path/to/markdown/directory",
        "FRONTMATTER_ENABLE_SEMANTIC": "true",
        "MCP_TIMEOUT": "300000"
      }
    }
  }
}

Note: MCP_TIMEOUT is in milliseconds (300000 = 5 minutes).

Installation (Optional)

If you prefer to install globally:

pip install frontmatter-mcp
# or
uv tool install frontmatter-mcp

Tools

query_inspect

Get schema information from frontmatter across files.

ParameterTypeDescription
globstringGlob pattern relative to base directory

Example:

// Input
{ "glob": "**/*.md" }

// Output
{
  "file_count": 186,
  "schema": {
    "date": { "type": "string", "count": 180, "nullable": true },
    "tags": { "type": "array", "count": 150, "nullable": true }
  }
}

// Output (with semantic search ready)
{
  "file_count": 186,
  "schema": {
    "date": { "type": "string", "count": 180, "nullable": true },
    "tags": { "type": "array", "count": 150, "nullable": true },
    "embedding": { "type": "FLOAT[256]", "nullable": false }
  }
}

query

Query frontmatter data with DuckDB SQL.

ParameterTypeDescription
globstringGlob pattern relative to base directory
sqlstringDuckDB SQL query referencing files table

Example:

// Input
{
  "glob": "**/*.md",
  "sql": "SELECT path, date FROM files WHERE date >= '2025-11-01' ORDER BY date DESC"
}

// Output
{
  "columns": ["path", "date"],
  "row_count": 24,
  "results": [
    {"path": "daily/2025-11-28.md", "date": "2025-11-28"},
    {"path": "daily/2025-11-27.md", "date": "2025-11-27"}
  ]
}

update

Update frontmatter properties in a single file.

ParameterTypeDescription
pathstringFile path relative to base directory
setobjectProperties to add or overwrite
unsetstring[]Property names to remove

Example:

// Input
{ "path": "notes/idea.md", "set": {"status": "published"} }

// Output
{ "path": "notes/idea.md", "frontmatter": {"title": "Idea", "status": "published"} }

batch_update

Update frontmatter properties in multiple files.

ParameterTypeDescription
globstringGlob pattern relative to base directory
setobjectProperties to add or overwrite
unsetstring[]Property names to remove

Example:

// Input
{ "glob": "drafts/*.md", "set": {"status": "review"} }

// Output
{ "updated_count": 5, "updated_files": ["drafts/a.md", "drafts/b.md", ...] }

batch_array_add

Add a value to an array property in multiple files.

ParameterTypeDescription
globstringGlob pattern relative to base directory
propertystringName of the array property
valueanyValue to add
allow_duplicatesboolAllow duplicate values (default: false)

Example:

// Input
{ "glob": "**/*.md", "property": "tags", "value": "reviewed" }

// Output
{ "updated_count": 42, "updated_files": ["a.md", "b.md", ...] }

batch_array_remove

Remove a value from an array property in multiple files.

ParameterTypeDescription
globstringGlob pattern relative to base directory
propertystringName of the array property
valueanyValue to remove

Example:

// Input
{ "glob": "**/*.md", "property": "tags", "value": "draft" }

// Output
{ "updated_count": 15, "updated_files": ["a.md", "b.md", ...] }

batch_array_replace

Replace a value in an array property in multiple files.

ParameterTypeDescription
globstringGlob pattern relative to base directory
propertystringName of the array property
old_valueanyValue to replace
new_valueanyNew value

Example:

// Input
{ "glob": "**/*.md", "property": "tags", "old_value": "draft", "new_value": "review" }

// Output
{ "updated_count": 10, "updated_files": ["a.md", "b.md", ...] }

batch_array_sort

Sort an array property in multiple files.

ParameterTypeDescription
globstringGlob pattern relative to base directory
propertystringName of the array property
reverseboolSort in descending order (default: false)

Example:

// Input
{ "glob": "**/*.md", "property": "tags" }

// Output
{ "updated_count": 20, "updated_files": ["a.md", "b.md", ...] }

batch_array_unique

Remove duplicate values from an array property in multiple files.

ParameterTypeDescription
globstringGlob pattern relative to base directory
propertystringName of the array property

Example:

// Input
{ "glob": "**/*.md", "property": "tags" }

// Output
{ "updated_count": 5, "updated_files": ["a.md", "b.md", ...] }

index_status

Get the status of the semantic search index.

This tool is only available when FRONTMATTER_ENABLE_SEMANTIC=true.

Example:

// Output (not started)
{ "state": "idle" }

// Output (indexing in progress)
{ "state": "indexing" }

// Output (ready)
{ "state": "ready" }

index_refresh

Refresh the semantic search index (differential update).

This tool is only available when FRONTMATTER_ENABLE_SEMANTIC=true.

Example:

// Output
{ "state": "indexing", "message": "Indexing started", "target_count": 665 }

// Output (when already indexing)
{ "state": "indexing", "message": "Indexing already in progress" }

Technical Notes

All Values Are Strings

All frontmatter values are passed to DuckDB as strings. Use TRY_CAST in SQL for type conversion when needed.

SELECT * FROM files
WHERE TRY_CAST(date AS DATE) >= '2025-11-01'

Arrays Are JSON Strings

Arrays like tags: [ai, python] are stored as JSON strings '["ai", "python"]'. Use from_json() and UNNEST to expand them.

SELECT path, tag
FROM files, UNNEST(from_json(tags, '[""]')) AS t(tag)
WHERE tag = 'ai'

Templater Expression Support

Files containing Obsidian Templater expressions (e.g., <% tp.date.now("YYYY-MM-DD") %>) are handled gracefully. These expressions are treated as strings and naturally excluded by date filtering.

Semantic Search

When semantic search is enabled, you can use the embed() function and embedding column in SQL queries. After running index_refresh, the markdown body content is indexed as vectors.

-- Find semantically similar documents
SELECT path, 1 - array_cosine_distance(embedding, embed('feeling better')) as score
FROM files
ORDER BY score DESC
LIMIT 10

-- Combine with frontmatter filters
SELECT path, date, 1 - array_cosine_distance(embedding, embed('motivation')) as score
FROM files
WHERE date >= '2025-11-01'
ORDER BY score DESC
LIMIT 10

Environment variables:

VariableDefaultDescription
FRONTMATTER_BASE_DIR(required)Base directory for files
FRONTMATTER_ENABLE_SEMANTICfalseEnable semantic search
FRONTMATTER_EMBEDDING_MODELcl-nagoya/ruri-v3-30mEmbedding model name
FRONTMATTER_CACHE_DIRFRONTMATTER_BASE_DIR/.frontmatter-mcpCache directory for embeddings

License

MIT