A Model Context Protocol (MCP) server that allows AI assistants like Claude to search through your blog posts using SerpApi and retrieve content via llms.txt indexing. Built with Python and designed for seamless integration with Claude Desktop.
- π Smart blog search using SerpApi for reliable, Google-powered search results
- π Full content retrieval via llms.txt indexing and GitHub raw content
- β‘ Fast and lightweight with minimal dependencies
- π§ Simple configuration with just a few required settings
- π§ͺ Comprehensive testing with unit, integration, and fixture-based tests
- π€ Claude Desktop ready with easy MCP configuration
Our server uses a modern, reliable architecture:
- SerpApi for site-specific blog post search (no complex GitHub API setup)
- llms.txt standard for AI-optimized content indexing
- GitHub raw content for fast markdown retrieval
- FastMCP framework for clean tool definitions
-
Clone and setup:
git clone https://github.com/jtemporal/blog-search-mcp-in-python.git cd blog-search-mcp-in-python uv sync
-
Configure your blog:
cp .config.example .config # Edit .config with your blog URL and SerpApi key
-
Test the setup:
uv run pytest tests/test_config.py -v
-
Inspect the server with MCP Inspector:
npx @modelcontextprotocol/inspector uv run src/server.py
Copy .config.example
to .config
and update with your blog details:
[MCP_SERVER]
blog_base_url = https://yourblog.com
serpapi_key = your-serpapi-key-here
server_name = Blog Search Server
log_level = INFO
- Sign up for free at SerpApi
- Get your API key from the dashboard
- Add it to your
.config
file
Note: SerpApi provides 100 free searches per month, which is usually sufficient for personal blog searching.
# Run all unit tests (default)
uv run pytest
# Verbose output
uv run pytest -v
# Run specific test file
uv run pytest tests/test_server.py -v
# Run integration tests (requires SerpApi key)
uv run pytest tests/test_integration.py -v -m integration
# Or run manually for detailed output
uv run python tests/test_integration.py
- Unit tests: Fast tests with mocked SerpApi responses and fixtures
- Integration tests: Real API calls to SerpApi and GitHub (requires valid API key)
- Fixtures: Test data stored in
tests/fixtures/
for better maintainability
pytest
- Runs unit tests only (default, integration tests excluded)pytest -m integration
- Runs integration tests onlypytest -m ""
- Runs all tests (unit + integration)
mcp-with-python-blog/
βββ src/
β βββ server.py # Main MCP server with tools
β βββ config.py # Configuration management
βββ tests/
β βββ fixtures/ # Test data in JSON files
β βββ conftest.py # Pytest fixtures (loads from fixtures/)
β βββ test_server.py # Unit tests with mocked responses
β βββ test_config.py # Configuration loading tests
β βββ test_integration.py # Integration tests (real API calls)
βββ blog-post/ # Blog post about this project
βββ .config # Your blog configuration
βββ .config.example # Configuration template
βββ pyproject.toml # Project configuration and dependencies
βββ uv.lock # Dependency lock file
# Run locally for testing
uv run mcp src/server.py
The MCP server provides two main tools:
-
search_posts(query: str)
- Search through blog posts using SerpApi- Performs site-specific Google search
- Returns titles, URLs, and excerpts
- Example: "Search for posts about Python"
-
get_post_content(title: str)
- Get full content of a specific post- Uses llms.txt index to find posts
- Fetches raw markdown from GitHub
- Supports partial title matching
- Example: "Get content for 'Python Tips'"
To use this MCP server with Claude Desktop:
-
Run the install command:
uv run mcp install "src/server.py" --with google-search-results
This will create the server configuration within Claude automatically.
-
Restart Claude Desktop completely
-
Test the integration:
- Start a new conversation
- Ask: "Search my blog for posts about Python"
- Claude should offer to use the
search_posts
tool
To use this MCP server with Cursor Desktop:
-
Locate your Cursor MCP config file:
- macOS:
~/.cursor/mcp.json
- Windows:
%APPDATA%\Cursor\mcp.json
- Linux:
~/.config/Cursor/mcp.json
- macOS:
-
Add the MCP server configuration:
{ "mcpServers": { "blog-search": { "command": "/path/to/uv", "args": [ "run", "--python", "3.12.11", "--with", "mcp[cli]", "--with", "google-search-results", "--with", "python-dotenv", "mcp", "run", "/absolute/path/to/your/project/src/server.py" ], "cwd": "/absolute/path/to/your/project" } } }
-
Update the paths:
- Replace
/path/to/uv
with your uv executable path (find withwhich uv
) - Replace
/absolute/path/to/your/project
with your project directory
- Replace
-
Restart Cursor completely and test the integration
Once configured with Claude Desktop, you can:
- "Search my blog for posts about Python"
- "Find articles related to web development"
- "Look for tutorials on data science"
- "Get the content of the Python tips post"
- "Show me the full article about Django"
- "Retrieve the content for 'Introduction to Data Science'"
- "Search for my post about JWT handling, then summarize the key points"
- "Find my Django tutorial and explain the main concepts"
- "Look up my Python posts and create a learning roadmap"
Your blog needs to provide an llms.txt
file at https://yourblog.com/llms.txt
that follows this format:
# LLM Feed for yourblog.com
This file contains links to blog posts in markdown format for easy LLM consumption.
- [Post Title 1](https://raw.githubusercontent.com/username/repo/main/_posts/post1.md)
- [Post Title 2](https://raw.githubusercontent.com/username/repo/main/_posts/post2.md)
...
This file serves as an index that links to the raw markdown content of your posts on GitHub.
If you don't have a blog or llms.txt
file feel free to use mine: https://jtemporal.com
.
- Python 3.12+
SerpApi
account (free tier available, sign up here)- Blog with llms.txt index (or you can use mine if you prefer:
https://jtemporal.com
) uv
package manager
-
Setup development environment:
git clone https://github.com/jtemporal/mcp-with-python-blog.git cd mcp-with-python-blog uv sync
-
Run tests:
uv run pytest -v
-
Add tests for new features in appropriate test files
-
Update fixtures in
tests/fixtures/
when adding new test data -
Follow the existing code style and add proper docstrings
Test data is stored in tests/fixtures/
as JSON files:
blog_posts.json
- Sample blog post dataconfig.json
- Test configurationserpapi_*.json
- Mock SerpApi responses
To add new test data, create a JSON file and load it in conftest.py
using the load_fixture()
function.
- SerpApi key not working: Verify your key at SerpApi dashboard
- No search results: Check that your blog URL is correct and publicly accessible
- llms.txt not found: Ensure your blog serves the llms.txt file at the root
- Claude Desktop not recognizing server: Check file paths and restart Claude completely
Run the server manually to see detailed logs:
uv run python src/server.py
Run integration tests to verify API connectivity:
uv run python tests/test_integration.py
MIT License - see LICENSE file for details.