f3a44a901e
Build and Deploy / build-and-push (push) Successful in 54s
- Move MCP tools registration to internal/mcp - cmd/mcp now only contains CLI entry logic - Support env vars TASKPOOL_URL/TASKPOOL_TOKEN for stdio mode - Remove duplicate client.go from cmd/mcp
154 lines
3.6 KiB
Go
154 lines
3.6 KiB
Go
package mcp
|
||
|
||
import (
|
||
"fmt"
|
||
"net/http"
|
||
"os"
|
||
"strings"
|
||
|
||
"github.com/engigu/taskpool/internal/mcp"
|
||
imc "github.com/engigu/taskpool/internal/mcp"
|
||
)
|
||
|
||
// Run 启动 TaskPool MCP Server
|
||
// 模式:
|
||
// - stdio(默认):通过 stdin/stdout 通信,适用于 Claude Desktop / Cursor 等本地 Agent
|
||
// - http:通过 HTTP/SSE 通信,适用于 Hermes / OpenClaw 等远程 Agent
|
||
//
|
||
// 环境变量:
|
||
// TASKPOOL_URL 面板地址,如 http://127.0.0.1:8052 或 https://panel.example.com
|
||
// TASKPOOL_TOKEN 设置页 OpenAPI Token
|
||
// MCP_HTTP_ADDR HTTP 模式监听地址(默认 :8053)
|
||
func Run(args []string) {
|
||
for _, a := range args {
|
||
if a == "-h" || a == "--help" {
|
||
printHelp()
|
||
return
|
||
}
|
||
}
|
||
|
||
mode := "stdio"
|
||
httpAddr := ":8053"
|
||
|
||
for i, a := range args {
|
||
if a == "--http" {
|
||
mode = "http"
|
||
if i+1 < len(args) && !strings.HasPrefix(args[i+1], "-") {
|
||
httpAddr = args[i+1]
|
||
}
|
||
}
|
||
}
|
||
|
||
if envAddr := os.Getenv("MCP_HTTP_ADDR"); envAddr != "" {
|
||
httpAddr = envAddr
|
||
}
|
||
|
||
if mode == "http" {
|
||
runHTTP(httpAddr)
|
||
} else {
|
||
runStdio()
|
||
}
|
||
}
|
||
|
||
func runStdio() {
|
||
// 使用内部 OpenAPI 客户端(自动获取服务器地址和 Token)
|
||
client := imc.GetOpenAPIClient()
|
||
fmt.Fprintf(os.Stderr, "[taskpool-mcp] mode=stdio url=%s token=%s\n", client.BaseURL, maskToken(client.Token))
|
||
|
||
s := mcp.GetServer()
|
||
// 设置外部客户端(用于 stdio 模式连接远程服务器)
|
||
if client.Token != "" {
|
||
mcp.SetExternalClient(client)
|
||
}
|
||
|
||
if err := mcp.ServeStdio(s); err != nil {
|
||
fmt.Fprintf(os.Stderr, "[taskpool-mcp] server error: %v\n", err)
|
||
os.Exit(1)
|
||
}
|
||
}
|
||
|
||
func runHTTP(addr string) {
|
||
client := imc.GetOpenAPIClient()
|
||
fmt.Fprintf(os.Stderr, "[taskpool-mcp] mode=http addr=%s url=%s token=%s\n", addr, client.BaseURL, maskToken(client.Token))
|
||
|
||
// 设置外部客户端
|
||
if client.Token != "" {
|
||
mcp.SetExternalClient(client)
|
||
}
|
||
|
||
handler := mcp.GetHTTPHandler()
|
||
|
||
// 添加健康检查端点
|
||
mux := http.NewServeMux()
|
||
mux.Handle("/mcp/", http.StripPrefix("/mcp", handler))
|
||
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||
w.WriteHeader(http.StatusOK)
|
||
w.Write([]byte(`{"status":"ok"}`))
|
||
})
|
||
|
||
fmt.Fprintf(os.Stderr, "[taskpool-mcp] MCP endpoint: http://%s/mcp\n", strings.TrimPrefix(addr, ":"))
|
||
if err := http.ListenAndServe(addr, mux); err != nil {
|
||
fmt.Fprintf(os.Stderr, "[taskpool-mcp] http server error: %v\n", err)
|
||
os.Exit(1)
|
||
}
|
||
}
|
||
|
||
func maskToken(t string) string {
|
||
t = strings.TrimSpace(t)
|
||
if t == "" {
|
||
return "(empty)"
|
||
}
|
||
if len(t) <= 8 {
|
||
return "****"
|
||
}
|
||
return t[:4] + "****" + t[len(t)-4:]
|
||
}
|
||
|
||
func printHelp() {
|
||
fmt.Fprintf(os.Stderr, `
|
||
TaskPool MCP Server(内置模式)
|
||
|
||
MCP Server 已集成到后端服务中,推荐直接使用内置端点:
|
||
http://your-server:8052/mcp
|
||
|
||
此命令用于:
|
||
- stdio 模式:本地 Agent(Claude Desktop / Cursor)
|
||
- 独立 HTTP 服务:需要单独端口时
|
||
|
||
用法:
|
||
taskpool mcp # stdio 模式
|
||
taskpool mcp --http # HTTP 模式,监听 :8053
|
||
taskpool mcp --http :9000 # HTTP 模式,指定端口
|
||
|
||
环境变量(stdio 模式可选):
|
||
TASKPOOL_URL 面板地址(默认本机)
|
||
TASKPOOL_TOKEN OpenAPI Token(默认从系统设置读取)
|
||
|
||
Agent 配置示例:
|
||
HTTP 模式(推荐):
|
||
{
|
||
"mcpServers": {
|
||
"taskpool": {
|
||
"url": "http://your-server:8052/mcp",
|
||
"headers": {
|
||
"Authorization": "Bearer 你的Token"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
stdio 模式:
|
||
{
|
||
"mcpServers": {
|
||
"taskpool": {
|
||
"command": "taskpool",
|
||
"args": ["mcp"]
|
||
}
|
||
}
|
||
}
|
||
|
||
前置条件:
|
||
系统设置 → 启用 OpenAPI → 生成 Token
|
||
`)
|
||
}
|