Frappe MCP Server
README.md
# Frappe Bench & Site Manager ā MCP Server
Manage your local Frappe benches and sites directly from Claude.ai (web + mobile).
Uses [FastMCP](https://github.com/jlowin/fastmcp) over HTTP with an Ngrok static tunnel.
```
š± Claude.ai ā š Ngrok (permanent URL) ā š„ļø MCP Server (localhost:8080) ā š§ Frappe Benches
```
---
## Tools Available
| Tool | Category | What it does |
| --------------------- | --------------- | -------------------------------------------- |
| `bench_restart` | Bench Ops | Restart supervisor/bench services |
| `list_sites` | Site Management | List benches, sites, apps, status |
| `frappe_api` | DocType Data | Authenticated REST API calls |
| `bench_execute` | Console | Run Python in Frappe context |
| `get_logs` | Logs | Fetch and filter site/bench log files |
| `get_config_overview` | Config | Show configured benches (no secrets exposed) |
---
## Prerequisites
- Python 3.10+
- One or more Frappe benches already set up and working locally
- [ngrok](https://ngrok.com) account (free tier works)
---
## Step 1: Install dependencies
```bash
git clone <this-repo>
cd frappe-mcp
pip install -r requirements.txt
```
---
## Step 2: Configure
```bash
cp config.example.json config.json
```
Edit `config.json`:
```json
{
"benches": [
{
"id": "bench1",
"label": "Main Dev Bench",
"path": "/home/youruser/frappe-bench",
"bench_cmd": "/home/youruser/frappe-bench/env/bin/bench"
}
],
"site_credentials": {
"mysite.localhost": {
"api_key": "YOUR_API_KEY",
"api_secret": "YOUR_API_SECRET",
"port": 8000
}
}
}
```
**Important:** `config.json` is gitignored ā never commit it.
---
## Step 3: Get Frappe API Credentials
For each site you want to access via `frappe_api` or `bench_execute`:
1. Open the Frappe site in your browser
2. Go to **Settings ā API Access**
3. Click **Generate Keys** (for your admin user)
4. Copy `api_key` and `api_secret` into `config.json ā site_credentials`
---
## Step 4: Start the MCP Server
```bash
python3 main.py
```
You should see:
```
š Frappe MCP Server starting...
Benches configured : 1
Sites with creds : 1
Listening on : http://0.0.0.0:8000
MCP endpoint : http://0.0.0.0:8000/mcp
Audit log : mcp_audit.log
```
---
## Step 5: Setup Ngrok (one-time)
### Install ngrok
**macOS (Homebrew):**
```bash
brew install ngrok/ngrok/ngrok
```
**Linux:**
```bash
curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null
echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | sudo tee /etc/apt/sources.list.d/ngrok.list
sudo apt update && sudo apt install ngrok
```
**Or download directly:** https://ngrok.com/download
### Add your auth token
```bash
ngrok config add-authtoken YOUR_AUTHTOKEN
```
Get your token from: https://dashboard.ngrok.com/get-started/your-authtoken
### Start tunnel with your free static domain
```bash
ngrok http 8080 --url=yourname.ngrok-free.app
```
> **Get a free static domain:** ngrok Dashboard ā Cloud Edge ā Domains ā New Domain
Your permanent MCP URL becomes:
```
https://yourname.ngrok-free.app/mcp
```
---
## Step 6: Connect to Claude.ai
1. Open [claude.ai](https://claude.ai) ā **Settings** ā **Integrations**
2. Click **Add Integration**
3. Enter URL: `https://yourname.ngrok-free.app/mcp`
4. Click **Add** ā that's it
Works from your phone and laptop anywhere as long as your laptop is running.
---
## Step 7: Auto-start on Boot (optional)
### Linux (systemd)
Create `/etc/systemd/system/frappe-mcp.service`:
```ini
[Unit]
Description=Frappe MCP Server
After=network.target
[Service]
Type=simple
User=youruser
WorkingDirectory=/home/youruser/frappe-mcp
ExecStart=/usr/bin/python3 /home/youruser/frappe-mcp/main.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
```
```bash
sudo systemctl daemon-reload
sudo systemctl enable frappe-mcp
sudo systemctl start frappe-mcp
```
### macOS (launchd)
Create `~/Library/LaunchAgents/com.frappe.mcp.plist`:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.frappe.mcp</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/python3</string>
<string>/Users/youruser/frappe-mcp/main.py</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/youruser/frappe-mcp</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/youruser/frappe-mcp/mcp_stdout.log</string>
<key>StandardErrorPath</key>
<string>/Users/youruser/frappe-mcp/mcp_stderr.log</string>
</dict>
</plist>
```
```bash
launchctl load ~/Library/LaunchAgents/com.frappe.mcp.plist
```
---
## Running Tests
```bash
python test_tools.py
```
No real Frappe bench needed ā all tools are tested with a mock config.
---
## Security Notes
- `config.json` is never committed (gitignored)
- API keys are never exposed in tool responses (`get_config_overview` shows `"configured"/"missing"` only)
- All subprocess calls use `shell=False` ā no shell injection possible
- `bench_execute` has a two-tier security scanner: hard blocks and soft warnings
- Blocked patterns (configurable): `DROP`, `TRUNCATE`, `os.system`, `exec(`, `eval(`, etc.
- Rate limit: 30 requests/minute per IP (configurable)
- All tool calls are appended to `mcp_audit.log`
---
## Example Claude Prompts
```
List all my Frappe sites and their status.
How many NGO records are on site1.localhost in bench1?
Show me the last 50 error log lines for site1.localhost ā filter for "PermissionError".
Restart the web worker on bench1.
What's in the mGrant Settings module field for site1.localhost?
Show me all active Grants on site1.localhost with fields name, grant_name, grant_status.
```
---
## File Structure
```
frappe-mcp/
āāā main.py # FastMCP server + tool registration
āāā config.py # Config loader + validator + singleton
āāā security.py # Input sanitizer, command blocker, rate limiter
āāā logger.py # Audit logger ā mcp_audit.log
āāā tools/
ā āāā bench_ops.py # bench_restart (+ Phase 2 stubs)
ā āāā site_manager.py # list_sites (+ Phase 2 stub)
ā āāā frappe_api.py # frappe_api (+ Phase 2 stub)
ā āāā executor.py # bench_execute
ā āāā log_reader.py # get_logs
āāā config.json # Your config (gitignored)
āāā config.example.json # Template (committed)
āāā requirements.txt
āāā .gitignore
āāā test_tools.py # Test suite (mock config, no bench needed)
āāā README.md
```
---
## Troubleshooting
**`config.json not found`**
ā Run `cp config.example.json config.json` and fill in your bench paths.
**`Bench path '/home/...' does not exist`**
ā The `path` in `config.json ā benches` must be an existing directory.
**`bench command not found`**
ā Use the full path to the bench binary, e.g. `/home/user/frappe-bench/env/bin/bench`.
**`No credentials configured for site`**
ā Add `api_key`/`api_secret` for that site to `config.json ā site_credentials`.
**`Authentication failed (401)`**
ā Regenerate API keys in the Frappe site: Settings ā API Access.
**Ngrok shows `ERR_NGROK_3200`**
ā Your static domain may not be activated. Check ngrok Dashboard ā Domains.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues