Hugo MCP Server: Connecting Claude.ai to a Static Hugo Site

โก In short
Connect Claude.ai to a Hugo site hosted in a KVM VM in 30 minutes: a FastAPI server exposes 6 MCP tools (read, create, modify, delete pages, rebuild the site) via JSON-RPC 2.0, an OAuth proxy reuses existing infrastructure, and every modification automatically triggers a Hugo rebuild + Cloudflare cache purge.
The code is available on GitHub:
- ๐ Hugo MCP Server: jmrGrav/hugo-mcp
- ๐ OAuth Proxy: jmrGrav/mcp-oauth-proxy
๐ง Why
Anthropic’s MCP (Model Context Protocol) allows Claude.ai to connect to external data sources via standardized tools. Unlike Grav CMS which is dynamic (PHP), Hugo generates pure static HTML โ making content management via MCP even more powerful: every modification is compiled and deployed instantly.
This homelab stack enables:
- Creating and modifying Hugo pages directly from Claude.ai without manually editing Markdown files
- Automatically rebuilding the site after every modification via
hugo --minify - Purging the Cloudflare cache in a targeted way after each change
- Managing bilingual content (FR + EN) in a single conversation
๐ง What was done
๐๏ธ Global architecture
Claude.ai
โ
โ HTTPS (OAuth 2.1 + PKCE)
โผ
โโโโโโโโโโโโโโโโโ
โ Cloudflare โ WAF + Cache
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ nginx โ SSL termination
โmcp-hugo.arleo โ location /mcp โ proxy
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ FastAPI proxy โ Port 8084 (NUC host)
โ OAuth 2.1 โ Token validation
โ mcp-proxy โ SHA-256 hashing
โโโโโโโโโฌโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโ
โ hugo-mcp โ Port 8000 (KVM VM)
โ FastAPI โ 6 MCP tools
โ Python 3.12 โ 192.168.122.69
โโโโโโโโโฌโโโโโโโโ
โ
โโโโโโโโโดโโโโโโโโโ
โผ โผ
~/hugo-site/ deploy.sh โ /var/www/hugo
content/*.md + Cloudflare purge๐ฆ Components
| Component | Technology | Role |
|---|---|---|
hugo-mcp | FastAPI + Python 3.12 | Exposes 6 MCP tools via JSON-RPC 2.0 |
mcp-oauth-proxy | FastAPI + Python | Handles OAuth 2.1, PKCE S256, tokens |
| nginx vhost | nginx | SSL reverse proxy, location /mcp |
| deploy.sh | bash | Hugo rebuild + rsync to /var/www/hugo |
| systemd units | systemd | Hardened services on VM + host |
๐ Step 1 โ Install hugo-mcp in the VM
# Clone the repo in the VM
git clone https://github.com/jmrGrav/hugo-mcp.git ~/hugo-mcp
cd ~/hugo-mcp
# Create the Python virtual environment
sudo apt install -y python3.12-venv
python3 -m venv venv
venv/bin/pip install -r requirements.txt
# Generate the MCP token
MCP_TOKEN=$(openssl rand -hex 32)
# Configure secrets
cat > .env << EOF
MCP_TOKEN=$MCP_TOKEN
CF_TOKEN=<your_cloudflare_token>
CF_ZONE_ID=<your_zone_id>
EOF
# Install the systemd service
sudo cp systemd/hugo-mcp.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now hugo-mcp
# Verify
curl -s http://localhost:8000/health๐ Step 2 โ Configure the OAuth proxy on the host
The OAuth proxy reuses mcp-oauth-proxy already installed for Grav, as an independent second instance on port 8084:
# Create Hugo MCP secrets
sudo mkdir -p /etc/hugo-mcp
sudo tee /etc/hugo-mcp/secrets.env << EOF
HUGO_MCP_URL=http://192.168.122.69:8000
HUGO_MCP_TOKEN=$MCP_TOKEN
MCP_CLIENT_ID=hugo-mcp
MCP_CLIENT_SECRET=$(openssl rand -hex 32)
TOKEN_SECRET=$(openssl rand -hex 32)
PROXY_PORT=8084
PROXY_BASE_URL=https://mcp-hugo.your-domain.com
EOF
sudo chmod 600 /etc/hugo-mcp/secrets.env
# Open port 8000 in the VM firewall
# only from the NUC host
sudo ufw allow from 192.168.122.1 to any port 8000 proto tcp
# Install and start the proxy service
sudo systemctl enable --now hugo-mcp-proxy๐ Step 3 โ Configure nginx
server {
listen 443 ssl http2;
server_name mcp-hugo.your-domain.com;
include snippets/ssl.conf;
location /mcp {
proxy_pass http://127.0.0.1:8084/mcp;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /.well-known/oauth-authorization-server {
proxy_pass http://127.0.0.1:8084/.well-known/oauth-authorization-server;
proxy_set_header Host $host;
}
location /oauth/ {
proxy_pass http://127.0.0.1:8084/oauth/;
proxy_set_header Host $host;
}
}๐ค Step 4 โ Connect Claude.ai
- Go to claude.ai โ Settings โ Connectors
- Click Add connector
- Enter the URL:
https://mcp-hugo.your-domain.com/mcp - Follow the OAuth flow โ authorize access
- The 6 tools appear in the connectors list โ
๐ ๏ธ The 6 available tools
| Tool | Description |
|---|---|
list_pages | List all pages with language and section filters |
get_page | Read the frontmatter and Markdown content of a page |
create_page | Create a page + Hugo rebuild + targeted CF purge |
update_page | Modify a page + Hugo rebuild + targeted CF purge |
delete_page | Delete a page + Hugo rebuild + full CF purge |
build_site | Full Hugo rebuild + deployment + full CF purge |
๐ Security
- OAuth 2.1 + PKCE S256 โ secure authentication flow compliant with RFC 9728
- SHA-256 token hashing โ tokens are never stored in plain text
- UFW โ VM port 8000 open only from the NUC host (192.168.122.1)
- Systemd hardening โ
NoNewPrivileges, service isolated in the VM - Targeted CF purge โ only modified URLs are purged
๐ Page modification flow
Claude.ai โ create_page / update_page
โ
โผ
hugo-mcp writes the .md file
โ
โผ
deploy.sh: hugo --minify
โ
โผ
rsync public/ โ /var/www/hugo/
โ
โผ
Cloudflare API: purge_cache (targeted)
โ
โผ
Page live in ~2 seconds๐ Conclusion
This stack transforms Hugo into an active context source for Claude.ai โ create, modify, and manage the static site through conversation, with automatic rebuild and cache purge. The whole setup reuses the OAuth infrastructure already in place for Grav and is fully available on GitHub under the MIT license.
Next steps:
- ๐ก Add a
list_assetstool to manage images and static files - ๐ก Implement a Git webhook to automatically rebuild after a push
- ๐ก Migrate content from Grav to Hugo by automating frontmatter conversion