const express = require('express'); const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const app = express(); app.use(express.json({limit:'10mb'})); const REPO_BASE = '/opt/zhuyuan/repos'; const USERS = JSON.parse(fs.readFileSync('/opt/zhuyuan/console/users.json','utf8')); const TOKENS = {}; app.post('/api/mcp/login',(req,res)=>{ const{username,password}=req.body; const u=USERS[username]; if(!u||u.pass!==password)return res.status(401).json({e:'wrong'}); const t=crypto.randomBytes(16).toString('hex'); TOKENS[t]={username,repos:u.repos}; res.json({token:t,repos:u.repos,url:'https://guanghuzhiqiu.top/mcp'}); }); function auth(req){const t=req.headers['x-mcp-token']||req.query.token;return TOKENS[t]||null} function allow(user,repo){return user.repos.some(r=>repo.startsWith(r))} app.post('/mcp/wake',(req,res)=>{ const u=auth(req);if(!u)return res.status(403).json({e:'unauth'}); res.json({speaker:'铸渊',user:u.username,repos:u.repos,brains:'https://guanghulab.com/code/bingshuo/hololake-brains'}); }); app.get('/mcp/brain_status',(req,res)=>{ const u=auth(req);if(!u)return res.status(403).json({e:'unauth'}); res.json({loaded:true,source:'https://guanghulab.com/code/bingshuo/hololake-brains',repos:u.repos}); }); app.get('/mcp/repo_read_file',(req,res)=>{ const u=auth(req);if(!u)return res.status(403).json({e:'unauth'}); const{repo,file}=req.query; if(!allow(u,repo))return res.status(403).json({e:'not your repo'}); try{res.json({content:execSync('git --git-dir='+path.join(REPO_BASE,repo)+' show HEAD:'+file,{timeout:5000}).toString()})}catch(e){res.status(404).json({e:'not found'})} }); app.get('/mcp/repo_list_files',(req,res)=>{ const u=auth(req);if(!u)return res.status(403).json({e:'unauth'}); const{repo}=req.query; if(!allow(u,repo))return res.status(403).json({e:'not your repo'}); try{res.json({files:execSync('git --git-dir='+path.join(REPO_BASE,repo)+' ls-tree -r --name-only HEAD',{timeout:5000}).toString().trim().split('\n').filter(Boolean)})}catch(e){res.json({files:[]})} }); app.get('/mcp/repo_status',(req,res)=>{ const u=auth(req);if(!u)return res.status(403).json({e:'unauth'}); const{repo}=req.query; if(!allow(u,repo))return res.status(403).json({e:'not your repo'}); try{ const log=execSync('git --git-dir='+path.join(REPO_BASE,repo)+' log --oneline -5 --format=%h|%s|%an|%ai',{timeout:5000}).toString().trim(); res.json({commits:log.split('\n').map(l=>{const[h,m,a,d]=l.split('|');return{hash:h,msg:m,author:a,date:d}})}); }catch(e){res.json({commits:[]})} }); app.get('/mcp/setup',(req,res)=>{ res.type('text').send('# 光湖 MCP 配置\n\n1. 获取token: POST /mcp/api/mcp/login\n2. 配置MCP URL: https://guanghuzhiqiu.top/mcp\n3. Header: x-mcp-token\n4. 工具: wake, brain_status, repo_read_file, repo_list_files, repo_status\n5. 共享大脑: https://guanghulab.com/code/bingshuo/hololake-brains'); }); app.get('/mcp/health',(req,res)=>res.json({ok:1})); app.listen(3950,'127.0.0.1',()=>console.log('MCP:3950'));