From 8cf91aed9ac05fbee8ef84d4cbf669a82b7c81cf Mon Sep 17 00:00:00 2001 From: openclaw Date: Fri, 11 Sep 2026 15:10:57 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20MCP=20Token=20=E5=A4=8D=E5=88=B6?= =?UTF-8?q?=E6=8C=89=E9=92=AE=20=E2=80=94=20=E5=90=8E=E5=8F=B0=E4=B8=80?= =?UTF-8?q?=E9=94=AE=E5=8F=96=E5=9B=9E=E6=98=8E=E6=96=87=20Token=20?= =?UTF-8?q?=E5=88=B0=E5=89=AA=E8=B4=B4=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handlers/mcp.go | 10 ++++++++++ main.go | 1 + templates/admin/dashboard.html | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/handlers/mcp.go b/handlers/mcp.go index 2443971..535ebf7 100644 --- a/handlers/mcp.go +++ b/handlers/mcp.go @@ -797,6 +797,16 @@ func MCPStatus(c *gin.Context) { }) } +// MCPGetToken 返回完整明文 Token(供后台「复制」按钮使用,需已登录后台会话) +func MCPGetToken(c *gin.Context) { + _, token := getMCPAuthState() + if token == "" { + c.JSON(http.StatusOK, gin.H{"success": false, "message": "Token 未生成,请先点「重新生成」"}) + return + } + c.JSON(http.StatusOK, gin.H{"success": true, "token": token}) +} + // MCPRegenerateToken 生成新随机 Token 并自动启用 MCP,返回完整新 Token(仅此一次明文展示) func MCPRegenerateToken(c *gin.Context) { b := make([]byte, 24) diff --git a/main.go b/main.go index b68f1b1..7ab464d 100644 --- a/main.go +++ b/main.go @@ -72,6 +72,7 @@ func main() { // MCP 管理(后台 UI) admin.GET("/api/mcp/status", handlers.MCPStatus) + admin.GET("/api/mcp/token", handlers.MCPGetToken) admin.POST("/api/mcp/token", handlers.MCPRegenerateToken) admin.POST("/api/mcp/enabled", handlers.MCPSetEnabled) } diff --git a/templates/admin/dashboard.html b/templates/admin/dashboard.html index ab7b9be..3a1ad02 100644 --- a/templates/admin/dashboard.html +++ b/templates/admin/dashboard.html @@ -139,6 +139,7 @@
+
@@ -200,6 +201,37 @@ }); } + async function copyMCPToken(btn) { + try { + const resp = await fetch('/admin/api/mcp/token'); + const res = await resp.json(); + if (!res.success) { alert('❌ ' + (res.message || 'Token 未生成')); return; } + const token = res.token; + let copied = false; + try { + await navigator.clipboard.writeText(token); + copied = true; + } catch (e) { + // 非 HTTPS/localhost 环境降级方案 + const ta = document.createElement('textarea'); + ta.value = token; + ta.style.position = 'fixed'; + ta.style.opacity = '0'; + document.body.appendChild(ta); + ta.focus(); ta.select(); + try { copied = document.execCommand('copy'); } catch (e2) {} + document.body.removeChild(ta); + } + if (copied) { + const old = btn.textContent; + btn.textContent = '✅ 已复制'; + setTimeout(() => { btn.textContent = old; }, 1500); + } else { + prompt('自动复制不可用,请手动复制 Token:', token); + } + } catch(e) { alert('获取 Token 失败'); } + } + async function regenMCPToken() { if (!confirm('确定重新生成 Token?\n\n旧 Token 将立即失效,已接入的 AI 客户端需要更新为新 Token。')) return; try {