UNPKG

cnb-mcp-server

Version:

MCP Server for the cnb API, enabling file operations, repository management, search functionality, and more.

420 lines (358 loc) 13.3 kB
#!/usr/bin/env node /** * CNB仓库工具测试脚本 - 根据API.txt中的接口优化 */ // 导入所需模块 const fs = require('fs'); const path = require('path'); const https = require('https'); // 从.env文件加载环境变量 function loadEnv() { try { const envPath = path.join(process.cwd(), '.env'); if (fs.existsSync(envPath)) { const envContent = fs.readFileSync(envPath, 'utf-8'); const envLines = envContent.split('\n'); envLines.forEach(line => { // 忽略注释和空行 if (line.trim() && !line.startsWith('#')) { const [key, value] = line.split('='); if (key && value) { process.env[key.trim()] = value.trim(); } } }); console.log('已从.env文件加载环境变量'); } else { console.log('.env文件不存在,将使用当前环境变量'); } } catch (error) { console.error(`加载.env文件失败: ${error.message}`); } } // 加载环境变量 loadEnv(); // 日志函数 function log(message, type = 'INFO') { const timestamp = new Date().toISOString(); console.log(`[${timestamp}] [${type}] ${message}`); fs.appendFileSync(path.join(process.cwd(), 'test-log.txt'), `[${timestamp}] [${type}] ${message}\n`); } // 创建HTTP请求函数 async function makeRequest(url, options = {}, body = null) { return new Promise((resolve, reject) => { const urlObj = new URL(url); const requestOptions = { hostname: urlObj.hostname, path: urlObj.pathname + urlObj.search, method: options.method || 'GET', headers: { 'Accept': 'application/json', ...(options.headers || {}) } }; log(`发送 ${requestOptions.method} 请求到 ${url}`, 'DEBUG'); const req = https.request(requestOptions, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { log(`响应状态: ${res.statusCode}`, 'DEBUG'); if (res.statusCode >= 200 && res.statusCode < 300) { try { const parsed = JSON.parse(data); resolve(parsed); } catch (e) { log(`解析JSON响应失败: ${e.message}`, 'ERROR'); resolve(data); } } else { log(`请求失败,状态码 ${res.statusCode}: ${data}`, 'ERROR'); reject(new Error(`请求失败,状态码 ${res.statusCode}: ${data}`)); } }); }); req.on('error', (error) => { log(`请求错误: ${error.message}`, 'ERROR'); reject(error); }); if (body) { log(`请求体: ${typeof body === 'string' ? body : JSON.stringify(body)}`, 'DEBUG'); req.write(typeof body === 'string' ? body : JSON.stringify(body)); } req.end(); }); } // 获取用户组织 async function getUserGroups() { try { const token = process.env.CNB_ACCESS_TOKEN; if (!token) { throw new Error('CNB_ACCESS_TOKEN 环境变量未设置'); } const response = await makeRequest('https://api.cnb.cool/user/groups', { headers: { 'Authorization': token } }); log(`获取到用户组织: ${JSON.stringify(response)}`, 'INFO'); return response; } catch (error) { log(`获取用户组织失败: ${error.message}`, 'ERROR'); throw error; } } // 获取用户仓库列表 async function getUserRepositories() { try { const token = process.env.CNB_ACCESS_TOKEN; if (!token) { throw new Error('CNB_ACCESS_TOKEN 环境变量未设置'); } const response = await makeRequest('https://api.cnb.cool/user/repos?page=1&page_size=10&desc=false', { headers: { 'Authorization': token } }); log(`获取到用户仓库列表: ${JSON.stringify(response)}`, 'INFO'); return response; } catch (error) { log(`获取用户仓库列表失败: ${error.message}`, 'ERROR'); throw error; } } // 检查仓库是否存在 async function checkRepositoryExists(name, organization = 'FFA') { try { const repos = await getUserRepositories(); const exists = repos.some(repo => repo.name === name); log(`仓库 ${name} ${exists ? '已存在' : '不存在'}`, 'INFO'); return exists; } catch (error) { log(`检查仓库存在性失败: ${error.message}`, 'ERROR'); return false; // 出错时默认不存在 } } // 获取仓库信息 async function getRepositoryInfo(owner, repo) { try { const token = process.env.CNB_ACCESS_TOKEN; if (!token) { throw new Error('CNB_ACCESS_TOKEN 环境变量未设置'); } const response = await makeRequest(`https://api.cnb.cool/${owner}/${repo}`, { headers: { 'Authorization': token } }); log(`获取到仓库信息: ${JSON.stringify(response)}`, 'INFO'); return response; } catch (error) { log(`获取仓库信息失败: ${error.message}`, 'ERROR'); throw error; } } // 创建仓库 async function createRepository(name, group = 'FFA') { try { // 先检查仓库是否存在 const exists = await checkRepositoryExists(name); if (exists) { log(`仓库 ${name} 已存在,无需创建`, 'INFO'); // 直接获取仓库信息 return await getRepositoryInfo(group, name); } const token = process.env.CNB_ACCESS_TOKEN; if (!token) { throw new Error('CNB_ACCESS_TOKEN 环境变量未设置'); } const body = { name, description: 'CNB MCP服务器项目 - 面向Model Context Protocol的服务端实现', visibility: 'public' }; log(`正在创建仓库 ${name}`, 'INFO'); const response = await makeRequest(`https://api.cnb.cool/${group}/-/repos`, { method: 'POST', headers: { 'Authorization': token, 'Content-Type': 'application/json' } }, body); if (response && Object.keys(response).length > 0) { log(`仓库创建成功: ${JSON.stringify(response)}`, 'INFO'); return response; } else { log(`仓库创建成功,但返回为空。正在获取仓库信息...`, 'INFO'); // 如果创建成功但返回为空,获取仓库信息 return await getRepositoryInfo(group, name); } } catch (error) { // 如果错误信息包含409冲突,尝试获取仓库信息 if (error.message && error.message.includes('409')) { log(`仓库可能已存在,正在获取信息: ${error.message}`, 'WARN'); return await getRepositoryInfo(group, name); } log(`创建仓库失败: ${error.message}`, 'ERROR'); throw error; } } // 推送文件 async function pushFiles(owner, repo, branch, message, files) { try { const token = process.env.CNB_ACCESS_TOKEN; if (!token) { throw new Error('CNB_ACCESS_TOKEN 环境变量未设置'); } log(`正在向 ${owner}/${repo} 推送 ${files.length} 个文件`, 'INFO'); const body = { branch, message, files }; const response = await makeRequest(`https://api.cnb.cool/${owner}/${repo}/push`, { method: 'POST', headers: { 'Authorization': token, 'Content-Type': 'application/json' } }, body); log(`推送文件成功: ${JSON.stringify(response)}`, 'INFO'); return response; } catch (error) { log(`推送文件失败: ${error.message}`, 'ERROR'); throw error; } } // 搜索公共仓库 async function searchPublicRepositories(query) { try { const token = process.env.CNB_ACCESS_TOKEN; // 构造请求URL const url = `https://cnb.cool/search/public-repos?key=${encodeURIComponent(query)}&page=1&page_size=10&order_by=stars&desc=true`; const headers = { 'Accept': 'application/vnd.cnb.web+json', 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', 'Host': 'cnb.cool', 'Connection': 'keep-alive' }; log(`搜索公共仓库: ${query}`, 'INFO'); const response = await makeRequest(url, { headers }); log(`搜索结果: 找到 ${response.items?.length || 0} 个仓库`, 'INFO'); return response; } catch (error) { log(`搜索公共仓库失败: ${error.message}`, 'ERROR'); throw error; } } // Fork仓库 async function forkRepository(owner, repo, targetGroup, options = {}) { try { const token = process.env.CNB_ACCESS_TOKEN; if (!token) { throw new Error('CNB_ACCESS_TOKEN 环境变量未设置'); } const body = { group: targetGroup }; // 添加可选参数 if (options.branch) body.branch = options.branch; if (options.name) body.name = options.name; if (options.description) body.description = options.description; log(`Fork仓库 ${owner}/${repo}${targetGroup}`, 'INFO'); const response = await makeRequest(`https://api.cnb.cool/${owner}/${repo}/-/forks`, { method: 'POST', headers: { 'Authorization': token, 'Content-Type': 'application/json' } }, body); log(`Fork仓库成功: ${JSON.stringify(response)}`, 'INFO'); return response; } catch (error) { log(`Fork仓库失败: ${error.message}`, 'ERROR'); throw error; } } // 主函数 async function main() { log('开始CNB仓库工具测试', 'INFO'); try { // 测试1: 获取用户组织 log('测试1: 获取用户组织', 'INFO'); const groups = await getUserGroups(); log(`用户组织: ${JSON.stringify(groups)}`, 'INFO'); const organization = groups && groups.length > 0 ? groups[0].path : 'FFA'; log(`使用组织: ${organization}`, 'INFO'); // 测试2: 获取用户仓库列表 log('测试2: 获取用户仓库列表', 'INFO'); const repos = await getUserRepositories(); log(`找到 ${repos.length} 个仓库`, 'INFO'); // 测试3: 搜索公共仓库 log('测试3: 搜索公共仓库', 'INFO'); try { const searchResults = await searchPublicRepositories('mcp'); log(`搜索到 ${searchResults.items?.length || 0} 个公共仓库`, 'INFO'); } catch (error) { log(`搜索仓库出错: ${error.message}`, 'WARN'); } // 测试4: 创建或获取仓库 const repoName = 'cnb-mcp-server'; log(`测试4: 创建或获取仓库 ${repoName}`, 'INFO'); try { const repo = await createRepository(repoName, organization); log(`仓库信息: ${JSON.stringify(repo)}`, 'INFO'); // 如果找到公共仓库,尝试Fork if (repos.length > 0) { // 测试5: Fork仓库 const sourceRepo = repos[0]; log(`测试5: 尝试Fork仓库 ${sourceRepo.path}`, 'INFO'); try { const forkResult = await forkRepository( sourceRepo.path.split('/')[0], sourceRepo.name, organization, { description: '通过API Fork的仓库' } ); log(`Fork结果: ${JSON.stringify(forkResult)}`, 'INFO'); } catch (error) { log(`Fork仓库出错: ${error.message}`, 'WARN'); } } } catch (error) { log(`创建仓库出错: ${error.message}`, 'WARN'); } // 测试6: 推送文件 log('测试6: 推送文件', 'INFO'); const files = [ { path: 'README.md', content: '# CNB MCP 服务器\n\n这是一个基于TypeScript的服务器项目,用于实现Model Context Protocol(MCP)的服务端,提供对CNB API的访问。\n\n## 功能特性\n\n- 提供统一的API接入方式\n- 支持用户认证和权限管理\n- 实现CNB仓库管理的主要功能\n- 提供日志记录和错误处理\n\n## 安装使用\n\n```bash\nnpm install\nnpm run build\nnpm start\n```\n\n## 配置说明\n\n在启动服务前,需要设置环境变量或创建.env文件:\n\n```\nPORT=3000\nCNB_API_URL=https://api.cnb.cool\nCNB_ACCESS_TOKEN=your_access_token\n```\n\n## API文档\n\n详细的API文档请参考 `/docs` 目录。\n\n## 许可证\n\nMIT\n', encoding: 'utf-8' }, { path: '.env.example', content: 'PORT=3000\nCNB_API_URL=https://api.cnb.cool\nCNB_ACCESS_TOKEN=your_access_token\n', encoding: 'utf-8' } ]; try { const pushResult = await pushFiles(organization, repoName, 'main', '初始提交:添加README和配置文件示例', files); log(`推送文件结果: ${JSON.stringify(pushResult)}`, 'INFO'); } catch (error) { log(`推送文件出错: ${error.message}`, 'ERROR'); } log('CNB仓库工具测试完成', 'INFO'); } catch (error) { log(`测试过程中出错: ${error.message}`, 'ERROR'); } } // 运行主函数 main().catch(error => { log(`程序执行出错: ${error.message}`, 'ERROR'); process.exit(1); });