🚀 Claude Runner - 帶作業調度功能的MCP服務器
Claude Runner是一個單文件MCP服務器,它通過cron表達式來調度和執行Claude Code CLI任務。該服務器具備Web儀表盤、Webhook支持、動態MCP服務器創建以及令牌/成本跟蹤等功能。
🚀 快速開始
前提條件
操作步驟
git clone https://github.com/floriansmeyers/SFLOW-AIRunner-MCP-PRD.git
cd SFLOW-AIRunner-MCP-PRD
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python server.py
首次運行時,服務器將執行以下操作:
- 創建包含所有表的
jobs.db SQLite數據庫。
- 自動生成OAuth憑證(會打印到標準錯誤輸出,請保存這些憑證!)。
- 啟動調度器循環(每60秒檢查一次)。
💻 運行方式
本地運行(stdio傳輸 - 適用於Claude Desktop)
python server.py
將以下配置添加到Claude Desktop的配置文件中(在macOS系統上為~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"claude-runner": {
"command": "/path/to/SFLOW-AIagents-MCP-Spinner/.venv/bin/python",
"args": ["/path/to/SFLOW-AIagents-MCP-Spinner/server.py"]
}
}
}
重要提示:請使用.venv內Python解釋器的完整路徑,以確保依賴項可用。
遠程運行(SSE傳輸 - 適用於Web儀表盤)
MCP_TRANSPORT=sse python server.py
同時使用兩種傳輸方式
MCP_TRANSPORT=both python server.py
⚙️ 環境變量
| 變量 |
是否必需 |
默認值 |
描述 |
ANTHROPIC_API_KEY |
是 |
- |
從console.anthropic.com獲取的API密鑰,通過Claude Agent SDK執行作業時必需。請勿使用個人Max/Pro訂閱。 |
MCP_TRANSPORT |
否 |
both |
傳輸模式:stdio、sse或both |
OAUTH_CLIENT_ID |
否 |
自動生成 |
OAuth客戶端ID |
OAUTH_CLIENT_SECRET |
否 |
自動生成 |
OAuth客戶端密鑰 |
OAUTH_SECRET_KEY |
否 |
自動生成 |
用於簽署JWT令牌的密鑰 |
DASHBOARD_USERNAME |
否 |
- |
儀表盤的基本認證用戶名 |
DASHBOARD_PASSWORD |
否 |
- |
儀表盤的基本認證密碼 |
NGROK_AUTHTOKEN |
否 |
- |
用於遠程隧道的ngrok認證令牌 |
PUBLIC_URL |
否 |
- |
覆蓋OAuth回調的基礎URL(例如https://your-domain.com) |
你可以創建一個.env文件來持久化這些變量:
ANTHROPIC_API_KEY=sk-ant-...
OAUTH_CLIENT_ID=your_client_id
OAUTH_CLIENT_SECRET=your_client_secret
OAUTH_SECRET_KEY=your_secret_key
DASHBOARD_USERNAME=admin
DASHBOARD_PASSWORD=secure_password
NGROK_AUTHTOKEN=your_ngrok_token
🔐 身份驗證(OAuth 2.1)
SSE傳輸需要OAuth 2.1身份驗證。首次啟動時,服務器會自動生成憑證並將其打印到標準錯誤輸出:
============================================================
OAUTH CREDENTIALS FOR CLAUDE CONNECTOR
============================================================
Client ID: abc123...
Client Secret: xyz789...
Save these to your .env file:
OAUTH_CLIENT_ID=abc123...
OAUTH_CLIENT_SECRET=xyz789...
============================================================
重要提示:請立即保存這些憑證。密鑰只會顯示一次,且無法恢復。
OAuth端點
| 端點 |
描述 |
/.well-known/oauth-authorization-server |
服務器元數據 |
/.well-known/oauth-protected-resource |
受保護資源元數據 |
/oauth/authorize |
授權端點 |
/oauth/token |
令牌端點 |
重新生成憑證
如果你丟失了憑證,可以從數據庫中刪除客戶端:
sqlite3 jobs.db "DELETE FROM oauth_clients WHERE client_name = 'Claude (auto)'"
然後重新啟動服務器以生成新的憑證。
📁 目錄結構
SFLOW-AIRunner-MCP-PRD/
├── server.py # 主MCP服務器
├── requirements.txt # Python依賴項
├── jobs.db # SQLite數據庫(自動創建)
├── .env # 環境變量(可選)
├── fixed-servers/ # 內置MCP服務器
│ └── email/
│ ├── server.py
│ └── metadata.json
├── dynamic_servers/ # 用戶創建的MCP服務器(自動創建)
│ ├── cat-facts/
│ └── r2-images/
├── deploy/ # 部署配置(systemd、nginx、部署腳本)
│ ├── deploy.sh
│ ├── mcpserver.service
│ ├── nginx.conf
│ └── README-HETZNER.md
└── claude_playground/ # 作業執行的工作目錄(自動創建)
💾 數據庫
SQLite數據庫位於./jobs.db,包含以下表:
表結構
| 表名 |
描述 |
jobs |
帶有cron表達式的調度任務 |
runs |
包含輸出、令牌和成本的執行歷史記錄 |
settings |
配置信息(允許的工具、MCP服務器、憑證) |
webhooks |
觸發提示的HTTP端點 |
oauth_clients |
OAuth客戶端憑證 |
oauth_codes |
OAuth授權碼 |
oauth_refresh_tokens |
OAuth刷新令牌 |
默認設置
首次運行時,會創建以下默認設置:
| 鍵 |
默認值 |
allowed_tools |
["WebSearch", "WebFetch", "Read", "Write", "Edit", "Bash"] |
mcp_servers |
[] |
mcp_env_vars |
{} |
webhook_base_url |
"http://localhost:8080" |
sandbox_mode |
true |
完全重置數據庫
若要完全重置數據庫,請執行以下操作:
rm jobs.db
python server.py
🛠️ 可用的MCP工具
作業管理
| 工具 |
描述 |
list_jobs |
列出所有調度作業 |
get_job(job_id) |
獲取特定作業 |
create_job(name, cron, prompt, ...) |
創建新的調度作業 |
update_job(job_id, ...) |
更新現有作業 |
delete_job(job_id) |
刪除作業及其運行記錄 |
trigger_job(job_id) |
手動立即觸發作業 |
運行管理
| 工具 |
描述 |
list_runs(job_id?, limit?) |
列出最近的運行記錄 |
get_run(run_id) |
獲取完整的運行詳情,包括輸出 |
kill_run(run_id) |
取消待處理/正在運行的運行 |
Webhook管理
| 工具 |
描述 |
create_webhook(name, prompt_template) |
創建Webhook端點 |
list_webhooks |
列出所有Webhook |
get_webhook(webhook_id) |
獲取Webhook詳情 |
update_webhook(webhook_id, ...) |
更新Webhook |
delete_webhook(webhook_id) |
刪除Webhook |
動態MCP服務器管理
| 工具 |
描述 |
create_mcp_server(name, code, ...) |
創建自定義MCP服務器 |
list_dynamic_mcp_servers |
列出用戶創建的服務器 |
get_dynamic_mcp_server(name) |
獲取服務器詳情 |
update_mcp_server(name, code, ...) |
更新動態服務器的代碼/描述 |
delete_mcp_server(name) |
刪除動態服務器 |
enable_mcp_server(name) |
啟用動態MCP服務器 |
disable_mcp_server(name) |
禁用動態MCP服務器 |
固定MCP服務器管理
| 工具 |
描述 |
list_fixed_mcp_servers |
列出內置服務器 |
enable_fixed_server(name) |
啟用固定服務器 |
disable_fixed_server(name) |
禁用固定服務器 |
內部MCP工具
| 工具 |
描述 |
invoke_internal_mcp_tool(tool, payload) |
在內部MCP服務器上調用工具 |
憑證管理
| 工具 |
描述 |
set_server_credential(server, key, value) |
設置憑證 |
get_server_credentials(server) |
查看憑證(掩碼顯示) |
list_required_credentials(server) |
查看所需憑證 |
get_unconfigured_servers |
查找缺少憑證的服務器 |
delete_server_credential(server, key) |
刪除憑證 |
💻 通過claude.ai使用
連接成功後,你可以完全通過自然語言與服務器進行交互。Claude會在幕後將你的請求轉換為相應的MCP工具調用。
重要提示:作業通過Claude Agent SDK執行,該SDK在底層運行本地Claude Code CLI。你需要在運行服務器的機器上安裝並認證Claude Code(claude必須在系統PATH中可用並已登錄)。
以下是主要的工作流程:
創建自定義工具
你可以讓Claude動態構建新的MCP工具服務器。服務器代碼會保存到dynamic_servers/<name>/,每個服務器都會有一個DATA_DIR路徑變量用於本地文件存儲。代碼中通過os.environ.get()或os.getenv()引用的環境變量會被自動檢測並標記為所需憑證。
你:創建一個工具,使用OpenWeatherMap API獲取給定城市的當前天氣。
Claude:[調用create_mcp_server — 將服務器保存到dynamic_servers/weather/,從代碼中檢測到OPENWEATHERMAP_API_KEY]
完成!我創建了一個帶有get_weather(city)工具的“weather”服務器。
它需要一個API密鑰 —— 現在要我保存你的密鑰嗎?
你:是的,密鑰是abc123
Claude:[調用set_server_credential("weather", "OPENWEATHERMAP_API_KEY", "abc123")]
憑證已保存。天氣服務器已準備好使用。
調度定期作業
作業使用Claude Agent SDK按cron計劃運行。你可以指定作業允許使用的工具(空數組[]表示允許使用所有工具)。
你:創建一個作業,在每個工作日的上午9點運行,總結我們的Azure DevOps衝刺情況。
Claude:[調用create_job,參數如下:]
名稱:"Daily Sprint Summary"
cron表達式:"0 9 * * 1-5"
提示:"連接到Azure DevOps並總結當前衝刺情況..."
工具:["mcp__azure-devops__wit_my_work_items", "mcp__email__send_email"]
作業已創建(ID:a1b2c3d4)。下次運行時間:明天上午9:00。
你:顯示最近的5次運行記錄
Claude:[調用list_runs,限制為5條記錄]
以下是最近的運行記錄 —— 4次成功完成,1次出錯...
設置Webhook
Webhook允許外部服務通過HTTP POST觸發提示。prompt_template支持{{payload}}表示完整的請求體,或{{payload.field.subfield}}表示嵌套值。
你:創建一個Webhook,每當創建新的Azure DevOps工作項時發送Slack摘要。POST請求體中的工作項標題位於resource.fields.System.Title。
Claude:[調用create_webhook,參數如下:]
名稱:"New Work Item Notifier"
提示模板:"創建了一個新的工作項:{{payload.resource.fields.System.Title}}。總結它併發布到Slack。"
Webhook已創建!使用以下URL發送POST請求以觸發它:
https://your-server.com/webhook/abc123def456...
你:測試一下 —— 手動觸發Webhook
Claude:我無法通過MCP觸發Webhook,但你可以使用curl進行測試:
curl -X POST https://your-server.com/webhook/abc123def456... \
-H "Content-Type: application/json" \
-d '{"resource":{"fields":{"System":{"Title":"Bug: login broken"}}}}'
直接調用工具
使用invoke_internal_mcp_tool可以立即在任何啟用的MCP服務器上調用工具,而無需創建作業。
你:使用天氣工具檢查柏林的天氣預報
Claude:[調用invoke_internal_mcp_tool,參數如下:]
工具:"mcp__weather__get_weather"
負載:{"city": "Berlin"}
柏林當前溫度為12°C,天氣多雲,預計下午有雨。
管理服務器和憑證
你可以啟用、禁用和更新內置(固定)和用戶創建(動態)的服務器,並管理它們的憑證。
你:有哪些可用的服務器?
Claude:[調用list_fixed_mcp_servers和list_dynamic_mcp_servers]
固定服務器:email(已啟用),playwright(已禁用)
動態服務器:weather(已啟用),url-shortener(已禁用)
你:啟用playwright並禁用天氣服務器
Claude:[調用enable_fixed_server("playwright")和disable_mcp_server("weather")]
完成 —— playwright已啟用,天氣服務器已禁用。
你:哪些服務器缺少憑證?
Claude:[調用get_unconfigured_servers]
電子郵件服務器需要SENDGRID_API_KEY和SENDER_EMAIL。
臨時提示
對於一次性任務,你可以使用Web儀表盤(SSE模式)中的快速運行功能立即執行提示,而無需創建作業。從claude.ai,你也可以手動觸發任何現有作業:
你:立即運行“Daily Sprint Summary”作業
Claude:[調用trigger_job("a1b2c3d4")]
運行已排隊(運行ID:e5f6g7h8)。我會回來查看結果。
⏰ Cron表達式示例
| 表達式 |
描述 |
0 9 * * 1-5 |
工作日上午9:00 |
0 */2 * * * |
每2小時 |
0 9 * * 1 |
週一上午9:00 |
*/30 * * * * |
每30分鐘 |
0 0 1 * * |
每月1日午夜 |
🛠️ 故障排除
ModuleNotFoundError: No module named 'dotenv'
Claude Desktop使用的是系統Python,而不是你的虛擬環境。請更新配置以使用虛擬環境的完整路徑:
{
"command": "/full/path/to/.venv/bin/python",
"args": ["/full/path/to/server.py"]
}
OAuth憑證丟失
刪除自動生成的客戶端並重新啟動:
sqlite3 jobs.db "DELETE FROM oauth_clients WHERE client_name = 'Claude (auto)'"
python server.py
作業未運行
- 檢查調度器是否正在運行(在日誌中查找“Scheduler loop started”)。
- 驗證作業是否已啟用:
sqlite3 jobs.db "SELECT enabled FROM jobs"。
- 檢查cron表達式是否有效。
- 在運行記錄中查找錯誤:
sqlite3 jobs.db "SELECT error FROM runs ORDER BY started_at DESC LIMIT 1"。
作業中MCP工具不可用
如果作業報告“我無法訪問[MCP服務器]工具”,請檢查工具名稱格式:
正確格式:mcp__<server>__<tool>(雙下劃線)
- 示例:
mcp__email__send_email
- 示例:
mcp__playwright__navigate
自動規範化格式:以下格式會自動轉換:
email:send_email → mcp__email__send_email
email.send_email → mcp__email__send_email
提示:使用空工具數組[]允許使用所有可用工具,或檢查響應中的tool_warnings以查看是否有工具被規範化。
服務器立即斷開連接
檢查標準錯誤輸出以查找錯誤。常見原因包括:
- 缺少依賴項(運行
pip install -r requirements.txt)
- Python版本低於3.11
- 數據庫被其他進程鎖定
Git "dubious ownership"錯誤
如果自動部署失敗並出現“fatal: detected dubious ownership in repository”錯誤,請執行以下操作:
git config --system --add safe.directory /opt/mcpserver/app
當倉庫的所有者與運行部署腳本的用戶不同時,會出現此錯誤。
Playwright瀏覽器未找到
如果Playwright因找不到瀏覽器而失敗,請手動安裝Chromium:
apt install -y libnss3 libnspr4 libatk1.0-0t64 libatk-bridge2.0-0t64 libcups2t64 \
libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 \
libpango-1.0-0 libcairo2 libasound2t64
su - mcpuser
cd /opt/mcpserver/app
source venv/bin/activate
playwright install chromium
驗證是否正常工作:
python -c "from playwright.sync_api import sync_playwright; p = sync_playwright().start(); b = p.chromium.launch(); print('OK'); b.close(); p.stop()"
🏗️ 架構
┌─────────────────────────────────────────────────────────────┐
│ MCP Server │
│ ┌───────────┐ ┌───────────────┐ ┌───────────────────┐ │
│ │ Job CRUD │ │ Scheduler │ │ Run Processor │ │
│ │ Tools │ │ (60s loop) │ │ (async) │ │
│ └─────┬─────┘ └───────┬───────┘ └─────────┬─────────┘ │
│ │ │ │ │
│ └────────────────┼────────────────────┘ │
│ ▼ │
│ ┌────────────────┐ │
│ │ SQLite │ │
│ │ (jobs.db) │ │
│ └────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ Claude Agent │ │
│ │ SDK │ │
│ └────────────────┘ │
└─────────────────────────────────────────────────────────────┘
服務器運行三個併發的異步組件:
- MCP服務器 - 通過FastMCP公開25多個工具。
- 調度器循環 - 每60秒檢查一次是否有到期的作業。
- 運行處理器循環 - 處理待處理的運行並執行它們。
📦 關鍵依賴項
fastmcp - MCP協議實現
claude-agent-sdk - 運行作業的核心執行引擎
croniter - Cron表達式解析
sendgrid, requests - 電子郵件提供商集成
uvicorn - 用於SSE傳輸的ASGI服務器
pyngrok - 用於從claude.ai進行遠程訪問的ngrok隧道
python-dotenv - .env文件加載
PyJWT - 用於OAuth的JWT令牌處理
beautifulsoup4 - 網頁抓取
boto3 - AWS集成
playwright - 瀏覽器自動化