shenju-enterprise-search-mcp
Version:
215 lines (187 loc) • 6.36 kB
JavaScript
/**
* 云函数入口点
* 将Express应用包装为函数计算处理程序
*/
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const MCPServer = require('./lib/mcp-server');
// 检测云函数环境
const isCloudFunction = process.env.FC_FUNCTION_NAME ||
process.env.SUPERGATEWAY ||
process.env.FC_RUNTIME_VERSION;
console.log(`环境检测: isCloudFunction=${!!isCloudFunction}`);
if (isCloudFunction) {
console.log('检测到云函数环境,不会监听任何端口');
}
// 创建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}`);
if (req.body && Object.keys(req.body).length > 0) {
console.log('请求体:', JSON.stringify(req.body).substring(0, 200) + '...');
}
// 增强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 > 200 ? bodyStr.substring(0, 200) + '...(已截断)' : 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.SEARCH_API_BASE_URL || 'https://api.enterprise-search.example.com/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 || '未设置'}`);
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服务',
version: "1.0.1",
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,
queries: req.queries || {}
}).substring(0, 200));
// 解决云函数中可能的路径映射问题
if (req.path) {
// 检查是否有需要处理的特殊路径
const path = req.path;
// 针对云函数环境的路径调整,确保能够匹配到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);
};
// 如果不是在云函数环境中,并且是直接执行此文件,则启动本地服务器
if (!isCloudFunction && require.main === module) {
const PORT = process.env.PORT || 8080;
const HOST = process.env.HOST || '0.0.0.0';
app.listen(PORT, HOST, () => {
console.log(`🚀 绅聚企业资料检索MCP服务器运行在 http://${HOST}:${PORT}`);
console.log('可用端点:');
console.log(`- 主页信息: http://${HOST}:${PORT}/`);
console.log(`- 健康检查: http://${HOST}:${PORT}/health`);
console.log(`- 工具列表: http://${HOST}:${PORT}/tool/list`);
console.log(`- 工具调用: http://${HOST}:${PORT}/tool/call`);
});
}