aliyun-bailian-mcp-server
Version:
191 lines (167 loc) • 5.39 kB
JavaScript
/**
* 阿里云函数计算入口点
* 将Express应用包装为函数计算处理程序
*/
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const MCPServer = require('./lib/mcp-server');
// 创建Express应用
const app = express();
app.use(cors());
app.use(bodyParser.json());
// 添加详细日志中间件
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
console.log('请求头:', JSON.stringify(req.headers));
if (req.body && Object.keys(req.body).length > 0) {
console.log('请求体:', JSON.stringify(req.body));
}
// 增强response对象,记录响应数据
const originalSend = res.send;
res.send = function(body) {
console.log(`[${new Date().toISOString()}] 响应状态: ${res.statusCode}`);
// 避免记录过大的响应体
const bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
console.log('响应体:', bodyStr.length > 1000 ? bodyStr.substring(0, 1000) + '...(已截断)' : bodyStr);
return originalSend.call(this, body);
};
next();
});
// 获取环境变量
const API_KEY = process.env.API_KEY;
const APP_ID = process.env.APP_ID;
const BASE_URL = process.env.BAILIAN_API_BASE_URL || 'https://dashscope.aliyuncs.com/compatible-mode/v1';
// 检查必要的环境变量
if (!API_KEY) {
console.error('错误: 未设置API_KEY环境变量');
}
// 创建MCP服务器
let mcpServer = null;
// 确保只创建一次MCP服务器实例(因为函数计算会重用实例)
function getMCPServer() {
if (!mcpServer) {
console.log(`初始化MCP服务器,API_KEY=${API_KEY ? '已设置' : '未设置'}, APP_ID=${APP_ID || '未设置'}, BASE_URL=${BASE_URL}`);
mcpServer = new MCPServer(API_KEY, APP_ID, BASE_URL);
// 创建定时器,每30分钟清理一次过期会话
setInterval(() => {
mcpServer.cleanSessions();
}, 1000 * 60 * 30);
}
return mcpServer;
}
// 健康检查接口
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() });
});
// 添加根路径接口,返回基本信息
app.get('/', (req, res) => {
res.status(200).json({
service: '阿里云百炼MCP服务',
status: 'running',
timestamp: new Date().toISOString(),
endpoints: ['/health', '/tool/list', '/tool/call']
});
});
// MCP Tool列表接口
app.get('/tool/list', (req, res) => {
try {
const mcpServer = getMCPServer();
const tools = mcpServer.listTools();
res.status(200).json({
jsonrpc: '2.0',
result: tools,
id: req.query.id || null
});
} catch (error) {
console.error('获取工具列表失败:', error);
res.status(500).json({
jsonrpc: '2.0',
error: {
code: -32603,
message: '服务器内部错误',
data: error.message
},
id: req.query.id || null
});
}
});
// MCP Tool调用接口
app.post('/tool/call', async (req, res) => {
const { id, method, params } = req.body;
// 检查请求格式
if (!method || !params) {
return res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32600,
message: '无效的请求',
data: 'method和params是必需的'
},
id
});
}
try {
const mcpServer = getMCPServer();
// 从方法名中提取工具名称
// 格式: <tool_name>.call
const toolName = method.split('.')[0];
// 调用工具
const result = await mcpServer.callTool(toolName, params);
// 返回结果
res.status(200).json({
jsonrpc: '2.0',
result: result.result,
id
});
} catch (error) {
console.error('工具调用失败:', error);
res.status(500).json({
jsonrpc: '2.0',
error: {
code: -32603,
message: error.message || '工具调用失败',
data: error.response?.data || null
},
id
});
}
});
// 函数计算处理程序
exports.handler = (req, resp, context) => {
console.log('函数计算触发, event:', JSON.stringify({
path: req.path,
method: req.method,
headers: req.headers,
queries: req.queries
}));
// 解决函数计算中可能的路径映射问题
if (req.path) {
// 检查是否有需要处理的特殊路径
const path = req.path;
// 针对FC环境的路径调整,确保能够匹配到Express路由
if (path === '/' || path === '') {
console.log('处理根路径请求');
// 根路径请求保持不变
} else if (path.endsWith('/tool/list')) {
// 处理/tool/list路径 - 可能存在路径前缀
console.log('重新映射路径到 /tool/list');
req.path = '/tool/list';
req.url = '/tool/list';
} else if (path.endsWith('/tool/call')) {
// 处理/tool/call路径 - 可能存在路径前缀
console.log('重新映射路径到 /tool/call');
req.path = '/tool/call';
req.url = '/tool/call';
} else if (path.endsWith('/health')) {
// 处理/health路径
console.log('重新映射路径到 /health');
req.path = '/health';
req.url = '/health';
} else {
console.log(`未知路径请求: ${path}, 尝试默认处理`);
}
}
// 确保将函数计算的路径正确映射到Express应用
app(req, resp);
};