---
title: "Grav MCP Server: connect Claude.ai to your website in 30 minutes"
description: "Complete tutorial to install an MCP server on Grav CMS and connect Claude.ai to your website using the grav-plugin-mcp-server plugin and FastAPI OAuth proxy."
url: "https://www.arleo.eu/en/posts/grav-mcp-server/"
language: "en"
datePublished: "2026-04-17T16:00:00+02:00"
dateModified: "2026-05-15T23:51:25+02:00"
tags: ["grav","mcp","claude","homelab"]
categories: ["infrastructure"]
contentSignal: "ai-train=no, search=yes, ai-input=yes"
---

# Grav MCP Server: connect Claude.ai to your website in 30 minutes
Complete tutorial to install an MCP server on Grav CMS and connect Claude.ai to your website using the grav-plugin-mcp-server plugin and FastAPI OAuth proxy.



## ⚡ In short

Connect Claude.ai to your Grav website in 30 minutes: a PHP plugin exposes 9 tools (read, create, update, delete pages, manage plugins) via JSON-RPC 2.0, a FastAPI proxy handles OAuth 2.1 authentication, and nginx bridges the internet to your server. Result: Claude can read and modify your site content directly from a conversation.

Code available on GitHub:

- 🔌 Grav plugin: [jmrGrav/grav-plugin-mcp-server](https://github.com/jmrGrav/grav-plugin-mcp-server)
- 🔐 OAuth proxy: [jmrGrav/mcp-oauth-proxy](https://github.com/jmrGrav/mcp-oauth-proxy)

## 🧠 Why

Anthropic's [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) allows Claude.ai to connect to external data sources via standardized tools. Until now, no MCP plugin existed for Grav CMS.

This homemade stack enables you to:

- **Read and modify pages** directly from Claude.ai without opening the Grav admin
- **Automate bilingual content creation** (FR + EN) in a single conversation
- **Trigger actions** on the site (clear cache, enable/disable plugins) by simple instruction
- **Chain tools**: create a page → IndexNow + Google Indexing trigger automatically

## 🔧 What was done

### 🏗️ Global architecture

```
        Claude.ai
            │
            │ HTTPS (OAuth 2.1 + PKCE)
            ▼
    ┌───────────────┐
    │  Cloudflare   │  WAF + Cache
    └───────┬───────┘
            │
            ▼
    ┌───────────────┐
    │     nginx     │  SSL termination
    │  mcp.site.com │  location /mcp → proxy
    └───────┬───────┘
            │
            ▼
    ┌───────────────┐
    │ FastAPI proxy │  Port 8083
    │  OAuth 2.1    │  Token validation
    │  mcp-proxy    │  SHA-256 hashing
    └───────┬───────┘
            │
            ▼
    ┌───────────────┐
    │  Grav plugin  │  Port 8090 (internal)
    │  JSON-RPC 2.0 │  9 MCP tools
    │  PHP 8.3      │
    └───────────────┘
```

### 📦 Components

| Component | Technology | Role |
|---|---|---|
| `grav-plugin-mcp-server` | PHP 8.3 | Exposes 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, `/mcp` location |
| systemd unit | systemd | Hardened service for the proxy |

### 🔌 Step 1 — Install the Grav plugin

```bash
# Clone the plugin
git clone https://github.com/jmrGrav/grav-plugin-mcp-server.git \
  /var/www/grav/user/plugins/mcp-server

# Permissions
sudo chown -R www-data:www-data /var/www/grav/user/plugins/mcp-server

# Clear Grav cache
cd /var/www/grav && sudo -u www-data php bin/grav clearcache
```

Add the internal vhost to your nginx Grav config:

```nginx
# Internal MCP vhost — accessible only from the proxy
server {
    listen 127.0.0.1:8090;
    server_name localhost;
    root /var/www/grav;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # Block direct access to the PHP plugin
    location ^~ /api/mcp {
        return 444;
    }
}
```

### 🔐 Step 2 — Install the OAuth proxy

```bash
# Clone the proxy
git clone https://github.com/jmrGrav/mcp-oauth-proxy.git \
  /opt/mcp-oauth-proxy

# Create dedicated user
sudo useradd -r -s /bin/false mcp-proxy

# Create Python virtual environment
cd /opt/mcp-oauth-proxy
python3 -m venv venv
venv/bin/pip install -r requirements.txt

# Configure secrets
sudo mkdir -p /etc/mcp-oauth-proxy
sudo cp secrets.env.example /etc/mcp-oauth-proxy/secrets.env
sudo nano /etc/mcp-oauth-proxy/secrets.env
# → Fill in MCP_CLIENT_ID, MCP_CLIENT_SECRET, MCP_TOKEN_SECRET

# Install systemd service
sudo cp systemd/mcp-oauth-proxy.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now mcp-oauth-proxy

# Verify
sudo systemctl status mcp-oauth-proxy
```

### 🌐 Step 3 — Configure nginx

```bash
# Copy the vhost template
sudo cp nginx/mcp-vhost.conf /etc/nginx/sites-available/mcp
# Update your-domain.com and SSL paths
sudo nano /etc/nginx/sites-available/mcp

# Enable the vhost
sudo ln -s /etc/nginx/sites-available/mcp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
```

### 🤝 Step 4 — Connect Claude.ai

1. Go to [claude.ai](https://claude.ai) → **Settings** → **Connectors**
2. Click **Add connector**
3. Enter the URL: `https://mcp.your-domain.com/mcp`
4. Follow the OAuth flow — authorize access
5. The 9 tools appear in the connectors list ✅

### 🛠️ The 9 available tools

| Tool | Description |
|---|---|
| `list_pages` | List all pages with their routes and languages |
| `get_page` | Read the Markdown content of a page |
| `create_page` | Create a new page |
| `update_page` | Update the content or title of a page |
| `delete_page` | Delete a page |
| `clear_cache` | Clear the Grav cache |
| `list_plugins` | List installed plugins |
| `list_themes` | List installed themes |
| `toggle_plugin` | Enable or disable a plugin |

### 🔒 Security

The proxy implements several layers of protection:

- **OAuth 2.1 + PKCE S256** — secure authentication flow compliant with RFC 9728
- **SHA-256 token hashing** — tokens are never stored in plain text
- **Atomic JSON writes** — no state file corruption
- **Systemd hardening** — `NoNewPrivileges`, `ProtectSystem=strict`, `IPAddressDeny=any`, `MemoryDenyWriteExecute`
- **nginx** — direct access to the PHP plugin blocked (`location ^~ /api/mcp { return 444; }`)

## 🏁 Conclusion

This stack turns Grav CMS into an active context source for Claude.ai — read, write, manage your site by simple conversation. The whole thing is reproducible on any nginx server with Python 3.11+, and the code is fully available on GitHub under the MIT license.

**To go further:**

- 💡 Add more MCP tools: media management, user management, system configuration
- 💡 Implement a per-tool permission system to restrict actions based on the OAuth token



## Tags

- grav
- mcp
- claude
- homelab

## Categories

- infrastructure
