Blog Knowledge MCP Server
An MCP (Model Context Protocol) server that exposes your blog as a knowledge base. Search past articles and create new drafts directly from Claude Code while working in other repositories.
Loading actions...
Skill content
Main instructions and any bundled files for this skill.
Blog Knowledge MCP Server
An MCP (Model Context Protocol) server that exposes your blog as a knowledge base. Search past articles and create new drafts directly from Claude Code while working in other repositories.
Why a Domain-Specific MCP?
MCP servers fall on a spectrum:
| Type | Example | Scope |
|---|---|---|
| General-purpose | Context7, filesystem, web search | Works for anyone, any project |
| Tool-specific | GitHub, Slack, Linear MCPs | Works for anyone using that tool |
| Domain-specific | This blog MCP | Works for your specific data/workflow |
This server is domain-specific by design:
- Tied to a specific blog's frontmatter schema
- Assumes a particular directory structure (
src/content/tech/,src/content/travel/) - Optimized for a personal knowledge management workflow
- Another person would need to fork and modify it for their setup
This is a feature, not a limitation. Domain-specific MCPs are powerful because they can:
- Make assumptions - knows the exact schema, no configuration needed
- Be opinionated - enforces your article template and tag taxonomy
- Integrate deeply - reads your exact content structure, suggests from your tags
The tradeoff compared to general-purpose MCPs:
| General-purpose | Domain-specific | |
|---|---|---|
| Setup | Install and go | Build it yourself |
| Flexibility | Works everywhere | Works for your workflow |
| Depth | Broad but shallow | Deep integration |
| Maintenance | Community/vendor | You |
The value here is turning your blog into a queryable knowledge base that follows you across all your projects.
Features
| Tool | Description |
|---|---|
search_blog | Semantic search - finds articles by meaning, not just keywords |
draft_article | Create new posts with proper frontmatter, capture problems/solutions |
list_articles | Browse existing content |
list_tags | See your tag taxonomy with usage counts |
suggest_tags | Get tag suggestions based on content |
reindex_blog | Rebuild the semantic search index after adding articles |
Semantic Search
Search uses AI embeddings (sentence-transformers) to find articles by meaning, not just keyword matching:
You: "how to fix my mobile app build"
→ Finds: "Rider MAUI iOS Targets Greyed Out" (similarity: 0.38)
"Building a Custom .NET MAUI Docker Image" (similarity: 0.28)
The query doesn't contain "MAUI", "iOS", or "Docker" - but semantic search understands the intent.
How it works:
- On first run, all articles are embedded using
all-MiniLM-L6-v2(runs locally on CPU) - Embeddings are cached in
.cache/embeddings.json - Only changed files are re-embedded on subsequent runs
- Falls back to keyword search if dependencies aren't installed
Project Structure
blog-mcp-server/
├── .gitignore
├── .venv/ # Virtual environment
├── .cache/ # Semantic search index (gitignored)
├── README.md
├── INSTALLATION.md # Detailed setup guide
├── pyproject.toml
├── scripts/
│ └── verify-setup.py # Pre-flight check script
└── src/blog_mcp/
├── __init__.py
├── server.py # MCP server entry point
├── services/
│ ├── __init__.py
│ └── indexer.py # Semantic embeddings & search
└── tools/
├── __init__.py
├── search.py # search_blog (uses semantic if available)
├── draft.py # draft_article
├── list.py # list_articles, list_tags
└── suggest.py # suggest_tags
Quick Start
# 1. Install
cd /path/to/blog-mcp-server
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[semantic]" # Include semantic search (recommended)
# 2. Verify (prints the claude mcp add command if successful)
BLOG_PATH=/path/to/blog BLOG_BASE_URL=https://your-url.com python3 scripts/verify-setup.py
# 3. Configure Claude Code (copy command from verify output)
claude mcp add blog-knowledge ...
# 4. Restart Claude Code and test
# Ask: "List my blog articles"
See INSTALLATION.md for detailed instructions and troubleshooting.
Example Output
You: "Search my blog for iOS simulator"
→ Found: "Rider MAUI iOS Targets Greyed Out or Missing"
URL: https://your-blog.com/tech/rider-maui-ios-targets-disappeared/
Tags: rider, maui, ios, dotnet, jetbrains
You: "What tags do I use?"
→ astro (7), serverless (4), seo (4), web-dev (3), dotnet (3), maui (3)...
Usage Examples
Once configured, Claude Code can use these tools in any repository:
Search for past solutions
You: "Have I written about iOS simulator issues?"
→ Finds your Rider MAUI article
You: "I'm getting iOS simulator issues in Rider"
Claude: [calls search_blog] "You wrote about this! See 'Rider MAUI iOS Targets
Greyed Out' - the fix was usually just booting a simulator first..."
Create a draft while working
You: "Draft a blog post about this Docker caching fix"
→ Creates src/content/tech/2025-12-30-docker-caching-fix.md
You: "Draft a blog post about this Docker caching fix we just solved"
Claude: [calls draft_article with context from session]
"Created draft at src/content/tech/2025-12-30-docker-layer-caching-fix.md
with the error messages and commands we used."
Explore your content
You: "What tags do I use most?"
→ Lists tags with counts
You: "What have I written about Cloudflare?"
Claude: [calls list_articles with tags=["cloudflare"]]
"You have 3 articles: Page Counter, Comments System, and Workers Guide"
Get tag suggestions
You: "What tags should I use for this Kubernetes article?"
Claude: [calls suggest_tags] "Based on your existing taxonomy, I suggest:
docker, serverless, devops. You don't have a 'kubernetes' tag yet."
Tool Details
search_blog
search_blog(
query: str, # Search terms
tags: list[str] = None, # Filter by tags
collection: str = None, # "tech" or "travel"
limit: int = 5 # Max results
)
Returns ranked results with title, path, description, tags, date, relevance score, and excerpt.
draft_article
draft_article(
title: str, # Required
collection: str = "tech", # "tech" or "travel"
description: str = None, # SEO description
tags: list[str] = None, # Article tags
problem: str = None, # Problem section content
solution: str = None, # Solution section content
code_snippets: list = None, # [{language, code, description}]
commands: list[str] = None, # Terminal commands
error_messages: list[str] = None # Errors encountered
)
Creates a markdown file with proper frontmatter at src/content/{collection}/{date}-{slug}.md.
list_articles
list_articles(
collection: str = None, # Filter to tech/travel
tags: list[str] = None, # Filter by tags
limit: int = 20 # Max results
)
Returns articles sorted by date (newest first).
list_tags
list_tags(
collection: str = None # Filter to tech/travel
)
Returns all tags with usage counts, sorted by frequency.
suggest_tags
suggest_tags(
content: str, # Content to analyze
existing_tags: list[str] = None # Already selected (excluded from suggestions)
)
Returns suggested tags from your taxonomy plus potential new tags.
Environment Variables
| Variable | Required | Description |
|---|---|---|
BLOG_PATH | Yes | Absolute path to your local blog repository |
BLOG_BASE_URL | Yes | Base URL of your published blog (for generating links) |
Both must be set when configuring the MCP server. Search results include:
path- local file path for editingurl- published URL for referencing
Development
# Activate venv
source .venv/bin/activate
# Install dev dependencies
pip3 install -e ".[dev]"
# Run server directly (for debugging)
python3 -m blog_mcp.server
Testing
# Run all tests
pytest
# Run with verbose output
pytest -v
# Run specific test file
pytest tests/test_search.py
# Run specific test class
pytest tests/test_search.py::TestKeywordSearch
# Run with coverage
pytest --cov=blog_mcp
Test Structure
| File | Tests |
|---|---|
test_search.py | Keyword search, semantic search, filtering, relevance |
test_indexer.py | Embedding generation, incremental indexing, change detection |
test_phone_a_friend.py | Context-based search, solution formatting |
test_draft.py | Article creation, frontmatter, slug generation |
test_list.py | Article listing, tag counting, collection filtering |
test_suggest.py | Tag suggestions, exclusions, new tag detection |
Tests use a mock blog with sample articles in a temp directory, so they don't depend on your actual blog content.
Related Projects
- Blog: https://gitlab.com/bicyclemark/blog - The Astro blog this server indexes
- Counter/Comments API: https://gitlab.com/bicyclemark/blog-counter-worker - Cloudflare Worker
Future Enhancements
- Read article content as MCP resources
- Git integration (auto-commit drafts)
- Template selection (how-to, troubleshooting, cheatsheet)
- Watch mode for auto-reindex on file changes
- Proactive retrieval (inject relevant context before every response)
Related Skills
Frontend Typescript Linting.mdc
TypeScript and ESLint rules that MUST be followed when creating, modifying, or reviewing any file under apps/frontend/, including .ts, .tsx, .js, and .jsx files. Also apply when discussing frontend li...
2. Apply Deepthink Protocol (reason about dependencies
risks